diff --git a/.gitignore b/.gitignore
index 857afdf..2dc13af9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -100,7 +100,6 @@
 /chrome/browser/media/engagement_internal
 /chrome/browser/performance_monitor/performance_monitor.xml
 /chrome/browser/protector/internal
-/chrome/browser/resources/chromeos/kiosk_next_home/internal
 /chrome/browser/resources/chromeos/quickoffice
 /chrome/browser/resources/pdf/html_office
 /chrome/browser/resources/media_router/extension/src/
diff --git a/BUILD.gn b/BUILD.gn
index 653e1d24..a5227be 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -158,6 +158,7 @@
       "//cc:cc_unittests",
       "//components/policy:policy_templates",
       "//components/ui_devtools/viz",
+      "//components/url_formatter/tools:format_url",
       "//components/viz:viz_perftests",
       "//components/viz:viz_unittests",
       "//components/viz/common:viz_benchmark",
@@ -230,6 +231,7 @@
     deps += [
       "//extensions:extensions_browsertests",
       "//extensions:extensions_unittests",
+      "//extensions/browser/api/declarative_net_request/filter_list_converter",
       "//extensions/shell:app_shell_unittests",
     ]
   }
diff --git a/DEPS b/DEPS
index 3e20236..30557a2 100644
--- a/DEPS
+++ b/DEPS
@@ -142,11 +142,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': '4e2033e84374e3487fa05dd64fd4da143d4498ea',
+  'skia_revision': 'afd2c10c989ce85cb9e7f8e05cb0df0ae50a1cfd',
   # 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': '948f4cb240c3ec4c09a798cc4d6e72bd6548221b',
+  'v8_revision': '5d13106e0fe0ddc34453a98ba01156e7f184f056',
   # 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.
@@ -154,11 +154,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '6d58266ed63a7aa2dbee3921320eb7fdf3125230',
+  'angle_revision': '625f5b2fbd89eb04b2e24853502acadbd40f1f73',
   # 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': '9770a4660fc9b592c272be33aa3d288673f3ef1b',
+  'swiftshader_revision': '74561df3e679cc788330c0b74309ddc0cb0b079e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -169,7 +169,7 @@
   #
   # Note this revision should be updated with
   # third_party/boringssl/roll_boringssl.py, not roll-dep.
-  'boringssl_revision': '92b7c89e6e8ba82924b57153bea68241cc45f658',
+  'boringssl_revision': 'cfcb0060e8b8fba92d275fa4ac27d369890ea9bf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -193,11 +193,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '81445c034aca36040b6311dc71a2cbed9548b262',
+  'freetype_revision': '7b1c7585d7ab929d9b29932d6697a22149162c13',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
-  'harfbuzz_revision': '659eeddb2df5b97cc01bd39e106381f65c9f41f1',
+  'harfbuzz_revision': '7185bd6ffb4dd8c0efebdab5b930e62c5695e3ab',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Emoji Segmenter
   # and whatever else without interference from each other.
@@ -205,7 +205,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': 'd556f63347d52f8666315d7fdf5931de751d55f3',
+  'catapult_revision': '6d47875c4c50c731aaa12bdbf41f31be65cea2d4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -261,7 +261,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': 'dfcb5a1e1042f2debae1ca11405d5d2e508e50c5',
+  'spv_tools_revision': '6ccb52b86492f8e27383abb821c8a808625c78ed',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -273,15 +273,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'shaderc_revision': '11cf0a3029edc26c133a15a2506b0c6b60e18c25',
+  'shaderc_revision': '48e07b5f07a4ef1664c8dba15c02de6658ced27e',
   # 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': '751252e3724b07062858af91e72dadb6379244ff',
+  'dawn_revision': 'eee3e41c554257a0ece66e70b2b4e1390ffa6ab0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '8f3a576515ada564d3e2b560220649f49a21dec1',
+  'quiche_revision': '790af403cbd5b4fa7f6b418441a6faed69d8c151',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -440,7 +440,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/ios/third_party/earl_grey/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'e272bbd853eefcef4b5aeff5009d87bba5adea1c',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '406ab37f2f0ec06a9c6c40218b19cba6c77ce548',
       'condition': 'checkout_ios',
   },
 
@@ -521,7 +521,7 @@
   },
 
   'src/ios/third_party/motion_transitioning_objc/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-motion/motion-transitioning-objc.git' + '@' + '8f360fc6f016af373276f858796a5e9f73498af9',
+      'url': Var('chromium_git') + '/external/github.com/material-motion/motion-transitioning-objc.git' + '@' + '5bb0d577dd78472536480496ace115bc593ed0e1',
       'condition': 'checkout_ios',
   },
 
@@ -637,7 +637,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_build_tools/aapt2',
-              'version': 'j6U3mv7-KG3PSDtVvTwycWzjwvFR1_sSdA540AYxpucC',
+              'version': 'version:3.6.0-alpha03-5516695-cr0',
           },
       ],
       'condition': 'checkout_android',
@@ -811,7 +811,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'effb0bcb66db74f394cb2f91607623cedb6f6e77',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '363f5f2c6246a8b7b0fb1c6d977bcf79690b305d',
       'condition': 'checkout_linux',
   },
 
@@ -826,7 +826,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '33a144a0bdb93f1440c7298b4a0fd7755dccc7b4',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'c2d20d35a549bf89f21363d41dcffac637ec8292',
       'condition': 'checkout_linux',
   },
 
@@ -836,7 +836,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd9ae33420aaf0679a2435e580cedff314929fa65',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '788d9e0de8132ff47891a9c141436ac6739e2e9f',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1196,7 +1196,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'a73180b38ccd827c09678392450b41a1ec1a508a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '5bc17b78d4fbed3975e9e7118f2bb579067d9f6f',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1364,7 +1364,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'f97f342fd352633ef1b2ced5cf1b340f994be515',
+    Var('webrtc_git') + '/src.git' + '@' + 'a47ba4119f27fa84831f16b2f7764a5439145a0d',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1405,7 +1405,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@194ce3f152a1e7a8c08ef9c7262986bdc3366bf7',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@923582ce437aa6d531f13197e3bcf9e07868a7f3',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index dcc916f..625a49f 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -801,6 +801,9 @@
     'content_bluetooth': {
       'filepath': 'content/.*bluetooth'
     },
+    'content_index': {
+      'filepath': 'content_index',
+    },
     'content_loader': {
       'filepath': 'chrome/browser/loader|' \
                   'content/browser/loader|' \
@@ -1094,14 +1097,6 @@
     'ios_web': {
       'filepath': 'ios/web(_view)?/',
     },
-    'kiosk_next': {
-      'filepath': 'ash/kiosk_next/|'\
-                  'ash/public/interfaces/kiosk_next_shell.mojom|'\
-                  'chrome/browser/chromeos/kiosk_next_home/|'\
-                  'chrome/browser/resources/chromeos/kiosk_next_home/|'\
-                  'chrome/browser/resources/settings/kiosk_next_shell_page/|'\
-                  'chrome/browser/ui/ash/kiosk_next_',
-    },
     'libaom': {
       'filepath': 'third_party/libaom/',
     },
@@ -2251,6 +2246,7 @@
     'compositor_animator': ['mdjones+watch@chromium.org'],
     'content_bluetooth': ['mattreynolds+watch@chromium.org',
                           'ortuno+watch@chromium.org'],
+    'content_index': ['rayankans+watch@chromium.org'],
     'content_loader': ['loading-reviews@chromium.org'],
     'content_renderer': ['mlamouri+watch-content@chromium.org'],
     'content_shell': ['jochen+watch@chromium.org',
@@ -2370,13 +2366,6 @@
                      'marq+watch@chromium.org'],
     'ios_web': ['ios-reviews+web@chromium.org',
                 'eugenebut@chromium.org'],
-    'kiosk_next': ['agawronska+watch@chromium.org',
-                   'brunoad+watch@chromium.org',
-                   'escordeiro+watch@chromium.org',
-                   'ltenorio+watch@chromium.org',
-                   'maroun+watch@chromium.org',
-                   'michaelpg+watch@chromium.org',
-                   'yilkal+watch@chromium.org'],
     'libaom': ['fgalligan@chromium.org',
                'johannkoenig@chromium.org',
                'jzern@chromium.org',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index b5b766b..fd1490e0 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -1075,6 +1075,7 @@
     ":android_webview_platform_services_java",
     ":android_webview_variations_utils_java",
     ":system_webview_manifest",
+    "//android_webview/apk:apk_java",
     "//base:base_java",
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/minidump_uploader:minidump_uploader_java",
diff --git a/android_webview/apk/java/AndroidManifest.xml b/android_webview/apk/java/AndroidManifest.xml
index e97fd2263..55366246 100644
--- a/android_webview/apk/java/AndroidManifest.xml
+++ b/android_webview/apk/java/AndroidManifest.xml
@@ -43,6 +43,9 @@
             {% if donor_package is not defined %}
                 <!-- If you change the variations services, also see
                      android_webview/test/shell/AndroidManifest.xml. -->
+                <!-- These have a separate android:process so that they can
+                     run in Monochrome with a different value for
+                     PathUtils.getDataDirectory() from Chrome. -->
                 <service android:name="org.chromium.android_webview.services.VariationsSeedServer"
                          android:exported="true"
                          android:process=":webview_service" />
diff --git a/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java b/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
index 540dd83b..596c292 100644
--- a/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
+++ b/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
@@ -30,7 +30,7 @@
     protected void attachBaseContext(Context context) {
         super.attachBaseContext(context);
         ContextUtils.initApplicationContext(this);
-        PathUtils.setPrivateDataDirectorySuffix("webview");
+        initPathUtils();
         initCommandLine();
     }
 
@@ -40,6 +40,11 @@
         FontPreloadingWorkaround.maybeInstallWorkaround(this);
     }
 
+    /** Ensures PathUtils is initialized. */
+    public static void initPathUtils() {
+        PathUtils.setPrivateDataDirectorySuffix("webview");
+    }
+
     // Overridden by webview shell to point to a different flags file.
     protected void initCommandLine() {
         CommandLineUtil.initCommandLine();
diff --git a/android_webview/browser/aw_download_manager_delegate.cc b/android_webview/browser/aw_download_manager_delegate.cc
index 0c3daa2c..a0ef3c4 100644
--- a/android_webview/browser/aw_download_manager_delegate.cc
+++ b/android_webview/browser/aw_download_manager_delegate.cc
@@ -71,6 +71,7 @@
     const std::string& mime_type,
     const std::string& request_origin,
     int64_t content_length,
+    bool is_transient,
     content::WebContents* web_contents) {
   if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
     return false;
diff --git a/android_webview/browser/aw_download_manager_delegate.h b/android_webview/browser/aw_download_manager_delegate.h
index a2280aa2e..250a4db 100644
--- a/android_webview/browser/aw_download_manager_delegate.h
+++ b/android_webview/browser/aw_download_manager_delegate.h
@@ -41,6 +41,7 @@
       const std::string& mime_type,
       const std::string& request_origin,
       int64_t content_length,
+      bool is_transient,
       content::WebContents* web_contents) override;
   void GetNextId(const content::DownloadIdCallback& callback) override;
 };
diff --git a/android_webview/browser/gfx/test/rendering_test.cc b/android_webview/browser/gfx/test/rendering_test.cc
index 591e529..1ae0a87 100644
--- a/android_webview/browser/gfx/test/rendering_test.cc
+++ b/android_webview/browser/gfx/test/rendering_test.cc
@@ -105,7 +105,7 @@
 }
 
 void RenderingTest::EndTest() {
-  ui_task_runner_->PostTask(FROM_HERE, run_loop_.QuitWhenIdleClosure());
+  run_loop_.QuitWhenIdle();
 }
 
 content::SynchronousCompositor* RenderingTest::ActiveCompositor() const {
diff --git a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
index 6b920ce..a6736e8 100644
--- a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
+++ b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
@@ -287,11 +287,6 @@
   std::unique_ptr<AwContentsIoThreadClient> io_thread_client =
       GetIoThreadClient();
 
-  if (!io_thread_client) {
-    SendErrorAndCompleteImmediately(net::ERR_ABORTED);
-    return;
-  }
-
   if (ShouldBlockURL(request_.url, io_thread_client.get())) {
     SendErrorAndCompleteImmediately(net::ERR_ACCESS_DENIED);
     return;
@@ -299,7 +294,7 @@
 
   request_.load_flags =
       UpdateLoadFlags(request_.load_flags, io_thread_client.get());
-  if (ShouldNotInterceptRequest()) {
+  if (!io_thread_client || ShouldNotInterceptRequest()) {
     // equivalent to no interception
     InterceptResponseReceived(nullptr);
   } else {
diff --git a/android_webview/browser/network_service/net_helpers.cc b/android_webview/browser/network_service/net_helpers.cc
index b165c89f..e55b407 100644
--- a/android_webview/browser/network_service/net_helpers.cc
+++ b/android_webview/browser/network_service/net_helpers.cc
@@ -27,6 +27,7 @@
 
 // Gets the net-layer load_flags which reflect |client|'s cache mode.
 int GetCacheModeForClient(AwContentsIoThreadClient* client) {
+  DCHECK(client);
   AwContentsIoThreadClient::CacheMode cache_mode = client->GetCacheMode();
   switch (cache_mode) {
     case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
@@ -50,6 +51,9 @@
 }  // namespace
 
 int UpdateLoadFlags(int load_flags, AwContentsIoThreadClient* client) {
+  if (!client)
+    return load_flags;
+
   if (client->ShouldBlockNetworkLoads()) {
     return UpdateCacheControlFlags(
         load_flags,
@@ -64,6 +68,9 @@
 }
 
 bool ShouldBlockURL(const GURL& url, AwContentsIoThreadClient* client) {
+  if (!client)
+    return false;
+
   // Part of implementation of WebSettings.allowContentAccess.
   if (url.SchemeIs(url::kContentScheme) && client->ShouldBlockContentUrls())
     return true;
diff --git a/android_webview/docs/prerelease.md b/android_webview/docs/prerelease.md
index e7fdd9a..0fbb91eb 100644
--- a/android_webview/docs/prerelease.md
+++ b/android_webview/docs/prerelease.md
@@ -32,6 +32,11 @@
   - Includes the latest code changes from the previous day
   - Has not been tested or used
 
+If you're looking for a specific of version of chromium, the latest versions
+released to each channel can be found on [Chromium
+Dash](https://chromiumdash.appspot.com/releases?platform=Android). WebView and
+Chrome for Android always release together on all OS levels.
+
 On Android 7 (Nougat) and later, you can install multiple channels at the same
 time. This allows you to play with our latest code, while still keeping a tested
 version of WebView around.
@@ -119,4 +124,4 @@
 - [WebView channels in detail](/android_webview/docs/channels.md)
 - [Chrome Release
   Channels](https://www.chromium.org/getting-involved/dev-channel)
-
+- [WebView Release History](https://chromiumdash.appspot.com/releases?platform=Android)
diff --git a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
index a417f21..904a2604 100644
--- a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
@@ -74,7 +74,9 @@
     private static final long MAX_REQUEST_PERIOD_MILLIS = TimeUnit.HOURS.toMillis(1);
 
     // Block in finishVariationsInit() for at most this value waiting for the seed. If the timeout
-    // is exceeded, proceed with variations disabled.
+    // is exceeded, proceed with variations disabled, and record the event in the
+    // Variations.SeedLoadResult histogram's "Seed Load Timed Out" bucket. See the discussion on
+    // https://crbug.com/936172 about the trade-offs of increasing or decreasing this value.
     private static final long SEED_LOAD_TIMEOUT_MILLIS = 20;
 
     private SeedLoadAndUpdateRunnable mRunnable;
diff --git a/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java b/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java
index 66450bf..de0e45b 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploadJobService.java
@@ -5,6 +5,8 @@
 
 import android.os.PersistableBundle;
 
+import com.android.webview.chromium.WebViewApkApplication;
+
 import org.chromium.components.minidump_uploader.MinidumpUploadJobService;
 import org.chromium.components.minidump_uploader.MinidumpUploader;
 import org.chromium.components.minidump_uploader.MinidumpUploaderImpl;
@@ -14,6 +16,11 @@
  */
 // OBS: This class needs to be public to be started from android.app.ActivityThread.
 public class AwMinidumpUploadJobService extends MinidumpUploadJobService {
+    public AwMinidumpUploadJobService() {
+        // Required when running in Monochrome.
+        WebViewApkApplication.initPathUtils();
+    }
+
     @Override
     protected MinidumpUploader createMinidumpUploader(PersistableBundle unusedExtras) {
         return new MinidumpUploaderImpl(new AwMinidumpUploaderDelegate());
diff --git a/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java b/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
index 38819cd..3be8b93 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
@@ -13,6 +13,8 @@
 import android.content.Context;
 import android.os.Build;
 
+import com.android.webview.chromium.WebViewApkApplication;
+
 import org.chromium.android_webview.VariationsUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -49,16 +51,20 @@
     private static JobScheduler sMockJobScheduler;
     private static VariationsSeedFetcher sMockDownloader;
 
-    private VariationsSeedHolder mSeedHolder;
     private FetchTask mFetchTask;
 
+    public AwVariationsSeedFetcher() {
+        // Required when running in Monochrome.
+        WebViewApkApplication.initPathUtils();
+    }
+
     private static String getChannelStr() {
         switch (VersionConstants.CHANNEL) {
             case Channel.STABLE: return "stable";
             case Channel.BETA:   return "beta";
             case Channel.DEV:    return "dev";
             case Channel.CANARY: return "canary";
-            default: return null; // This is the case for stand-alone WebView.
+            default: return null;
         }
     }
 
@@ -138,7 +144,8 @@
                 }
 
                 if (newSeed != null) {
-                    mSeedHolder.updateSeed(newSeed, /*onFinished=*/() -> jobFinished(mParams));
+                    VariationsSeedHolder.getInstance().updateSeed(
+                            newSeed, /*onFinished=*/() -> jobFinished(mParams));
                     shouldFinish = false; // jobFinished will be deferred until updateSeed is done.
                 }
             } catch (IOException e) {
@@ -154,12 +161,6 @@
     }
 
     @Override
-    public void onCreate() {
-        super.onCreate();
-        mSeedHolder = VariationsSeedHolder.getInstance();
-    }
-
-    @Override
     public boolean onStartJob(JobParameters params) {
         // If this process has survived since the last run of this job, mFetchTask could still
         // exist. Either way, (re)create it with the new params.
diff --git a/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java b/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
index 143a11f..0cc939d0 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
@@ -12,6 +12,8 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 
+import com.android.webview.chromium.WebViewApkApplication;
+
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
@@ -43,6 +45,11 @@
         }
     };
 
+    public CrashReceiverService() {
+        // Required when running in Monochrome.
+        WebViewApkApplication.initPathUtils();
+    }
+
     /**
      * Copies minidumps in a synchronized way, waiting for any already started copying operations to
      * finish before copying the current dumps.
diff --git a/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedHolder.java b/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedHolder.java
index 5d3da5e..24ea238 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedHolder.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedHolder.java
@@ -31,7 +31,7 @@
 public class VariationsSeedHolder {
     private static final String TAG = "VariationsSeedHolder";
 
-    private static VariationsSeedHolder sInstance;
+    private static final VariationsSeedHolder sInstance = new VariationsSeedHolder();
 
     private static void writeSeedWithoutClosing(SeedInfo seed, ParcelFileDescriptor destination) {
         // writeSeed() will close "out", but closing "out" will not close "destination".
@@ -142,9 +142,6 @@
     }
 
     /* package */ static VariationsSeedHolder getInstance() {
-        if (sInstance == null) {
-            sInstance = new VariationsSeedHolder();
-        }
         return sInstance;
     }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedServer.java b/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedServer.java
index e0f8877..569f77b 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedServer.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedServer.java
@@ -9,29 +9,28 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 
+import com.android.webview.chromium.WebViewApkApplication;
+
 /**
  * VariationsSeedServer is a bound service that shares the Variations seed with all the WebViews
  * on the system. A WebView will bind and call getSeed, passing a file descriptor to which the
  * service should write the seed.
  */
 public class VariationsSeedServer extends Service {
-    private VariationsSeedHolder mSeedHolder;
-
     private final IVariationsSeedServer.Stub mBinder = new IVariationsSeedServer.Stub() {
         @Override
         public void getSeed(ParcelFileDescriptor newSeedFile, long oldSeedDate) {
-            mSeedHolder.writeSeedIfNewer(newSeedFile, oldSeedDate);
+            VariationsSeedHolder.getInstance().writeSeedIfNewer(newSeedFile, oldSeedDate);
         }
     };
 
+    public VariationsSeedServer() {
+        // Required when running in Monochrome.
+        WebViewApkApplication.initPathUtils();
+    }
+
     @Override
     public IBinder onBind(Intent intent) {
         return mBinder;
     }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mSeedHolder = VariationsSeedHolder.getInstance();
-    }
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 5ba0f2af..e11d829 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -517,16 +517,16 @@
     "session/session_observer.h",
     "session/teleport_warning_dialog.cc",
     "session/teleport_warning_dialog.h",
-    "shelf/app_list_button.cc",
-    "shelf/app_list_button.h",
-    "shelf/app_list_button_controller.cc",
-    "shelf/app_list_button_controller.h",
     "shelf/assistant_overlay.cc",
     "shelf/assistant_overlay.h",
     "shelf/back_button.cc",
     "shelf/back_button.h",
     "shelf/default_shelf_view.cc",
     "shelf/default_shelf_view.h",
+    "shelf/home_button.cc",
+    "shelf/home_button.h",
+    "shelf/home_button_controller.cc",
+    "shelf/home_button_controller.h",
     "shelf/ink_drop_button_listener.h",
     "shelf/kiosk_next_shelf_view.cc",
     "shelf/kiosk_next_shelf_view.h",
@@ -561,6 +561,8 @@
     "shelf/shelf_control_button.h",
     "shelf/shelf_controller.cc",
     "shelf/shelf_controller.h",
+    "shelf/shelf_focus_cycler.cc",
+    "shelf/shelf_focus_cycler.h",
     "shelf/shelf_layout_manager.cc",
     "shelf/shelf_layout_manager.h",
     "shelf/shelf_layout_manager_observer.h",
@@ -965,6 +967,8 @@
     "system/unified/top_shortcut_button.h",
     "system/unified/top_shortcuts_view.cc",
     "system/unified/top_shortcuts_view.h",
+    "system/unified/unified_managed_device_view.cc",
+    "system/unified/unified_managed_device_view.h",
     "system/unified/unified_notifier_settings_controller.cc",
     "system/unified/unified_notifier_settings_controller.h",
     "system/unified/unified_slider_bubble_controller.cc",
@@ -1230,11 +1234,11 @@
     "wm/window_transient_descendant_iterator.cc",
     "wm/window_transient_descendant_iterator.h",
     "wm/window_util.cc",
+    "wm/wm_default_layout_manager.cc",
+    "wm/wm_default_layout_manager.h",
     "wm/wm_event.cc",
     "wm/wm_shadow_controller_delegate.cc",
     "wm/wm_shadow_controller_delegate.h",
-    "wm/wm_snap_to_pixel_layout_manager.cc",
-    "wm/wm_snap_to_pixel_layout_manager.h",
     "wm/wm_window_animations.cc",
     "wm/wm_window_animations.h",
     "wm/work_area_insets.cc",
@@ -1691,8 +1695,8 @@
     "rotator/screen_rotation_animator_unittest.cc",
     "screen_util_unittest.cc",
     "session/session_controller_impl_unittest.cc",
-    "shelf/app_list_button_unittest.cc",
     "shelf/back_button_unittest.cc",
+    "shelf/home_button_unittest.cc",
     "shelf/login_shelf_view_unittest.cc",
     "shelf/shelf_application_menu_model_unittest.cc",
     "shelf/shelf_background_animator_unittest.cc",
@@ -1778,6 +1782,7 @@
     "system/unified/page_indicator_view_unittest.cc",
     "system/unified/quiet_mode_feature_pod_controller_unittest.cc",
     "system/unified/top_shortcuts_view_unittest.cc",
+    "system/unified/unified_managed_device_view_unittest.cc",
     "system/unified/unified_system_info_view_unittest.cc",
     "system/unified/unified_system_tray_controller_unittest.cc",
     "system/unified/unified_system_tray_unittest.cc",
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index 8781c8a1..64182cf 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -43,7 +43,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/rotator/window_rotation.h"
 #include "ash/session/session_controller_impl.h"
-#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
@@ -61,6 +61,7 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/touch/touch_hud_debug.h"
 #include "ash/utility/screenshot_controller.h"
+#include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/screen_pinning_controller.h"
@@ -122,6 +123,8 @@
 const char kFeatureDisabledByPolicyToastId[] = "disabled_by_policy_error";
 constexpr int kToastDurationMs = 2500;
 
+constexpr char kVirtualDesksToastId[] = "virtual_desks_toast";
+
 // Path of the json file that contains side volume button location info.
 constexpr char kSideVolumeButtonLocationFilePath[] =
     "/usr/share/chromeos-assets/side_volume_button/location.json";
@@ -247,6 +250,125 @@
       WindowCycleController::FORWARD);
 }
 
+void HandleActivateDesk(const ui::Accelerator& accelerator) {
+  DCHECK(features::IsVirtualDesksEnabled());
+  auto* desks_controller = DesksController::Get();
+
+  // An on-going desk switch animation might be in progress. For now skip this
+  // accelerator. Later we might want to consider queueing these animations, or
+  // cancelling the on-going ones and start over.
+  // TODO(afakhry): Discuss with UX.
+  if (desks_controller->AreDesksBeingModified())
+    return;
+
+  const Desk* desk_to_activate = nullptr;
+  switch (accelerator.key_code()) {
+    case ui::VKEY_OEM_4:
+      desk_to_activate = desks_controller->GetPreviousDesk();
+      base::RecordAction(base::UserMetricsAction("Accel_Desks_ActivateLeft"));
+      break;
+    case ui::VKEY_OEM_6:
+      desk_to_activate = desks_controller->GetNextDesk();
+      base::RecordAction(base::UserMetricsAction("Accel_Desks_ActivateRight"));
+      break;
+
+    default:
+      NOTREACHED();
+  }
+
+  // TODO(afakhry): Finalize the hit-the-wall animation with UX.
+  // https://crbug.com/977434.
+  if (desk_to_activate)
+    desks_controller->ActivateDesk(desk_to_activate);
+}
+
+void HandleMoveActiveItem(const ui::Accelerator& accelerator) {
+  DCHECK(features::IsVirtualDesksEnabled());
+  auto* desks_controller = DesksController::Get();
+  if (desks_controller->AreDesksBeingModified())
+    return;
+
+  aura::Window* window_to_move = nullptr;
+  auto* overview_controller = Shell::Get()->overview_controller();
+  const bool in_overview = overview_controller->InOverviewSession();
+  if (in_overview) {
+    window_to_move =
+        overview_controller->overview_session()->GetHighlightedWindow();
+  } else {
+    window_to_move = wm::GetActiveWindow();
+  }
+
+  if (!window_to_move)
+    return;
+
+  Desk* target_desk = nullptr;
+  switch (accelerator.key_code()) {
+    case ui::VKEY_OEM_4:
+      target_desk = desks_controller->GetPreviousDesk();
+      base::RecordAction(base::UserMetricsAction("Accel_Desks_MoveWindowLeft"));
+      break;
+    case ui::VKEY_OEM_6:
+      target_desk = desks_controller->GetNextDesk();
+      base::RecordAction(
+          base::UserMetricsAction("Accel_Desks_MoveWindowRight"));
+      break;
+
+    default:
+      NOTREACHED();
+  }
+
+  if (!target_desk)
+    return;
+
+  // TODO(afakhry): Finalize window movement animation to another desk outside
+  // of overview with UX. https://crbug.com/977434.
+  desks_controller->MoveWindowFromActiveDeskTo(window_to_move, target_desk);
+  if (in_overview) {
+    // We should not exit overview as a result of this shortcut.
+    DCHECK(overview_controller->InOverviewSession());
+    overview_controller->overview_session()->PositionWindows(/*animate=*/true);
+  }
+}
+
+void HandleNewDesk() {
+  DCHECK(features::IsVirtualDesksEnabled());
+  auto* desks_controller = DesksController::Get();
+  if (!desks_controller->CanCreateDesks()) {
+    ShowToast(kVirtualDesksToastId,
+              l10n_util::GetStringUTF16(IDS_ASH_DESKS_MAX_NUM_REACHED));
+    return;
+  }
+
+  if (desks_controller->AreDesksBeingModified())
+    return;
+
+  // Add a new desk and switch to it.
+  const size_t new_desk_index = desks_controller->desks().size();
+  desks_controller->NewDesk();
+  const Desk* desk = desks_controller->desks()[new_desk_index].get();
+  desks_controller->ActivateDesk(desk);
+  base::RecordAction(base::UserMetricsAction("Accel_Desks_NewDesk"));
+}
+
+void HandleRemoveCurrentDesk() {
+  DCHECK(features::IsVirtualDesksEnabled());
+
+  auto* desks_controller = DesksController::Get();
+  if (!desks_controller->CanRemoveDesks()) {
+    ShowToast(kVirtualDesksToastId,
+              l10n_util::GetStringUTF16(IDS_ASH_DESKS_MIN_NUM_REACHED));
+    return;
+  }
+
+  if (desks_controller->AreDesksBeingModified())
+    return;
+
+  // TODO(afakhry): Finalize the desk removal animation outside of overview with
+  // UX. https://crbug.com/977434.
+  desks_controller->RemoveDesk(desks_controller->active_desk());
+  base::RecordAction(base::UserMetricsAction("Accel_Desks_RemoveDesk"));
+}
+
 void HandleRotatePaneFocus(FocusCycler::Direction direction) {
   switch (direction) {
     // TODO(stevet): Not sure if this is the same as IDC_FOCUS_NEXT_PANE.
@@ -549,7 +671,7 @@
 
   Shelf::ForWindow(Shell::GetRootWindowForNewWindows())
       ->shelf_widget()
-      ->GetAppListButton()
+      ->GetHomeButton()
       ->OnPressed(show_source, accelerator.time_stamp());
 }
 
@@ -1406,6 +1528,11 @@
     case CYCLE_BACKWARD_MRU:
     case CYCLE_FORWARD_MRU:
       return CanHandleCycleMru(accelerator);
+    case DESKS_ACTIVATE_DESK:
+    case DESKS_MOVE_ACTIVE_ITEM:
+    case DESKS_NEW_DESK:
+    case DESKS_REMOVE_CURRENT_DESK:
+      return features::IsVirtualDesksEnabled();
     case DEBUG_PRINT_LAYER_HIERARCHY:
     case DEBUG_PRINT_VIEW_HIERARCHY:
     case DEBUG_PRINT_WINDOW_HIERARCHY:
@@ -1581,6 +1708,18 @@
     case CYCLE_FORWARD_MRU:
       HandleCycleForwardMRU(accelerator);
       break;
+    case DESKS_ACTIVATE_DESK:
+      HandleActivateDesk(accelerator);
+      break;
+    case DESKS_MOVE_ACTIVE_ITEM:
+      HandleMoveActiveItem(accelerator);
+      break;
+    case DESKS_NEW_DESK:
+      HandleNewDesk();
+      break;
+    case DESKS_REMOVE_CURRENT_DESK:
+      HandleRemoveCurrentDesk();
+      break;
     case DEBUG_PRINT_LAYER_HIERARCHY:
     case DEBUG_PRINT_VIEW_HIERARCHY:
     case DEBUG_PRINT_WINDOW_HIERARCHY:
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index 558f95c..d47adc8 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -327,6 +327,8 @@
     base::size(kActionsAllowedInPinnedMode);
 
 const AcceleratorAction kActionsNeedingWindow[] = {
+    // clang-format off
+    DESKS_MOVE_ACTIVE_ITEM,
     MOVE_ACTIVE_WINDOW_BETWEEN_DISPLAYS,
     ROTATE_WINDOW,
     TOGGLE_FULLSCREEN,
@@ -334,6 +336,7 @@
     WINDOW_CYCLE_SNAP_LEFT,
     WINDOW_CYCLE_SNAP_RIGHT,
     WINDOW_MINIMIZE,
+    // clang-format on
 };
 
 const size_t kActionsNeedingWindowLength = base::size(kActionsNeedingWindow);
diff --git a/ash/accelerators/accelerator_table_unittest.cc b/ash/accelerators/accelerator_table_unittest.cc
index 7d0cfe5d..6f60deaf 100644
--- a/ash/accelerators/accelerator_table_unittest.cc
+++ b/ash/accelerators/accelerator_table_unittest.cc
@@ -17,10 +17,10 @@
 namespace {
 
 // The number of non-Search-based accelerators.
-constexpr int kNonSearchAcceleratorsNum = 91;
+constexpr int kNonSearchAcceleratorsNum = 93;
 // The hash of non-Search-based accelerators. See HashAcceleratorData().
 constexpr char kNonSearchAcceleratorsHash[] =
-    "398661b02a3d8c541c300981cc4d4a55";
+    "11460792b090e968a6e94e7832fbda1c";
 
 struct Cmp {
   bool operator()(const AcceleratorData& lhs,
diff --git a/ash/accelerators/debug_commands.cc b/ash/accelerators/debug_commands.cc
index dd5247d..d3fbb02 100644
--- a/ash/accelerators/debug_commands.cc
+++ b/ash/accelerators/debug_commands.cc
@@ -67,7 +67,7 @@
   if (name.empty())
     name = "\"\"";
   const gfx::Vector2dF& subpixel_position_offset =
-      window->layer()->subpixel_position_offset();
+      window->layer()->GetSubpixelOffset();
   *out << indent_str;
   *out << name << " (" << window << ")"
        << " type=" << window->type();
@@ -80,8 +80,6 @@
                ? aura::Window::OcclusionStateToString(window->occlusion_state())
                : "")
        << " " << window->bounds().ToString();
-  if (window->GetProperty(::wm::kSnapChildrenToPixelBoundary))
-    *out << " [snapped]";
   if (!subpixel_position_offset.IsZero())
     *out << " subpixel offset=" + subpixel_position_offset.ToString();
   std::string* tree_id = window->GetProperty(ui::kChildAXTreeID);
@@ -168,7 +166,7 @@
 
 void HandleToggleTabletMode() {
   TabletModeController* controller = Shell::Get()->tablet_mode_controller();
-  controller->SetEnabledForTest(!controller->InTabletMode());
+  controller->SetEnabledForDev(!controller->InTabletMode());
 }
 
 void HandleTriggerCrash() {
diff --git a/ash/accelerometer/accelerometer_reader.cc b/ash/accelerometer/accelerometer_reader.cc
index 14d2215..10a76de 100644
--- a/ash/accelerometer/accelerometer_reader.cc
+++ b/ash/accelerometer/accelerometer_reader.cc
@@ -724,23 +724,19 @@
 }
 
 void AccelerometerReader::AddObserver(Observer* observer) {
-  if (accelerometer_file_reader_)
-    accelerometer_file_reader_->AddObserver(observer);
+  accelerometer_file_reader_->AddObserver(observer);
 }
 
 void AccelerometerReader::RemoveObserver(Observer* observer) {
-  if (accelerometer_file_reader_)
-    accelerometer_file_reader_->RemoveObserver(observer);
+  accelerometer_file_reader_->RemoveObserver(observer);
 }
 
 void AccelerometerReader::StartListenToTabletModeController() {
-  if (accelerometer_file_reader_)
-    accelerometer_file_reader_->StartListenToTabletModeController();
+  accelerometer_file_reader_->StartListenToTabletModeController();
 }
 
 void AccelerometerReader::StopListenToTabletModeController() {
-  if (accelerometer_file_reader_)
-    accelerometer_file_reader_->StopListenToTabletModeController();
+  accelerometer_file_reader_->StopListenToTabletModeController();
 }
 
 AccelerometerReader::AccelerometerReader()
@@ -748,11 +744,4 @@
 
 AccelerometerReader::~AccelerometerReader() = default;
 
-void AccelerometerReader::DisableForTest() {
-  if (accelerometer_file_reader_) {
-    accelerometer_file_reader_->StopListenToTabletModeController();
-    accelerometer_file_reader_.reset();
-  }
-}
-
 }  // namespace ash
diff --git a/ash/accelerometer/accelerometer_reader.h b/ash/accelerometer/accelerometer_reader.h
index 44c3689..9f8e5ca 100644
--- a/ash/accelerometer/accelerometer_reader.h
+++ b/ash/accelerometer/accelerometer_reader.h
@@ -49,10 +49,6 @@
   void StartListenToTabletModeController();
   void StopListenToTabletModeController();
 
-  void DisableForTest();
-
-  bool is_disabled() const { return !accelerometer_file_reader_; }
-
  protected:
   AccelerometerReader();
   virtual ~AccelerometerReader();
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 2ba29ce..0b595d24 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -84,7 +84,7 @@
       return app_list::TabletModeAnimationTransition::
           kHideHomeLauncherForWindow;
     case HomeScreenDelegate::AnimationTrigger::kLauncherButton:
-      return app_list::TabletModeAnimationTransition::kAppListButtonShow;
+      return app_list::TabletModeAnimationTransition::kHomeButtonShow;
     case HomeScreenDelegate::AnimationTrigger::kDragRelease:
       return launcher_should_show
                  ? app_list::TabletModeAnimationTransition::kDragReleaseShow
@@ -809,7 +809,7 @@
     focused_view->SchedulePaint();
 }
 
-ash::ShelfAction AppListControllerImpl::OnAppListButtonPressed(
+ash::ShelfAction AppListControllerImpl::OnHomeButtonPressed(
     int64_t display_id,
     app_list::AppListShowSource show_source,
     base::TimeTicks event_time_stamp) {
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 1a41e1e..c1fcdfe 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -278,15 +278,14 @@
 
   void SetKeyboardTraversalMode(bool engaged);
 
-  // Handles app list button press event. (Search key should trigger the same
+  // Handles home button press event. (Search key should trigger the same
   // behavior.) All three parameters are only used in clamshell mode.
   // |display_id| is the id of display where app list should toggle.
   // |show_source| is the source of the event. |event_time_stamp| records the
   // event timestamp.
-  ash::ShelfAction OnAppListButtonPressed(
-      int64_t display_id,
-      app_list::AppListShowSource show_source,
-      base::TimeTicks event_time_stamp);
+  ash::ShelfAction OnHomeButtonPressed(int64_t display_id,
+                                       app_list::AppListShowSource show_source,
+                                       base::TimeTicks event_time_stamp);
 
   // Returns current visibility of the Assistant page.
   bool IsShowingEmbeddedAssistantUI() const;
diff --git a/ash/app_list/app_list_metrics.h b/ash/app_list/app_list_metrics.h
index caa3941..293a540 100644
--- a/ash/app_list/app_list_metrics.h
+++ b/ash/app_list/app_list_metrics.h
@@ -235,8 +235,8 @@
   // Release drag to hide the launcher (launcher animates the rest of the way).
   kDragReleaseHide,
 
-  // Click the AppList button in tablet mode.
-  kAppListButtonShow,
+  // Click the Home button in tablet mode.
+  kHomeButtonShow,
 
   // Activate a window from shelf to hide the launcher in tablet mode.
   kHideHomeLauncherForWindow,
diff --git a/ash/app_list/app_list_presenter_delegate_impl.cc b/ash/app_list/app_list_presenter_delegate_impl.cc
index 9bccc60..b406415 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.cc
+++ b/ash/app_list/app_list_presenter_delegate_impl.cc
@@ -18,8 +18,8 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
-#include "ash/shelf/app_list_button.h"
 #include "ash/shelf/back_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
@@ -178,13 +178,13 @@
       return;
   }
 
-  // If the event happened on the app list button, it'll get handled by the
+  // If the event happened on the home button, it'll get handled by the
   // button.
   Shelf* shelf = Shelf::ForWindow(target);
-  AppListButton* app_list_button = shelf->shelf_widget()->GetAppListButton();
-  if (app_list_button && app_list_button->GetWidget() &&
-      target == app_list_button->GetWidget()->GetNativeWindow() &&
-      app_list_button->bounds().Contains(event->location())) {
+  HomeButton* home_button = shelf->shelf_widget()->GetHomeButton();
+  if (home_button && home_button->GetWidget() &&
+      target == home_button->GetWidget()->GetNativeWindow() &&
+      home_button->bounds().Contains(event->location())) {
     return;
   }
 
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 9e53666..f632fa1 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -1385,8 +1385,8 @@
     GetAppListTestHelper()->WaitUntilIdle();
   }
 
-  void PressAppListButton() {
-    Shell::Get()->app_list_controller()->OnAppListButtonPressed(
+  void PressHomeButton() {
+    Shell::Get()->app_list_controller()->OnHomeButtonPressed(
         GetPrimaryDisplayId(), app_list::AppListShowSource::kShelfButton,
         base::TimeTicks());
     GetAppListTestHelper()->WaitUntilIdle();
@@ -1762,9 +1762,8 @@
   GetAppListTestHelper()->CheckVisibility(true);
 }
 
-// Tests that the app list button will minimize all windows.
-TEST_F(AppListPresenterDelegateHomeLauncherTest,
-       AppListButtonMinimizeAllWindows) {
+// Tests that the home button will minimize all windows.
+TEST_F(AppListPresenterDelegateHomeLauncherTest, HomeButtonMinimizeAllWindows) {
   // Show app list in tablet mode. Maximize all windows.
   EnableTabletMode(true);
   GetAppListTestHelper()->CheckVisibility(true);
@@ -1788,8 +1787,8 @@
   auto ordering =
       Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kActiveDesk);
 
-  // Press app list button.
-  PressAppListButton();
+  // Press home button.
+  PressHomeButton();
   EXPECT_TRUE(state1->IsMinimized());
   EXPECT_TRUE(state2->IsMinimized());
   EXPECT_TRUE(state3->IsMinimized());
@@ -1801,9 +1800,8 @@
   EXPECT_TRUE(std::equal(ordering.begin(), ordering.end(), new_order.begin()));
 }
 
-// Tests that the app list button will end split view mode.
-TEST_F(AppListPresenterDelegateHomeLauncherTest,
-       AppListButtonEndSplitViewMode) {
+// Tests that the home button will end split view mode.
+TEST_F(AppListPresenterDelegateHomeLauncherTest, HomeButtonEndSplitViewMode) {
   // Show app list in tablet mode. Enter split view mode.
   EnableTabletMode(true);
   GetAppListTestHelper()->CheckVisibility(true);
@@ -1813,14 +1811,14 @@
   split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT);
   EXPECT_TRUE(split_view_controller->InSplitViewMode());
 
-  // Press app list button.
-  PressAppListButton();
+  // Press home button.
+  PressHomeButton();
   EXPECT_FALSE(split_view_controller->InSplitViewMode());
   GetAppListTestHelper()->CheckVisibility(true);
 }
 
-// Tests that the app list button will end overview mode.
-TEST_F(AppListPresenterDelegateHomeLauncherTest, AppListButtonEndOverviewMode) {
+// Tests that the home button will end overview mode.
+TEST_F(AppListPresenterDelegateHomeLauncherTest, HomeButtonEndOverviewMode) {
   // Show app list in tablet mode. Enter overview mode.
   EnableTabletMode(true);
   GetAppListTestHelper()->CheckVisibility(true);
@@ -1829,8 +1827,8 @@
   overview_controller->StartOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
 
-  // Press app list button.
-  PressAppListButton();
+  // Press home button.
+  PressHomeButton();
   EXPECT_FALSE(overview_controller->InOverviewSession());
   GetAppListTestHelper()->CheckVisibility(true);
 }
diff --git a/ash/app_list/app_list_unittest.cc b/ash/app_list/app_list_unittest.cc
index 75ab567..41dbbeb 100644
--- a/ash/app_list/app_list_unittest.cc
+++ b/ash/app_list/app_list_unittest.cc
@@ -7,7 +7,7 @@
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/app_list/presenter/app_list_presenter_impl.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_view_test_api.h"
@@ -22,39 +22,39 @@
 using AppListTest = AshTestBase;
 
 // An integration test to toggle the app list by pressing the shelf button.
-TEST_F(AppListTest, PressAppListButtonToShowAndDismiss) {
+TEST_F(AppListTest, PressHomeButtonToShowAndDismiss) {
   aura::Window* root_window = Shell::GetPrimaryRootWindow();
   Shelf* shelf = Shelf::ForWindow(root_window);
   ShelfWidget* shelf_widget = shelf->shelf_widget();
   ShelfView* shelf_view = shelf->GetShelfViewForTesting();
   ShelfViewTestAPI(shelf_view).RunMessageLoopUntilAnimationsDone();
-  AppListButton* app_list_button = shelf_widget->GetAppListButton();
-  // Ensure animations progressed to give the app list button a non-empty size.
-  ASSERT_GT(app_list_button->GetBoundsInScreen().height(), 0);
+  HomeButton* home_button = shelf_widget->GetHomeButton();
+  // Ensure animations progressed to give the home button a non-empty size.
+  ASSERT_GT(home_button->GetBoundsInScreen().height(), 0);
 
   aura::Window* app_list_container =
       root_window->GetChildById(kShellWindowId_AppListContainer);
   ui::test::EventGenerator generator(root_window);
 
-  // Click the app list button to show the app list.
+  // Click the home button to show the app list.
   auto* controller = Shell::Get()->app_list_controller();
   auto* presenter = controller->presenter();
   EXPECT_FALSE(controller->GetTargetVisibility());
   EXPECT_FALSE(presenter->GetTargetVisibility());
   EXPECT_EQ(0u, app_list_container->children().size());
-  EXPECT_FALSE(app_list_button->IsShowingAppList());
+  EXPECT_FALSE(home_button->IsShowingAppList());
   generator.set_current_screen_location(
-      app_list_button->GetBoundsInScreen().CenterPoint());
+      home_button->GetBoundsInScreen().CenterPoint());
   generator.ClickLeftButton();
   EXPECT_TRUE(presenter->GetTargetVisibility());
   EXPECT_EQ(1u, app_list_container->children().size());
-  EXPECT_TRUE(app_list_button->IsShowingAppList());
+  EXPECT_TRUE(home_button->IsShowingAppList());
 
   // Click the button again to dismiss the app list; it will animate to close.
   generator.ClickLeftButton();
   EXPECT_FALSE(controller->GetTargetVisibility());
   EXPECT_EQ(1u, app_list_container->children().size());
-  EXPECT_FALSE(app_list_button->IsShowingAppList());
+  EXPECT_FALSE(home_button->IsShowingAppList());
 }
 
 }  // namespace ash
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index e15df455..ba0f680 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -274,7 +274,7 @@
           "DragReleaseHide",
           value);
       break;
-    case TabletModeAnimationTransition::kAppListButtonShow:
+    case TabletModeAnimationTransition::kHomeButtonShow:
       UMA_HISTOGRAM_PERCENTAGE(
           "Apps.HomeLauncherTransition.AnimationSmoothness."
           "PressAppListButtonShow",
@@ -1894,7 +1894,7 @@
 
     // Reset the search box to be shown again. This is done after the animation
     // is complete in order to minimize work during the animation.
-    search_box_view_->SetSearchBoxActive(false /*active*/, ui::ET_LAST);
+    search_box_view_->ClearSearchAndDeactivateSearchBox();
   }
 
   // Layout if the animation was completed.
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 3c5d24e..e1f6b2c9 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -3334,10 +3334,11 @@
   // GhostImageView that will fade in.
   last_ghost_view_ = current_ghost_view_;
 
-  current_ghost_view_ = new GhostImageView(
-      drag_view_, IsFolderItem(drag_view_->item()) /* is_folder */,
-      folder_delegate_, GetExpectedTileBounds(reorder_placeholder_),
-      reorder_placeholder_.page);
+  current_ghost_view_ =
+      new GhostImageView(IsFolderItem(drag_view_->item()) /* is_folder */,
+                         folder_delegate_, reorder_placeholder_.page);
+  current_ghost_view_->Init(drag_view_,
+                            GetExpectedTileBounds(reorder_placeholder_));
   AddChildView(current_ghost_view_);
   current_ghost_view_->FadeIn();
 }
diff --git a/ash/app_list/views/assistant/dialog_plate.cc b/ash/app_list/views/assistant/dialog_plate.cc
index efcddd1c..83f984a9 100644
--- a/ash/app_list/views/assistant/dialog_plate.cc
+++ b/ash/app_list/views/assistant/dialog_plate.cc
@@ -327,7 +327,8 @@
   voice_input_toggle_ = ash::AssistantButton::Create(
       this, ash::kMicIcon, kButtonSizeDip, kIconSizeDip,
       IDS_ASH_ASSISTANT_DIALOG_PLATE_MIC_ACCNAME,
-      ash::AssistantButtonId::kVoiceInputToggle);
+      ash::AssistantButtonId::kVoiceInputToggle,
+      IDS_ASH_ASSISTANT_DIALOG_PLATE_MIC_TOOLTIP);
   keyboard_layout_container_->AddChildView(voice_input_toggle_);
 
   input_modality_layout_container_->AddChildView(keyboard_layout_container_);
@@ -378,7 +379,8 @@
   keyboard_input_toggle_ = ash::AssistantButton::Create(
       this, ash::kKeyboardIcon, kButtonSizeDip, kIconSizeDip,
       IDS_ASH_ASSISTANT_DIALOG_PLATE_KEYBOARD_ACCNAME,
-      ash::AssistantButtonId::kKeyboardInputToggle);
+      ash::AssistantButtonId::kKeyboardInputToggle,
+      IDS_ASH_ASSISTANT_DIALOG_PLATE_KEYBOARD_TOOLTIP);
   voice_layout_container_->AddChildView(keyboard_input_toggle_);
 
   input_modality_layout_container_->AddChildView(voice_layout_container_);
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index 7a11403..7cae89b 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -142,16 +142,13 @@
 void ContentsView::ResetForShow() {
   SetActiveState(ash::AppListState::kStateApps);
   GetAppsContainerView()->ResetForShowApps();
-  // We clear the search when hiding so when app list appears it is not showing
-  // search results.
-  GetSearchBoxView()->ClearSearch();
-
+  GetSearchBoxView()->ResetForShow();
   // In side shelf, the opacity of the contents is not animated so set it to the
   // final state. In tablet mode, opacity of the elements is controlled by the
   // HomeLauncherGestureHandler which expects these elements to be opaque.
   // Otherwise the contents animate from 0 to 1 so set the initial opacity to 0.
   const float initial_opacity =
-      app_list_view_->is_side_shelf() || app_list_view()->is_tablet_mode()
+      app_list_view_->is_side_shelf() || app_list_view_->is_tablet_mode()
           ? 1.0f
           : 0.0f;
   GetSearchBoxView()->layer()->SetOpacity(initial_opacity);
diff --git a/ash/app_list/views/ghost_image_view.cc b/ash/app_list/views/ghost_image_view.cc
index b34ebec..69a481f 100644
--- a/ash/app_list/views/ghost_image_view.cc
+++ b/ash/app_list/views/ghost_image_view.cc
@@ -11,6 +11,7 @@
 #include "ash/app_list/model/app_list_folder_item.h"
 #include "ash/app_list/model/app_list_item_list.h"
 #include "ash/app_list/views/app_list_item_view.h"
+#include "ash/public/cpp/app_list/app_list_config.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/animation/tween.h"
@@ -28,7 +29,6 @@
 constexpr int kInFolderGhostColor = gfx::kGoogleGrey700;
 constexpr base::TimeDelta kGhostFadeInOutLength =
     base::TimeDelta::FromMilliseconds(180);
-constexpr int kInnerFolderGhostIconRadius = 14;
 constexpr gfx::Tween::Type kGhostTween = gfx::Tween::FAST_OUT_SLOW_IN;
 constexpr int kAlphaGradient = 2;
 constexpr int kAlphaChannelFilter = 180;
@@ -43,37 +43,45 @@
 
 }  // namespace
 
-GhostImageView::GhostImageView(AppListItemView* drag_view,
-                               bool is_folder,
-                               bool is_in_folder,
-                               const gfx::Rect& drop_target_bounds,
-                               int page)
+GhostImageView::GhostImageView(bool is_folder, bool is_in_folder, int page)
     : is_hiding_(false),
       is_in_folder_(is_in_folder),
       is_folder_(is_folder),
-      page_(page),
-      drop_target_bounds_(drop_target_bounds),
-      icon_bounds_(drag_view->GetIconBounds()) {
+      page_(page) {}
+
+GhostImageView::~GhostImageView() {
+  StopObservingImplicitAnimations();
+}
+
+void GhostImageView::Init(AppListItemView* drag_view,
+                          const gfx::Rect& drop_target_bounds) {
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   layer()->SetOpacity(0.0f);
   SetBoundsRect(drop_target_bounds);
+  icon_bounds_ = drag_view->GetIconBounds();
 
   if (is_folder_) {
     AppListFolderItem* folder_item =
         static_cast<AppListFolderItem*>(drag_view->item());
     num_items_ = std::min(FolderImage::kNumFolderTopItems,
                           folder_item->item_list()->item_count());
+
+    // Create an outline for each item within the folder icon.
+    for (size_t i = 0; i < num_items_.value(); i++) {
+      gfx::ImageSkia inner_icon_outline =
+          gfx::ImageSkiaOperations::CreateResizedImage(
+              folder_item->item_list()->item_at(i)->icon(),
+              skia::ImageOperations::RESIZE_BEST,
+              AppListConfig::instance().item_icon_in_folder_icon_size());
+      inner_folder_icon_outlines_.push_back(GetIconOutline(inner_icon_outline));
+    }
   } else {
     // Create outline of app icon and set |outline_| to it.
     outline_ = GetIconOutline(drag_view->GetIconImage());
   }
 }
 
-GhostImageView::~GhostImageView() {
-  StopObservingImplicitAnimations();
-}
-
 void GhostImageView::FadeOut() {
   if (is_hiding_)
     return;
@@ -87,7 +95,7 @@
 
 void GhostImageView::SetTransitionOffset(
     const gfx::Vector2d& transition_offset) {
-  SetPosition(drop_target_bounds_.origin() + transition_offset);
+  SetPosition(bounds().origin() + transition_offset);
 }
 
 const char* GhostImageView::GetClassName() const {
@@ -138,9 +146,10 @@
         FolderImage::GetTopIconsBounds(icon_bounds_, num_items_.value());
 
     // Draw ghost items within the ghost folder circle.
-    for (gfx::Rect bounds : top_icon_bounds) {
-      canvas->DrawCircle(gfx::PointF(bounds.CenterPoint()),
-                         kInnerFolderGhostIconRadius, circle_flags);
+    for (size_t i = 0; i < num_items_.value(); i++) {
+      canvas->DrawImageInt(inner_folder_icon_outlines_[i],
+                           top_icon_bounds[i].x() - kGhostImagePadding,
+                           top_icon_bounds[i].y() - kGhostImagePadding);
     }
   } else {
     canvas->DrawImageInt(outline_, icon_bounds_.x() - kGhostImagePadding,
@@ -162,6 +171,7 @@
     const gfx::ImageSkia& original_icon) {
   gfx::ImageSkia icon_outline;
 
+  original_icon.EnsureRepsForSupportedScales();
   for (gfx::ImageSkiaRep rep : original_icon.image_reps()) {
     // Only generate the outline for the ImageSkiaRep with the highest supported
     // scale.
diff --git a/ash/app_list/views/ghost_image_view.h b/ash/app_list/views/ghost_image_view.h
index 97f6adf..8f7abbed 100644
--- a/ash/app_list/views/ghost_image_view.h
+++ b/ash/app_list/views/ghost_image_view.h
@@ -20,13 +20,12 @@
 class GhostImageView : public views::ImageView,
                        public ui::ImplicitAnimationObserver {
  public:
-  GhostImageView(AppListItemView* drag_view,
-                 bool is_folder,
-                 bool is_in_folder,
-                 const gfx::Rect& drop_target_bounds,
-                 int page);
+  GhostImageView(bool is_folder, bool is_in_folder, int page);
   ~GhostImageView() override;
 
+  // Initialize the GhostImageView.
+  void Init(AppListItemView* drag_view, const gfx::Rect& drop_target_bounds);
+
   // Begins the fade out animation.
   void FadeOut();
 
@@ -67,10 +66,6 @@
   // Page this this view belongs to, used to calculate transition offset.
   int page_;
 
-  // Bounds for the location of the GhostImageView in parent AppGridView's
-  // coordinates.
-  gfx::Rect drop_target_bounds_;
-
   // Icon bounds used to determine size and placement of the GhostImageView.
   gfx::Rect icon_bounds_;
 
@@ -80,6 +75,10 @@
   // The outline of the dragged item's icon. Used as the ghost image.
   gfx::ImageSkia outline_;
 
+  // The outlines of the top icons within a folder. Used for the folder ghost
+  // image.
+  std::vector<gfx::ImageSkia> inner_folder_icon_outlines_;
+
   DISALLOW_COPY_AND_ASSIGN(GhostImageView);
 };
 
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index 71756e9..5125f69 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -113,6 +113,12 @@
   SearchBoxViewBase::Init();
 }
 
+void SearchBoxView::ResetForShow() {
+  ClearSearch();
+  SetSearchBoxBackgroundCornerRadius(
+      GetSearchBoxBorderCornerRadiusForState(contents_view_->GetActiveState()));
+}
+
 void SearchBoxView::ClearSearch() {
   search_box::SearchBoxViewBase::ClearSearch();
   app_list_view_->SetStateFromSearchBoxView(
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h
index 20a4421..8819bea 100644
--- a/ash/app_list/views/search_box_view.h
+++ b/ash/app_list/views/search_box_view.h
@@ -39,6 +39,9 @@
 
   void Init(bool is_tablet_mode);
 
+  // Resets state of SearchBoxView so it can be reshown.
+  void ResetForShow();
+
   // Returns the total focus ring spacing for use in folders.
   static int GetFocusRingSpacing();
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 0ef32d4d..6a9c319c 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -751,6 +751,12 @@
       <message name="IDS_ASH_DESKS_DESK_4_MINI_VIEW_TITLE" desc="The label of the fourth virtual desk thumbnail.">
         Desk 4
       </message>
+      <message name="IDS_ASH_DESKS_MAX_NUM_REACHED" desc="Message shown to users when they attempt to add a new virtual desk when the maximum number of desks has been reached.">
+        Maximum number of desks reached.
+      </message>
+      <message name="IDS_ASH_DESKS_MIN_NUM_REACHED" desc="Message shown to users when they attempt to remove the last virtual.">
+        Last desk can't be removed.
+      </message>
 
       <!-- Status tray charging strings. -->
       <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE" desc="The title of a notification indicating that a low-current USB charger has been connected.">
@@ -1841,10 +1847,16 @@
       <message name="IDS_ASH_ASSISTANT_DIALOG_PLATE_KEYBOARD_ACCNAME" desc="Accessibility name for a keyboard image button when clicked shows an input textfield for user to type in queries.">
         Start a text query
       </message>
+      <message name="IDS_ASH_ASSISTANT_DIALOG_PLATE_KEYBOARD_TOOLTIP" desc="Tooltip for a keyboard image button when clicked shows an input textfield for user to type in queries.">
+        Text input
+      </message>
       <message name="IDS_ASH_ASSISTANT_DIALOG_PLATE_MIC_ACCNAME" desc="Accessibility name for a microphone image button when clicked starts listening for user's voice queries.">
         Start a voice query
       </message>
-      <message name="IDS_ASH_ASSISTANT_DIALOG_PLATE_SETTINGS_ACCNAME" desc="Accessibility name for a settings image button when clicked shows Assistant settings.">
+      <message name="IDS_ASH_ASSISTANT_DIALOG_PLATE_MIC_TOOLTIP" desc="Tooltip for a microphone image button when clicked starts listening for user's voice queries.">
+        Voice input
+      </message>
+      <message name="IDS_ASH_ASSISTANT_DIALOG_PLATE_SETTINGS_ACCNAME_TOOLTIP" desc="Accessibility name and tooltip for a settings image button when clicked shows Assistant settings.">
         Assistant Settings
       </message>
       <message name="IDS_ASH_ASSISTANT_ERROR_GENERIC" desc="Generic message shown when Assistant is in an error state.">
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_MAX_NUM_REACHED.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_MAX_NUM_REACHED.png.sha1
new file mode 100644
index 0000000..c065a47
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_DESKS_MAX_NUM_REACHED.png.sha1
@@ -0,0 +1 @@
+a6d8fb78118105d67fd89a1d7ea36b7e4adc553f
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_MIN_NUM_REACHED.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_MIN_NUM_REACHED.png.sha1
new file mode 100644
index 0000000..3c9d5a97
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_DESKS_MIN_NUM_REACHED.png.sha1
@@ -0,0 +1 @@
+f281c268cc6b39199140576bddb4a992d33af508
\ No newline at end of file
diff --git a/ash/assistant/ui/assistant_web_view.cc b/ash/assistant/ui/assistant_web_view.cc
index 210347f..5a854d9 100644
--- a/ash/assistant/ui/assistant_web_view.cc
+++ b/ash/assistant/ui/assistant_web_view.cc
@@ -121,8 +121,10 @@
 
   RemoveContents();
 
-  delegate_->GetNavigableContentsFactoryForView(
-      contents_factory_.BindNewPipeAndPassReceiver());
+  if (!contents_factory_.is_bound()) {
+    delegate_->GetNavigableContentsFactoryForView(
+        contents_factory_.BindNewPipeAndPassReceiver());
+  }
 
   auto contents_params = content::mojom::NavigableContentsParams::New();
   contents_params->suppress_navigations = true;
diff --git a/ash/assistant/ui/base/assistant_button.cc b/ash/assistant/ui/base/assistant_button.cc
index 57eb7d9..396c7ed3 100644
--- a/ash/assistant/ui/base/assistant_button.cc
+++ b/ash/assistant/ui/base/assistant_button.cc
@@ -54,9 +54,14 @@
                                             int icon_size_in_dip,
                                             int accessible_name_id,
                                             AssistantButtonId button_id,
+                                            base::Optional<int> tooltip_id,
                                             SkColor icon_color) {
   auto* button = new AssistantButton(listener, button_id);
   button->SetAccessibleName(l10n_util::GetStringUTF16(accessible_name_id));
+
+  if (tooltip_id)
+    button->SetTooltipText(l10n_util::GetStringUTF16(tooltip_id.value()));
+
   button->SetImage(views::Button::STATE_NORMAL,
                    gfx::CreateVectorIcon(icon, icon_size_in_dip, icon_color));
   button->SetPreferredSize(gfx::Size(size_in_dip, size_in_dip));
diff --git a/ash/assistant/ui/base/assistant_button.h b/ash/assistant/ui/base/assistant_button.h
index c8c3aace..0175854 100644
--- a/ash/assistant/ui/base/assistant_button.h
+++ b/ash/assistant/ui/base/assistant_button.h
@@ -9,6 +9,7 @@
 
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/views/controls/button/image_button.h"
@@ -34,13 +35,15 @@
   ~AssistantButton() override;
 
   // Creates an ImageButton with the default Assistant styles.
-  static views::ImageButton* Create(views::ButtonListener* listener,
-                                    const gfx::VectorIcon& icon,
-                                    int size_in_dip,
-                                    int icon_size_in_dip,
-                                    int accessible_name_id,
-                                    AssistantButtonId button_id,
-                                    SkColor icon_color = gfx::kGoogleGrey700);
+  static views::ImageButton* Create(
+      views::ButtonListener* listener,
+      const gfx::VectorIcon& icon,
+      int size_in_dip,
+      int icon_size_in_dip,
+      int accessible_name_id,
+      AssistantButtonId button_id,
+      base::Optional<int> tooltip_id = base::nullopt,
+      SkColor icon_color = gfx::kGoogleGrey700);
 
   // views::Button:
   const char* GetClassName() const override;
diff --git a/ash/assistant/ui/caption_bar.cc b/ash/assistant/ui/caption_bar.cc
index a634664..3b4da372 100644
--- a/ash/assistant/ui/caption_bar.cc
+++ b/ash/assistant/ui/caption_bar.cc
@@ -35,7 +35,7 @@
                                         views::ButtonListener* listener) {
   return AssistantButton::Create(listener, icon, kCaptionButtonSizeDip,
                                  kVectorIconSizeDip, accessible_name_id,
-                                 button_id, gfx::kGoogleGrey700);
+                                 button_id);
 }
 
 }  // namespace
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.cc b/ash/assistant/ui/dialog_plate/dialog_plate.cc
index 4051f40..3a7678b 100644
--- a/ash/assistant/ui/dialog_plate/dialog_plate.cc
+++ b/ash/assistant/ui/dialog_plate/dialog_plate.cc
@@ -277,10 +277,11 @@
   InitVoiceLayoutContainer();
 
   // Settings.
-  settings_button_ =
-      AssistantButton::Create(this, kSettingsIcon, kButtonSizeDip, kIconSizeDip,
-                              IDS_ASH_ASSISTANT_DIALOG_PLATE_SETTINGS_ACCNAME,
-                              AssistantButtonId::kSettings);
+  settings_button_ = AssistantButton::Create(
+      this, kSettingsIcon, kButtonSizeDip, kIconSizeDip,
+      IDS_ASH_ASSISTANT_DIALOG_PLATE_SETTINGS_ACCNAME_TOOLTIP,
+      AssistantButtonId::kSettings,
+      IDS_ASH_ASSISTANT_DIALOG_PLATE_SETTINGS_ACCNAME_TOOLTIP);
   AddChildView(settings_button_);
 
   // Artificially trigger event to set initial state.
@@ -329,7 +330,8 @@
   voice_input_toggle_ =
       AssistantButton::Create(this, kMicIcon, kButtonSizeDip, kIconSizeDip,
                               IDS_ASH_ASSISTANT_DIALOG_PLATE_MIC_ACCNAME,
-                              AssistantButtonId::kVoiceInputToggle);
+                              AssistantButtonId::kVoiceInputToggle,
+                              IDS_ASH_ASSISTANT_DIALOG_PLATE_MIC_TOOLTIP);
   keyboard_layout_container_->AddChildView(voice_input_toggle_);
 
   input_modality_layout_container_->AddChildView(keyboard_layout_container_);
@@ -354,7 +356,8 @@
   keyboard_input_toggle_ =
       AssistantButton::Create(this, kKeyboardIcon, kButtonSizeDip, kIconSizeDip,
                               IDS_ASH_ASSISTANT_DIALOG_PLATE_KEYBOARD_ACCNAME,
-                              AssistantButtonId::kKeyboardInputToggle);
+                              AssistantButtonId::kKeyboardInputToggle,
+                              IDS_ASH_ASSISTANT_DIALOG_PLATE_KEYBOARD_TOOLTIP);
   voice_layout_container_->AddChildView(keyboard_input_toggle_);
 
   // Spacer.
diff --git a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc b/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
index e5997d1..7be9f01 100644
--- a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
+++ b/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
@@ -1292,6 +1292,47 @@
        {{ui::VKEY_K, ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN}}},
 
       {// |categories|
+       {ShortcutCategory::kTabAndWindow},
+       IDS_KSV_DESCRIPTION_DESKS_NEW_DESK,
+       IDS_KSV_SHORTCUT_TWO_MODIFIERS_ONE_KEY,
+       // |accelerator_ids|
+       {{ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN}}},
+      {// |categories|
+       {ShortcutCategory::kTabAndWindow},
+       IDS_KSV_DESCRIPTION_DESKS_REMOVE_CURRENT_DESK,
+       IDS_KSV_SHORTCUT_TWO_MODIFIERS_ONE_KEY,
+       // |accelerator_ids|
+       {{ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN}}},
+
+      {// |categories|
+       {ShortcutCategory::kTabAndWindow},
+       IDS_KSV_DESCRIPTION_DESKS_ACTIVATE_LEFT_DESK,
+       IDS_KSV_SHORTCUT_TWO_MODIFIERS_ONE_KEY,
+       // |accelerator_ids|
+       {{ui::VKEY_OEM_4, ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN}}},
+      {// |categories|
+       {ShortcutCategory::kTabAndWindow},
+       IDS_KSV_DESCRIPTION_DESKS_ACTIVATE_RIGHT_DESK,
+       IDS_KSV_SHORTCUT_TWO_MODIFIERS_ONE_KEY,
+       // |accelerator_ids|
+       {{ui::VKEY_OEM_6, ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN}}},
+
+      {// |categories|
+       {ShortcutCategory::kTabAndWindow},
+       IDS_KSV_DESCRIPTION_DESKS_MOVE_ACTIVE_ITEM_LEFT_DESK,
+       IDS_KSV_SHORTCUT_THREE_MODIFIERS_ONE_KEY,
+       // |accelerator_ids|
+       {{ui::VKEY_OEM_4,
+         ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN}}},
+      {// |categories|
+       {ShortcutCategory::kTabAndWindow},
+       IDS_KSV_DESCRIPTION_DESKS_MOVE_ACTIVE_ITEM_RIGHT_DESK,
+       IDS_KSV_SHORTCUT_THREE_MODIFIERS_ONE_KEY,
+       // |accelerator_ids|
+       {{ui::VKEY_OEM_6,
+         ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN}}},
+
+      {// |categories|
        {ShortcutCategory::kPageAndBrowser},
        IDS_KSV_DESCRIPTION_SHOW_IDC_FOCUS_MENU_BAR,
        IDS_KSV_SHORTCUT_ONE_KEY,
diff --git a/ash/components/shortcut_viewer_strings.grdp b/ash/components/shortcut_viewer_strings.grdp
index 1ca004e..3bda251 100644
--- a/ash/components/shortcut_viewer_strings.grdp
+++ b/ash/components/shortcut_viewer_strings.grdp
@@ -633,4 +633,22 @@
   <message name="IDS_KSV_SHORTCUT_IDC_SHOW_APP_MENU" desc="Description of the command in keyboard shortcut viewer.">
     <ph name="alt">$1<ex>Alt</ex></ph><ph name="separator">$2<ex>+</ex></ph><ph name="e">$3<ex>e</ex></ph> or <ph name="f">$4<ex>f</ex></ph>
   </message>
+  <message name="IDS_KSV_DESCRIPTION_DESKS_NEW_DESK" desc="Description of the command in keyboard shortcut viewer.">
+    Create a new virtual desk
+  </message>
+  <message name="IDS_KSV_DESCRIPTION_DESKS_REMOVE_CURRENT_DESK" desc="Description of the command in keyboard shortcut viewer.">
+    Remove the current virtual desk
+  </message>
+  <message name="IDS_KSV_DESCRIPTION_DESKS_ACTIVATE_LEFT_DESK" desc="Description of the command in keyboard shortcut viewer.">
+    Activate the virtual desk on the left
+  </message>
+  <message name="IDS_KSV_DESCRIPTION_DESKS_ACTIVATE_RIGHT_DESK" desc="Description of the command in keyboard shortcut viewer.">
+    Activate the virtual desk on the right
+  </message>
+  <message name="IDS_KSV_DESCRIPTION_DESKS_MOVE_ACTIVE_ITEM_LEFT_DESK" desc="Description of the command in keyboard shortcut viewer.">
+    Move active window to the desk on the left
+  </message>
+  <message name="IDS_KSV_DESCRIPTION_DESKS_MOVE_ACTIVE_ITEM_RIGHT_DESK" desc="Description of the command in keyboard shortcut viewer.">
+    Move active window to the desk on the right
+  </message>
 </grit-part>
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index b54e556f..5bd6545 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -46,6 +46,7 @@
 #include "ui/compositor/compositor_switches.h"
 #include "ui/display/display.h"
 #include "ui/display/display_layout.h"
+#include "ui/display/manager/display_configurator.h"
 #include "ui/display/manager/display_layout_store.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
@@ -207,6 +208,10 @@
 
 void WindowTreeHostManager::Start() {
   display::Screen::GetScreen()->AddObserver(this);
+  Shell::Get()
+      ->display_configurator()
+      ->content_protection_manager()
+      ->AddObserver(this);
   Shell::Get()->display_manager()->set_delegate(this);
 }
 
@@ -221,6 +226,10 @@
   cursor_window_controller_.reset();
   mirror_window_controller_.reset();
 
+  Shell::Get()
+      ->display_configurator()
+      ->content_protection_manager()
+      ->RemoveObserver(this);
   display::Screen::GetScreen()->RemoveObserver(this);
 
   int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
@@ -646,6 +655,18 @@
   }
 }
 
+void WindowTreeHostManager::OnDisplaySecurityChanged(int64_t display_id,
+                                                     bool secure) {
+  AshWindowTreeHost* host = GetAshWindowTreeHostForDisplayId(display_id);
+  // No host for internal display in docked mode.
+  if (!host)
+    return;
+
+  ui::Compositor* compositor = host->AsWindowTreeHost()->compositor();
+  compositor->SetOutputIsSecure(secure);
+  compositor->ScheduleFullRedraw();
+}
+
 void WindowTreeHostManager::CreateOrUpdateMirroringDisplay(
     const display::DisplayInfoList& info_list) {
   if (GetDisplayManager()->IsInMirrorMode() ||
@@ -767,18 +788,6 @@
 void WindowTreeHostManager::PostDisplayConfigurationChange() {
   focus_activation_store_->Restore();
 
-  display::DisplayManager* display_manager = GetDisplayManager();
-  for (const display::Display& display :
-       display_manager->active_display_list()) {
-    bool output_is_secure =
-        !display_manager->IsInMirrorMode() && display.IsInternal();
-    ui::Compositor* compositor = GetAshWindowTreeHostForDisplayId(display.id())
-                                     ->AsWindowTreeHost()
-                                     ->compositor();
-    compositor->SetOutputIsSecure(output_is_secure);
-    compositor->ScheduleFullRedraw();
-  }
-
   for (auto& observer : observers_)
     observer.OnDisplayConfigurationChanged();
   UpdateMouseLocationAfterDisplayChange();
diff --git a/ash/display/window_tree_host_manager.h b/ash/display/window_tree_host_manager.h
index 02b9e31..4cba45d 100644
--- a/ash/display/window_tree_host_manager.h
+++ b/ash/display/window_tree_host_manager.h
@@ -23,6 +23,7 @@
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/input_method_delegate.h"
 #include "ui/display/display_observer.h"
+#include "ui/display/manager/content_protection_manager.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/gfx/geometry/point.h"
 
@@ -47,6 +48,7 @@
 class ASH_EXPORT WindowTreeHostManager
     : public display::DisplayObserver,
       public aura::WindowTreeHostObserver,
+      public display::ContentProtectionManager::Observer,
       public display::DisplayManager::Delegate,
       public ui::internal::InputMethodDelegate {
  public:
@@ -164,6 +166,9 @@
   // aura::WindowTreeHostObserver overrides:
   void OnHostResized(aura::WindowTreeHost* host) override;
 
+  // display::ContentProtectionManager::Observer overrides:
+  void OnDisplaySecurityChanged(int64_t display_id, bool secure) override;
+
   // display::DisplayManager::Delegate overrides:
   void CreateOrUpdateMirroringDisplay(
       const display::DisplayInfoList& info_list) override;
diff --git a/ash/first_run/first_run_helper_impl.cc b/ash/first_run/first_run_helper_impl.cc
index 223e0cfc..74eca845 100644
--- a/ash/first_run/first_run_helper_impl.cc
+++ b/ash/first_run/first_run_helper_impl.cc
@@ -11,7 +11,7 @@
 #include "ash/first_run/desktop_cleaner.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
-#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
@@ -43,8 +43,8 @@
 
 gfx::Rect FirstRunHelperImpl::GetAppListButtonBounds() {
   Shelf* shelf = Shelf::ForWindow(Shell::GetPrimaryRootWindow());
-  AppListButton* app_button = shelf->shelf_widget()->GetAppListButton();
-  return app_button->GetBoundsInScreen();
+  HomeButton* home_button = shelf->shelf_widget()->GetHomeButton();
+  return home_button->GetBoundsInScreen();
 }
 
 gfx::Rect FirstRunHelperImpl::OpenTrayBubble() {
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index c633aaa..b9aecda5 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -713,7 +713,7 @@
                         0 /*fixed_width*/, 0 /*min_width*/);
   auto add_view = [&](views::View* view) {
     grid_layout->StartRow(0 /*vertical_resize*/, 0 /*column_set_id*/);
-    grid_layout->AddView(view);
+    grid_layout->AddExistingView(view);
   };
   auto add_padding = [&](int amount) {
     grid_layout->AddPaddingRow(0 /*vertical_resize*/, amount /*size*/);
diff --git a/ash/login/ui/login_user_view.cc b/ash/login/ui/login_user_view.cc
index b5ae0107..d626091b 100644
--- a/ash/login/ui/login_user_view.cc
+++ b/ash/login/ui/login_user_view.cc
@@ -703,20 +703,20 @@
   // Add views in rendering order.
   // Image
   layout->StartRow(0 /*vertical_resize*/, kImageColumnId);
-  layout->AddView(user_image_);
+  layout->AddExistingView(user_image_);
 
   add_padding(kVerticalSpacingBetweenEntriesDp);
 
   // Label/dropdown.
   layout->StartRow(0 /*vertical_resize*/, kLabelDropdownColumnId);
-  layout->AddView(user_label_);
+  layout->AddExistingView(user_label_);
   if (dropdown_)
-    layout->AddView(dropdown_);
+    layout->AddExistingView(dropdown_);
 
   if (user_domain_) {
     add_padding(kVerticalSpacingBetweenUserNameAndDomainDp);
     layout->StartRow(0 /*vertical_resize*/, kLabelDomainColumnId);
-    layout->AddView(user_domain_);
+    layout->AddExistingView(user_domain_);
   }
 }
 
diff --git a/ash/login/ui/parent_access_view.cc b/ash/login/ui/parent_access_view.cc
index 95c4cc9d..a90eaf60 100644
--- a/ash/login/ui/parent_access_view.cc
+++ b/ash/login/ui/parent_access_view.cc
@@ -112,6 +112,9 @@
     case ParentAccessRequestReason::kChangeTime:
       title_id = IDS_ASH_LOGIN_PARENT_ACCESS_TITLE_CHANGE_TIME;
       break;
+    case ParentAccessRequestReason::kChangeTimezone:
+      title_id = IDS_ASH_LOGIN_PARENT_ACCESS_TITLE_CHANGE_TIMEZONE;
+      break;
   }
   return l10n_util::GetStringUTF16(title_id);
 }
@@ -123,12 +126,17 @@
       description_id = IDS_ASH_LOGIN_PARENT_ACCESS_DESCRIPTION;
       break;
     case ParentAccessRequestReason::kChangeTime:
+    case ParentAccessRequestReason::kChangeTimezone:
       description_id = IDS_ASH_LOGIN_PARENT_ACCESS_GENERIC_DESCRIPTION;
       break;
   }
   return l10n_util::GetStringUTF16(description_id);
 }
 
+base::string16 GetAccessibleTitle() {
+  return l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_DIALOG_NAME);
+}
+
 // Accessible input field. Customizes field description and focus behavior.
 class AccessibleInputField : public views::Textfield {
  public:
@@ -630,6 +638,10 @@
   return access_code_view_;
 }
 
+base::string16 ParentAccessView::GetAccessibleWindowTitle() const {
+  return GetAccessibleTitle();
+}
+
 void ParentAccessView::ButtonPressed(views::Button* sender,
                                      const ui::Event& event) {
   if (sender == back_button_) {
@@ -720,8 +732,7 @@
 void ParentAccessView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   views::View::GetAccessibleNodeData(node_data);
   node_data->role = ax::mojom::Role::kDialog;
-  node_data->SetName(
-      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_DIALOG_NAME));
+  node_data->SetName(GetAccessibleTitle());
 }
 
 }  // namespace ash
diff --git a/ash/login/ui/parent_access_view.h b/ash/login/ui/parent_access_view.h
index 6880af07..176db90 100644
--- a/ash/login/ui/parent_access_view.h
+++ b/ash/login/ui/parent_access_view.h
@@ -98,6 +98,7 @@
   // views::DialogDelegateView:
   ui::ModalType GetModalType() const override;
   views::View* GetInitiallyFocusedView() override;
+  base::string16 GetAccessibleWindowTitle() const override;
 
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/ash/login/ui/parent_access_view_unittest.cc b/ash/login/ui/parent_access_view_unittest.cc
index 6d65630..240a44f 100644
--- a/ash/login/ui/parent_access_view_unittest.cc
+++ b/ash/login/ui/parent_access_view_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/optional.h"
 #include "base/test/bind_test_util.h"
 #include "components/account_id/account_id.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
@@ -33,6 +34,27 @@
 
 namespace {
 
+// Struct containing the correct title and description that are displayed when
+// the dialog is instantiated with a given ParentAccessRequestReason.
+struct ViewModifiersTestData {
+  ParentAccessRequestReason reason;
+  // The title string id.
+  int title;
+  // The description string id.
+  int description;
+};
+
+const ViewModifiersTestData kViewModifiersTestData[] = {
+    {ParentAccessRequestReason::kUnlockTimeLimits,
+     IDS_ASH_LOGIN_PARENT_ACCESS_TITLE,
+     IDS_ASH_LOGIN_PARENT_ACCESS_DESCRIPTION},
+    {ParentAccessRequestReason::kChangeTime,
+     IDS_ASH_LOGIN_PARENT_ACCESS_TITLE_CHANGE_TIME,
+     IDS_ASH_LOGIN_PARENT_ACCESS_GENERIC_DESCRIPTION},
+    {ParentAccessRequestReason::kChangeTimezone,
+     IDS_ASH_LOGIN_PARENT_ACCESS_TITLE_CHANGE_TIMEZONE,
+     IDS_ASH_LOGIN_PARENT_ACCESS_GENERIC_DESCRIPTION}};
+
 class ParentAccessViewTest : public LoginTestBase {
  protected:
   ParentAccessViewTest()
@@ -89,31 +111,26 @@
   DISALLOW_COPY_AND_ASSIGN(ParentAccessViewTest);
 };
 
+class ParentAccessViewModifiersTest
+    : public ParentAccessViewTest,
+      public ::testing::WithParamInterface<ViewModifiersTestData> {};
+
 }  // namespace
 
-// Tests that title and description are correctly set when reason is unlock time
-// limits.
-TEST_F(ParentAccessViewTest, UnlockTimeLimitsStrings) {
-  StartView(ParentAccessRequestReason::kUnlockTimeLimits);
+// Tests that title and description are correctly set.
+TEST_P(ParentAccessViewModifiersTest, CheckStrings) {
+  const ViewModifiersTestData& test_data = GetParam();
+  StartView(test_data.reason);
   ParentAccessView::TestApi test_api(view_);
-  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_TITLE),
+  EXPECT_EQ(l10n_util::GetStringUTF16(test_data.title),
             test_api.title_label()->GetText());
-  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_DESCRIPTION),
+  EXPECT_EQ(l10n_util::GetStringUTF16(test_data.description),
             test_api.description_label()->GetText());
 }
 
-// Tests that title and description are correctly set when reason is change
-// time.
-TEST_F(ParentAccessViewTest, ChangeTimeStrings) {
-  StartView(ParentAccessRequestReason::kChangeTime);
-  ParentAccessView::TestApi test_api(view_);
-  EXPECT_EQ(
-      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_TITLE_CHANGE_TIME),
-      test_api.title_label()->GetText());
-  EXPECT_EQ(l10n_util::GetStringUTF16(
-                IDS_ASH_LOGIN_PARENT_ACCESS_GENERIC_DESCRIPTION),
-            test_api.description_label()->GetText());
-}
+INSTANTIATE_TEST_SUITE_P(,
+                         ParentAccessViewModifiersTest,
+                         testing::ValuesIn(kViewModifiersTestData));
 
 // Tests that back button works.
 TEST_F(ParentAccessViewTest, BackButton) {
diff --git a/ash/public/cpp/accelerators.cc b/ash/public/cpp/accelerators.cc
index 3588808..a7d99a4d 100644
--- a/ash/public/cpp/accelerators.cc
+++ b/ash/public/cpp/accelerators.cc
@@ -188,6 +188,32 @@
     // release builds.
     {true, ui::VKEY_U, kDebugModifier, PRINT_UI_HIERARCHIES},
 
+    // Virtual Desks shortcuts.
+    // Desk creation and removal:
+    // Due to https://crbug.com/976487, Search + "=" is always automatically
+    // rewritten to F12, and so is Search + "-" to F11. So we had to implement
+    // the following two shortcuts as Ctrl + F11/F12 until we resolve the above
+    // issue, accepting the fact that these two shortcuts might sometimes be
+    // consumed by apps and pages (since they're not search-based).
+    // TODO(afakhry): Change the following to Ctrl+Search+"+"/"-" once
+    // https://crbug.com/976487 is fixed.
+    {true, ui::VKEY_F12, ui::EF_CONTROL_DOWN, DESKS_NEW_DESK},
+    {true, ui::VKEY_F11, ui::EF_CONTROL_DOWN, DESKS_REMOVE_CURRENT_DESK},
+    // Desk activation:
+    {true, ui::VKEY_OEM_4, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
+     DESKS_ACTIVATE_DESK},
+    {true, ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
+     DESKS_ACTIVATE_DESK},
+    // Moving windows to desks:
+    {true, ui::VKEY_OEM_4,
+     ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
+     DESKS_MOVE_ACTIVE_ITEM},
+    {true, ui::VKEY_OEM_6,
+     ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
+     DESKS_MOVE_ACTIVE_ITEM},
+    // TODO(afakhry): Implement activating and moving windows to a desk by
+    // its index directly.
+
     // TODO(yusukes): Handle VKEY_MEDIA_STOP, and
     // VKEY_MEDIA_LAUNCH_MAIL.
 };
diff --git a/ash/public/cpp/accelerators.h b/ash/public/cpp/accelerators.h
index 51d93a8..cc5c823df 100644
--- a/ash/public/cpp/accelerators.h
+++ b/ash/public/cpp/accelerators.h
@@ -25,6 +25,10 @@
   BRIGHTNESS_UP,
   CYCLE_BACKWARD_MRU,
   CYCLE_FORWARD_MRU,
+  DESKS_ACTIVATE_DESK,
+  DESKS_MOVE_ACTIVE_ITEM,
+  DESKS_NEW_DESK,
+  DESKS_REMOVE_CURRENT_DESK,
   DEV_ADD_REMOVE_DISPLAY,
   DEV_TOGGLE_UNIFIED_DESKTOP,
   DISABLE_CAPS_LOCK,
diff --git a/ash/public/cpp/ash_switches.cc b/ash/public/cpp/ash_switches.cc
index c08ec24..48a7efe 100644
--- a/ash/public/cpp/ash_switches.cc
+++ b/ash/public/cpp/ash_switches.cc
@@ -69,7 +69,6 @@
 const char kAshUiMode[] = "force-tablet-mode";
 
 // Values for the kAshUiMode flag.
-const char kAshUiModeAuto[] = "auto";
 const char kAshUiModeClamshell[] = "clamshell";
 const char kAshUiModeTablet[] = "touch_view";
 
diff --git a/ash/public/cpp/ash_switches.h b/ash/public/cpp/ash_switches.h
index f7a1e5b..dace823 100644
--- a/ash/public/cpp/ash_switches.h
+++ b/ash/public/cpp/ash_switches.h
@@ -29,7 +29,6 @@
 ASH_PUBLIC_EXPORT extern const char kAshForceEnableStylusTools[];
 ASH_PUBLIC_EXPORT extern const char kAshPowerButtonPosition[];
 ASH_PUBLIC_EXPORT extern const char kAshUiMode[];
-ASH_PUBLIC_EXPORT extern const char kAshUiModeAuto[];
 ASH_PUBLIC_EXPORT extern const char kAshUiModeClamshell[];
 ASH_PUBLIC_EXPORT extern const char kAshUiModeTablet[];
 ASH_PUBLIC_EXPORT extern const char kAshHideNotificationsForFactory[];
diff --git a/ash/public/cpp/first_run_helper.h b/ash/public/cpp/first_run_helper.h
index 35b57f8b..e0b5ed45 100644
--- a/ash/public/cpp/first_run_helper.h
+++ b/ash/public/cpp/first_run_helper.h
@@ -30,7 +30,7 @@
   // should cancel the tutorial (e.g. the device is shutting down).
   static std::unique_ptr<FirstRunHelper> Start(base::OnceClosure on_cancelled);
 
-  // Returns the bounds of the app list button on the primary display in screen
+  // Returns the bounds of the home button on the primary display in screen
   // coordinates.
   virtual gfx::Rect GetAppListButtonBounds() = 0;
 
diff --git a/ash/public/cpp/login_types.h b/ash/public/cpp/login_types.h
index 343dcc68..2135f39 100644
--- a/ash/public/cpp/login_types.h
+++ b/ash/public/cpp/login_types.h
@@ -292,8 +292,10 @@
 enum class ParentAccessRequestReason {
   // Unlock a Chromebook that is locked due to a Time Limit policy.
   kUnlockTimeLimits,
-  // Change time or timezone of the Chromebook.
+  // Update values on the date time dialog.
   kChangeTime,
+  // Update values on the timezone settings page.
+  kChangeTimezone,
 };
 
 }  // namespace ash
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 5b244c5..9b61987 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -870,7 +870,7 @@
   always_on_top_controller_ = std::make_unique<AlwaysOnTopController>(
       always_on_top_container, pip_container);
 
-  wm::WmSnapToPixelLayoutManager::InstallOnContainers(root);
+  wm::WmDefaultLayoutManager::InstallOnContainers(root);
 
   // Make it easier to resize windows that partially overlap the shelf. Must
   // occur after the ShelfLayoutManager is constructed by ShelfWidget.
@@ -940,7 +940,6 @@
   aura::Window* app_list_tablet_mode_container =
       CreateContainer(kShellWindowId_HomeScreenContainer, "HomeScreenContainer",
                       non_lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(app_list_tablet_mode_container);
   app_list_tablet_mode_container->SetProperty(::wm::kUsesScreenCoordinatesKey,
                                               true);
 
@@ -951,7 +950,6 @@
     aura::Window* container = CreateContainer(
         id, desks_util::GetDeskContainerName(id), non_lock_screen_containers);
     ::wm::SetChildWindowVisibilityChangesAnimated(container);
-    wm::SetSnapsChildrenToPhysicalPixelBoundary(container);
     container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
     wm::SetChildrenUseExtendedHitRegionForWindow(container);
 
@@ -964,52 +962,44 @@
       CreateContainer(kShellWindowId_AlwaysOnTopContainer,
                       "AlwaysOnTopContainer", non_lock_screen_containers);
   ::wm::SetChildWindowVisibilityChangesAnimated(always_on_top_container);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(always_on_top_container);
   always_on_top_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
 
   aura::Window* app_list_container =
       CreateContainer(kShellWindowId_AppListContainer, "AppListContainer",
                       non_lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(app_list_container);
   app_list_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
 
   aura::Window* pip_container = CreateContainer(
       kShellWindowId_PipContainer, "PipContainer", non_lock_screen_containers);
   ::wm::SetChildWindowVisibilityChangesAnimated(pip_container);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(pip_container);
   pip_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
 
   aura::Window* arc_ime_parent_container = CreateContainer(
       kShellWindowId_ArcImeWindowParentContainer, "ArcImeWindowParentContainer",
       non_lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(arc_ime_parent_container);
   arc_ime_parent_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
   arc_ime_parent_container->SetLayoutManager(
       new ArcVirtualKeyboardContainerLayoutManager(arc_ime_parent_container));
   aura::Window* arc_vk_container =
       CreateContainer(kShellWindowId_ArcVirtualKeyboardContainer,
                       "ArcVirtualKeyboardContainer", arc_ime_parent_container);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(arc_vk_container);
   arc_vk_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
 
   aura::Window* shelf_container_parent = lock_screen_related_containers;
   aura::Window* shelf_container = CreateContainer(
       kShellWindowId_ShelfContainer, "ShelfContainer", shelf_container_parent);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(shelf_container);
   shelf_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
   shelf_container->SetProperty(kLockedToRootKey, true);
 
   aura::Window* shelf_bubble_container =
       CreateContainer(kShellWindowId_ShelfBubbleContainer,
                       "ShelfBubbleContainer", non_lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(shelf_bubble_container);
   shelf_bubble_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
   shelf_bubble_container->SetProperty(kLockedToRootKey, true);
 
   aura::Window* modal_container =
       CreateContainer(kShellWindowId_SystemModalContainer,
                       "SystemModalContainer", non_lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(modal_container);
   ::wm::SetChildWindowVisibilityChangesAnimated(modal_container);
   modal_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
   wm::SetChildrenUseExtendedHitRegionForWindow(modal_container);
@@ -1017,13 +1007,11 @@
   aura::Window* lock_container =
       CreateContainer(kShellWindowId_LockScreenContainer, "LockScreenContainer",
                       lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(lock_container);
   lock_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
 
   aura::Window* lock_action_handler_container =
       CreateContainer(kShellWindowId_LockActionHandlerContainer,
                       "LockActionHandlerContainer", lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(lock_action_handler_container);
   ::wm::SetChildWindowVisibilityChangesAnimated(lock_action_handler_container);
   lock_action_handler_container->SetProperty(::wm::kUsesScreenCoordinatesKey,
                                              true);
@@ -1031,7 +1019,6 @@
   aura::Window* lock_modal_container =
       CreateContainer(kShellWindowId_LockSystemModalContainer,
                       "LockSystemModalContainer", lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(lock_modal_container);
   ::wm::SetChildWindowVisibilityChangesAnimated(lock_modal_container);
   lock_modal_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
   wm::SetChildrenUseExtendedHitRegionForWindow(lock_modal_container);
@@ -1039,21 +1026,18 @@
   aura::Window* status_container =
       CreateContainer(kShellWindowId_StatusContainer, "StatusContainer",
                       lock_screen_related_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(status_container);
   status_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
   status_container->SetProperty(kLockedToRootKey, true);
 
   aura::Window* power_menu_container =
       CreateContainer(kShellWindowId_PowerMenuContainer, "PowerMenuContainer",
                       lock_screen_related_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(power_menu_container);
   power_menu_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
 
   aura::Window* settings_bubble_container =
       CreateContainer(kShellWindowId_SettingBubbleContainer,
                       "SettingBubbleContainer", lock_screen_related_containers);
   ::wm::SetChildWindowVisibilityChangesAnimated(settings_bubble_container);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(settings_bubble_container);
   settings_bubble_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
   settings_bubble_container->SetProperty(kLockedToRootKey, true);
 
@@ -1070,8 +1054,6 @@
   aura::Window* virtual_keyboard_parent_container = CreateContainer(
       kShellWindowId_ImeWindowParentContainer, "ImeWindowParentContainer",
       lock_screen_related_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(
-      virtual_keyboard_parent_container);
   virtual_keyboard_parent_container->SetProperty(
       ::wm::kUsesScreenCoordinatesKey, true);
   virtual_keyboard_parent_container->SetLayoutManager(
@@ -1080,7 +1062,6 @@
   aura::Window* virtual_keyboard_container = CreateContainer(
       kShellWindowId_VirtualKeyboardContainer, "VirtualKeyboardContainer",
       virtual_keyboard_parent_container);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(virtual_keyboard_container);
   virtual_keyboard_container->SetProperty(::wm::kUsesScreenCoordinatesKey,
                                           true);
   virtual_keyboard_container->SetLayoutManager(
@@ -1090,26 +1071,22 @@
       CreateContainer(kShellWindowId_MenuContainer, "MenuContainer",
                       lock_screen_related_containers);
   ::wm::SetChildWindowVisibilityChangesAnimated(menu_container);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(menu_container);
   menu_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
 
   aura::Window* autoclick_container = CreateContainer(
       kShellWindowId_AutoclickContainer, "AutoclickBubbleContainer",
       lock_screen_related_containers);
   autoclick_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(autoclick_container);
 
   aura::Window* drag_drop_container = CreateContainer(
       kShellWindowId_DragImageAndTooltipContainer,
       "DragImageAndTooltipContainer", lock_screen_related_containers);
   ::wm::SetChildWindowVisibilityChangesAnimated(drag_drop_container);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(drag_drop_container);
   drag_drop_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
 
   aura::Window* overlay_container =
       CreateContainer(kShellWindowId_OverlayContainer, "OverlayContainer",
                       lock_screen_related_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(overlay_container);
   overlay_container->SetProperty(::wm::kUsesScreenCoordinatesKey, true);
   overlay_container->SetLayoutManager(
       new OverlayLayoutManager(overlay_container));  // Takes ownership.
diff --git a/ash/shelf/assistant_overlay.cc b/ash/shelf/assistant_overlay.cc
index f3a963a4..ab2c2fb 100644
--- a/ash/shelf/assistant_overlay.cc
+++ b/ash/shelf/assistant_overlay.cc
@@ -11,7 +11,7 @@
 
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/ink_drop_button_listener.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
@@ -592,7 +592,7 @@
   DISALLOW_COPY_AND_ASSIGN(AssistantIconBackground);
 };
 
-AssistantOverlay::AssistantOverlay(AppListButton* host_view)
+AssistantOverlay::AssistantOverlay(HomeButton* host_view)
     : ripple_layer_(std::make_unique<ui::Layer>()),
       icon_layer_(std::make_unique<AssistantIcon>()),
       background_layer_(std::make_unique<AssistantIconBackground>()),
diff --git a/ash/shelf/assistant_overlay.h b/ash/shelf/assistant_overlay.h
index 6db62fd4..c53d251 100644
--- a/ash/shelf/assistant_overlay.h
+++ b/ash/shelf/assistant_overlay.h
@@ -15,13 +15,13 @@
 
 namespace ash {
 
-class AppListButton;
+class HomeButton;
 class AssistantIconBackground;
 class AssistantIcon;
 
 class ASH_EXPORT AssistantOverlay : public views::View {
  public:
-  explicit AssistantOverlay(AppListButton* host_view);
+  explicit AssistantOverlay(HomeButton* host_view);
   ~AssistantOverlay() override;
 
   void StartAnimation(bool show_icon);
@@ -55,7 +55,7 @@
   std::unique_ptr<AssistantIcon> icon_layer_;
   std::unique_ptr<AssistantIconBackground> background_layer_;
 
-  AppListButton* host_view_;
+  HomeButton* host_view_;
 
   AnimationState animation_state_ = AnimationState::HIDDEN;
 
diff --git a/ash/shelf/default_shelf_view.cc b/ash/shelf/default_shelf_view.cc
index 0703b959..729a4394 100644
--- a/ash/shelf/default_shelf_view.cc
+++ b/ash/shelf/default_shelf_view.cc
@@ -6,8 +6,8 @@
 
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/shelf_model.h"
-#include "ash/shelf/app_list_button.h"
 #include "ash/shelf/back_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_app_button.h"
@@ -96,7 +96,7 @@
     y = shelf()->PrimaryAxisValue(y, y + kShelfControlSize + button_spacing);
   }
 
-  GetAppListButton()->set_ideal_bounds(gfx::Rect(
+  GetHomeButton()->set_ideal_bounds(gfx::Rect(
       x, y, shelf()->PrimaryAxisValue(kShelfControlSize, kShelfButtonSize),
       shelf()->PrimaryAxisValue(kShelfButtonSize, kShelfControlSize)));
 }
@@ -150,14 +150,14 @@
     // Calculate the bounds for the back and home buttons separately.
     CalculateBackAndHomeButtonsIdealBounds();
 
-    int app_list_button_position =
-        shelf()->PrimaryAxisValue(GetAppListButton()->ideal_bounds().right(),
-                                  GetAppListButton()->ideal_bounds().bottom());
+    int home_button_position =
+        shelf()->PrimaryAxisValue(GetHomeButton()->ideal_bounds().right(),
+                                  GetHomeButton()->ideal_bounds().bottom());
 
-    x = shelf()->PrimaryAxisValue(app_list_button_position, 0);
-    y = shelf()->PrimaryAxisValue(0, app_list_button_position);
+    x = shelf()->PrimaryAxisValue(home_button_position, 0);
+    y = shelf()->PrimaryAxisValue(0, home_button_position);
 
-    // A larger minimum padding after the app list button is required:
+    // A larger minimum padding after the home button is required:
     // increment with the necessary extra amount.
     x += shelf()->PrimaryAxisValue(kAppIconGroupMargin - button_spacing, 0);
     y += shelf()->PrimaryAxisValue(0, kAppIconGroupMargin - button_spacing);
@@ -185,8 +185,7 @@
           kAppIconGroupMargin + (available_size_for_app_icons - icons_size) / 2;
     }
 
-    if (padding_for_centering >
-        app_list_button_position + kAppIconGroupMargin) {
+    if (padding_for_centering > home_button_position + kAppIconGroupMargin) {
       // Only shift buttons to the right, never let them interfere with the
       // left-aligned system buttons.
       x = shelf()->PrimaryAxisValue(padding_for_centering, 0);
@@ -280,12 +279,12 @@
   return std::make_unique<BackButton>(this);
 }
 
-std::unique_ptr<AppListButton> DefaultShelfView::CreateHomeButton() {
-  return std::make_unique<AppListButton>(this, shelf());
+std::unique_ptr<HomeButton> DefaultShelfView::CreateHomeButton() {
+  return std::make_unique<HomeButton>(this, shelf());
 }
 
 int DefaultShelfView::GetAvailableSpaceForAppIcons() const {
-  // Subtract space already allocated to the app list button, and the back
+  // Subtract space already allocated to the home button, and the back
   // button if applicable.
   return shelf()->PrimaryAxisValue(width(), height()) - kShelfButtonSpacing -
          (IsTabletModeEnabled() ? 2 : 1) * kShelfControlSize -
@@ -325,7 +324,7 @@
 
   // An easy way to check whether the apps fit at the exact center of the
   // screen is to imagine that we have another status widget on the other
-  // side (the status widget is always bigger than the app list button plus
+  // side (the status widget is always bigger than the home button plus
   // the back button if applicable) and see if the apps can fit in the middle.
   int available_space_for_screen_centering =
       screen_size - 2 * (status_widget_size + kAppIconGroupMargin);
diff --git a/ash/shelf/default_shelf_view.h b/ash/shelf/default_shelf_view.h
index 163541f7..3fc0f50 100644
--- a/ash/shelf/default_shelf_view.h
+++ b/ash/shelf/default_shelf_view.h
@@ -38,7 +38,7 @@
   void CalculateIdealBounds() override;
   void LayoutAppListAndBackButtonHighlight() override;
   std::unique_ptr<BackButton> CreateBackButton() override;
-  std::unique_ptr<AppListButton> CreateHomeButton() override;
+  std::unique_ptr<HomeButton> CreateHomeButton() override;
 
  private:
   struct AppCenteringStrategy {
@@ -47,7 +47,7 @@
   };
 
   // Returns the size that's actually available for app icons. Size occupied
-  // by the app list button and back button plus all appropriate margins is
+  // by the home button and back button plus all appropriate margins is
   // not available for app icons.
   int GetAvailableSpaceForAppIcons() const;
 
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/home_button.cc
similarity index 82%
rename from ash/shelf/app_list_button.cc
rename to ash/shelf/home_button.cc
index 4a975a8..519bbaf 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/home_button.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/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 
 #include <math.h>  // std::ceil
 
@@ -30,9 +30,9 @@
 }  // namespace
 
 // static
-const char AppListButton::kViewClassName[] = "ash/AppListButton";
+const char HomeButton::kViewClassName[] = "ash/HomeButton";
 
-AppListButton::AppListButton(ShelfView* shelf_view, Shelf* shelf)
+HomeButton::HomeButton(ShelfView* shelf_view, Shelf* shelf)
     : ShelfControlButton(shelf_view), controller_(this, shelf) {
   DCHECK(shelf_view);
   DCHECK(shelf);
@@ -44,29 +44,29 @@
   SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
 }
 
-AppListButton::~AppListButton() = default;
+HomeButton::~HomeButton() = default;
 
-void AppListButton::OnGestureEvent(ui::GestureEvent* event) {
+void HomeButton::OnGestureEvent(ui::GestureEvent* event) {
   if (!controller_.MaybeHandleGestureEvent(event, shelf_view()))
     Button::OnGestureEvent(event);
 }
 
-const char* AppListButton::GetClassName() const {
+const char* HomeButton::GetClassName() const {
   return kViewClassName;
 }
 
-void AppListButton::OnVoiceInteractionAvailabilityChanged() {
+void HomeButton::OnVoiceInteractionAvailabilityChanged() {
   SchedulePaint();
 }
 
-bool AppListButton::IsShowingAppList() const {
+bool HomeButton::IsShowingAppList() const {
   return controller_.is_showing_app_list();
 }
 
-void AppListButton::OnPressed(app_list::AppListShowSource show_source,
-                              base::TimeTicks time_stamp) {
+void HomeButton::OnPressed(app_list::AppListShowSource show_source,
+                           base::TimeTicks time_stamp) {
   ShelfAction shelf_action =
-      Shell::Get()->app_list_controller()->OnAppListButtonPressed(
+      Shell::Get()->app_list_controller()->OnHomeButtonPressed(
           shelf_view()->GetDisplayId(), show_source, time_stamp);
   if (shelf_action == SHELF_ACTION_APP_LIST_DISMISSED) {
     GetInkDrop()->SnapToActivated();
@@ -74,7 +74,7 @@
   }
 }
 
-void AppListButton::PaintButtonContents(gfx::Canvas* canvas) {
+void HomeButton::PaintButtonContents(gfx::Canvas* canvas) {
   gfx::PointF circle_center(GetCenterPoint());
 
   // Paint a white ring as the foreground for the app list circle. The ceil/dsf
@@ -118,8 +118,8 @@
   }
 }
 
-bool AppListButton::DoesIntersectRect(const views::View* target,
-                                      const gfx::Rect& rect) const {
+bool HomeButton::DoesIntersectRect(const views::View* target,
+                                   const gfx::Rect& rect) const {
   DCHECK_EQ(target, this);
   gfx::Rect button_bounds = target->GetLocalBounds();
   // Increase clickable area for the button from
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/home_button.h
similarity index 78%
rename from ash/shelf/app_list_button.h
rename to ash/shelf/home_button.h
index 3606dd8..76abc80 100644
--- a/ash/shelf/app_list_button.h
+++ b/ash/shelf/home_button.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SHELF_APP_LIST_BUTTON_H_
-#define ASH_SHELF_APP_LIST_BUTTON_H_
+#ifndef ASH_SHELF_HOME_BUTTON_H_
+#define ASH_SHELF_HOME_BUTTON_H_
 
 #include <memory>
 
 #include "ash/app_list/app_list_metrics.h"
 #include "ash/ash_export.h"
-#include "ash/shelf/app_list_button_controller.h"
+#include "ash/shelf/home_button_controller.h"
 #include "ash/shelf/shelf_control_button.h"
 #include "base/macros.h"
 #include "ui/views/view_targeter_delegate.h"
@@ -26,13 +26,13 @@
 //
 // If Assistant is enabled, the button is filled in; long-pressing it will
 // launch Assistant.
-class ASH_EXPORT AppListButton : public ShelfControlButton,
-                                 public views::ViewTargeterDelegate {
+class ASH_EXPORT HomeButton : public ShelfControlButton,
+                              public views::ViewTargeterDelegate {
  public:
   static const char kViewClassName[];
 
-  AppListButton(ShelfView* shelf_view, Shelf* shelf);
-  ~AppListButton() override;
+  HomeButton(ShelfView* shelf_view, Shelf* shelf);
+  ~HomeButton() override;
 
   // views::Button:
   void OnGestureEvent(ui::GestureEvent* event) override;
@@ -58,11 +58,11 @@
                          const gfx::Rect& rect) const override;
 
   // The controller used to determine the button's behavior.
-  AppListButtonController controller_;
+  HomeButtonController controller_;
 
-  DISALLOW_COPY_AND_ASSIGN(AppListButton);
+  DISALLOW_COPY_AND_ASSIGN(HomeButton);
 };
 
 }  // namespace ash
 
-#endif  // ASH_SHELF_APP_LIST_BUTTON_H_
+#endif  // ASH_SHELF_HOME_BUTTON_H_
diff --git a/ash/shelf/app_list_button_controller.cc b/ash/shelf/home_button_controller.cc
similarity index 86%
rename from ash/shelf/app_list_button_controller.cc
rename to ash/shelf/home_button_controller.cc
index bd9ba84..67c95abd 100644
--- a/ash/shelf/app_list_button_controller.cc
+++ b/ash/shelf/home_button_controller.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/shelf/app_list_button_controller.h"
+#include "ash/shelf/home_button_controller.h"
 
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/assistant/assistant_controller.h"
 #include "ash/home_screen/home_screen_controller.h"
 #include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/session/session_controller_impl.h"
-#include "ash/shelf/app_list_button.h"
 #include "ash/shelf/assistant_overlay.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
@@ -42,8 +42,7 @@
 
 }  // namespace
 
-AppListButtonController::AppListButtonController(AppListButton* button,
-                                                 Shelf* shelf)
+HomeButtonController::HomeButtonController(HomeButton* button, Shelf* shelf)
     : button_(button), shelf_(shelf) {
   DCHECK(button_);
   DCHECK(shelf_);
@@ -64,7 +63,7 @@
   }
 }
 
-AppListButtonController::~AppListButtonController() {
+HomeButtonController::~HomeButtonController() {
   Shell* shell = Shell::Get();
 
   // AppListController and TabletModeController are destroyed early when Shell
@@ -77,8 +76,8 @@
   VoiceInteractionController::Get()->RemoveLocalObserver(this);
 }
 
-bool AppListButtonController::MaybeHandleGestureEvent(ui::GestureEvent* event,
-                                                      ShelfView* shelf_view) {
+bool HomeButtonController::MaybeHandleGestureEvent(ui::GestureEvent* event,
+                                                   ShelfView* shelf_view) {
   switch (event->type()) {
     case ui::ET_GESTURE_TAP:
     case ui::ET_GESTURE_TAP_CANCEL:
@@ -106,7 +105,7 @@
             base::TimeDelta::FromMilliseconds(
                 kVoiceInteractionAnimationDelayMs),
             base::BindOnce(
-                &AppListButtonController::StartVoiceInteractionAnimation,
+                &HomeButtonController::StartVoiceInteractionAnimation,
                 base::Unretained(this)));
       }
 
@@ -120,7 +119,7 @@
         return false;
 
       base::RecordAction(base::UserMetricsAction(
-          "VoiceInteraction.Started.AppListButtonLongPress"));
+          "VoiceInteraction.Started.HomeButtonLongPress"));
       assistant_overlay_->BurstAnimation();
       event->SetHandled();
       Shell::Get()->shell_state()->SetRootWindowForNewWindows(
@@ -145,7 +144,7 @@
   }
 }
 
-bool AppListButtonController::IsVoiceInteractionAvailable() {
+bool HomeButtonController::IsVoiceInteractionAvailable() {
   VoiceInteractionController* controller = VoiceInteractionController::Get();
   bool settings_enabled = controller->settings_enabled().value_or(false);
   bool consent_given = controller->consent_status() ==
@@ -157,14 +156,14 @@
          (settings_enabled || !consent_given);
 }
 
-bool AppListButtonController::IsVoiceInteractionRunning() {
+bool HomeButtonController::IsVoiceInteractionRunning() {
   return VoiceInteractionController::Get()->voice_interaction_state().value_or(
              mojom::VoiceInteractionState::STOPPED) ==
          mojom::VoiceInteractionState::RUNNING;
 }
 
-void AppListButtonController::OnAppListVisibilityChanged(bool shown,
-                                                         int64_t display_id) {
+void HomeButtonController::OnAppListVisibilityChanged(bool shown,
+                                                      int64_t display_id) {
   if (display::Screen::GetScreen()
           ->GetDisplayNearestWindow(button_->GetWidget()->GetNativeWindow())
           .id() != display_id) {
@@ -176,7 +175,7 @@
     OnAppListDismissed();
 }
 
-void AppListButtonController::OnActiveUserSessionChanged(
+void HomeButtonController::OnActiveUserSessionChanged(
     const AccountId& account_id) {
   button_->OnVoiceInteractionAvailabilityChanged();
   // Initialize voice interaction overlay when primary user session becomes
@@ -187,11 +186,11 @@
   }
 }
 
-void AppListButtonController::OnTabletModeStarted() {
+void HomeButtonController::OnTabletModeStarted() {
   button_->AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
 }
 
-void AppListButtonController::OnVoiceInteractionStatusChanged(
+void HomeButtonController::OnVoiceInteractionStatusChanged(
     mojom::VoiceInteractionState state) {
   button_->OnVoiceInteractionAvailabilityChanged();
 
@@ -230,20 +229,20 @@
   }
 }
 
-void AppListButtonController::OnVoiceInteractionSettingsEnabled(bool enabled) {
+void HomeButtonController::OnVoiceInteractionSettingsEnabled(bool enabled) {
   button_->OnVoiceInteractionAvailabilityChanged();
 }
 
-void AppListButtonController::OnVoiceInteractionConsentStatusUpdated(
+void HomeButtonController::OnVoiceInteractionConsentStatusUpdated(
     mojom::ConsentStatus consent_status) {
   button_->OnVoiceInteractionAvailabilityChanged();
 }
 
-void AppListButtonController::StartVoiceInteractionAnimation() {
+void HomeButtonController::StartVoiceInteractionAnimation() {
   assistant_overlay_->StartAnimation(false);
 }
 
-void AppListButtonController::OnAppListShown() {
+void HomeButtonController::OnAppListShown() {
   // Do not show a highlight if the home screen is available, since the home
   // screen view is always open in the background.
   if (!Shell::Get()->home_screen_controller()->IsHomeScreenAvailable())
@@ -252,13 +251,13 @@
   shelf_->UpdateAutoHideState();
 }
 
-void AppListButtonController::OnAppListDismissed() {
+void HomeButtonController::OnAppListDismissed() {
   button_->AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
   is_showing_app_list_ = false;
   shelf_->UpdateAutoHideState();
 }
 
-void AppListButtonController::InitializeVoiceInteractionOverlay() {
+void HomeButtonController::InitializeVoiceInteractionOverlay() {
   assistant_overlay_ = new AssistantOverlay(button_);
   button_->AddChildView(assistant_overlay_);
   assistant_overlay_->SetVisible(false);
diff --git a/ash/shelf/app_list_button_controller.h b/ash/shelf/home_button_controller.h
similarity index 76%
rename from ash/shelf/app_list_button_controller.h
rename to ash/shelf/home_button_controller.h
index 7203fada..3ffa870b 100644
--- a/ash/shelf/app_list_button_controller.h
+++ b/ash/shelf/home_button_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SHELF_APP_LIST_BUTTON_CONTROLLER_H_
-#define ASH_SHELF_APP_LIST_BUTTON_CONTROLLER_H_
+#ifndef ASH_SHELF_HOME_BUTTON_CONTROLLER_H_
+#define ASH_SHELF_HOME_BUTTON_CONTROLLER_H_
 
 #include <memory>
 
@@ -25,26 +25,26 @@
 namespace ash {
 
 class AssistantOverlay;
-class AppListButton;
+class HomeButton;
 class Shelf;
 class ShelfView;
 
-// Controls behavior of the AppListButton, including a possible long-press
+// Controls behavior of the HomeButton, including a possible long-press
 // action (for Assistant).
-// Behavior is tested indirectly in AppListButtonTest and ShelfViewInkDropTest.
-class AppListButtonController : public AppListControllerObserver,
-                                public SessionObserver,
-                                public TabletModeObserver,
-                                public DefaultVoiceInteractionObserver {
+// Behavior is tested indirectly in HomeButtonTest and ShelfViewInkDropTest.
+class HomeButtonController : public AppListControllerObserver,
+                             public SessionObserver,
+                             public TabletModeObserver,
+                             public DefaultVoiceInteractionObserver {
  public:
-  AppListButtonController(AppListButton* button, Shelf* shelf);
-  ~AppListButtonController() override;
+  HomeButtonController(HomeButton* button, Shelf* shelf);
+  ~HomeButtonController() override;
 
   // Maybe handles a gesture event based on the event and whether voice
   // interaction is available.
   // |shelf_view| is used to determine whether it is safe to animate the ripple.
   //
-  // Returns true if the event is consumed; otherwise, AppListButton should pass
+  // Returns true if the event is consumed; otherwise, HomeButton should pass
   // the event along to Button to consume.
   bool MaybeHandleGestureEvent(ui::GestureEvent* event, ShelfView* shelf_view);
 
@@ -86,7 +86,7 @@
   bool is_showing_app_list_ = false;
 
   // The button that owns this controller.
-  AppListButton* const button_;
+  HomeButton* const button_;
 
   // The shelf the button resides in.
   Shelf* const shelf_;
@@ -98,9 +98,9 @@
   std::unique_ptr<base::OneShotTimer> assistant_animation_hide_delay_timer_;
   base::TimeTicks voice_interaction_start_timestamp_;
 
-  DISALLOW_COPY_AND_ASSIGN(AppListButtonController);
+  DISALLOW_COPY_AND_ASSIGN(HomeButtonController);
 };
 
 }  // namespace ash
 
-#endif  // ASH_SHELF_APP_LIST_BUTTON_CONTROLLER_H_
+#endif  // ASH_SHELF_HOME_BUTTON_CONTROLLER_H_
diff --git a/ash/shelf/app_list_button_unittest.cc b/ash/shelf/home_button_unittest.cc
similarity index 86%
rename from ash/shelf/app_list_button_unittest.cc
rename to ash/shelf/home_button_unittest.cc
index bc5c773..f3eb520 100644
--- a/ash/shelf/app_list_button_unittest.cc
+++ b/ash/shelf/home_button_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/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 
 #include <memory>
 #include <string>
@@ -41,21 +41,18 @@
   return ui::GestureEvent(0, 0, ui::EF_NONE, base::TimeTicks(), details);
 }
 
-// TODO(michaelpg): Rename AppListButtonTest => HomeButtonTest.
-class AppListButtonTest : public AshTestBase {
+class HomeButtonTest : public AshTestBase {
  public:
-  AppListButtonTest() = default;
-  ~AppListButtonTest() override = default;
+  HomeButtonTest() = default;
+  ~HomeButtonTest() override = default;
 
   // AshTestBase:
-  void SetUp() override {
-    AshTestBase::SetUp();
-  }
+  void SetUp() override { AshTestBase::SetUp(); }
 
   void SendGestureEvent(ui::GestureEvent* event) {
     GetPrimaryShelf()
         ->GetShelfViewForTesting()
-        ->GetAppListButton()
+        ->GetHomeButton()
         ->OnGestureEvent(event);
   }
 
@@ -65,25 +62,25 @@
     // Send the gesture event to the secondary display.
     Shelf::ForWindow(Shell::GetAllRootWindows()[1])
         ->GetShelfViewForTesting()
-        ->GetAppListButton()
+        ->GetHomeButton()
         ->OnGestureEvent(event);
   }
 
-  const AppListButton* app_list_button() const {
-    return GetPrimaryShelf()->GetShelfViewForTesting()->GetAppListButton();
+  const HomeButton* home_button() const {
+    return GetPrimaryShelf()->GetShelfViewForTesting()->GetHomeButton();
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(AppListButtonTest);
+  DISALLOW_COPY_AND_ASSIGN(HomeButtonTest);
 };
 
-TEST_F(AppListButtonTest, SwipeUpToOpenFullscreenAppList) {
+TEST_F(HomeButtonTest, SwipeUpToOpenFullscreenAppList) {
   Shelf* shelf = GetPrimaryShelf();
   EXPECT_EQ(SHELF_ALIGNMENT_BOTTOM, shelf->alignment());
 
-  // Start the drags from the center of the app list button.
-  gfx::Point start = app_list_button()->GetCenterPoint();
-  views::View::ConvertPointToScreen(app_list_button(), &start);
+  // Start the drags from the center of the home button.
+  gfx::Point start = home_button()->GetCenterPoint();
+  views::View::ConvertPointToScreen(home_button(), &start);
   // Swiping up less than the threshold should trigger a peeking app list.
   gfx::Point end = start;
   end.set_y(shelf->GetIdealBounds().bottom() -
@@ -110,15 +107,15 @@
   GetAppListTestHelper()->CheckState(ash::AppListViewState::kFullscreenAllApps);
 }
 
-TEST_F(AppListButtonTest, ClickToOpenAppList) {
+TEST_F(HomeButtonTest, ClickToOpenAppList) {
   Shelf* shelf = GetPrimaryShelf();
   EXPECT_EQ(SHELF_ALIGNMENT_BOTTOM, shelf->alignment());
 
-  gfx::Point center = app_list_button()->GetCenterPoint();
-  views::View::ConvertPointToScreen(app_list_button(), &center);
+  gfx::Point center = home_button()->GetCenterPoint();
+  views::View::ConvertPointToScreen(home_button(), &center);
   GetEventGenerator()->MoveMouseTo(center);
 
-  // Click on the app list button should toggle the app list.
+  // Click on the home button should toggle the app list.
   GetEventGenerator()->ClickLeftButton();
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
@@ -145,7 +142,7 @@
   GetAppListTestHelper()->CheckState(ash::AppListViewState::kClosed);
 }
 
-TEST_F(AppListButtonTest, ButtonPositionInTabletMode) {
+TEST_F(HomeButtonTest, ButtonPositionInTabletMode) {
   // Finish all setup tasks. In particular we want to finish the
   // GetSwitchStates post task in (Fake)PowerManagerClient which is triggered
   // by TabletModeController otherwise this will cause tablet mode to exit
@@ -155,14 +152,14 @@
   ShelfViewTestAPI test_api(GetPrimaryShelf()->GetShelfViewForTesting());
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
   test_api.RunMessageLoopUntilAnimationsDone();
-  EXPECT_GT(app_list_button()->bounds().x(), 0);
+  EXPECT_GT(home_button()->bounds().x(), 0);
 
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
   test_api.RunMessageLoopUntilAnimationsDone();
-  EXPECT_EQ(ShelfConstants::button_spacing(), app_list_button()->bounds().x());
+  EXPECT_EQ(ShelfConstants::button_spacing(), home_button()->bounds().x());
 }
 
-TEST_F(AppListButtonTest, LongPressGesture) {
+TEST_F(HomeButtonTest, LongPressGesture) {
   ui::ScopedAnimationDurationScaleMode animation_duration_mode(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
   // Simulate two user with primary user as active.
@@ -197,7 +194,7 @@
                                                ->visibility());
 }
 
-TEST_F(AppListButtonTest, LongPressGestureWithSecondaryUser) {
+TEST_F(HomeButtonTest, LongPressGestureWithSecondaryUser) {
   // Disallowed by secondary user.
   VoiceInteractionController::Get()->NotifyFeatureAllowed(
       mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER);
@@ -224,7 +221,7 @@
                                                ->visibility());
 }
 
-TEST_F(AppListButtonTest, LongPressGestureWithSettingsDisabled) {
+TEST_F(HomeButtonTest, LongPressGestureWithSettingsDisabled) {
   // Simulate two user with primary user as active.
   CreateUserSessions(2);
 
@@ -252,7 +249,7 @@
                                                ->visibility());
 }
 
-class KioskNextHomeButtonTest : public AppListButtonTest {
+class KioskNextHomeButtonTest : public HomeButtonTest {
  public:
   KioskNextHomeButtonTest() {
     scoped_feature_list_.InitAndEnableFeature(features::kKioskNextShell);
@@ -260,13 +257,13 @@
 
   void SetUp() override {
     set_start_session(false);
-    AppListButtonTest::SetUp();
+    HomeButtonTest::SetUp();
     client_ = std::make_unique<MockKioskNextShellClient>();
   }
 
   void TearDown() override {
     client_.reset();
-    AppListButtonTest::TearDown();
+    HomeButtonTest::TearDown();
   }
 
  private:
@@ -289,8 +286,8 @@
   test_api.RunMessageLoopUntilAnimationsDone();
 
   // Tapping the home button should exit Overview mode.
-  gfx::Point center = app_list_button()->GetCenterPoint();
-  views::View::ConvertPointToScreen(app_list_button(), &center);
+  gfx::Point center = home_button()->GetCenterPoint();
+  views::View::ConvertPointToScreen(home_button(), &center);
   GetEventGenerator()->GestureTapDownAndUp(center);
   test_api.RunMessageLoopUntilAnimationsDone();
   EXPECT_FALSE(overview_controller->InOverviewSession());
diff --git a/ash/shelf/kiosk_next_shelf_view.cc b/ash/shelf/kiosk_next_shelf_view.cc
index dc55061..4b1fb4e4 100644
--- a/ash/shelf/kiosk_next_shelf_view.cc
+++ b/ash/shelf/kiosk_next_shelf_view.cc
@@ -9,8 +9,8 @@
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/home_screen/home_screen_controller.h"
 #include "ash/public/cpp/shelf_model.h"
-#include "ash/shelf/app_list_button.h"
 #include "ash/shelf/back_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
@@ -63,17 +63,17 @@
 };
 
 // Kiosk Next home button with permanent round rectangle background.
-class KioskNextHomeButton : public AppListButton {
+class KioskNextHomeButton : public HomeButton {
  public:
   KioskNextHomeButton(ShelfView* shelf_view, Shelf* shelf)
-      : AppListButton(shelf_view, shelf) {}
+      : HomeButton(shelf_view, shelf) {}
   ~KioskNextHomeButton() override = default;
 
  private:
-  // views::AppListButton:
+  // views::HomeButton:
   void PaintButtonContents(gfx::Canvas* canvas) override {
     PaintBackground(canvas, GetContentsBounds());
-    AppListButton::PaintButtonContents(canvas);
+    HomeButton::PaintButtonContents(canvas);
   }
 
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override {
@@ -151,7 +151,7 @@
   GetBackButton()->set_ideal_bounds(gfx::Rect(
       x, y, kKioskNextShelfControlWidthDp, kKioskNextShelfControlHeightDp));
   x += (kKioskNextShelfControlWidthDp + control_buttons_spacing);
-  GetAppListButton()->set_ideal_bounds(gfx::Rect(
+  GetHomeButton()->set_ideal_bounds(gfx::Rect(
       x, y, kKioskNextShelfControlWidthDp, kKioskNextShelfControlHeightDp));
 }
 
@@ -159,7 +159,7 @@
   return std::make_unique<KioskNextBackButton>(this);
 }
 
-std::unique_ptr<AppListButton> KioskNextShelfView::CreateHomeButton() {
+std::unique_ptr<HomeButton> KioskNextShelfView::CreateHomeButton() {
   return std::make_unique<KioskNextHomeButton>(this, shelf());
 }
 
diff --git a/ash/shelf/kiosk_next_shelf_view.h b/ash/shelf/kiosk_next_shelf_view.h
index 35a6259a..31e9394 100644
--- a/ash/shelf/kiosk_next_shelf_view.h
+++ b/ash/shelf/kiosk_next_shelf_view.h
@@ -35,7 +35,7 @@
   void Init() override;
   void CalculateIdealBounds() override;
   std::unique_ptr<BackButton> CreateBackButton() override;
-  std::unique_ptr<AppListButton> CreateHomeButton() override;
+  std::unique_ptr<HomeButton> CreateHomeButton() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(KioskNextShelfView);
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index df2b89d..0324a082 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -15,6 +15,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf_bezel_event_handler.h"
 #include "ash/shelf/shelf_controller.h"
+#include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_observer.h"
 #include "ash/shelf/shelf_widget.h"
@@ -60,7 +61,8 @@
 
 Shelf::Shelf()
     : shelf_locking_manager_(this),
-      bezel_event_handler_(std::make_unique<ShelfBezelEventHandler>(this)) {}
+      bezel_event_handler_(std::make_unique<ShelfBezelEventHandler>(this)),
+      shelf_focus_cycler_(std::make_unique<ShelfFocusCycler>(this)) {}
 
 Shelf::~Shelf() = default;
 
diff --git a/ash/shelf/shelf.h b/ash/shelf/shelf.h
index ed19ab8..9a37ed1 100644
--- a/ash/shelf/shelf.h
+++ b/ash/shelf/shelf.h
@@ -31,6 +31,7 @@
 
 enum class AnimationChangeType;
 class ShelfBezelEventHandler;
+class ShelfFocusCycler;
 class ShelfLayoutManager;
 class ShelfLayoutManagerTest;
 class ShelfLockingManager;
@@ -174,6 +175,9 @@
   ShelfAutoHideBehavior auto_hide_behavior() const {
     return auto_hide_behavior_;
   }
+
+  ShelfFocusCycler* shelf_focus_cycler() { return shelf_focus_cycler_.get(); }
+
   void set_is_tablet_mode_animation_running(bool value) {
     is_tablet_mode_animation_running_ = value;
   }
@@ -220,12 +224,15 @@
   // Forwards touch gestures on a bezel sensor to the shelf.
   std::unique_ptr<ShelfBezelEventHandler> bezel_event_handler_;
 
+  // Hands focus off to different parts of the shelf.
+  std::unique_ptr<ShelfFocusCycler> shelf_focus_cycler_;
+
   // True while the animation to enter or exit tablet mode is running. Sometimes
   // this value is true when the shelf movements are not actually animating
   // (animation value = 0.0). This is because this is set to true when we
   // enter/exit tablet mode but the animation is not started until a shelf
   // OnBoundsChanged is called because of tablet mode. Use this value to sync
-  // the animation for AppListButton.
+  // the animation for HomeButton.
   bool is_tablet_mode_animation_running_ = false;
 
   // Used by ScopedAutoHideLock to maintain the state of the lock for auto-hide
diff --git a/ash/shelf/shelf_constants.h b/ash/shelf/shelf_constants.h
index 57e455b..64d1d278 100644
--- a/ash/shelf/shelf_constants.h
+++ b/ash/shelf/shelf_constants.h
@@ -21,7 +21,7 @@
 // Size of the icons within shelf buttons.
 constexpr int kShelfButtonIconSize = 44;
 
-// Size for controls like the app list button, back button, etc.
+// Size for controls like the home button, back button, etc.
 constexpr int kShelfControlSize = 40;
 
 ASH_EXPORT constexpr SkColor kShelfControlPermanentHighlightBackground =
diff --git a/ash/shelf/shelf_context_menu_model.cc b/ash/shelf/shelf_context_menu_model.cc
index dd5f546..773569c 100644
--- a/ash/shelf/shelf_context_menu_model.cc
+++ b/ash/shelf/shelf_context_menu_model.cc
@@ -59,7 +59,7 @@
 ShelfContextMenuModel::ShelfContextMenuModel(ShelfItemDelegate* delegate,
                                              int64_t display_id)
     : ui::SimpleMenuModel(this), delegate_(delegate), display_id_(display_id) {
-  // Add shelf and wallpaper items if ShelfView or AppListButton are selected.
+  // Add shelf and wallpaper items if ShelfView or HomeButton are selected.
   if (!delegate || delegate_->app_id() == kAppListId)
     AddShelfAndWallpaperItems();
 }
diff --git a/ash/shelf/shelf_focus_cycler.cc b/ash/shelf/shelf_focus_cycler.cc
new file mode 100644
index 0000000..c7b704b
--- /dev/null
+++ b/ash/shelf/shelf_focus_cycler.cc
@@ -0,0 +1,57 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/shelf/shelf_focus_cycler.h"
+
+#include "ash/focus_cycler.h"
+#include "ash/shelf/login_shelf_view.h"
+#include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "ash/system/status_area_widget.h"
+#include "ash/system/status_area_widget_delegate.h"
+#include "ash/system/tray/system_tray_notifier.h"
+
+namespace ash {
+
+ShelfFocusCycler::ShelfFocusCycler(Shelf* shelf) : shelf_(shelf) {}
+
+void ShelfFocusCycler::FocusOut(bool reverse, SourceView source_view) {
+  switch (source_view) {
+    case SourceView::kShelfView: {
+      StatusAreaWidget* status_area_widget = shelf_->GetStatusAreaWidget();
+      status_area_widget->status_area_widget_delegate()
+          ->set_default_last_focusable_child(reverse);
+      Shell::Get()->focus_cycler()->FocusWidget(status_area_widget);
+      break;
+    }
+    case SourceView::kSystemTrayView: {
+      // Focus can move to the shelf, or the focus will be delegated to system
+      // tray focus observers.
+      bool should_focus_shelf = true;
+      // If we are using a views-based shelf:
+      // * If we're in an active session, always bring focus to the shelf
+      //   whether we are going in reverse or not.
+      // * Otherwise (login/lock screen, OOBE), bring focus to the shelf only
+      //   if we're going in reverse; if we're going forward, let the system
+      //   tray focus observers focus the lock/login view.
+      ShelfWidget* shelf_widget = shelf_->shelf_widget();
+      if (shelf_widget->login_shelf_view()->GetVisible()) {
+        // Login/lock screen or OOBE.
+        should_focus_shelf = reverse;
+      }
+
+      if (should_focus_shelf) {
+        shelf_widget->set_default_last_focusable_child(reverse);
+        shelf_widget->set_activated_from_other_widget(true);
+        Shell::Get()->focus_cycler()->FocusWidget(shelf_widget);
+        shelf_widget->FocusFirstOrLastFocusableChild(reverse);
+      } else {
+        Shell::Get()->system_tray_notifier()->NotifyFocusOut(reverse);
+      }
+      break;
+    }
+  }
+}
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/shelf/shelf_focus_cycler.h b/ash/shelf/shelf_focus_cycler.h
new file mode 100644
index 0000000..2bd0ced
--- /dev/null
+++ b/ash/shelf/shelf_focus_cycler.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SHELF_SHELF_FOCUS_CYCLER_H_
+#define ASH_SHELF_SHELF_FOCUS_CYCLER_H_
+
+#include "base/macros.h"
+
+namespace ash {
+class Shelf;
+
+// The containers that rely on ShelfFocusCycler to move focus outside of their
+// view trees.
+enum class SourceView {
+  kShelfView = 0,
+  kSystemTrayView,
+};
+
+// ShelfFocusCycler handles the special focus transitions from the Login UI,
+// Shelf, and Status Tray.
+class ShelfFocusCycler {
+ public:
+  explicit ShelfFocusCycler(Shelf* shelf);
+  ~ShelfFocusCycler() = default;
+
+  // Moves focus from one container to the next. |reverse| will move the focus
+  // to the container to the left in LTR. RTL does not need to be accounted
+  // for when calling this function.
+  void FocusOut(bool reverse, SourceView source_view);
+
+ private:
+  // Owned by RootWindowController.
+  Shelf* shelf_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShelfFocusCycler);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SHELF_SHELF_FOCUS_CYCLER_H_
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index b6ce992..07625b5d 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -605,7 +605,7 @@
 
 void ShelfLayoutManager::SetChildBounds(aura::Window* child,
                                         const gfx::Rect& requested_bounds) {
-  wm::WmSnapToPixelLayoutManager::SetChildBounds(child, requested_bounds);
+  wm::WmDefaultLayoutManager::SetChildBounds(child, requested_bounds);
   // We may contain other widgets (such as frame maximize bubble) but they don't
   // effect the layout in anyway.
   if (!updating_bounds_ &&
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 1e9b8b5..c84c56f 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -19,7 +19,7 @@
 #include "ash/system/locale/locale_update_controller_impl.h"
 #include "ash/wm/lock_state_observer.h"
 #include "ash/wm/overview/overview_observer.h"
-#include "ash/wm/wm_snap_to_pixel_layout_manager.h"
+#include "ash/wm/wm_default_layout_manager.h"
 #include "ash/wm/workspace/workspace_types.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
@@ -61,7 +61,7 @@
                                       public OverviewObserver,
                                       public ::wm::ActivationChangeObserver,
                                       public LockStateObserver,
-                                      public wm::WmSnapToPixelLayoutManager,
+                                      public wm::WmDefaultLayoutManager,
                                       public display::DisplayObserver,
                                       public SessionObserver,
                                       public WallpaperControllerObserver,
@@ -129,7 +129,7 @@
   // Cancel the drag if the shelf is in drag progress.
   void CancelDragOnShelfIfInProgress();
 
-  // wm::WmSnapToPixelLayoutManager:
+  // wm::WmDefaultLayoutManager:
   void OnWindowResized() override;
   void SetChildBounds(aura::Window* child,
                       const gfx::Rect& requested_bounds) override;
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index a56dc92..fd1d8a91 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -32,7 +32,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
 #include "ash/session/session_controller_impl.h"
-#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/shelf_controller.h"
@@ -88,8 +88,8 @@
 namespace ash {
 namespace {
 
-void PressAppListButton() {
-  Shell::Get()->app_list_controller()->OnAppListButtonPressed(
+void PressHomeButton() {
+  Shell::Get()->app_list_controller()->OnHomeButtonPressed(
       display::Screen::GetScreen()->GetPrimaryDisplay().id(),
       app_list::AppListShowSource::kShelfButton, base::TimeTicks());
 }
@@ -1862,7 +1862,7 @@
 
   // Calculate drag start point and end point. |start_point| and |target_point|
   // make sure that mouse event is received by Shelf/AppListView instead of
-  // child views (like AppListButton and SearchBoxView).
+  // child views (like HomeButton and SearchBoxView).
   int x = shelf_bounds_in_screen.x() + shelf_bounds_in_screen.width() / 4;
   int y = shelf_bounds_in_screen.CenterPoint().y();
   gfx::Point start_point(x, y);
@@ -2685,7 +2685,7 @@
       &update_event, GetShelfWidget()->GetNativeView());
 
   // Emulate to press the AppList button while dragging the Shelf.
-  PressAppListButton();
+  PressHomeButton();
   EXPECT_TRUE(GetPrimaryShelf()->IsVisible());
 
   // Release the press.
@@ -2707,7 +2707,7 @@
   // things:
   // (1) Shelf is hidden
   // (2) Shelf has correct bounds in screen coordinate.
-  PressAppListButton();
+  PressHomeButton();
   EXPECT_EQ(GetScreenAvailableBounds().bottom_left() +
                 gfx::Point(0, -kHiddenShelfInScreenPortion).OffsetFromOrigin(),
             GetPrimaryShelf()
@@ -2741,10 +2741,10 @@
   GetPrimaryShelf()->shelf_widget()->OnGestureEvent(&update_event);
 
   // Press the AppList button by mouse.
-  views::View* app_list_button =
-      GetPrimaryShelf()->GetShelfViewForTesting()->GetAppListButton();
+  views::View* home_button =
+      GetPrimaryShelf()->GetShelfViewForTesting()->GetHomeButton();
   GetEventGenerator()->MoveMouseTo(
-      app_list_button->GetBoundsInScreen().CenterPoint());
+      home_button->GetBoundsInScreen().CenterPoint());
   GetEventGenerator()->ClickLeftButton();
 
   // End the gesture event.
@@ -3023,14 +3023,14 @@
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
 
-  // Tap app list button should not open the app list and shelf should keep
+  // Tap home button should not open the app list and shelf should keep
   // hidden.
-  gfx::Rect app_list_button_bounds =
-      shelf->GetShelfViewForTesting()->GetAppListButton()->GetBoundsInScreen();
+  gfx::Rect home_button_bounds =
+      shelf->GetShelfViewForTesting()->GetHomeButton()->GetBoundsInScreen();
   gfx::Rect display_bounds =
       display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
-  app_list_button_bounds.Intersect(display_bounds);
-  GetEventGenerator()->GestureTapAt(app_list_button_bounds.CenterPoint());
+  home_button_bounds.Intersect(display_bounds);
+  GetEventGenerator()->GestureTapAt(home_button_bounds.CenterPoint());
   GetAppListTestHelper()->CheckVisibility(false);
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
 }
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index d7e1d68..83fc073 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ash/public/cpp/shelf_model.h"
-#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_bubble.h"
 #include "ash/shelf/shelf_view.h"
@@ -47,14 +47,14 @@
 };
 
 TEST_F(ShelfTooltipManagerTest, ShowTooltip) {
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   EXPECT_TRUE(tooltip_manager_->IsVisible());
   EXPECT_FALSE(IsTimerRunning());
 }
 
 TEST_F(ShelfTooltipManagerTest, ShowTooltipWithDelay) {
   // ShowTooltipWithDelay should start the timer instead of showing immediately.
-  tooltip_manager_->ShowTooltipWithDelay(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltipWithDelay(shelf_view_->GetHomeButton());
   EXPECT_FALSE(tooltip_manager_->IsVisible());
   EXPECT_TRUE(IsTimerRunning());
   // TODO: Test that the delayed tooltip is shown, without flaky failures.
@@ -93,7 +93,7 @@
 }
 
 TEST_F(ShelfTooltipManagerTest, HideWhenShelfIsHidden) {
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
 
   // Create a full-screen window to hide the shelf.
@@ -105,18 +105,18 @@
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
   // Do not show the view if the shelf is hidden.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
   // ShowTooltipWithDelay doesn't even start the timer for the hidden shelf.
-  tooltip_manager_->ShowTooltipWithDelay(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltipWithDelay(shelf_view_->GetHomeButton());
   EXPECT_FALSE(IsTimerRunning());
 }
 
 TEST_F(ShelfTooltipManagerTest, HideWhenShelfIsAutoHideHidden) {
   // Create a visible window so auto-hide behavior can actually hide the shelf.
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
 
   GetPrimaryShelf()->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
@@ -127,11 +127,11 @@
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
   // Do not show the view if the shelf is hidden.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
   // ShowTooltipWithDelay doesn't even run the timer for the hidden shelf.
-  tooltip_manager_->ShowTooltipWithDelay(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltipWithDelay(shelf_view_->GetHomeButton());
   EXPECT_FALSE(IsTimerRunning());
 
   // Close the window to show the auto-hide shelf; tooltips should now show.
@@ -142,11 +142,11 @@
   ASSERT_EQ(SHELF_AUTO_HIDE_SHOWN, GetPrimaryShelf()->GetAutoHideState());
 
   // The tooltip should show for an auto-hide-shown shelf.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   EXPECT_TRUE(tooltip_manager_->IsVisible());
 
   // ShowTooltipWithDelay should run the timer for an auto-hide-shown shelf.
-  tooltip_manager_->ShowTooltipWithDelay(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltipWithDelay(shelf_view_->GetHomeButton());
   EXPECT_TRUE(IsTimerRunning());
 }
 
@@ -155,28 +155,28 @@
   gfx::Rect shelf_bounds = shelf_view_->GetBoundsInScreen();
 
   // Should hide if the mouse exits the shelf area.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
   generator->MoveMouseTo(shelf_bounds.CenterPoint());
   generator->SendMouseExit();
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
   // Should hide if the mouse is pressed in the shelf area.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
   generator->MoveMouseTo(shelf_bounds.CenterPoint());
   generator->PressLeftButton();
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
   // Should hide for touch events in the shelf.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
   generator->set_current_screen_location(shelf_bounds.CenterPoint());
   generator->PressTouch();
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
   // Should hide for gesture events in the shelf.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
   generator->GestureTapDownAndUp(shelf_bounds.CenterPoint());
   EXPECT_FALSE(tooltip_manager_->IsVisible());
@@ -186,7 +186,7 @@
   ui::test::EventGenerator* generator = GetEventGenerator();
 
   // Should hide for touches outside the shelf.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
   generator->set_current_screen_location(gfx::Point());
   generator->PressTouch();
@@ -194,7 +194,7 @@
   generator->ReleaseTouch();
 
   // Should hide for touch events on the tooltip.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
   generator->set_current_screen_location(
       GetTooltip()->GetWindowBoundsInScreen().CenterPoint());
@@ -203,7 +203,7 @@
   generator->ReleaseTouch();
 
   // Should hide for gestures outside the shelf.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
   generator->GestureTapDownAndUp(gfx::Point());
   EXPECT_FALSE(tooltip_manager_->IsVisible());
@@ -213,14 +213,14 @@
   ui::test::EventGenerator* generator = GetEventGenerator();
 
   // Should hide when 'Esc' is pressed.
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   ASSERT_TRUE(tooltip_manager_->IsVisible());
   generator->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 }
 
 TEST_F(ShelfTooltipManagerTest, ShelfTooltipDoesNotAffectPipWindow) {
-  tooltip_manager_->ShowTooltip(shelf_view_->GetAppListButton());
+  tooltip_manager_->ShowTooltip(shelf_view_->GetHomeButton());
   EXPECT_TRUE(tooltip_manager_->IsVisible());
 
   auto display = display::Screen::GetScreen()->GetPrimaryDisplay();
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 5089da9..094cf0dd 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -20,9 +20,9 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/scoped_root_window_for_new_windows.h"
 #include "ash/screen_util.h"
-#include "ash/shelf/app_list_button.h"
 #include "ash/shelf/back_button.h"
 #include "ash/shelf/default_shelf_view.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_app_button.h"
@@ -31,6 +31,7 @@
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/shelf_context_menu_model.h"
 #include "ash/shelf/shelf_controller.h"
+#include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/shelf/shelf_menu_model_adapter.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
@@ -38,8 +39,6 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/model/virtual_keyboard_model.h"
-#include "ash/system/status_area_widget.h"
-#include "ash/system/status_area_widget_delegate.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/root_window_finder.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
@@ -168,7 +167,7 @@
     if (IsTabletModeEnabled())
       focusable_views.push_back(main_shelf->GetBackButton());
 
-    focusable_views.push_back(main_shelf->GetAppListButton());
+    focusable_views.push_back(main_shelf->GetHomeButton());
     for (int i = main_shelf->first_visible_index();
          i <= main_shelf->last_visible_index(); ++i) {
       focusable_views.push_back(main_shelf->view_model()->view_at(i));
@@ -354,7 +353,7 @@
   ConfigureChildView(back_button_ptr.get());
   back_button_ = AddChildView(std::move(back_button_ptr));
 
-  std::unique_ptr<AppListButton> home_button_ptr = CreateHomeButton();
+  std::unique_ptr<HomeButton> home_button_ptr = CreateHomeButton();
   ConfigureChildView(home_button_ptr.get());
   home_button_ = AddChildView(std::move(home_button_ptr));
   home_button_->set_context_menu_controller(this);
@@ -379,7 +378,7 @@
 
 gfx::Rect ShelfView::GetIdealBoundsOfItemIcon(const ShelfID& id) {
   if (id == ShelfID(kAppListId))
-    return GetMirroredRect(GetAppListButton()->ideal_bounds());
+    return GetMirroredRect(GetHomeButton()->ideal_bounds());
   if (id == ShelfID(kBackButtonId))
     return GetMirroredRect(GetBackButton()->ideal_bounds());
 
@@ -417,7 +416,7 @@
   return overflow_bubble_.get() && overflow_bubble_->IsShowing();
 }
 
-AppListButton* ShelfView::GetAppListButton() const {
+HomeButton* ShelfView::GetHomeButton() const {
   return home_button_;
 }
 
@@ -445,21 +444,21 @@
 }
 
 bool ShelfView::ShouldHideTooltip(const gfx::Point& cursor_location) const {
-  // If this is the app list button, only show the tooltip if the app list is
+  // If this is the home button, only show the tooltip if the app list is
   // not already showing.
-  const AppListButton* app_list_button = GetAppListButton();
-  if (app_list_button &&
-      app_list_button->GetMirroredBounds().Contains(cursor_location)) {
-    return app_list_button->IsShowingAppList();
+  const HomeButton* home_button = GetHomeButton();
+  if (home_button &&
+      home_button->GetMirroredBounds().Contains(cursor_location)) {
+    return home_button->IsShowingAppList();
   }
   return !visible_shelf_item_bounds_union_.Contains(cursor_location);
 }
 
 bool ShelfView::ShouldShowTooltipForView(const views::View* view) const {
-  // If this is the app list button, only show the tooltip if the app list is
+  // If this is the home button, only show the tooltip if the app list is
   // not already showing.
-  if (view == GetAppListButton())
-    return !GetAppListButton()->IsShowingAppList();
+  if (view == GetHomeButton())
+    return !GetHomeButton()->IsShowingAppList();
   if (view == overflow_button_)
     return true;
   // Don't show a tooltip for a view that's currently being dragged.
@@ -517,10 +516,10 @@
   if (model_->item_count() == 0) {
     // There are no apps.
     return shelf_->IsHorizontalAlignment()
-               ? gfx::Size(GetAppListButton()->bounds().right(),
+               ? gfx::Size(GetHomeButton()->bounds().right(),
                            ShelfConstants::shelf_size())
                : gfx::Size(ShelfConstants::shelf_size(),
-                           GetAppListButton()->bounds().bottom());
+                           GetHomeButton()->bounds().bottom());
   }
 
   int last_button_index = last_visible_index_;
@@ -633,13 +632,13 @@
     return;
   }
 
-  if (sender == GetAppListButton()) {
+  if (sender == GetHomeButton()) {
     Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);
     const app_list::AppListShowSource show_source =
         event.IsShiftDown() ? app_list::kShelfButtonFullscreen
                             : app_list::kShelfButton;
-    GetAppListButton()->OnPressed(show_source, event.time_stamp());
+    GetHomeButton()->OnPressed(show_source, event.time_stamp());
     return;
   }
 
@@ -859,14 +858,14 @@
   if (is_overflow_mode())
     return main_shelf()->FindFirstFocusableChild();
   return IsTabletModeEnabled() ? static_cast<views::View*>(GetBackButton())
-                               : static_cast<views::View*>(GetAppListButton());
+                               : static_cast<views::View*>(GetHomeButton());
 }
 
 views::View* ShelfView::FindLastFocusableChild() {
   if (IsShowingOverflowBubble())
     return overflow_shelf()->FindLastFocusableChild();
   if (view_model_->view_size() == 0)
-    return static_cast<views::View*>(GetAppListButton());
+    return static_cast<views::View*>(GetHomeButton());
   return overflow_button_->GetVisible()
              ? overflow_button_
              : view_model_->view_at(last_visible_index());
@@ -885,18 +884,8 @@
     return;
   }
 
-  // The logic here seems backwards, but is actually correct. For instance if
-  // the ShelfView's internal focus cycling logic attemmpts to focus the first
-  // child (e.g. app list button) after hitting Tab, we intercept that and
-  // instead, advance through to the status area.
-  if ((reverse && button == FindLastFocusableChild()) ||
-      (!reverse && button == FindFirstFocusableChild())) {
-    StatusAreaWidget* status_area_widget =
-        Shelf::ForWindow(GetWidget()->GetNativeWindow())->GetStatusAreaWidget();
-    status_area_widget->status_area_widget_delegate()
-        ->set_default_last_focusable_child(reverse);
-    Shell::Get()->focus_cycler()->FocusWidget(status_area_widget);
-  }
+  if (ShouldFocusOut(reverse, button))
+    shelf_->shelf_focus_cycler()->FocusOut(reverse, SourceView::kShelfView);
 }
 
 int64_t ShelfView::GetDisplayId() const {
@@ -1052,7 +1041,7 @@
 
   // The back and app buttons are not part of the view model, treat them
   // separately.
-  if (view == GetAppListButton() || view == GetBackButton())
+  if (view == GetHomeButton() || view == GetBackButton())
     return !repost;
 
   // Ignore if this is a repost event on the last pressed shelf item.
@@ -1114,7 +1103,7 @@
     return;  // View is being deleted, ignore request.
 
   // The home and back buttons should be handled separately.
-  DCHECK(view != GetAppListButton() || view != GetBackButton());
+  DCHECK(view != GetHomeButton() || view != GetBackButton());
 
   // Only when the repost event occurs on the same shelf item, we should ignore
   // the call in ShelfView::ButtonPressed(...).
@@ -1225,7 +1214,7 @@
   // Handle back and home separately.
   ShelfControlButton* back = GetBackButton();
   bounds_animator_->AnimateViewTo(back, back->ideal_bounds());
-  ShelfControlButton* home = GetAppListButton();
+  ShelfControlButton* home = GetHomeButton();
   bounds_animator_->AnimateViewTo(home, home->ideal_bounds());
   if (home->border())
     home->SetBorder(views::NullBorder());
@@ -1640,6 +1629,15 @@
   return false;
 }
 
+bool ShelfView::ShouldFocusOut(bool reverse, views::View* button) {
+  // The logic here seems backwards, but is actually correct. For instance if
+  // the ShelfView's internal focus cycling logic attemmpts to focus the first
+  // child (e.g. home button) after hitting Tab, we intercept that and
+  // instead, advance through to the status area.
+  return (reverse && button == FindLastFocusableChild()) ||
+         (!reverse && button == FindFirstFocusableChild());
+}
+
 std::pair<int, int> ShelfView::GetDragRange(int index) {
   int min_index = -1;
   int max_index = -1;
@@ -2050,10 +2048,10 @@
   tooltip_.Close();
   if (overflow_bubble_)
     overflow_bubble_->Hide();
-  // For crbug.com/587931, because AppListButton layout logic is in OnPaint.
-  AppListButton* app_list_button = GetAppListButton();
-  if (app_list_button)
-    app_list_button->SchedulePaint();
+  // For crbug.com/587931, because HomeButton layout logic is in OnPaint.
+  HomeButton* home_button = GetHomeButton();
+  if (home_button)
+    home_button->SchedulePaint();
 
   AnnounceShelfAlignment();
 }
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index f20e1f46a..fe0ea61b 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -47,7 +47,7 @@
 }  // namespace views
 
 namespace ash {
-class AppListButton;
+class HomeButton;
 class BackButton;
 class DragImageView;
 class OverflowBubble;
@@ -151,7 +151,7 @@
 
   void set_focused_button(ShelfButton* focused) { focused_button_ = focused; }
 
-  AppListButton* GetAppListButton() const;
+  HomeButton* GetHomeButton() const;
   BackButton* GetBackButton() const;
   OverflowButton* GetOverflowButton() const;
 
@@ -270,7 +270,7 @@
 
   // The three methods below return the first or last focusable child of the
   // set including both the main shelf and the overflow shelf it it's showing.
-  // - The first focusable child is either the app list button, or the back
+  // - The first focusable child is either the home button, or the back
   //   button in tablet mode.
   // - The last focusable child can be either 1) the last app icon on the main
   //   shelf if there aren't enough apps to overflow, 2) the overflow button
@@ -294,6 +294,7 @@
   // Returns the main shelf. This can be called on either the main shelf
   // or the overflow shelf.
   ShelfView* main_shelf() { return main_shelf_ ? main_shelf_ : this; }
+
   // Returns the overflow shelf. This can be called on either the main shelf
   // or the overflow shelf. Returns nullptr if the overflow shelf isn't visible.
   ShelfView* overflow_shelf() {
@@ -340,7 +341,7 @@
   virtual views::View* CreateViewForItem(const ShelfItem& item);
 
   virtual std::unique_ptr<BackButton> CreateBackButton() = 0;
-  virtual std::unique_ptr<AppListButton> CreateHomeButton() = 0;
+  virtual std::unique_ptr<HomeButton> CreateHomeButton() = 0;
 
   // Lays out control buttons background.
   // Child classes should implement this method if control buttons background
@@ -425,6 +426,9 @@
   // Returns true if |typea| and |typeb| should be in the same drag range.
   bool SameDragType(ShelfItemType typea, ShelfItemType typeb) const;
 
+  // Returns true if focus should move out of the ShelfView view tree.
+  bool ShouldFocusOut(bool reverse, views::View* button);
+
   // Returns the range (in the model) the item at the specified index can be
   // dragged to.
   std::pair<int, int> GetDragRange(int index);
@@ -563,7 +567,7 @@
   std::unique_ptr<views::BoundsAnimator> bounds_animator_;
 
   BackButton* back_button_ = nullptr;
-  AppListButton* home_button_ = nullptr;
+  HomeButton* home_button_ = nullptr;
   OverflowButton* overflow_button_ = nullptr;
 
   std::unique_ptr<OverflowBubble> overflow_bubble_;
diff --git a/ash/shelf/shelf_view_test_api.h b/ash/shelf/shelf_view_test_api.h
index ac633791..d3f6712a 100644
--- a/ash/shelf/shelf_view_test_api.h
+++ b/ash/shelf/shelf_view_test_api.h
@@ -34,8 +34,8 @@
   // Number of icons displayed.
   int GetButtonCount();
 
-  // Retrieve the button at |index|, doesn't support the app list button,
-  // because the app list button is not a ShelfAppButton.
+  // Retrieve the button at |index|, doesn't support the home button,
+  // because the home button is not a ShelfAppButton.
   ShelfAppButton* GetButton(int index);
 
   // Retrieve the view at |index|.
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index a29bec4..2d616439 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -27,8 +27,8 @@
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
 #include "ash/session/session_controller_impl.h"
-#include "ash/shelf/app_list_button.h"
 #include "ash/shelf/back_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/overflow_bubble.h"
 #include "ash/shelf/overflow_bubble_view.h"
 #include "ash/shelf/overflow_bubble_view_test_api.h"
@@ -390,7 +390,7 @@
   }
 
   // Similar to SimulateViewPressed, but the index must not be for the app list,
-  // since the app list button is not a ShelfAppButton.
+  // since the home button is not a ShelfAppButton.
   ShelfAppButton* SimulateButtonPressed(ShelfView::Pointer pointer,
                                         int button_index) {
     ShelfAppButton* button = test_api_->GetButton(button_index);
@@ -723,7 +723,7 @@
   const gfx::Rect bounds_3 = shelf_view_->GetIdealBoundsOfItemIcon(id_3);
 
   EXPECT_EQ(shelf_view_->GetIdealBoundsOfItemIcon(ShelfID(kAppListId)),
-            shelf_view_->GetAppListButton()->GetMirroredBounds());
+            shelf_view_->GetHomeButton()->GetMirroredBounds());
   EXPECT_EQ(shelf_view_->GetIdealBoundsOfItemIcon(ShelfID(kBackButtonId)),
             shelf_view_->GetBackButton()->GetMirroredBounds());
 
@@ -1339,7 +1339,7 @@
 TEST_F(ShelfViewTest, ButtonTitlesTest) {
   AddButtonsUntilOverflow();
   EXPECT_EQ(base::UTF8ToUTF16("Launcher"),
-            shelf_view_->GetAppListButton()->GetAccessibleName());
+            shelf_view_->GetHomeButton()->GetAccessibleName());
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_SHELF_BACK_BUTTON_TITLE),
             shelf_view_->GetBackButton()->GetAccessibleName());
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_SHELF_OVERFLOW_NAME),
@@ -1402,10 +1402,10 @@
   // |AddItem| call seems to sometimes be missing some re-layout steps. We
   // should find out what's going on there.
   shelf_view_->UpdateVisibleShelfItemBoundsUnion();
-  const AppListButton* app_list_button = shelf_view_->GetAppListButton();
+  const HomeButton* home_button = shelf_view_->GetHomeButton();
 
   // Make sure we're not showing the app list.
-  EXPECT_FALSE(app_list_button->IsShowingAppList())
+  EXPECT_FALSE(home_button->IsShowingAppList())
       << "We should not be showing the app list";
 
   // The tooltip shouldn't hide if the mouse is on normal buttons.
@@ -1418,10 +1418,10 @@
         << "ShelfView tries to hide on button " << i;
   }
 
-  // The tooltip should hide if placed in between the app list button and the
+  // The tooltip should hide if placed in between the home button and the
   // first shelf button.
-  const int left = app_list_button->bounds().right();
-  // Find the first shelf button that's to the right of the app list button.
+  const int left = home_button->bounds().right();
+  // Find the first shelf button that's to the right of the home button.
   int right = 0;
   for (int i = 0; i < test_api_->GetButtonCount(); ++i) {
     ShelfAppButton* button = test_api_->GetButton(i);
@@ -1433,13 +1433,13 @@
   }
   const int center_x =
       shelf_view_->GetMirroredXInView(left + (right - left) / 2);
-  EXPECT_TRUE(shelf_view_->ShouldHideTooltip(gfx::Point(
-      center_x, app_list_button->GetMirroredBounds().left_center().y())))
-      << "Tooltip should hide between app list button and first shelf item";
+  EXPECT_TRUE(shelf_view_->ShouldHideTooltip(
+      gfx::Point(center_x, home_button->GetMirroredBounds().left_center().y())))
+      << "Tooltip should hide between home button and first shelf item";
 
   // The tooltip should not hide on the app-list button.
   EXPECT_FALSE(shelf_view_->ShouldHideTooltip(
-      app_list_button->GetMirroredBounds().CenterPoint()));
+      home_button->GetMirroredBounds().CenterPoint()));
 
   // The tooltip shouldn't hide if the mouse is in the gap between two buttons.
   gfx::Rect app_button_rect = GetButtonByID(app_button_id)->GetMirroredBounds();
@@ -1458,7 +1458,7 @@
 
     all_area.Union(button->GetMirroredBounds());
   }
-  all_area.Union(shelf_view_->GetAppListButton()->GetMirroredBounds());
+  all_area.Union(shelf_view_->GetHomeButton()->GetMirroredBounds());
   EXPECT_FALSE(shelf_view_->ShouldHideTooltip(all_area.origin()));
   EXPECT_FALSE(shelf_view_->ShouldHideTooltip(
       gfx::Point(all_area.right() - 1, all_area.bottom() - 1)));
@@ -1487,10 +1487,10 @@
         << "ShelfView tries to hide on button " << i;
   }
 
-  // The tooltip should hide on the app list button if the app list is visible.
-  AppListButton* app_list_button = shelf_view_->GetAppListButton();
+  // The tooltip should hide on the home button if the app list is visible.
+  HomeButton* home_button = shelf_view_->GetHomeButton();
   EXPECT_TRUE(shelf_view_->ShouldHideTooltip(
-      app_list_button->GetMirroredBounds().CenterPoint()));
+      home_button->GetMirroredBounds().CenterPoint()));
 }
 
 // Test that by moving the mouse cursor off the button onto the bubble it closes
@@ -1505,8 +1505,8 @@
   EXPECT_FALSE(tooltip_manager->IsVisible());
 
   // Move the mouse over the button and check that it is visible.
-  AppListButton* app_list_button = shelf_view_->GetAppListButton();
-  gfx::Rect bounds = app_list_button->GetBoundsInScreen();
+  HomeButton* home_button = shelf_view_->GetHomeButton();
+  gfx::Rect bounds = home_button->GetBoundsInScreen();
   generator->MoveMouseTo(bounds.CenterPoint());
   // Wait for the timer to go off.
   base::RunLoop().RunUntilIdle();
@@ -1533,7 +1533,7 @@
 }
 
 // Resizing shelf view while an add animation without fade-in is running,
-// which happens when overflow happens. App list button should end up in its
+// which happens when overflow happens. Home button should end up in its
 // new ideal bounds.
 TEST_F(ShelfViewTest, ResizeDuringOverflowAddAnimation) {
   // All buttons should be visible.
@@ -1559,12 +1559,12 @@
   // Finish the animation.
   test_api_->RunMessageLoopUntilAnimationsDone();
 
-  // App list button should ends up in its new ideal bounds.
-  const int app_list_button_index = test_api_->GetButtonCount() - 1;
+  // Home button should ends up in its new ideal bounds.
+  const int home_button_index = test_api_->GetButtonCount() - 1;
   const gfx::Rect& app_list_ideal_bounds =
-      test_api_->GetIdealBoundsByIndex(app_list_button_index);
+      test_api_->GetIdealBoundsByIndex(home_button_index);
   const gfx::Rect& app_list_bounds =
-      test_api_->GetBoundsByIndex(app_list_button_index);
+      test_api_->GetBoundsByIndex(home_button_index);
   EXPECT_EQ(app_list_ideal_bounds, app_list_bounds);
 }
 
@@ -2109,7 +2109,7 @@
 
   // Enable tablet mode to show the back button. Wait for tablet mode animations
   // to finish in order for the BackButton to move out from under the
-  // AppListButton.
+  // HomeButton.
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
   test_api_->RunMessageLoopUntilAnimationsDone();
 
@@ -2238,11 +2238,11 @@
   EXPECT_EQ(first_app_id, model_->items()[last_index].id);
 }
 
-// Tests that the app list button does shows a context menu on right click.
-TEST_F(ShelfViewTest, AppListButtonDoesShowContextMenu) {
+// Tests that the home button does shows a context menu on right click.
+TEST_F(ShelfViewTest, HomeButtonDoesShowContextMenu) {
   ui::test::EventGenerator* generator = GetEventGenerator();
-  const AppListButton* app_list_button = shelf_view_->GetAppListButton();
-  generator->MoveMouseTo(app_list_button->GetBoundsInScreen().CenterPoint());
+  const HomeButton* home_button = shelf_view_->GetHomeButton();
+  generator->MoveMouseTo(home_button->GetBoundsInScreen().CenterPoint());
   generator->PressRightButton();
   EXPECT_TRUE(test_api_->CloseMenu());
 }
@@ -2305,11 +2305,11 @@
   EXPECT_TRUE(shelf_view_->GetOverflowButton()->GetVisible());
   // Now that the apps + overflow button are centered over the available space
   // on the shelf, check that the the distance between the left app and the
-  // app list button is equal to the distance between the overflow button
+  // home button is equal to the distance between the overflow button
   // and the status area.
   ExpectWithinOnePixel(
       app_buttons[0]->GetBoundsInScreen().x() -
-          shelf_view_->GetAppListButton()->GetBoundsInScreen().right(),
+          shelf_view_->GetHomeButton()->GetBoundsInScreen().right(),
       status_area_->GetBoundsInScreen().x() -
           shelf_view_->GetOverflowButton()->GetBoundsInScreen().right());
 }
@@ -2532,14 +2532,14 @@
         if (button->GetVisible())
           EXPECT_TRUE(visible_bounds.Contains(button->GetBoundsInScreen()));
       }
-    CheckAppListButtonIsInBounds();
+    CheckHomeButtonIsInBounds();
   }
 
-  void CheckAppListButtonIsInBounds() {
+  void CheckHomeButtonIsInBounds() {
     gfx::Rect visible_bounds = shelf_view_->GetVisibleItemsBoundsInScreen();
-    gfx::Rect app_list_button_bounds =
-        shelf_view_->GetAppListButton()->GetBoundsInScreen();
-    EXPECT_TRUE(visible_bounds.Contains(app_list_button_bounds));
+    gfx::Rect home_button_bounds =
+        shelf_view_->GetHomeButton()->GetBoundsInScreen();
+    EXPECT_TRUE(visible_bounds.Contains(home_button_bounds));
   }
 
  private:
@@ -2671,15 +2671,15 @@
   }
 
  protected:
-  void InitAppListButtonInkDrop() {
-    app_list_button_ = shelf_view_->GetAppListButton();
+  void InitHomeButtonInkDrop() {
+    home_button_ = shelf_view_->GetHomeButton();
 
-    auto app_list_button_ink_drop =
+    auto home_button_ink_drop =
         std::make_unique<InkDropSpy>(std::make_unique<views::InkDropImpl>(
-            app_list_button_, app_list_button_->size()));
-    app_list_button_ink_drop_ = app_list_button_ink_drop.get();
-    views::test::InkDropHostViewTestApi(app_list_button_)
-        .SetInkDrop(std::move(app_list_button_ink_drop), false);
+            home_button_, home_button_->size()));
+    home_button_ink_drop_ = home_button_ink_drop.get();
+    views::test::InkDropHostViewTestApi(home_button_)
+        .SetInkDrop(std::move(home_button_ink_drop), false);
   }
 
   void InitBrowserButtonInkDrop() {
@@ -2693,8 +2693,8 @@
         .SetInkDrop(std::move(browser_button_ink_drop));
   }
 
-  AppListButton* app_list_button_ = nullptr;
-  InkDropSpy* app_list_button_ink_drop_ = nullptr;
+  HomeButton* home_button_ = nullptr;
+  InkDropSpy* home_button_ink_drop_ = nullptr;
   ShelfAppButton* browser_button_ = nullptr;
   InkDropSpy* browser_button_ink_drop_ = nullptr;
 
@@ -2702,34 +2702,34 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfViewInkDropTest);
 };
 
-// Tests that changing visibility of the app list transitions app list button's
+// Tests that changing visibility of the app list transitions home button's
 // ink drop states correctly.
-TEST_F(ShelfViewInkDropTest, AppListButtonWhenVisibilityChanges) {
-  InitAppListButtonInkDrop();
+TEST_F(ShelfViewInkDropTest, HomeButtonWhenVisibilityChanges) {
+  InitHomeButtonInkDrop();
 
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
 
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTIVATED));
 
   GetAppListTestHelper()->DismissAndRunLoop();
 
   EXPECT_EQ(views::InkDropState::HIDDEN,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::DEACTIVATED));
 }
 
-// Tests that when the app list is hidden, mouse press on the app list button,
+// Tests that when the app list is hidden, mouse press on the home button,
 // which shows the app list, transitions ink drop states correctly. Also, tests
 // that mouse drag and mouse release does not affect the ink drop state.
-TEST_F(ShelfViewInkDropTest, AppListButtonMouseEventsWhenHidden) {
-  InitAppListButtonInkDrop();
+TEST_F(ShelfViewInkDropTest, HomeButtonMouseEventsWhenHidden) {
+  InitHomeButtonInkDrop();
 
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->MoveMouseTo(app_list_button_->GetBoundsInScreen().CenterPoint());
+  generator->MoveMouseTo(home_button_->GetBoundsInScreen().CenterPoint());
 
   // Mouse press on the button, which shows the app list, should end up in the
   // activated state.
@@ -2737,71 +2737,69 @@
 
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTION_PENDING,
                           views::InkDropState::ACTIVATED));
 
   // Dragging mouse out and back and releasing the button should not change the
   // ink drop state.
-  generator->MoveMouseBy(app_list_button_->width(), 0);
-  generator->MoveMouseBy(-app_list_button_->width(), 0);
+  generator->MoveMouseBy(home_button_->width(), 0);
+  generator->MoveMouseBy(-home_button_->width(), 0);
   generator->ReleaseLeftButton();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
-              IsEmpty());
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
 }
 
-// Tests that when the app list is visible, mouse press on the app list button,
+// Tests that when the app list is visible, mouse press on the home button,
 // which dismisses the app list, transitions ink drop states correctly. Also,
 // tests that mouse drag and mouse release does not affect the ink drop state.
-TEST_F(ShelfViewInkDropTest, AppListButtonMouseEventsWhenVisible) {
-  InitAppListButtonInkDrop();
+TEST_F(ShelfViewInkDropTest, HomeButtonMouseEventsWhenVisible) {
+  InitHomeButtonInkDrop();
 
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTIVATED));
 
   // Mouse press on the button, which dismisses the app list, should end up in
   // the hidden state.
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->MoveMouseTo(app_list_button_->GetBoundsInScreen().CenterPoint());
+  generator->MoveMouseTo(home_button_->GetBoundsInScreen().CenterPoint());
   generator->PressLeftButton();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(views::InkDropState::HIDDEN,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTION_PENDING,
                           views::InkDropState::DEACTIVATED,
                           views::InkDropState::HIDDEN));
 
   // Dragging mouse out and back and releasing the button should not change the
   // ink drop state.
-  generator->MoveMouseBy(app_list_button_->width(), 0);
-  generator->MoveMouseBy(-app_list_button_->width(), 0);
+  generator->MoveMouseBy(home_button_->width(), 0);
+  generator->MoveMouseBy(-home_button_->width(), 0);
   generator->ReleaseLeftButton();
   EXPECT_EQ(views::InkDropState::HIDDEN,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
-              IsEmpty());
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
 }
 
-// Tests that when the app list is hidden, tapping on the app list button
+// Tests that when the app list is hidden, tapping on the home button
 // transitions ink drop states correctly.
-TEST_F(ShelfViewInkDropTest, AppListButtonGestureTapWhenHidden) {
-  InitAppListButtonInkDrop();
+TEST_F(ShelfViewInkDropTest, HomeButtonGestureTapWhenHidden) {
+  InitHomeButtonInkDrop();
 
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->MoveMouseTo(app_list_button_->GetBoundsInScreen().CenterPoint());
+  generator->MoveMouseTo(home_button_->GetBoundsInScreen().CenterPoint());
 
   // Touch press on the button should end up in the pending state.
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTION_PENDING,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTION_PENDING));
 
   // Touch release on the button, which shows the app list, should end up in the
@@ -2810,111 +2808,105 @@
 
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTION_TRIGGERED,
                           views::InkDropState::ACTIVATED));
 }
 
-// Tests that when the app list is visible, tapping on the app list button
+// Tests that when the app list is visible, tapping on the home button
 // transitions ink drop states correctly.
-TEST_F(ShelfViewInkDropTest, AppListButtonGestureTapWhenVisible) {
-  InitAppListButtonInkDrop();
+TEST_F(ShelfViewInkDropTest, HomeButtonGestureTapWhenVisible) {
+  InitHomeButtonInkDrop();
 
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
 
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTIVATED));
 
   // Touch press and release on the button, which dismisses the app list, should
   // end up in the hidden state.
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->MoveMouseTo(app_list_button_->GetBoundsInScreen().CenterPoint());
+  generator->MoveMouseTo(home_button_->GetBoundsInScreen().CenterPoint());
   generator->PressTouch();
   generator->ReleaseTouch();
   GetAppListTestHelper()->WaitUntilIdle();
   EXPECT_EQ(views::InkDropState::HIDDEN,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::DEACTIVATED,
                           views::InkDropState::HIDDEN));
 }
 
-// Tests that when the app list is hidden, tapping down on the app list button
+// Tests that when the app list is hidden, tapping down on the home button
 // and dragging the touch point transitions ink drop states correctly.
-TEST_F(ShelfViewInkDropTest, AppListButtonGestureTapDragWhenHidden) {
-  InitAppListButtonInkDrop();
+TEST_F(ShelfViewInkDropTest, HomeButtonGestureTapDragWhenHidden) {
+  InitHomeButtonInkDrop();
 
   ui::test::EventGenerator* generator = GetEventGenerator();
-  gfx::Point touch_location =
-      app_list_button_->GetBoundsInScreen().CenterPoint();
+  gfx::Point touch_location = home_button_->GetBoundsInScreen().CenterPoint();
   generator->MoveMouseTo(touch_location);
 
   // Touch press on the button should end up in the pending state.
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTION_PENDING,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTION_PENDING));
 
   // Dragging the touch point should hide the pending ink drop.
-  touch_location.Offset(app_list_button_->width(), 0);
+  touch_location.Offset(home_button_->width(), 0);
   generator->MoveTouch(touch_location);
   EXPECT_EQ(views::InkDropState::HIDDEN,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTION_TRIGGERED));
 
   // Touch release should not change the ink drop state.
   generator->ReleaseTouch();
   EXPECT_EQ(views::InkDropState::HIDDEN,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
-              IsEmpty());
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
 }
 
-// Tests that when the app list is visible, tapping down on the app list button
+// Tests that when the app list is visible, tapping down on the home button
 // and dragging the touch point will not change ink drop states.
-TEST_F(ShelfViewInkDropTest, AppListButtonGestureTapDragWhenVisible) {
-  InitAppListButtonInkDrop();
+TEST_F(ShelfViewInkDropTest, HomeButtonGestureTapDragWhenVisible) {
+  InitHomeButtonInkDrop();
 
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
 
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTIVATED));
 
   // Touch press on the button, dragging the touch point, and releasing, which
   // will not dismisses the app list, should end up in the |ACTIVATED| state.
   ui::test::EventGenerator* generator = GetEventGenerator();
-  gfx::Point touch_location =
-      app_list_button_->GetBoundsInScreen().CenterPoint();
+  gfx::Point touch_location = home_button_->GetBoundsInScreen().CenterPoint();
   generator->MoveMouseTo(touch_location);
 
   // Touch press on the button should not change the ink drop state.
   generator->PressTouch();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
-              IsEmpty());
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
 
   // Dragging the touch point should not hide the pending ink drop.
-  touch_location.Offset(app_list_button_->width(), 0);
+  touch_location.Offset(home_button_->width(), 0);
   generator->MoveTouch(touch_location);
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
-              IsEmpty());
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
 
   // Touch release should not change the ink drop state.
   generator->ReleaseTouch();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            app_list_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_THAT(app_list_button_ink_drop_->GetAndResetRequestedStates(),
-              IsEmpty());
+            home_button_ink_drop_->GetTargetInkDropState());
+  EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
 }
 
 // Tests that clicking on a shelf item that does not show a menu transitions ink
@@ -3081,9 +3073,9 @@
 
   views::Button* button = browser_button_;
 
-  // Show a context menu on the app list button.
+  // Show a context menu on the home button.
   generator->MoveMouseTo(
-      shelf_view_->GetAppListButton()->GetBoundsInScreen().CenterPoint());
+      shelf_view_->GetHomeButton()->GetBoundsInScreen().CenterPoint());
   generator->PressRightButton();
   generator->ReleaseRightButton();
   EXPECT_TRUE(shelf_view_->IsShowingMenu());
@@ -3606,7 +3598,7 @@
 
   // The home button is focused initially because the back button is only
   // visible in tablet mode.
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->HasFocus());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->HasFocus());
 }
 
 // Tests that the expected views have focus when cycling through shelf items
@@ -3627,7 +3619,7 @@
 TEST_F(ShelfViewFocusTest, BackwardCycling) {
   // The first element is currently focused. Let's advance to the last element
   // first.
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->HasFocus());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->HasFocus());
   DoTab();
   DoTab();
   DoTab();
@@ -3651,7 +3643,7 @@
 // Verifies that focus moves as expected between the shelf and the status area.
 TEST_F(ShelfViewFocusTest, FocusCyclingBetweenShelfAndStatusWidget) {
   // The first element of the shelf (the home button) is focused at start.
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->HasFocus());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->HasFocus());
 
   // Focus the next few elements.
   DoTab();
@@ -3680,7 +3672,7 @@
   // And keep going forward, now we should be cycling back to the first shelf
   // element.
   DoTab();
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->HasFocus());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->HasFocus());
   ExpectNotFocused(status_area_);
 }
 
@@ -3731,14 +3723,14 @@
 
   EXPECT_EQ(last_item_on_main_shelf_index_, items_ - 5);
   EXPECT_TRUE(shelf_view_->shelf_widget()->IsActive());
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->HasFocus());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->HasFocus());
 }
 
 TEST_F(ShelfViewOverflowFocusTest, OpenOverflow) {
   OpenOverflow();
   ASSERT_TRUE(overflow_shelf_test_api_);
   EXPECT_TRUE(shelf_view_->IsShowingOverflowBubble());
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->HasFocus());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->HasFocus());
 }
 
 // Tests that when cycling through the items with tab, the items in the overflow
@@ -3845,7 +3837,7 @@
   // Focus the shelf again.
   DoTab();
   ExpectFocused(shelf_view_);
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->HasFocus());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->HasFocus());
   ExpectNotFocused(status_area_);
 
   // Now advance to the last item on the main shelf.
@@ -3883,7 +3875,7 @@
   while (status_area_->GetWidget()->IsActive())
     DoTab();
   // This should have brought focus to the first element on the shelf.
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->HasFocus());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->HasFocus());
 }
 
 class KioskNextShelfViewTest : public ShelfViewTest {
@@ -3937,7 +3929,7 @@
               Shell::Get()->kiosk_next_shell_controller()->shelf_model());
 
   // The home and back buttons are always visible.
-  EXPECT_TRUE(shelf_view_->GetAppListButton()->GetVisible());
+  EXPECT_TRUE(shelf_view_->GetHomeButton()->GetVisible());
   EXPECT_TRUE(shelf_view_->GetBackButton()->GetVisible());
 
   ASSERT_FALSE(shelf_view_->GetOverflowButton()->GetVisible());
@@ -3992,7 +3984,7 @@
     EXPECT_TRUE(expected_button_area_bounds.Contains(back_button_bounds));
 
     const gfx::Rect home_button_bounds =
-        shelf_view_->GetAppListButton()->ideal_bounds();
+        shelf_view_->GetHomeButton()->ideal_bounds();
     EXPECT_FALSE(home_button_bounds.IsEmpty());
     EXPECT_TRUE(expected_button_area_bounds.Contains(home_button_bounds));
 
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 3670b57..56332e1 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -16,8 +16,8 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
-#include "ash/shelf/app_list_button.h"
 #include "ash/shelf/default_shelf_view.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/kiosk_next_shelf_view.h"
 #include "ash/shelf/login_shelf_view.h"
 #include "ash/shelf/overflow_bubble.h"
@@ -398,7 +398,6 @@
   // access it later in shutdown.
   shelf_layout_manager_->PrepareForShutdown();
 
-  background_animator_.RemoveObserver(status_area_widget_.get());
   Shell::Get()->focus_cycler()->RemoveWidget(status_area_widget_.get());
   status_area_widget_.reset();
 
@@ -421,7 +420,6 @@
       std::make_unique<StatusAreaWidget>(status_container, shelf_);
   status_area_widget_->Initialize();
   Shell::Get()->focus_cycler()->AddWidget(status_area_widget_.get());
-  background_animator_.AddObserver(status_area_widget_.get());
   status_container->SetLayoutManager(new StatusAreaLayoutManager(this));
 }
 
@@ -455,7 +453,7 @@
 }
 
 bool ShelfWidget::IsShowingAppList() const {
-  return GetAppListButton() && GetAppListButton()->IsShowingAppList();
+  return GetHomeButton() && GetHomeButton()->IsShowingAppList();
 }
 
 bool ShelfWidget::IsShowingMenu() const {
@@ -490,8 +488,8 @@
                    bounds.height());
 }
 
-AppListButton* ShelfWidget::GetAppListButton() const {
-  return shelf_view_->GetAppListButton();
+HomeButton* ShelfWidget::GetHomeButton() const {
+  return shelf_view_->GetHomeButton();
 }
 
 BackButton* ShelfWidget::GetBackButton() const {
diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h
index 35c993c12..fd0ab4b 100644
--- a/ash/shelf/shelf_widget.h
+++ b/ash/shelf/shelf_widget.h
@@ -23,7 +23,7 @@
 
 namespace ash {
 enum class AnimationChangeType;
-class AppListButton;
+class HomeButton;
 class BackButton;
 class FocusCycler;
 class LoginShelfView;
@@ -87,7 +87,7 @@
   gfx::Rect GetScreenBoundsOfItemIconForWindow(aura::Window* window);
 
   // Returns the button that opens the app launcher.
-  AppListButton* GetAppListButton() const;
+  HomeButton* GetHomeButton() const;
 
   // Returns the browser back button.
   BackButton* GetBackButton() const;
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index 0b0623b..f93a252 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -187,7 +187,6 @@
 }
 
 void ShellTestApi::SetTabletModeEnabledForTest(bool enable) {
-  AccelerometerReader::GetInstance()->DisableForTest();
   shell_->tablet_mode_controller()->SetEnabledForTest(enable);
 }
 
diff --git a/ash/strings/ash_strings_ar.xtb b/ash/strings/ash_strings_ar.xtb
index 4c16d13d..9e060e2 100644
--- a/ash/strings/ash_strings_ar.xtb
+++ b/ash/strings/ash_strings_ar.xtb
@@ -263,7 +263,7 @@
 <translation id="4895488851634969361">البطارية مملوءة.</translation>
 <translation id="490375751687810070">عمودي</translation>
 <translation id="4905614135390995787">تم تغيير اختصار تشغيل وضع التباين العالي. يُرجى استخدام <ph name="NEW_SHORTCUT" /> بدلاً من <ph name="OLD_SHORTCUT" />.</translation>
-<translation id="490788395437447240">مستوى شحن البطارية <ph name="BATTERY_PERCENTAGE" />%</translation>
+<translation id="490788395437447240">مستوى شحن البطارية: <ph name="BATTERY_PERCENTAGE" />%</translation>
 <translation id="4917385247580444890">قوية</translation>
 <translation id="4918086044614829423">قبول</translation>
 <translation id="4924411785043111640">إعادة التشغيل وإعادة الضبط</translation>
@@ -301,7 +301,7 @@
 <translation id="5648021990716966815">مقبس الميكروفون</translation>
 <translation id="5669267381087807207">تفعيل</translation>
 <translation id="5673434351075758678">من "<ph name="FROM_LOCALE" />" إلى "<ph name="TO_LOCALE" />" بعد مزامنة الإعدادات.</translation>
-<translation id="5682642926269496722">‏لا يتوفر "مساعد Google" لجلسة مستخدم حالية.</translation>
+<translation id="5682642926269496722">‏لا يتوفر "مساعد Google" لحساب المستخدم الحالي.</translation>
 <translation id="5691772641933328258">لم يتمّ التعرُّف على البصمة</translation>
 <translation id="5710450975648804523">تمّ تفعيل وضع "عدم الإزعاج"</translation>
 <translation id="574392208103952083">متوسط</translation>
@@ -378,7 +378,7 @@
 <translation id="6713285437468012787">تم إقران جهاز بلوتوث "<ph name="DEVICE_NAME" />" وهو الآن متاح لجميع المستخدمين. يمكنك إزالة هذا الإقران باستخدام الإعدادات.</translation>
 <translation id="6715542151869432661">لم يتم العثور على أي أجهزة جوّال.</translation>
 <translation id="6723839937902243910">الطاقة</translation>
-<translation id="6727969043791803658">الجهاز متصل، ومستوى شحن البطارية <ph name="BATTERY_PERCENTAGE" /> %</translation>
+<translation id="6727969043791803658">البلوتوث متصل، ومستوى شحن البطارية: <ph name="BATTERY_PERCENTAGE" /> %</translation>
 <translation id="6751826523481687655">تتبّع الأداء قيْد التفعيل</translation>
 <translation id="6790428901817661496">التشغيل</translation>
 <translation id="6803622936009808957">تعذر إجراء النسخ المطابق للعروض نظرًا لعدم العثور على درجات دقة متوافقة. تم الدخول إلى سطح المكتب الممتد بدلاً من ذلك.</translation>
diff --git a/ash/strings/ash_strings_hi.xtb b/ash/strings/ash_strings_hi.xtb
index 6248712..2a11688 100644
--- a/ash/strings/ash_strings_hi.xtb
+++ b/ash/strings/ash_strings_hi.xtb
@@ -336,7 +336,7 @@
 <translation id="6064337552080329342">माफ़ करें, आपके पासवर्ड की पुष्टि नहीं की जा सकी. कृपया फिर से कोशिश करें.</translation>
 <translation id="607652042414456612">आपका कंप्यूर आस-पास के ब्लूटूथ डिवाइस के लिए खोजे जाने योग्य है और वह "<ph name="NAME" />" के रूप में <ph name="ADDRESS" /> पते के साथ दिखाई देगा</translation>
 <translation id="6106745654298855237"><ph name="POWER_SOURCE" /> चार्ज हो रहा है</translation>
-<translation id="612734058257491180">मेहमान के तौर पर ब्राउज़ करने के सेशन में Google Assistant की सुविधा उपलब्ध नहीं होती.</translation>
+<translation id="612734058257491180">Google Assistant की सुविधा मेहमान के तौर पर ब्राउज़ करने के सेशन में उपलब्ध नहीं है.</translation>
 <translation id="615957422585914272">ऑन-स्क्रीन कीबोर्ड दिखाएं</translation>
 <translation id="6164005077879661055">'निगरानी में रखे गए इस उपयोगकर्ता' को हटाने के बाद, निगरानी में रखे गए उपयोगकर्ता से जुड़ीं सभी फ़ाइलें और 'स्थानीय डेटा' हमेशा के लिए मिट जाएंगे. 'निगरानी में रखे गए इस उपयोगकर्ता' की देखी गईं वेबसाइटें और सेटिंग <ph name="MANAGEMENT_URL" /> पर प्रबंधक को अब भी दिखेंगी.</translation>
 <translation id="6165508094623778733">ज़्यादा जानें</translation>
diff --git a/ash/strings/ash_strings_hr.xtb b/ash/strings/ash_strings_hr.xtb
index df94120..7903a09 100644
--- a/ash/strings/ash_strings_hr.xtb
+++ b/ash/strings/ash_strings_hr.xtb
@@ -59,7 +59,7 @@
 <translation id="1761222317188459878">Prebacivanje mrežne veze. <ph name="STATE_TEXT" /></translation>
 <translation id="1771761307086386028">Pomicanje udesno</translation>
 <translation id="1812997170047690955">Što je na mojem zaslonu?</translation>
-<translation id="1820355135508821526">Značajka <ph name="FEATURE_NAME" /> onemogućena je pravilom koje se primjenjuje na uređaj. Obratite se administratoru i zatražite promjenu.</translation>
+<translation id="1820355135508821526">Značajka <ph name="FEATURE_NAME" /> onemogućena je zbog pravila koje se primjenjuje na uređaj. Obratite se administratoru i zatražite promjenu.</translation>
 <translation id="1823873187264960516">Ethernet: <ph name="ADDRESS" /></translation>
 <translation id="1836215606488044471">Asistent (učitavanje...)</translation>
 <translation id="1850504506766569011">Wi-Fi je isključen.</translation>
diff --git a/ash/strings/ash_strings_nl.xtb b/ash/strings/ash_strings_nl.xtb
index 6b029b9..2c745db 100644
--- a/ash/strings/ash_strings_nl.xtb
+++ b/ash/strings/ash_strings_nl.xtb
@@ -431,7 +431,7 @@
 <translation id="7593891976182323525">Zoeken of Shift</translation>
 <translation id="7645176681409127223"><ph name="USER_NAME" /> (eigenaar)</translation>
 <translation id="7647488630410863958">Ontgrendel het apparaat om je meldingen weer te geven</translation>
-<translation id="7649070708921625228">Help</translation>
+<translation id="7649070708921625228">Hulp</translation>
 <translation id="7654687942625752712">Houd beide volumetoetsen gedurende vijf seconden ingedrukt om gesproken feedback uit te schakelen.</translation>
 <translation id="7692480393933218409">De aangesloten USB-C-apparaten worden opgeladen</translation>
 <translation id="7705524343798198388">VPN</translation>
diff --git a/ash/strings/ash_strings_pt-PT.xtb b/ash/strings/ash_strings_pt-PT.xtb
index 55985f3..981c7c8 100644
--- a/ash/strings/ash_strings_pt-PT.xtb
+++ b/ash/strings/ash_strings_pt-PT.xtb
@@ -335,7 +335,7 @@
 <translation id="6064337552080329342">Lamentamos, mas não foi possível validar a sua palavra-passe. Tente novamente.</translation>
 <translation id="607652042414456612">O seu computador é detetável para os dispositivos Bluetooth nas proximidades e aparecerá como "<ph name="NAME" />" com o endereço <ph name="ADDRESS" /></translation>
 <translation id="6106745654298855237">A carregar <ph name="POWER_SOURCE" /></translation>
-<translation id="612734058257491180">O Assistente Google não está disponível em Sessões de convidado.</translation>
+<translation id="612734058257491180">O Assistente Google não está disponível em sessões de convidado.</translation>
 <translation id="615957422585914272">Mostrar teclado no ecrã</translation>
 <translation id="6164005077879661055">Todos os ficheiros e dados locais associados ao utilizador supervisionado serão definitivamente eliminados depois de este utilizador supervisionado ser removido. Os Sites visitados e as definições deste utilizador supervisionado poderão continuar visíveis para o gestor em <ph name="MANAGEMENT_URL" />.</translation>
 <translation id="6165508094623778733">Saiba mais</translation>
diff --git a/ash/strings/ash_strings_ro.xtb b/ash/strings/ash_strings_ro.xtb
index f52f64fb..6a7580e 100644
--- a/ash/strings/ash_strings_ro.xtb
+++ b/ash/strings/ash_strings_ro.xtb
@@ -335,7 +335,7 @@
 <translation id="6064337552080329342">Ne pare rău, parola nu a putut fi confirmată. Încearcă din nou.</translation>
 <translation id="607652042414456612">Computerul poate fi detectat de dispozitivele Bluetooth din apropiere și va apărea ca „<ph name="NAME" />” cu adresa <ph name="ADDRESS" /></translation>
 <translation id="6106745654298855237">Se încarcă <ph name="POWER_SOURCE" /></translation>
-<translation id="612734058257491180">Asistentul Google nu este disponibil într-o sesiune publică.</translation>
+<translation id="612734058257491180">Asistentul Google nu este disponibil într-o sesiune pentru invitați.</translation>
 <translation id="615957422585914272">Afișați tastatura pe ecran</translation>
 <translation id="6164005077879661055">Toate fișierele și datele locale asociate acestui utilizator monitorizat vor fi șterse definitiv după eliminarea acestui utilizator monitorizat. Site-urile web accesate și setările acestui utilizator monitorizat pot fi în continuare vizibile pentru manager la <ph name="MANAGEMENT_URL" />.</translation>
 <translation id="6165508094623778733">Află mai multe</translation>
diff --git a/ash/strings/ash_strings_te.xtb b/ash/strings/ash_strings_te.xtb
index 0dec8f0..a5b7abe 100644
--- a/ash/strings/ash_strings_te.xtb
+++ b/ash/strings/ash_strings_te.xtb
@@ -59,7 +59,7 @@
 <translation id="1761222317188459878">నెట్‌వర్క్ కనెక్షన్‌ను టోగుల్ చేయండి. <ph name="STATE_TEXT" /></translation>
 <translation id="1771761307086386028">కుడికి స్క్రోల్ చేయి</translation>
 <translation id="1812997170047690955">నా స్క్రీన్‌పై ఏమి ఉన్నాయి?</translation>
-<translation id="1820355135508821526">ఈ పరికరానికి వర్తింపజేసిన విధానం కారణంగా <ph name="FEATURE_NAME" /> నిలిపివేయబడింది. మార్పులను అభ్యర్థించండానికి, దయచేసి మీ నిర్వాహకుడితో మాట్లాడండి.</translation>
+<translation id="1820355135508821526">ఈ పరికరానికి వర్తింపజేసిన విధానం కారణంగా <ph name="FEATURE_NAME" /> నిలిపివేయబడింది. మార్పులను అభ్యర్థించడానికి, దయచేసి మీ నిర్వాహకుడితో మాట్లాడండి.</translation>
 <translation id="1823873187264960516">ఈథర్‌నెట్: <ph name="ADDRESS" /></translation>
 <translation id="1836215606488044471">సహాయకం (లోడ్ అవుతోంది...)</translation>
 <translation id="1850504506766569011">Wi-Fi ఆఫ్ చేయబడింది.</translation>
diff --git a/ash/strings/ash_strings_tr.xtb b/ash/strings/ash_strings_tr.xtb
index 21c3b168..4254937c 100644
--- a/ash/strings/ash_strings_tr.xtb
+++ b/ash/strings/ash_strings_tr.xtb
@@ -114,7 +114,7 @@
 <translation id="2582112259361606227">Güncellemek için yeniden başlat</translation>
 <translation id="2597972630681408282">Gece Işığı: <ph name="NIGHT_LIGHT_STATUS" /></translation>
 <translation id="2617342710774726426">SIM kart kilitli</translation>
-<translation id="2651765386227849097">Google Asistan bu cihazda engellendi.</translation>
+<translation id="2651765386227849097">Google Asistan bu cihazda devre dışı bırakıldı.</translation>
 <translation id="2653659639078652383">Gönder</translation>
 <translation id="2658778018866295321">Tıkla ve sürükle</translation>
 <translation id="2700493154570097719">Klavyenizi ayarlayın</translation>
diff --git a/ash/strings/ash_strings_uk.xtb b/ash/strings/ash_strings_uk.xtb
index eb8efd6..8ed944c 100644
--- a/ash/strings/ash_strings_uk.xtb
+++ b/ash/strings/ash_strings_uk.xtb
@@ -377,7 +377,7 @@
 <translation id="6713285437468012787">Пристрій Bluetooth "<ph name="DEVICE_NAME" />" підключено. Він доступний для всіх користувачів. Відключити пристрій можна в Налаштуваннях.</translation>
 <translation id="6715542151869432661">Мобільних пристроїв не знайдено.</translation>
 <translation id="6723839937902243910">Заряд</translation>
-<translation id="6727969043791803658">Під'єднано, заряд акумулятора – <ph name="BATTERY_PERCENTAGE" />%</translation>
+<translation id="6727969043791803658">Під'єднано, акумулятор заряджено на <ph name="BATTERY_PERCENTAGE" />%</translation>
 <translation id="6751826523481687655">Відстеження ефективності ввімкнено</translation>
 <translation id="6790428901817661496">Відтворити</translation>
 <translation id="6803622936009808957">Не вдалося дублювати зображення екранів, оскільки не знайдено підтримувані значення роздільної здатності. Натомість запущено режим розширеного робочого столу.</translation>
diff --git a/ash/strings/ash_strings_zh-TW.xtb b/ash/strings/ash_strings_zh-TW.xtb
index 142be2f..438048d 100644
--- a/ash/strings/ash_strings_zh-TW.xtb
+++ b/ash/strings/ash_strings_zh-TW.xtb
@@ -114,7 +114,7 @@
 <translation id="2582112259361606227">重新啟動即可更新</translation>
 <translation id="2597972630681408282">夜燈功能:<ph name="NIGHT_LIGHT_STATUS" /></translation>
 <translation id="2617342710774726426">SIM 卡已鎖定</translation>
-<translation id="2651765386227849097">這個裝置已停用 Google 助理。</translation>
+<translation id="2651765386227849097">這個裝置上的 Google 助理已停用。</translation>
 <translation id="2653659639078652383">提交</translation>
 <translation id="2658778018866295321">按住並拖曳</translation>
 <translation id="2700493154570097719">設定鍵盤</translation>
@@ -301,7 +301,7 @@
 <translation id="5648021990716966815">麥克風插孔</translation>
 <translation id="5669267381087807207">啟用中</translation>
 <translation id="5673434351075758678">同步處理設定後,「<ph name="FROM_LOCALE" />」已變更為「<ph name="TO_LOCALE" />」。</translation>
-<translation id="5682642926269496722">Google 助理不適用於目前的使用者帳戶。</translation>
+<translation id="5682642926269496722">Google 助理不支援目前的使用者帳戶。</translation>
 <translation id="5691772641933328258">指紋辨識失敗</translation>
 <translation id="5710450975648804523">「零打擾」模式已開啟</translation>
 <translation id="574392208103952083">中</translation>
@@ -335,7 +335,7 @@
 <translation id="6064337552080329342">很抱歉!系統無法驗證你的密碼,請再試一次。</translation>
 <translation id="607652042414456612">你的電腦已開放附近的藍牙裝置搜尋,顯示名稱為「<ph name="NAME" />」,位址則是 <ph name="ADDRESS" /></translation>
 <translation id="6106745654298855237">正在為「<ph name="POWER_SOURCE" />」充電</translation>
-<translation id="612734058257491180">Google 助理不適用於訪客工作階段。</translation>
+<translation id="612734058257491180">Google 助理不支援訪客工作階段。</translation>
 <translation id="615957422585914272">顯示螢幕小鍵盤</translation>
 <translation id="6164005077879661055">將這位受監管的使用者移除後,系統即會永久刪除所有與該使用者相關的檔案和本機資料。管理員仍可前往 <ph name="MANAGEMENT_URL" /> 查看這位受監管使用者的網頁瀏覽記錄和設定。</translation>
 <translation id="6165508094623778733">瞭解詳情</translation>
diff --git a/ash/system/audio/unified_volume_view.cc b/ash/system/audio/unified_volume_view.cc
index 8baaffc6..90196b74a 100644
--- a/ash/system/audio/unified_volume_view.cc
+++ b/ash/system/audio/unified_volume_view.cc
@@ -151,7 +151,7 @@
   float level = CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.f;
 
   // Indicate that the slider is inactive when it's muted.
-  slider()->UpdateState(!is_muted);
+  slider()->SetIsActive(!is_muted);
 
   // The button should be gray whay muted and colored otherwise.
   button()->SetToggled(!is_muted);
@@ -165,7 +165,7 @@
   // there will be a small discrepancy between slider's value and volume level
   // on audio side. To avoid the jittering in slider UI, do not set change
   // slider value if the change is less than the threshold.
-  if (std::abs(level - slider()->value()) < kSliderIgnoreUpdateThreshold)
+  if (std::abs(level - slider()->GetValue()) < kSliderIgnoreUpdateThreshold)
     return;
 
   SetSliderValue(level, by_user);
diff --git a/ash/system/message_center/arc/arc_notification_content_view.cc b/ash/system/message_center/arc/arc_notification_content_view.cc
index 4decaf1..98c0538 100644
--- a/ash/system/message_center/arc/arc_notification_content_view.cc
+++ b/ash/system/message_center/arc/arc_notification_content_view.cc
@@ -8,7 +8,6 @@
 #include "ash/system/message_center/arc/arc_notification_surface.h"
 #include "ash/system/message_center/arc/arc_notification_view.h"
 // TODO(https://crbug.com/768439): Remove nogncheck when moved to ash.
-#include "ash/wm/window_util.h"  // nogncheck
 #include "base/auto_reset.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
@@ -478,11 +477,6 @@
   UpdatePreferredSize();
   surface_->Attach(this);
 
-  // The texture for this window can be placed at subpixel position
-  // with fractional scale factor. Force to align it at the pixel
-  // boundary here, and when layout is updated in Layout().
-  ::wm::SnapWindowToPixelBoundary(surface_->GetWindow());
-
   // Creates slide helper after this view is added to its parent.
   slide_helper_.reset(new SlideHelper(this));
 
@@ -655,9 +649,6 @@
   }
 
   UpdateControlButtonsVisibility();
-
-  if (is_surface_visible)
-    ::wm::SnapWindowToPixelBoundary(surface_->GetWindow());
 }
 
 void ArcNotificationContentView::OnPaint(gfx::Canvas* canvas) {
diff --git a/ash/system/message_center/message_center_scroll_bar.cc b/ash/system/message_center/message_center_scroll_bar.cc
index fc60310e..7140ba1 100644
--- a/ash/system/message_center/message_center_scroll_bar.cc
+++ b/ash/system/message_center/message_center_scroll_bar.cc
@@ -55,6 +55,10 @@
   return result;
 }
 
+const char* MessageCenterScrollBar::GetClassName() const {
+  return "MessageCenterScrollBar";
+}
+
 void MessageCenterScrollBar::OnGestureEvent(ui::GestureEvent* event) {
   if (!stats_recorded_ && (event->type() == ui::ET_GESTURE_SCROLL_BEGIN)) {
     CollectScrollActionReason(ScrollActionReason::kByTouch);
diff --git a/ash/system/message_center/message_center_scroll_bar.h b/ash/system/message_center/message_center_scroll_bar.h
index eac1646..63eb6e9 100644
--- a/ash/system/message_center/message_center_scroll_bar.h
+++ b/ash/system/message_center/message_center_scroll_bar.h
@@ -29,6 +29,7 @@
   // View overrides:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   bool OnMouseWheel(const ui::MouseWheelEvent& event) override;
+  const char* GetClassName() const override;
 
   // ui::EventHandler overrides:
   void OnGestureEvent(ui::GestureEvent* event) override;
diff --git a/ash/system/message_center/notifier_settings_view.cc b/ash/system/message_center/notifier_settings_view.cc
index 35d2607..ef60e13 100644
--- a/ash/system/message_center/notifier_settings_view.cc
+++ b/ash/system/message_center/notifier_settings_view.cc
@@ -30,6 +30,7 @@
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image.h"
@@ -92,8 +93,6 @@
 constexpr gfx::Insets kQuietModeLabelPadding(16, 0, 15, 0);
 constexpr gfx::Insets kQuietModeTogglePadding(0, 14);
 constexpr SkColor kTopBorderColor = SkColorSetA(SK_ColorBLACK, 0x1F);
-constexpr SkColor kDisabledNotifierFilterColor =
-    SkColorSetA(SK_ColorWHITE, 0xB8);
 const int kLabelFontSizeDelta = 1;
 
 // NotifierButtonWrapperView ---------------------------------------------------
@@ -123,20 +122,11 @@
   const char* GetClassName() const override;
 
  private:
-  // Initialize |disabled_filter_|. Should be called once.
-  void CreateDisabledFilter();
-
   std::unique_ptr<views::Painter> focus_painter_;
 
   // NotifierButton to wrap.
   views::View* contents_;
 
-  // A view to add semi-transparent filter on top of |contents_|.
-  // It is only visible when NotifierButton is disabled (e.g. the setting is
-  // enforced by administrator.) The color of the NotifierButton would be dim
-  // and users notice they can't change the setting.
-  views::View* disabled_filter_ = nullptr;
-
   DISALLOW_COPY_AND_ASSIGN(NotifierButtonWrapperView);
 };
 
@@ -154,18 +144,6 @@
   int y = std::max((height() - contents_height) / 2, 0);
   contents_->SetBounds(0, y, contents_width, contents_height);
 
-  // Since normally we don't show |disabled_filter_|, initialize it lazily.
-  if (!contents_->GetEnabled()) {
-    if (!disabled_filter_)
-      CreateDisabledFilter();
-    disabled_filter_->SetVisible(true);
-    gfx::Rect filter_bounds = GetContentsBounds();
-    filter_bounds.set_width(filter_bounds.width() - kEntryIconSize);
-    disabled_filter_->SetBoundsRect(filter_bounds);
-  } else if (disabled_filter_) {
-    disabled_filter_->SetVisible(false);
-  }
-
   SetFocusBehavior(contents_->GetEnabled() ? FocusBehavior::ALWAYS
                                            : FocusBehavior::NEVER);
 }
@@ -217,15 +195,6 @@
   return "NotifierButtonWrapperView";
 }
 
-void NotifierButtonWrapperView::CreateDisabledFilter() {
-  DCHECK(!disabled_filter_);
-  disabled_filter_ = new views::View;
-  disabled_filter_->SetBackground(
-      views::CreateSolidBackground(kDisabledNotifierFilterColor));
-  disabled_filter_->set_can_process_events_within_subtree(false);
-  AddChildView(disabled_filter_);
-}
-
 // ScrollContentsView ----------------------------------------------------------
 
 class ScrollContentsView : public views::View {
@@ -331,6 +300,12 @@
   if (notifier.enforced) {
     Button::SetEnabled(false);
     checkbox->SetEnabled(false);
+
+    icon_view->SetPaintToLayer();
+    icon_view->layer()->SetFillsBoundsOpaquely(false);
+    icon_view->layer()->SetOpacity(gfx::kDisabledControlAlpha / float{0xFF});
+    name_view->SetEnabledColor(
+        SkColorSetA(name_view->GetEnabledColor(), gfx::kDisabledControlAlpha));
   }
 
   // Add the views before the layout is assigned. Because GridChanged() may be
@@ -409,17 +384,17 @@
   cs->AddPaddingColumn(1, 0);
 
   layout->StartRow(0, 0);
-  layout->AddView(checkbox_);
-  layout->AddView(icon_view_);
-  layout->AddView(name_view_);
+  layout->AddExistingView(checkbox_);
+  layout->AddExistingView(icon_view_);
+  layout->AddExistingView(name_view_);
 
   if (!GetEnabled()) {
-    views::ImageView* policy_enforced_icon = new views::ImageView();
+    auto policy_enforced_icon = std::make_unique<views::ImageView>();
     policy_enforced_icon->SetImage(gfx::CreateVectorIcon(
         kSystemMenuBusinessIcon, kEntryIconSize, kUnifiedMenuIconColor));
     cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::FIXED,
                   kEntryIconSize, 0);
-    layout->AddView(policy_enforced_icon);
+    layout->AddView(std::move(policy_enforced_icon));
   }
 
   Layout();
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index c4ed668..b002c1a 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -222,7 +222,10 @@
 }
 
 UnifiedMessageListView::~UnifiedMessageListView() {
-  MessageCenter::Get()->RemoveObserver(this);
+  // The MessageCenter may be destroyed already during shutdown. See
+  // crbug.com/946153.
+  if (MessageCenter::Get())
+    MessageCenter::Get()->RemoveObserver(this);
 
   model_->ClearNotificationChanges();
   for (auto* view : children())
diff --git a/ash/system/network/network_info.h b/ash/system/network/network_info.h
index 8509ba46..7a4a839a 100644
--- a/ash/system/network/network_info.h
+++ b/ash/system/network/network_info.h
@@ -36,8 +36,8 @@
       chromeos::network_config::mojom::ConnectionStateType::kNotConnected;
   chromeos::network_config::mojom::NetworkType type =
       chromeos::network_config::mojom::NetworkType::kWiFi;
-  chromeos::network_config::mojom::ONCSource source =
-      chromeos::network_config::mojom::ONCSource::kUnknown;
+  chromeos::network_config::mojom::OncSource source =
+      chromeos::network_config::mojom::OncSource::kNone;
   int battery_percentage = 0;
   std::string captive_portal_provider_name;
 };
diff --git a/ash/system/network/network_list_view.cc b/ash/system/network/network_list_view.cc
index daf3f6e..bf07c5ab 100644
--- a/ash/system/network/network_list_view.cc
+++ b/ash/system/network/network_list_view.cc
@@ -52,7 +52,7 @@
 using chromeos::network_config::mojom::NetworkStateProperties;
 using chromeos::network_config::mojom::NetworkStatePropertiesPtr;
 using chromeos::network_config::mojom::NetworkType;
-using chromeos::network_config::mojom::ONCSource;
+using chromeos::network_config::mojom::OncSource;
 using chromeos::network_config::mojom::ProxyMode;
 
 namespace ash {
@@ -394,8 +394,8 @@
 
 views::View* NetworkListView::CreatePolicyView(const NetworkInfo& info) {
   // Check if the network is managed by policy.
-  ONCSource source = info.source;
-  if (source != ONCSource::kDevicePolicy && source != ONCSource::kUserPolicy)
+  OncSource source = info.source;
+  if (source != OncSource::kDevicePolicy && source != OncSource::kUserPolicy)
     return nullptr;
 
   views::ImageView* controlled_icon = TrayPopupUtils::CreateMainImageView();
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index 10a0a00..f97cb499 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -77,16 +77,15 @@
   // a modal dialog is present.
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
-
-  bool kiosk_next = Shell::Get()->kiosk_next_shell_controller()->IsEnabled();
-  bool events_blocked = Shell::Get()
-                            ->tablet_mode_controller()
-                            ->AreInternalInputDeviceEventsBlocked();
   bool active_session = session_controller->GetSessionState() ==
                         session_manager::SessionState::ACTIVE;
   bool app_mode = session_controller->IsRunningInAppMode();
 
-  SetVisible(!kiosk_next && events_blocked && active_session && !app_mode);
+  bool kiosk_next = Shell::Get()->kiosk_next_shell_controller()->IsEnabled();
+  bool should_show =
+      Shell::Get()->tablet_mode_controller()->ShouldShowOverviewButton();
+
+  SetVisible(should_show && active_session && !app_mode && !kiosk_next);
 }
 
 void OverviewButtonTray::OnGestureEvent(ui::GestureEvent* event) {
diff --git a/ash/system/status_area_layout_manager.cc b/ash/system/status_area_layout_manager.cc
index 175d403..a0d16e4 100644
--- a/ash/system/status_area_layout_manager.cc
+++ b/ash/system/status_area_layout_manager.cc
@@ -40,7 +40,7 @@
   // area and the shelf isn't in the process of doing a layout.
   if (child != shelf_widget_->status_area_widget()->GetNativeWindow() ||
       in_layout_) {
-    wm::WmSnapToPixelLayoutManager::SetChildBounds(child, requested_bounds);
+    wm::WmDefaultLayoutManager::SetChildBounds(child, requested_bounds);
     return;
   }
 
@@ -49,7 +49,7 @@
   if (requested_bounds == child->GetTargetBounds())
     return;
 
-  wm::WmSnapToPixelLayoutManager::SetChildBounds(child, requested_bounds);
+  wm::WmDefaultLayoutManager::SetChildBounds(child, requested_bounds);
   LayoutStatusArea();
 }
 
diff --git a/ash/system/status_area_layout_manager.h b/ash/system/status_area_layout_manager.h
index 2b67501..3784de8 100644
--- a/ash/system/status_area_layout_manager.h
+++ b/ash/system/status_area_layout_manager.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SYSTEM_STATUS_AREA_LAYOUT_MANAGER_H_
 #define ASH_SYSTEM_STATUS_AREA_LAYOUT_MANAGER_H_
 
-#include "ash/wm/wm_snap_to_pixel_layout_manager.h"
+#include "ash/wm/wm_default_layout_manager.h"
 #include "base/macros.h"
 
 namespace ash {
@@ -15,12 +15,12 @@
 // StatusAreaLayoutManager is a layout manager responsible for the status area.
 // In any case when status area needs relayout it redirects this call to
 // ShelfLayoutManager.
-class StatusAreaLayoutManager : public wm::WmSnapToPixelLayoutManager {
+class StatusAreaLayoutManager : public wm::WmDefaultLayoutManager {
  public:
   explicit StatusAreaLayoutManager(ShelfWidget* shelf_widget);
   ~StatusAreaLayoutManager() override;
 
-  // Overridden from wm::WmSnapToPixelLayoutManager:
+  // Overridden from wm::WmDefaultLayoutManager:
   void OnWindowResized() override;
   void SetChildBounds(aura::Window* child,
                       const gfx::Rect& requested_bounds) override;
diff --git a/ash/system/status_area_widget.h b/ash/system/status_area_widget.h
index 7f924bf..2407e0b0 100644
--- a/ash/system/status_area_widget.h
+++ b/ash/system/status_area_widget.h
@@ -9,7 +9,6 @@
 #include "ash/kiosk_next/kiosk_next_shell_observer.h"
 #include "ash/login_status.h"
 #include "ash/public/cpp/shelf_types.h"
-#include "ash/shelf/shelf_background_animator_observer.h"
 #include "base/macros.h"
 #include "ui/views/widget/widget.h"
 
@@ -35,7 +34,6 @@
 // so that it can be shown in cases where the rest of the shelf is hidden (e.g.
 // on secondary monitors at the login screen).
 class ASH_EXPORT StatusAreaWidget : public views::Widget,
-                                    public ShelfBackgroundAnimatorObserver,
                                     public KioskNextShellObserver {
  public:
   StatusAreaWidget(aura::Window* status_container, Shelf* shelf);
diff --git a/ash/system/status_area_widget_delegate.cc b/ash/system/status_area_widget_delegate.cc
index a6710a8..73070d4 100644
--- a/ash/system/status_area_widget_delegate.cc
+++ b/ash/system/status_area_widget_delegate.cc
@@ -8,6 +8,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
+#include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/tray_constants.h"
@@ -65,6 +66,10 @@
 }
 
 bool StatusAreaWidgetDelegate::ShouldFocusOut(bool reverse) {
+  // Never bring the focus out if it's not a views-based shelf as it is visually
+  // not on par with the status widget.
+  if (!ShelfWidget::IsUsingViewsShelf())
+    return false;
   views::View* focused_view = GetFocusManager()->GetFocusedView();
   return (reverse && focused_view == GetFirstFocusableChild()) ||
          (!reverse && focused_view == GetLastFocusableChild());
@@ -142,7 +147,7 @@
     layout->StartRow(0, 0);
     for (auto* child : children()) {
       if (child->GetVisible())
-        layout->AddView(child);
+        layout->AddExistingView(child);
     }
   } else {
     columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
@@ -152,7 +157,7 @@
       if (!child->GetVisible())
         continue;
       layout->StartRow(0, 0);
-      layout->AddView(child);
+      layout->AddExistingView(child);
     }
   }
 
diff --git a/ash/system/time/time_view.cc b/ash/system/time/time_view.cc
index 735b3de..dafe955 100644
--- a/ash/system/time/time_view.cc
+++ b/ash/system/time/time_view.cc
@@ -80,6 +80,12 @@
     AddChildView(horizontal_label_.get());
   } else {
     RemoveChildView(horizontal_label_.get());
+    // Remove the current layout manager since it could be the FillLayout which
+    // only allows one child.
+    SetLayoutManager(nullptr);
+    // Pre-add the children since ownership is being retained by this.
+    AddChildView(vertical_label_hours_.get());
+    AddChildView(vertical_label_minutes_.get());
     views::GridLayout* layout =
         SetLayoutManager(std::make_unique<views::GridLayout>());
     const int kColumnId = 0;
@@ -89,9 +95,10 @@
                        0, views::GridLayout::USE_PREF, 0, 0);
     layout->AddPaddingRow(0, kClockLeadingPadding);
     layout->StartRow(0, kColumnId);
-    layout->AddView(vertical_label_hours_.get());
+    // Add the views as existing since ownership isn't being transferred.
+    layout->AddExistingView(vertical_label_hours_.get());
     layout->StartRow(0, kColumnId);
-    layout->AddView(vertical_label_minutes_.get());
+    layout->AddExistingView(vertical_label_minutes_.get());
     layout->AddPaddingRow(0, kVerticalClockMinutesTopOffset);
   }
   Layout();
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index d110e2f..cee0639 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -13,6 +13,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/login_shelf_view.h"
 #include "ash/shelf/shelf_constants.h"
+#include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
@@ -270,37 +271,7 @@
   if (!delegate || !delegate->ShouldFocusOut(reverse))
     return;
 
-  // At this point, we know we should focus out of the status widget. It
-  // remains to be determined whether we should bring focus to the shelf, or
-  // whether we should delegate to system tray focus observers to decide
-  // where the focus goes next.
-  bool should_focus_shelf = true;
-
-  if (!ShelfWidget::IsUsingViewsShelf()) {
-    // Never bring the focus to the shelf if it's not a views-based shelf as
-    // it is visually not on par with the status widget.
-    return;
-  }
-
-  // If we are using a views-based shelf:
-  // * If we're in an active session, always bring focus to the shelf whether
-  //   we are going in reverse or not.
-  // * Otherwise (login/lock screen, OOBE), bring focus to the shelf only
-  //   if we're going in reverse; if we're going forward, let the system tray
-  //   focus observers focus the lock/login view.
-  if (shelf->shelf_widget()->login_shelf_view()->GetVisible()) {
-    // Login/lock screen or OOBE.
-    should_focus_shelf = reverse;
-  }
-
-  if (should_focus_shelf) {
-    shelf->shelf_widget()->set_default_last_focusable_child(reverse);
-    shelf->shelf_widget()->set_activated_from_other_widget(true);
-    Shell::Get()->focus_cycler()->FocusWidget(shelf->shelf_widget());
-    shelf->shelf_widget()->FocusFirstOrLastFocusableChild(reverse);
-  } else {
-    Shell::Get()->system_tray_notifier()->NotifyFocusOut(reverse);
-  }
+  shelf_->shelf_focus_cycler()->FocusOut(reverse, SourceView::kSystemTrayView);
 }
 
 void TrayBackgroundView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index b271463..e42301a61 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -172,9 +172,12 @@
 constexpr SkColor kUnifiedFeaturePodHoverColor =
     SkColorSetRGB(0xff, 0xff, 0xff);
 constexpr SkColor kUnifiedRecordingIconColor = gfx::kGoogleRedDark600;
+constexpr SkColor kUnifiedManagedDeviceIconColor =
+    SkColorSetRGB(0x9a, 0xa0, 0xa6);
 
 constexpr gfx::Insets kUnifiedMenuItemPadding(0, 16, 16, 16);
 constexpr gfx::Insets kUnifiedSystemInfoViewPadding(4, 16, 16, 16);
+constexpr gfx::Insets kUnifiedManagedDeviceViewPadding(4, 19, 4, 16);
 constexpr gfx::Insets kUnifiedSliderRowPadding(0, 12, 8, 16);
 constexpr gfx::Insets kUnifiedSliderBubblePadding(12, 0, 4, 0);
 constexpr gfx::Insets kUnifiedSliderPadding(0, 16);
@@ -211,6 +214,7 @@
 // A dark disc with |kTrayItemSize| diameter is drawn in the background.
 constexpr int kTrayTopShortcutButtonIconSize = 20;
 
+constexpr int kUnifiedManagedDeviceSpacing = 4;
 constexpr int kUnifiedSystemInfoHeight = 16;
 constexpr int kUnifiedSystemInfoSpacing = 8;
 constexpr int kUnifiedSystemInfoSeparatorColor =
diff --git a/ash/system/unified/top_shortcuts_view.cc b/ash/system/unified/top_shortcuts_view.cc
index 571cbd3..756cb83b 100644
--- a/ash/system/unified/top_shortcuts_view.cc
+++ b/ash/system/unified/top_shortcuts_view.cc
@@ -247,4 +247,8 @@
       !Shell::Get()->accessibility_controller()->spoken_feedback_enabled());
 }
 
+const char* TopShortcutsView::GetClassName() const {
+  return "TopShortcutsView";
+}
+
 }  // namespace ash
diff --git a/ash/system/unified/top_shortcuts_view.h b/ash/system/unified/top_shortcuts_view.h
index 645310bc..a408bf8 100644
--- a/ash/system/unified/top_shortcuts_view.h
+++ b/ash/system/unified/top_shortcuts_view.h
@@ -59,6 +59,9 @@
   // AccessibilityObserver:
   void OnAccessibilityStatusChanged() override;
 
+  // views::View
+  const char* GetClassName() const override;
+
  private:
   friend class TopShortcutsViewTest;
 
diff --git a/ash/system/unified/unified_managed_device_view.cc b/ash/system/unified/unified_managed_device_view.cc
new file mode 100644
index 0000000..78b9143
--- /dev/null
+++ b/ash/system/unified/unified_managed_device_view.cc
@@ -0,0 +1,99 @@
+// Copyright 2019 The Chromium 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/system/unified/unified_managed_device_view.h"
+
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/enterprise/enterprise_domain_observer.h"
+#include "ash/system/model/enterprise_domain_model.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/supervised/supervised_icon_string.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_utils.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace ash {
+
+UnifiedManagedDeviceView::UnifiedManagedDeviceView()
+    : icon_(new views::ImageView), label_(new views::Label) {
+  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kHorizontal,
+      kUnifiedManagedDeviceViewPadding, kUnifiedManagedDeviceSpacing));
+  layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kCenter);
+
+  icon_->SetPreferredSize(
+      gfx::Size(kUnifiedSystemInfoHeight, kUnifiedSystemInfoHeight));
+  AddChildView(icon_);
+
+  label_->SetAutoColorReadabilityEnabled(false);
+  label_->SetSubpixelRenderingEnabled(false);
+  label_->SetEnabledColor(kUnifiedMenuSecondaryTextColor);
+  AddChildView(label_);
+
+  Shell::Get()->session_controller()->AddObserver(this);
+  Shell::Get()->system_tray_model()->enterprise_domain()->AddObserver(this);
+  Update();
+}
+
+UnifiedManagedDeviceView::~UnifiedManagedDeviceView() {
+  Shell::Get()->system_tray_model()->enterprise_domain()->RemoveObserver(this);
+  Shell::Get()->session_controller()->RemoveObserver(this);
+}
+
+void UnifiedManagedDeviceView::OnLoginStatusChanged(LoginStatus status) {
+  Update();
+}
+
+void UnifiedManagedDeviceView::OnEnterpriseDomainChanged() {
+  Update();
+}
+
+const char* UnifiedManagedDeviceView::GetClassName() const {
+  return "UnifiedManagedDeviceView";
+}
+
+void UnifiedManagedDeviceView::Update() {
+  SessionControllerImpl* session = Shell::Get()->session_controller();
+  EnterpriseDomainModel* model =
+      Shell::Get()->system_tray_model()->enterprise_domain();
+  std::string enterprise_domain_name = model->enterprise_display_domain();
+
+  if (session->ShouldDisplayManagedUI() || model->active_directory_managed() ||
+      !enterprise_domain_name.empty()) {
+    // Show enterpised managed UI.
+    icon_->SetImage(gfx::CreateVectorIcon(kSystemTrayManagedIcon,
+                                          kUnifiedManagedDeviceIconColor));
+
+    if (!enterprise_domain_name.empty()) {
+      label_->SetText(l10n_util::GetStringFUTF16(
+          IDS_ASH_ENTERPRISE_DEVICE_MANAGED_BY,
+          base::UTF8ToUTF16(enterprise_domain_name)));
+    } else {
+      label_->SetText(
+          l10n_util::GetStringUTF16(IDS_ASH_ENTERPRISE_DEVICE_MANAGED));
+    }
+
+    SetVisible(true);
+  } else if (session->IsUserSupervised()) {
+    // Show supervised user UI (locally supervised or Family Link).
+    icon_->SetImage(gfx::CreateVectorIcon(GetSupervisedUserIcon(),
+                                          kUnifiedManagedDeviceIconColor));
+    label_->SetText(GetSupervisedUserMessage());
+    SetVisible(true);
+  } else {
+    SetVisible(false);
+  }
+}
+
+}  // namespace ash
diff --git a/ash/system/unified/unified_managed_device_view.h b/ash/system/unified/unified_managed_device_view.h
new file mode 100644
index 0000000..f088f5c6
--- /dev/null
+++ b/ash/system/unified/unified_managed_device_view.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_UNIFIED_UNIFIED_MANAGED_DEVICE_VIEW_H_
+#define ASH_SYSTEM_UNIFIED_UNIFIED_MANAGED_DEVICE_VIEW_H_
+
+#include "ash/session/session_observer.h"
+#include "ash/system/enterprise/enterprise_domain_observer.h"
+#include "base/macros.h"
+#include "ui/views/view.h"
+
+namespace views {
+class Label;
+class ImageView;
+}  // namespace views
+
+namespace ash {
+
+// Row in the unified system tray bubble shown when the device is currently
+// managed by an administrator (by a domain admin or FamilyLink).
+class ASH_EXPORT UnifiedManagedDeviceView : public views::View,
+                                            public SessionObserver,
+                                            public EnterpriseDomainObserver {
+ public:
+  UnifiedManagedDeviceView();
+  ~UnifiedManagedDeviceView() override;
+
+  // SessionObserver:
+  void OnLoginStatusChanged(LoginStatus status) override;
+
+  // EnterpriseDomainObserver:
+  void OnEnterpriseDomainChanged() override;
+
+  // views::TrayItemView:
+  const char* GetClassName() const override;
+
+ private:
+  void Update();
+
+  // Owned by views hierarchy.
+  views::ImageView* const icon_;
+  views::Label* const label_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnifiedManagedDeviceView);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_UNIFIED_UNIFIED_MANAGED_DEVICE_VIEW_H_
diff --git a/ash/system/unified/unified_managed_device_view_unittest.cc b/ash/system/unified/unified_managed_device_view_unittest.cc
new file mode 100644
index 0000000..47097eb
--- /dev/null
+++ b/ash/system/unified/unified_managed_device_view_unittest.cc
@@ -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.
+
+#include "ash/system/unified/unified_managed_device_view.h"
+
+#include "ash/session/session_controller_impl.h"
+#include "ash/session/test_session_controller_client.h"
+#include "ash/shell.h"
+#include "ash/system/model/enterprise_domain_model.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/test/ash_test_base.h"
+
+#include "ash/system/unified/unified_system_tray_view.h"
+
+namespace ash {
+
+class UnifiedManagedDeviceViewTest : public AshTestBase {
+ public:
+  UnifiedManagedDeviceViewTest() = default;
+  ~UnifiedManagedDeviceViewTest() override = default;
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+    managed_device_view_ = std::make_unique<UnifiedManagedDeviceView>();
+  }
+
+  void TearDown() override {
+    managed_device_view_.reset();
+    AshTestBase::TearDown();
+  }
+
+ protected:
+  std::unique_ptr<UnifiedManagedDeviceView> managed_device_view_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UnifiedManagedDeviceViewTest);
+};
+
+TEST_F(UnifiedManagedDeviceViewTest, EnterpriseManagedDevice) {
+  // By default, UnifiedManagedDeviceView is not shown.
+  EXPECT_FALSE(managed_device_view_->GetVisible());
+
+  // Simulate enterprise information becoming available.
+  const bool active_directory = false;
+  Shell::Get()
+      ->system_tray_model()
+      ->enterprise_domain()
+      ->SetEnterpriseDisplayDomain("example.com", active_directory);
+
+  EXPECT_TRUE(managed_device_view_->GetVisible());
+}
+
+TEST_F(UnifiedManagedDeviceViewTest, ActiveDirectoryManagedDevice) {
+  // Simulate active directory information becoming available.
+  const std::string empty_domain;
+  const bool active_directory = true;
+  Shell::Get()
+      ->system_tray_model()
+      ->enterprise_domain()
+      ->SetEnterpriseDisplayDomain(empty_domain, active_directory);
+
+  EXPECT_TRUE(managed_device_view_->GetVisible());
+}
+
+using UnifiedManagedDeviceViewNoSessionTest = NoSessionAshTestBase;
+
+TEST_F(UnifiedManagedDeviceViewNoSessionTest, SupervisedUserDevice) {
+  SessionControllerImpl* session = Shell::Get()->session_controller();
+  ASSERT_FALSE(session->IsActiveUserSessionStarted());
+
+  // Before login the UnifiedManagedDeviceView is invisible.
+  std::unique_ptr<UnifiedManagedDeviceView> managed_device_view =
+      std::make_unique<UnifiedManagedDeviceView>();
+  EXPECT_FALSE(managed_device_view->GetVisible());
+  managed_device_view.reset();
+
+  // Simulate a supervised user logging in.
+  TestSessionControllerClient* client = GetSessionControllerClient();
+  client->Reset();
+  client->AddUserSession("child@test.com", user_manager::USER_TYPE_SUPERVISED);
+  client->SetSessionState(session_manager::SessionState::ACTIVE);
+  UserSession user_session = *session->GetUserSession(0);
+  user_session.custodian_email = "parent@test.com";
+  session->UpdateUserSession(std::move(user_session));
+
+  // Now the UnifiedManagedDeviceView is visible.
+  managed_device_view = std::make_unique<UnifiedManagedDeviceView>();
+  ASSERT_TRUE(managed_device_view->GetVisible());
+}
+
+}  // namespace ash
diff --git a/ash/system/unified/unified_slider_view.cc b/ash/system/unified/unified_slider_view.cc
index 8c9f2fe37..6c3acb0e 100644
--- a/ash/system/unified/unified_slider_view.cc
+++ b/ash/system/unified/unified_slider_view.cc
@@ -130,7 +130,7 @@
   // Prevent an accessibility event while initiallizing this view. Typically
   // the first update of the slider value is conducted by the caller function
   // to reflect the current value.
-  slider_->set_enable_accessibility_events(false);
+  slider_->SetEnableAccessibilityEvents(false);
 
   slider_->GetViewAccessibility().OverrideName(
       l10n_util::GetStringUTF16(accessible_name_id));
@@ -152,7 +152,7 @@
 
   slider_->SetValue(value);
   if (by_user)
-    slider_->set_enable_accessibility_events(true);
+    slider_->SetEnableAccessibilityEvents(true);
 }
 
 const char* UnifiedSliderView::GetClassName() const {
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index e191d20..adcef6e 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -235,6 +235,10 @@
   }
 }
 
+const char* UnifiedSystemTray::GetClassName() const {
+  return "UnifiedSystemTray";
+}
+
 void UnifiedSystemTray::SetTrayEnabled(bool enabled) {
   // We should close bubble at this point. If it remains opened and interactive,
   // it can be dangerous (http://crbug.com/497080).
diff --git a/ash/system/unified/unified_system_tray.h b/ash/system/unified/unified_system_tray.h
index f175be9..51ac2cd4 100644
--- a/ash/system/unified/unified_system_tray.h
+++ b/ash/system/unified/unified_system_tray.h
@@ -105,6 +105,7 @@
   void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
   void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
+  const char* GetClassName() const override;
 
   UnifiedSystemTrayModel* model() { return model_.get(); }
 
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index 7c7d3103..cb6503f 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -451,6 +451,10 @@
   PreferredSizeChanged();
 }
 
+const char* UnifiedSystemTrayView::GetClassName() const {
+  return "UnifiedSystemTrayView";
+}
+
 views::FocusTraversable* UnifiedSystemTrayView::GetFocusTraversable() {
   return this;
 }
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h
index eea0872..e307a89 100644
--- a/ash/system/unified/unified_system_tray_view.h
+++ b/ash/system/unified/unified_system_tray_view.h
@@ -124,6 +124,7 @@
   // views::View:
   void OnGestureEvent(ui::GestureEvent* event) override;
   void ChildPreferredSizeChanged(views::View* child) override;
+  const char* GetClassName() const override;
   views::FocusTraversable* GetFocusTraversable() override;
 
   // views::FocusTraversable:
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 56083f57..59751fb 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -30,6 +30,7 @@
 #include "ash/test_shell_delegate.h"
 #include "ash/utility/screenshot_controller.h"
 #include "ash/window_factory.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_positioner.h"
 #include "ash/wm/work_area_insets.h"
 #include "base/memory/ptr_util.h"
@@ -157,6 +158,8 @@
 
 void AshTestBase::TearDown() {
   teardown_called_ = true;
+  // Make sure that we can exit tablet mode before shutdown correctly.
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
   Shell::Get()->session_controller()->NotifyChromeTerminating();
 
   // Flush the message loop to finish pending release tasks.
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index a552cc6..73f14ab 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -13,6 +13,7 @@
 #include "ash/wm/desks/root_window_desk_switch_animator.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/overview/overview_item.h"
 #include "ash/wm/window_util.h"
 #include "base/auto_reset.h"
 #include "base/logging.h"
@@ -99,6 +100,20 @@
   return desks_.size() < desks_util::kMaxNumberOfDesks;
 }
 
+Desk* DesksController::GetNextDesk() const {
+  int next_index = GetDeskIndex(active_desk_);
+  if (++next_index >= static_cast<int>(desks_.size()))
+    return nullptr;
+  return desks_[next_index].get();
+}
+
+Desk* DesksController::GetPreviousDesk() const {
+  int previous_index = GetDeskIndex(active_desk_);
+  if (--previous_index < 0)
+    return nullptr;
+  return desks_[previous_index].get();
+}
+
 bool DesksController::CanRemoveDesks() const {
   return desks_.size() > 1;
 }
@@ -254,7 +269,26 @@
   DCHECK_NE(active_desk_, target_desk);
 
   base::AutoReset<bool> in_progress(&are_desks_being_modified_, true);
+
+  auto* overview_controller = Shell::Get()->overview_controller();
+  const bool in_overview = overview_controller->InOverviewSession();
+
   active_desk_->MoveWindowToDesk(window, target_desk);
+
+  if (in_overview) {
+    DCHECK(overview_controller->InOverviewSession());
+    auto* overview_session = overview_controller->overview_session();
+    auto* item = overview_session->GetOverviewItemForWindow(window);
+    DCHECK(item);
+    // Restore the dragged item window, so that its transform is reset to
+    // identity.
+    item->RestoreWindow(/*reset_transform=*/true);
+    // The item no longer needs to be in the overview grid.
+    overview_session->RemoveItem(item);
+  }
+
+  // A window moving out of the active desk cannot be active.
+  wm::DeactivateWindow(window);
 }
 
 void DesksController::OnRootWindowAdded(aura::Window* root_window) {
@@ -387,7 +421,7 @@
 
   // Mark the new desk as active first, so that deactivating windows on the
   // `old_active` desk do not activate other windows on the same desk. See
-  // `ash::IsWindowConsideredVisibleForActivation()`.
+  // `ash::AshFocusRules::GetNextActivatableWindow()`.
   Desk* old_active = active_desk_;
   active_desk_ = const_cast<Desk*>(desk);
 
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h
index 8bdbe04..8137dd5 100644
--- a/ash/wm/desks/desks_controller.h
+++ b/ash/wm/desks/desks_controller.h
@@ -78,6 +78,12 @@
   // there is at least one single desk at any time.
   bool CanRemoveDesks() const;
 
+  // Returns the next / previous desks to the currently active desk. Returns
+  // nullptr if the active desk is the first on the left or the last on the
+  // right, and previous and next desks are requested respectively.
+  Desk* GetNextDesk() const;
+  Desk* GetPreviousDesk() const;
+
   // Creates a new desk. CanCreateDesks() must be checked before calling this.
   void NewDesk();
 
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index fe705a27..f2e376e 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -6,9 +6,11 @@
 
 #include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/public/cpp/ash_features.h"
+#include "ash/public/cpp/event_rewriter_controller.h"
 #include "ash/public/cpp/multi_user_window_manager.h"
 #include "ash/public/cpp/multi_user_window_manager_delegate.h"
 #include "ash/shell.h"
+#include "ash/sticky_keys/sticky_keys_controller.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/window_factory.h"
 #include "ash/wm/desks/close_desk_button.h"
@@ -35,6 +37,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
+#include "ui/chromeos/events/event_rewriter_chromeos.h"
 #include "ui/compositor_extra/shadow.h"
 #include "ui/events/gesture_detection/gesture_configuration.h"
 #include "ui/events/test/event_generator.h"
@@ -1617,6 +1620,223 @@
   EXPECT_FALSE(win3->IsVisible());
 }
 
+// Simulates the same behavior of event rewriting that key presses go through.
+class DesksAcceleratorsTest : public DesksTest,
+                              public ui::EventRewriterChromeOS::Delegate {
+ public:
+  DesksAcceleratorsTest() = default;
+  ~DesksAcceleratorsTest() override = default;
+
+  // DesksTest:
+  void SetUp() override {
+    DesksTest::SetUp();
+
+    auto* event_rewriter_controller = EventRewriterController::Get();
+    event_rewriter_controller->AddEventRewriter(
+        std::make_unique<ui::EventRewriterChromeOS>(
+            this, Shell::Get()->sticky_keys_controller()));
+  }
+
+  // ui::EventRewriterChromeOS::Delegate:
+  bool RewriteModifierKeys() override { return true; }
+  bool GetKeyboardRemappedPrefValue(const std::string& pref_name,
+                                    int* result) const override {
+    return false;
+  }
+  bool TopRowKeysAreFunctionKeys() const override { return false; }
+  bool IsExtensionCommandRegistered(ui::KeyboardCode key_code,
+                                    int flags) const override {
+    return false;
+  }
+  bool IsSearchKeyAcceleratorReserved() const override { return true; }
+
+  void SendAccelerator(ui::KeyboardCode key_code, int flags) {
+    ui::test::EventGenerator* generator = GetEventGenerator();
+    generator->PressKey(key_code, flags);
+    generator->ReleaseKey(key_code, flags);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DesksAcceleratorsTest);
+};
+
+TEST_F(DesksAcceleratorsTest, NewDesk) {
+  auto* controller = DesksController::Get();
+  // It's possible to add up to `kMaxNumberOfDesks` desks using the shortcut.
+  const int flags = ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN;
+  for (size_t num_desks = 1; num_desks < desks_util::kMaxNumberOfDesks;
+       ++num_desks) {
+    DeskSwitchAnimationWaiter waiter;
+    SendAccelerator(ui::VKEY_OEM_PLUS, flags);
+    waiter.Wait();
+    // The newly created desk should be activated.
+    ASSERT_EQ(num_desks + 1, controller->desks().size());
+    EXPECT_TRUE(controller->desks().back()->is_active());
+  }
+
+  // When we reach the limit, the shortcut does nothing.
+  EXPECT_EQ(desks_util::kMaxNumberOfDesks, controller->desks().size());
+  SendAccelerator(ui::VKEY_OEM_PLUS, flags);
+  EXPECT_EQ(desks_util::kMaxNumberOfDesks, controller->desks().size());
+}
+
+TEST_F(DesksAcceleratorsTest, CannotRemoveLastDesk) {
+  auto* controller = DesksController::Get();
+  // Removing the last desk is not possible.
+  ASSERT_EQ(1u, controller->desks().size());
+  const int flags = ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN;
+  SendAccelerator(ui::VKEY_OEM_MINUS, flags);
+  ASSERT_EQ(1u, controller->desks().size());
+}
+
+TEST_F(DesksAcceleratorsTest, RemoveDesk) {
+  auto* controller = DesksController::Get();
+  // Create a few desks and remove them outside and inside overview using the
+  // shortcut.
+  controller->NewDesk();
+  controller->NewDesk();
+  ASSERT_EQ(3u, controller->desks().size());
+  Desk* desk_1 = controller->desks()[0].get();
+  Desk* desk_2 = controller->desks()[1].get();
+  Desk* desk_3 = controller->desks()[2].get();
+  EXPECT_TRUE(desk_1->is_active());
+  const int flags = ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN;
+  SendAccelerator(ui::VKEY_OEM_MINUS, flags);
+  ASSERT_EQ(2u, controller->desks().size());
+  EXPECT_TRUE(desk_2->is_active());
+
+  // Using the accelerator doesn't result in exiting overview.
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->StartOverview();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  SendAccelerator(ui::VKEY_OEM_MINUS, flags);
+  ASSERT_EQ(1u, controller->desks().size());
+  EXPECT_TRUE(desk_3->is_active());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+}
+
+TEST_F(DesksAcceleratorsTest, LeftRightDeskActivation) {
+  auto* controller = DesksController::Get();
+  controller->NewDesk();
+  ASSERT_EQ(2u, controller->desks().size());
+  Desk* desk_1 = controller->desks()[0].get();
+  Desk* desk_2 = controller->desks()[1].get();
+  EXPECT_TRUE(desk_1->is_active());
+  // No desk on left, nothing should happen.
+  const int flags = ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN;
+  SendAccelerator(ui::VKEY_OEM_4, flags);
+  EXPECT_TRUE(desk_1->is_active());
+
+  // Go right until there're no more desks on the right.
+  {
+    DeskSwitchAnimationWaiter waiter;
+    SendAccelerator(ui::VKEY_OEM_6, flags);
+    waiter.Wait();
+    EXPECT_TRUE(desk_2->is_active());
+  }
+
+  // Nothing happens.
+  SendAccelerator(ui::VKEY_OEM_6, flags);
+  EXPECT_TRUE(desk_2->is_active());
+
+  // Go back left.
+  {
+    DeskSwitchAnimationWaiter waiter;
+    SendAccelerator(ui::VKEY_OEM_4, flags);
+    waiter.Wait();
+    EXPECT_TRUE(desk_1->is_active());
+  }
+}
+
+TEST_F(DesksAcceleratorsTest, MoveWindowLeftRightDesk) {
+  auto* controller = DesksController::Get();
+  controller->NewDesk();
+  ASSERT_EQ(2u, controller->desks().size());
+  Desk* desk_1 = controller->desks()[0].get();
+  Desk* desk_2 = controller->desks()[1].get();
+  EXPECT_TRUE(desk_1->is_active());
+
+  auto window = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  wm::ActivateWindow(window.get());
+  EXPECT_EQ(window.get(), wm::GetActiveWindow());
+
+  // Moving window left when this is the left-most desk. Nothing happens.
+  const int flags =
+      ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN;
+  SendAccelerator(ui::VKEY_OEM_4, flags);
+  EXPECT_EQ(window.get(), wm::GetActiveWindow());
+  EXPECT_TRUE(DoesActiveDeskContainWindow(window.get()));
+
+  // Move window right, it should be deactivated.
+  SendAccelerator(ui::VKEY_OEM_6, flags);
+  EXPECT_EQ(nullptr, wm::GetActiveWindow());
+  EXPECT_TRUE(desk_1->windows().empty());
+  EXPECT_TRUE(base::Contains(desk_2->windows(), window.get()));
+
+  // No more active windows on this desk, nothing happens.
+  SendAccelerator(ui::VKEY_OEM_6, flags);
+  EXPECT_TRUE(desk_1->windows().empty());
+  EXPECT_EQ(nullptr, wm::GetActiveWindow());
+
+  // Activate desk 2, and do the same set of tests.
+  ActivateDesk(desk_2);
+  EXPECT_EQ(window.get(), wm::GetActiveWindow());
+
+  // Move right does nothing.
+  SendAccelerator(ui::VKEY_OEM_6, flags);
+  EXPECT_EQ(window.get(), wm::GetActiveWindow());
+
+  SendAccelerator(ui::VKEY_OEM_4, flags);
+  EXPECT_TRUE(desk_2->windows().empty());
+  EXPECT_EQ(nullptr, wm::GetActiveWindow());
+  EXPECT_TRUE(base::Contains(desk_1->windows(), window.get()));
+}
+
+TEST_F(DesksAcceleratorsTest, MoveWindowLeftRightDeskOverview) {
+  auto* controller = DesksController::Get();
+  controller->NewDesk();
+  ASSERT_EQ(2u, controller->desks().size());
+  Desk* desk_1 = controller->desks()[0].get();
+  Desk* desk_2 = controller->desks()[1].get();
+  EXPECT_TRUE(desk_1->is_active());
+
+  auto win0 = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  auto win1 = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  wm::ActivateWindow(win0.get());
+  EXPECT_EQ(win0.get(), wm::GetActiveWindow());
+
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->StartOverview();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  const int flags =
+      ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN;
+  // In overview, while no window is highlighted, nothing should happen.
+  const size_t num_windows_before = desk_1->windows().size();
+  EXPECT_TRUE(desk_2->windows().empty());
+  SendAccelerator(ui::VKEY_OEM_6, flags);
+  ASSERT_EQ(num_windows_before, desk_1->windows().size());
+  EXPECT_TRUE(desk_2->windows().empty());
+
+  // It's possible to move the highlighted window.
+  auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  overview_grid->Move(/*reverse=*/false, /*animate=*/false);
+  EXPECT_EQ(win0.get(), overview_grid->SelectedWindow()->GetWindow());
+  SendAccelerator(ui::VKEY_OEM_6, flags);
+  EXPECT_FALSE(DoesActiveDeskContainWindow(win0.get()));
+  EXPECT_TRUE(base::Contains(desk_2->windows(), win0.get()));
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+
+  // The highlight widget should move to the next window.
+  EXPECT_EQ(win1.get(), overview_grid->SelectedWindow()->GetWindow());
+  SendAccelerator(ui::VKEY_OEM_6, flags);
+  EXPECT_FALSE(DoesActiveDeskContainWindow(win1.get()));
+  EXPECT_TRUE(base::Contains(desk_2->windows(), win1.get()));
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+
+  // No more highlighted windows.
+  EXPECT_FALSE(overview_grid->SelectedWindow());
+}
+
 // TODO(afakhry): Add more tests:
 // - Always on top windows are not tracked by any desk.
 // - Reusing containers when desks are removed and created.
diff --git a/ash/wm/lock_action_handler_layout_manager.h b/ash/wm/lock_action_handler_layout_manager.h
index 849975e..a2a68ed 100644
--- a/ash/wm/lock_action_handler_layout_manager.h
+++ b/ash/wm/lock_action_handler_layout_manager.h
@@ -48,7 +48,7 @@
       LockScreenActionBackgroundController* action_background_controller);
   ~LockActionHandlerLayoutManager() override;
 
-  // WmSnapToPixelLayoutManager:
+  // WmDefaultLayoutManager:
   void OnWindowAddedToLayout(aura::Window* child) override;
   void OnChildWindowVisibilityChanged(aura::Window* child,
                                       bool visibile) override;
diff --git a/ash/wm/lock_layout_manager.cc b/ash/wm/lock_layout_manager.cc
index d2375f2..293b7a3d 100644
--- a/ash/wm/lock_layout_manager.cc
+++ b/ash/wm/lock_layout_manager.cc
@@ -17,7 +17,7 @@
 namespace ash {
 
 LockLayoutManager::LockLayoutManager(aura::Window* window, Shelf* shelf)
-    : wm::WmSnapToPixelLayoutManager(),
+    : wm::WmDefaultLayoutManager(),
       window_(window),
       root_window_(window->GetRootWindow()),
       shelf_observer_(this) {
diff --git a/ash/wm/lock_layout_manager.h b/ash/wm/lock_layout_manager.h
index 99512a8..011ed60 100644
--- a/ash/wm/lock_layout_manager.h
+++ b/ash/wm/lock_layout_manager.h
@@ -10,7 +10,7 @@
 #include "ash/public/cpp/keyboard/keyboard_controller_observer.h"
 #include "ash/shelf/shelf_observer.h"
 #include "ash/shell_observer.h"
-#include "ash/wm/wm_snap_to_pixel_layout_manager.h"
+#include "ash/wm/wm_default_layout_manager.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "ui/aura/window_observer.h"
@@ -36,7 +36,7 @@
 // virtual keyboard changes inner workspace of each WebContents.
 // For all windows in LockScreenContainer default wm::WindowState is replaced
 // with LockWindowState.
-class ASH_EXPORT LockLayoutManager : public wm::WmSnapToPixelLayoutManager,
+class ASH_EXPORT LockLayoutManager : public wm::WmDefaultLayoutManager,
                                      public aura::WindowObserver,
                                      public ShellObserver,
                                      public ShelfObserver,
@@ -45,7 +45,7 @@
   LockLayoutManager(aura::Window* window, Shelf* shelf);
   ~LockLayoutManager() override;
 
-  // Overridden from WmSnapToPixelLayoutManager:
+  // Overridden from WmDefaultLayoutManager:
   void OnWindowResized() override;
   void OnWindowAddedToLayout(aura::Window* child) override;
   void OnWillRemoveWindowFromLayout(aura::Window* child) override;
diff --git a/ash/wm/overlay_layout_manager.h b/ash/wm/overlay_layout_manager.h
index 7108cc94..3cd3eb6 100644
--- a/ash/wm/overlay_layout_manager.h
+++ b/ash/wm/overlay_layout_manager.h
@@ -6,7 +6,7 @@
 #define ASH_WM_OVERLAY_LAYOUT_MANAGER_H_
 
 #include "ash/ash_export.h"
-#include "ash/wm/wm_snap_to_pixel_layout_manager.h"
+#include "ash/wm/wm_default_layout_manager.h"
 #include "base/macros.h"
 #include "ui/display/display_observer.h"
 
@@ -18,7 +18,7 @@
 
 // Updates the bounds of widgets in the overlay container whenever the display
 // bounds change. Keeps children snapped to pixel bounds.
-class ASH_EXPORT OverlayLayoutManager : public wm::WmSnapToPixelLayoutManager,
+class ASH_EXPORT OverlayLayoutManager : public wm::WmDefaultLayoutManager,
                                         public display::DisplayObserver {
  public:
   explicit OverlayLayoutManager(aura::Window* overlay_container);
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index cd17fda..1cc2575 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -391,10 +391,11 @@
         root_window_->layer()->GetCompositor(), single_animation_in_clamshell);
   }
 
-  overview_session_ = nullptr;
-
   while (!window_list_.empty())
     RemoveItem(window_list_.back().get());
+
+  // RemoveItem() uses `overview_session_`, so clear it at the end.
+  overview_session_ = nullptr;
 }
 
 void OverviewGrid::PrepareForOverview() {
@@ -542,7 +543,7 @@
 OverviewItem* OverviewGrid::SelectedWindow() const {
   if (!selection_widget_)
     return nullptr;
-  CHECK(selected_index_ < window_list_.size());
+  CHECK_LT(selected_index_, window_list_.size());
   return window_list_[selected_index_].get();
 }
 
@@ -592,12 +593,24 @@
                              return item->GetWindow() == window;
                            });
   DCHECK(iter != window_list_.rend());
+  const size_t removed_index = GetOverviewItemIndex(overview_item);
   window_observer_.Remove(window);
   window_state_observer_.Remove(wm::GetWindowState(window));
   // Erase from the list first because deleting OverviewItem can lead to
   // iterating through the |window_list_|.
   std::unique_ptr<OverviewItem> tmp = std::move(*iter);
   window_list_.erase(std::next(iter).base());
+
+  if (empty()) {
+    selected_index_ = 0;
+    selection_widget_.reset();
+  } else if (selection_widget_ && !overview_session_->is_shutting_down()) {
+    const bool send_focus_alert = selected_index_ == removed_index;
+    if (selected_index_ >= removed_index && selected_index_ != 0)
+      --selected_index_;
+    if (send_focus_alert)
+      SelectedWindow()->SendAccessibleSelectionEvent();
+  }
 }
 
 void OverviewGrid::AddDropTargetForDraggingFromOverview(
@@ -623,8 +636,6 @@
 void OverviewGrid::RemoveDropTarget() {
   DCHECK(drop_target_widget_);
   OverviewItem* drop_target = GetDropTarget();
-  if (selection_widget_ && selected_index_ >= GetOverviewItemIndex(drop_target))
-    --selected_index_;
   overview_session_->RemoveItem(drop_target);
   drop_target_widget_.reset();
 }
@@ -832,6 +843,7 @@
 }
 
 void OverviewGrid::OnWindowDestroying(aura::Window* window) {
+  // TODO(sammiequon): Consider making this use `RemoveItem()`.
   window_observer_.Remove(window);
   window_state_observer_.Remove(wm::GetWindowState(window));
   auto iter = GetOverviewItemIterContainingWindow(window);
@@ -939,6 +951,7 @@
   for (auto& window : window_list()) {
     window->set_disable_mask(true);
     window->UpdateMaskAndShadow();
+    window->StopWidgetAnimation();
   }
 }
 
@@ -1325,12 +1338,6 @@
       return false;
 
     desks_controller->MoveWindowFromActiveDeskTo(dragged_window, target_desk);
-    // Restore the dragged item window, so that its transform is reset to
-    // identity.
-    drag_item->RestoreWindow(/*reset_transform=*/true);
-
-    // The item no longer needs to be in the overview grid.
-    overview_session_->RemoveItem(drag_item);
     return true;
   }
 
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index f6faf07..fb9e043a 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -809,6 +809,11 @@
   UpdateCannotSnapWarningVisibility();
 }
 
+void OverviewItem::StopWidgetAnimation() {
+  DCHECK(item_widget_.get());
+  item_widget_->GetNativeWindow()->layer()->GetAnimator()->StopAnimating();
+}
+
 void OverviewItem::SetOpacity(float opacity) {
   item_widget_->SetOpacity(opacity);
   transform_window_.SetOpacity(opacity);
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h
index 5ebbcbd4..0546b446 100644
--- a/ash/wm/overview/overview_item.h
+++ b/ash/wm/overview/overview_item.h
@@ -180,6 +180,9 @@
   // if there was no starting animation.
   void OnStartingAnimationComplete();
 
+  // Stops the current animation of |item_widget_|.
+  void StopWidgetAnimation();
+
   // Changes the opacity of all the windows the item owns.
   void SetOpacity(float opacity);
   float GetOpacity();
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index f10be95a..d089b1af 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -735,6 +735,15 @@
   return nullptr;
 }
 
+aura::Window* OverviewSession::GetHighlightedWindow() {
+  DCHECK_LT(selected_grid_index_, grid_list_.size());
+  auto* grid = grid_list_[selected_grid_index_].get();
+  if (!grid->is_selecting())
+    return nullptr;
+
+  return grid->SelectedWindow()->GetWindow();
+}
+
 void OverviewSession::SuspendReposition() {
   for (auto& grid : grid_list_)
     grid->set_suspend_reposition(true);
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 5dd0548..3daafc5 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -223,6 +223,9 @@
   // Gets the window which keeps focus for the duration of overview mode.
   aura::Window* GetOverviewFocusWindow();
 
+  // Returns the window highlighted by the selector widget.
+  aura::Window* GetHighlightedWindow();
+
   // Suspends/Resumes window re-positiong in overview.
   void SuspendReposition();
   void ResumeReposition();
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 58c08c6a..c20eee4 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -327,7 +327,11 @@
     Shell::Get()->activation_client()->AddObserver(this);
     Shell::Get()->NotifySplitViewModeStarting();
 
-    divider_position_ = GetDefaultDividerPosition(window);
+    // If there is pre-set |divider_position_|, use it. It can happen during
+    // tablet <-> clamshell transition or multi-user transition.
+    divider_position_ = (divider_position_ < 0)
+                            ? GetDefaultDividerPosition(window)
+                            : divider_position_;
     default_snap_position_ = snap_position;
 
     // There is no divider bar in clamshell splitview mode.
@@ -705,6 +709,14 @@
   return window && (window == left_window_ || window == right_window_);
 }
 
+void SplitViewController::InitDividerPositionForTransition(
+    int divider_position) {
+  // This should only be called before the actual carry-over happens.
+  DCHECK(!InSplitViewMode());
+  DCHECK_EQ(divider_position_, -1);
+  divider_position_ = divider_position;
+}
+
 void SplitViewController::OnWindowDragStarted(aura::Window* dragged_window) {
   DCHECK(dragged_window);
   if (IsWindowInSplitView(dragged_window))
@@ -768,10 +780,10 @@
     const gfx::Rect& new_bounds,
     ui::PropertyChangeReason reason) {
   if (split_view_type_ != SplitViewType::kClamshellType ||
-      reason == ui::PropertyChangeReason::FROM_ANIMATION) {
+      reason == ui::PropertyChangeReason::FROM_ANIMATION ||
+      !InSplitViewMode()) {
     return;
   }
-  DCHECK(InSplitViewMode());
 
   wm::WindowState* window_state = wm::GetWindowState(window);
   const bool is_window_moved = window_state->is_dragged() &&
@@ -1109,14 +1121,40 @@
   UpdateSnappedWindowsAndDividerBounds();
 }
 
-void SplitViewController::OnTabletModeStarted() {
+void SplitViewController::OnTabletModeStarting() {
   split_view_type_ = SplitViewType::kTabletType;
 }
 
+void SplitViewController::OnTabletModeStarted() {
+  // If splitview is active when tablet mode is starting, do the clamshell mode
+  // splitview to tablet mode splitview transition by adding the split view
+  // divider bar and also adjust the |divider_position_| so that it's on one of
+  // the three fixed positions.
+  if (InSplitViewMode()) {
+    divider_position_ = GetClosestFixedDividerPosition();
+    split_view_divider_ = std::make_unique<SplitViewDivider>(
+        this, GetDefaultSnappedWindow()->GetRootWindow());
+    UpdateSnappedWindowsAndDividerBounds();
+    NotifyDividerPositionChanged();
+  }
+}
+
 void SplitViewController::OnTabletModeEnding() {
-  if (IsClamshellSplitViewModeEnabled())
+  if (IsClamshellSplitViewModeEnabled()) {
     split_view_type_ = SplitViewType::kClamshellType;
-  EndSplitView();
+
+    // If splitview is active when tablet mode is ending, simply destroy the
+    // split view divider bar as we don't have the bar in clamshell split view
+    // mode.
+    if (InSplitViewMode())
+      split_view_divider_.reset();
+  } else if (InSplitViewMode()) {
+    // If clamshell splitview mode is not enabled, fall back to the old
+    // behavior: end splitview and overivew and all windows will return to its
+    // old window state before entering tablet mode.
+    EndSplitView();
+    EndOverview();
+  }
 }
 
 void SplitViewController::OnTabletControllerDestroyed() {
@@ -1320,19 +1358,19 @@
           window);
 
   // |divide_position_| might not be properly initialized yet.
-  divider_position_ = (divider_position_ < 0)
-                          ? GetDefaultDividerPosition(window)
-                          : divider_position_;
+  int divider_position = (divider_position_ < 0)
+                             ? GetDefaultDividerPosition(window)
+                             : divider_position_;
   gfx::Rect divider_bounds;
   if (split_view_type_ == SplitViewType::kTabletType) {
     divider_bounds = SplitViewDivider::GetDividerBoundsInScreen(
         work_area_bounds_in_screen, GetCurrentScreenOrientation(),
-        divider_position_, false /* is_dragging */);
+        divider_position, false /* is_dragging */);
   } else {
     if (IsCurrentScreenOrientationLandscape())
-      divider_bounds.set_x(work_area_bounds_in_screen.x() + divider_position_);
+      divider_bounds.set_x(work_area_bounds_in_screen.x() + divider_position);
     else
-      divider_bounds.set_y(work_area_bounds_in_screen.y() + divider_position_);
+      divider_bounds.set_y(work_area_bounds_in_screen.y() + divider_position);
   }
 
   SplitRect(work_area_bounds_in_screen, divider_bounds,
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 7a4a801..2849bc5 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -59,18 +59,17 @@
   // top of the screen.
   enum SnapPosition { NONE, LEFT, RIGHT };
 
-  // Why splitview was ended. For now, all reasons will be kNormal except when
-  // the home launcher button is pressed, an unsnappable window just got
-  // activated, the active user session changed, or the window dragging
-  // started.
+  // Why splitview was ended.
   enum class EndReason {
     kNormal = 0,
     kHomeLauncherPressed,
     kUnsnappableWindowActivated,
     kActiveUserChanged,
     kWindowDragStarted,
-    // TODO(950827): Consider not ending Split-View on PIP expand.
+    // TODO(edcourtney): Consider not ending Split-View on PIP expand.
+    // See crbug.com/950827.
     kPipExpanded,
+    kExitTabletMode,
   };
 
   // The behaviors of split view are very different when in tablet mode and in
@@ -147,6 +146,14 @@
   // Returns true if |window| is a snapped window in splitview.
   bool IsWindowInSplitView(const aura::Window* window) const;
 
+  // This function is only supposed to be called during clamshell <-> tablet
+  // transition or multi-user transition, when we need to carry over one/two
+  // snapped windows into splitview, we calculate the divider position based on
+  // the one or two to-be-snapped windows' bounds so that we can keep the
+  // snapped windows' bounds after transition (instead of putting them always
+  // on the middle split position).
+  void InitDividerPositionForTransition(int divider_position);
+
   // Called when a window (either it's browser window or an app window) start/
   // end being dragged.
   void OnWindowDragStarted(aura::Window* dragged_window);
@@ -192,6 +199,7 @@
                                uint32_t metrics) override;
 
   // TabletModeObserver:
+  void OnTabletModeStarting() override;
   void OnTabletModeStarted() override;
   void OnTabletModeEnding() override;
   void OnTabletControllerDestroyed() override;
diff --git a/ash/wm/splitview/split_view_divider.cc b/ash/wm/splitview/split_view_divider.cc
index bedb6a2b..54443cd 100644
--- a/ash/wm/splitview/split_view_divider.cc
+++ b/ash/wm/splitview/split_view_divider.cc
@@ -250,8 +250,10 @@
   Shell::Get()->activation_client()->RemoveObserver(this);
   divider_widget_->Close();
   split_view_window_targeter_.reset();
-  for (auto* iter : observed_windows_)
-    iter->RemoveObserver(this);
+  for (auto* window : observed_windows_) {
+    window->RemoveObserver(this);
+    ::wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
+  }
   observed_windows_.clear();
 }
 
diff --git a/ash/wm/system_modal_container_layout_manager.cc b/ash/wm/system_modal_container_layout_manager.cc
index c835c0b..fd6716a 100644
--- a/ash/wm/system_modal_container_layout_manager.cc
+++ b/ash/wm/system_modal_container_layout_manager.cc
@@ -103,7 +103,7 @@
 void SystemModalContainerLayoutManager::SetChildBounds(
     aura::Window* child,
     const gfx::Rect& requested_bounds) {
-  WmSnapToPixelLayoutManager::SetChildBounds(child, requested_bounds);
+  WmDefaultLayoutManager::SetChildBounds(child, requested_bounds);
   if (IsBoundsCentered(requested_bounds))
     windows_to_center_.insert(child);
   else
diff --git a/ash/wm/system_modal_container_layout_manager.h b/ash/wm/system_modal_container_layout_manager.h
index af3c5fe..1d54c55a 100644
--- a/ash/wm/system_modal_container_layout_manager.h
+++ b/ash/wm/system_modal_container_layout_manager.h
@@ -11,7 +11,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/cpp/keyboard/keyboard_controller_observer.h"
-#include "ash/wm/wm_snap_to_pixel_layout_manager.h"
+#include "ash/wm/wm_default_layout_manager.h"
 #include "base/macros.h"
 #include "ui/aura/window_observer.h"
 
@@ -26,7 +26,7 @@
 // System modal windows which are centered on the screen will be kept centered
 // when the container size changes.
 class ASH_EXPORT SystemModalContainerLayoutManager
-    : public wm::WmSnapToPixelLayoutManager,
+    : public wm::WmDefaultLayoutManager,
       public aura::WindowObserver,
       public KeyboardControllerObserver {
  public:
@@ -35,7 +35,7 @@
 
   bool has_window_dimmer() const { return window_dimmer_ != nullptr; }
 
-  // Overridden from WmSnapToPixelLayoutManager:
+  // Overridden from WmDefaultLayoutManager:
   void OnChildWindowVisibilityChanged(aura::Window* child,
                                       bool visible) override;
   void OnWindowResized() override;
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 21f55a2..2aeb5ab 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -123,9 +123,8 @@
       switches::kAshEnableTabletMode);
 }
 
-// Checks the command line to see which force tablet mode is turned on, if
-// any.
-TabletModeController::UiMode GetTabletMode() {
+// Returns the UiMode given by the force-table-mode command line.
+TabletModeController::UiMode GetUiMode() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kAshUiMode)) {
     std::string switch_value =
@@ -171,6 +170,49 @@
   return screenshot_layer;
 }
 
+// The default behavior in Clamshell mode.
+constexpr TabletModeController::TabletModeBehavior kDefault{};
+
+// Defines the behavior of the tablet mode when enabled by sensor.
+constexpr TabletModeController::TabletModeBehavior kOnBySensor{
+    /*use_sensor=*/true,
+    /*observe_display_events=*/true,
+    /*observe_external_pointer_device_events=*/true,
+    /*block_internal_input_device=*/true,
+    /*always_show_overview_button_in_tablet_mode=*/false,
+};
+
+// Defines the behavior that sticks to the current mode. Used to
+// implement --force-tablet-mode flag.
+constexpr TabletModeController::TabletModeBehavior kLockInCurrentMode{
+    /*use_sensor=*/false,
+    /*observe_display_events=*/false,
+    /*observe_external_pointer_device_events=*/false,
+    /*block_internal_input_device=*/false,
+    /*always_show_overview_button_in_tablet_mode=*/true,
+};
+
+// Defines the behavior used for testing. It prevents the device from
+// switching the mode due to sensor events during the test.
+constexpr TabletModeController::TabletModeBehavior kOnForTest{
+    /*use_sensor=*/false,
+    /*observe_display_events=*/true,
+    /*observe_external_pointer_device_events=*/true,
+    /*block_internal_input_device=*/true,
+    /*always_show_overview_button_in_tablet_mode=*/false,
+};
+
+// Used for development purpose (currently debug shortcut shift-ctrl-alt). This
+// ignores the sensor but allows to switch upon docked mode and external
+// pointing device. It also forces to show the overview button.
+constexpr TabletModeController::TabletModeBehavior kOnForDev{
+    /*use_sensor=*/false,
+    /*observe_display_events=*/true,
+    /*observe_external_pointer_device_events=*/true,
+    /*block_internal_input_device=*/false,
+    /*always_show_overview_button_in_tablet_mode=*/true,
+};
+
 }  // namespace
 
 // Class which records animation smoothness when entering or exiting tablet
@@ -202,6 +244,9 @@
 
 constexpr char TabletModeController::kLidAngleHistogramName[];
 
+////////////////////////////////////////////////////////////////////////////////
+// TabletModeContrller, public:
+
 TabletModeController::TabletModeController()
     : event_blocker_(std::make_unique<InternalInputDevicesEventBlocker>()),
       tablet_mode_usage_interval_start_time_(base::Time::Now()),
@@ -328,6 +373,10 @@
     fps_counter_->LogUma();
   fps_counter_.reset();
 
+  // Stop other animations (STEP_END), then update the tablet mode ui.
+  if (tablet_mode_window_manager_ && delete_screenshot)
+    tablet_mode_window_manager_->StopWindowAnimations();
+
   if (delete_screenshot)
     DeleteScreenshot();
 }
@@ -344,24 +393,31 @@
 }
 
 void TabletModeController::SetEnabledForTest(bool enabled) {
-  // Disable Accelerometer and PowerManagerClient observers to prevent possible
-  // tablet mode overrides. It won't be possible to physically switch to/from
-  // tablet mode after calling this function. This is needed for tests that
-  // run on DUTs and require switching to/back tablet mode in runtime, like some
-  // ARC++ Tast tests.
-  AccelerometerReader::GetInstance()->RemoveObserver(this);
-  chromeos::PowerManagerClient::Get()->RemoveObserver(this);
+  tablet_mode_behavior_ = enabled ? kOnForTest : kDefault;
+
   SetTabletModeEnabledInternal(enabled);
+  // Notify observers to update the tray button.
+  for (auto& observer : tablet_mode_observers_)
+    observer.OnTabletModeEventsBlockingChanged();
 }
 
 void TabletModeController::OnShellInitialized() {
-  force_ui_mode_ = GetTabletMode();
-  if (force_ui_mode_ == UiMode::kTabletMode)
-    AttemptEnterTabletMode();
+  switch (GetUiMode()) {
+    case UiMode::kTabletMode:
+      tablet_mode_behavior_ = kLockInCurrentMode;
+      AttemptEnterTabletMode();
+      break;
+    case UiMode::kClamshell:
+      tablet_mode_behavior_ = kLockInCurrentMode;
+      AttemptLeaveTabletMode();
+      break;
+    case UiMode::kNone:
+      break;
+  }
 }
 
 void TabletModeController::OnDisplayConfigurationChanged() {
-  if (!AllowUiModeChange())
+  if (!tablet_mode_behavior_.observe_display_events)
     return;
 
   if (!HasActiveInternalDisplay()) {
@@ -400,9 +456,6 @@
 
 void TabletModeController::OnAccelerometerUpdated(
     scoped_refptr<const AccelerometerUpdate> update) {
-  if (!AllowUiModeChange())
-    return;
-
   // When ChromeOS EC lid angle driver is present, EC can handle lid angle
   // calculation, thus Chrome side lid angle calculation is disabled. In this
   // case, TabletModeController no longer listens to accelerometer events.
@@ -424,6 +477,9 @@
   if (!HasActiveInternalDisplay())
     return;
 
+  if (!tablet_mode_behavior_.use_sensor)
+    return;
+
   // Whether or not we enter tablet mode affects whether we handle screen
   // rotation, so determine whether to enter tablet mode first.
   if (update->IsReadingStable(ACCELEROMETER_SOURCE_SCREEN) &&
@@ -440,26 +496,24 @@
 void TabletModeController::LidEventReceived(
     chromeos::PowerManagerClient::LidState state,
     const base::TimeTicks& time) {
-  if (!AllowUiModeChange())
-    return;
-
   VLOG(1) << "Lid event received: " << static_cast<int>(state);
   const bool open = state == chromeos::PowerManagerClient::LidState::OPEN;
   lid_is_closed_ = !open;
-
-  if (!tablet_mode_switch_is_on_)
+  if (tablet_mode_behavior_.use_sensor && !tablet_mode_switch_is_on_)
     AttemptLeaveTabletMode();
 }
 
 void TabletModeController::TabletModeEventReceived(
     chromeos::PowerManagerClient::TabletMode mode,
     const base::TimeTicks& time) {
-  if (!AllowUiModeChange())
+  if (!tablet_mode_behavior_.use_sensor)
     return;
 
   VLOG(1) << "Tablet mode event received: " << static_cast<int>(mode);
   const bool on = mode == chromeos::PowerManagerClient::TabletMode::ON;
+
   tablet_mode_switch_is_on_ = on;
+  tablet_mode_behavior_ = on ? kOnBySensor : kDefault;
 
   // Do not change if docked.
   if (!HasActiveInternalDisplay())
@@ -534,7 +588,7 @@
 }
 
 void TabletModeController::OnKioskNextEnabled() {
-  force_ui_mode_ = UiMode::kTabletMode;
+  tablet_mode_behavior_ = kLockInCurrentMode;
   AttemptEnterTabletMode();
 }
 
@@ -579,11 +633,28 @@
   StopObservingAnimation(/*record_stats=*/false, /*delete_screenshot=*/true);
 }
 
+void TabletModeController::SetEnabledForDev(bool enabled) {
+  tablet_mode_behavior_ = enabled ? kOnForDev : kDefault;
+
+  SetTabletModeEnabledInternal(enabled);
+  // Notify observers to update the tray button.
+  for (auto& observer : tablet_mode_observers_)
+    observer.OnTabletModeEventsBlockingChanged();
+}
+
+bool TabletModeController::ShouldShowOverviewButton() const {
+  return InTabletMode() &&
+         (AreInternalInputDeviceEventsBlocked() ||
+          tablet_mode_behavior_.always_show_overview_button_in_tablet_mode);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabletModeContrller, private:
+
 // TODO(jcliang): Remove SetTabletModeEnabledInternal
 // (http://crbug.com/620241).
 void TabletModeController::SetTabletModeEnabledInternal(bool should_enable) {
-  bool is_enabled = InTabletMode();
-  if (should_enable == is_enabled)
+  if (InTabletMode() == should_enable)
     return;
 
   // Hide the context menu on entering tablet mode to prevent users from
@@ -696,9 +767,11 @@
 
   // Toggle tablet mode on or off when corresponding thresholds are passed.
   if (is_angle_stable && lid_angle_ <= kExitTabletModeAngle) {
+    tablet_mode_behavior_ = kDefault;
     AttemptLeaveTabletMode();
   } else if (!lid_is_closed_ && lid_angle_ >= kEnterTabletModeAngle &&
              (is_angle_stable || CanUseUnstableLidAngle())) {
+    tablet_mode_behavior_ = kOnBySensor;
     AttemptEnterTabletMode();
   }
 
@@ -716,9 +789,6 @@
   if (!result.has_value())
     return;
 
-  if (AccelerometerReader::GetInstance()->is_disabled())
-    return;
-
   LidEventReceived(result->lid_state, base::TimeTicks::Now());
   TabletModeEventReceived(result->tablet_mode, base::TimeTicks::Now());
 }
@@ -740,7 +810,9 @@
 }
 
 void TabletModeController::AttemptEnterTabletMode() {
-  if (InTabletMode() || has_external_pointing_device_) {
+  if (InTabletMode() ||
+      (has_external_pointing_device_ &&
+       tablet_mode_behavior_.observe_external_pointer_device_events)) {
     UpdateInternalInputDevicesEventBlocker();
     return;
   }
@@ -793,14 +865,7 @@
   return TABLET_MODE_INTERVAL_INACTIVE;
 }
 
-bool TabletModeController::AllowUiModeChange() const {
-  return force_ui_mode_ == UiMode::kNone;
-}
-
 void TabletModeController::HandlePointingDeviceAddedOrRemoved() {
-  if (!AllowUiModeChange())
-    return;
-
   bool has_external_pointing_device = false;
   // Check if there is an external mouse device.
   for (const ui::InputDevice& mouse :
@@ -831,6 +896,9 @@
 
   has_external_pointing_device_ = has_external_pointing_device;
 
+  if (!tablet_mode_behavior_.observe_external_pointer_device_events)
+    return;
+
   // Enter clamshell mode whenever an external pointing device is attached.
   if (has_external_pointing_device) {
     AttemptLeaveTabletMode();
@@ -866,17 +934,20 @@
   bool should_block_internal_events = false;
   if (InTabletMode()) {
     // If we are currently in tablet mode, the internal input events should
-    // always be blocked.
-    should_block_internal_events = (force_ui_mode_ == UiMode::kNone);
-  } else if (HasActiveInternalDisplay() &&
-             (LidAngleInTabletModeRange() || tablet_mode_switch_is_on_)) {
+    // be blocked if its specified by the behavior.
+    should_block_internal_events =
+        tablet_mode_behavior_.block_internal_input_device;
+  } else if (HasActiveInternalDisplay()) {
     // If we are currently in clamshell mode, the intenral input events should
-    // only be blocked if the current lid angle belongs to tablet mode angle
-    // or |tablet_mode_switch_is_on_| is true.
-    // Note if we don't have an active internal display, the device is currently
-    // in docked mode, and the user may still want to use the internal keyboard
-    // and mouse in docked mode, we don't block internal events in this case.
-    should_block_internal_events = true;
+    // only be blocked if the current lid angle belongs to tablet mode angle,
+    // or |tablet_mode_switch_on_| is true and with input device blocking is
+    // on. Note if we don't have an active internal display, the device is
+    // currently in docked mode, and the user may still want to use the internal
+    // keyboard and mouse in docked mode, we don't block internal events in this
+    // case.
+    should_block_internal_events =
+        (LidAngleInTabletModeRange() || tablet_mode_switch_is_on_) &&
+        tablet_mode_behavior_.block_internal_input_device;
   }
 
   if (should_block_internal_events == AreInternalInputDeviceEventsBlocked())
@@ -906,6 +977,8 @@
 }
 
 void TabletModeController::FinishInitTabletMode() {
+  for (auto& observer : tablet_mode_observers_)
+    observer.OnTabletModeStarting();
   tablet_mode_window_manager_ = std::make_unique<TabletModeWindowManager>();
   tablet_mode_window_manager_->Init();
 
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.h b/ash/wm/tablet_mode/tablet_mode_controller.h
index bc2ef66..af635ef 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.h
+++ b/ash/wm/tablet_mode/tablet_mode_controller.h
@@ -172,6 +172,24 @@
     ++tab_drag_in_splitview_count_;
   }
 
+  // Enable/disable the tablet mode for development. Please see cc file
+  // for more details.
+  void SetEnabledForDev(bool enabled);
+
+  // Returns true if the system tray should have a overview button.
+  bool ShouldShowOverviewButton() const;
+
+  // Defines how the tablet mode controller controls the
+  // tablet mode and its transition between clamshell mode.
+  // This is defined as a public to define constexpr in cc.
+  struct TabletModeBehavior {
+    bool use_sensor = true;
+    bool observe_display_events = true;
+    bool observe_external_pointer_device_events = true;
+    bool block_internal_input_device = false;
+    bool always_show_overview_button_in_tablet_mode = false;
+  };
+
  private:
   class TabletModeTransitionFpsCounter;
   friend class TabletModeControllerTestApi;
@@ -242,11 +260,6 @@
   // otherwise returns TABLET_MODE_INTERNAL_INACTIVE.
   TabletModeIntervalType CurrentTabletModeIntervalType();
 
-  // Checks whether we want to allow change the current ui mode to tablet mode
-  // or clamshell mode. This returns false if the user set a flag for the
-  // software to behave in a certain way regardless of configuration.
-  bool AllowUiModeChange() const;
-
   // Called when a pointing device config is changed, or when a device list is
   // sent from device manager. This will exit tablet mode if needed.
   void HandlePointingDeviceAddedOrRemoved();
@@ -364,9 +377,6 @@
   // mode.
   TabletModeToggleObserver* toggle_observer_ = nullptr;
 
-  // Tracks whether a flag is used to force ui mode.
-  UiMode force_ui_mode_ = UiMode::kNone;
-
   State state_ = State::kInClamshellMode;
 
   // Calls RecordLidAngle() periodically.
@@ -404,6 +414,8 @@
 
   base::ObserverList<TabletModeObserver>::Unchecked tablet_mode_observers_;
 
+  TabletModeBehavior tablet_mode_behavior_;
+
   base::WeakPtrFactory<TabletModeController> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(TabletModeController);
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc b/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc
index e9565ca..201c4f6d 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc
@@ -18,11 +18,11 @@
 TabletModeControllerTestApi::~TabletModeControllerTestApi() = default;
 
 void TabletModeControllerTestApi::EnterTabletMode() {
-  tablet_mode_controller_->AttemptEnterTabletMode();
+  tablet_mode_controller_->SetEnabledForTest(true);
 }
 
 void TabletModeControllerTestApi::LeaveTabletMode() {
-  tablet_mode_controller_->AttemptLeaveTabletMode();
+  tablet_mode_controller_->SetEnabledForTest(false);
 }
 
 void TabletModeControllerTestApi::AttachExternalMouse() {
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
index 5218a85..4a95c375 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
@@ -43,6 +43,7 @@
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
 #include "ui/display/test/display_manager_test_api.h"
+#include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/device_data_manager_test_api.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/event_handler.h"
@@ -672,6 +673,10 @@
     power_manager_client()->SetTabletMode(
         chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks::Now());
     TabletModeControllerTest::SetUp();
+    // Remove TabletModeController as an observer of input device events to
+    // prevent interfering with the test.
+    ui::DeviceDataManager::GetInstance()->RemoveObserver(
+        tablet_mode_controller());
   }
 };
 
@@ -1581,7 +1586,6 @@
   // observe and record smoothness for one.
   auto window = CreateTestWindow(gfx::Rect(200, 200));
   auto window2 = CreateTestWindow(gfx::Rect(300, 200));
-
   // Tests that we get one enter and one exit animation smoothess histogram when
   // entering and exiting tablet mode with a normal window.
   ui::Layer* layer = window->layer();
@@ -1597,7 +1601,7 @@
   layer = window->layer();
   layer2 = window2->layer();
   tablet_mode_controller()->SetEnabledForTest(false);
-  EXPECT_TRUE(layer->GetAnimator()->is_animating());
+  EXPECT_FALSE(layer->GetAnimator()->is_animating());
   EXPECT_TRUE(layer2->GetAnimator()->is_animating());
   layer->GetAnimator()->StopAnimating();
   layer2->GetAnimator()->StopAnimating();
diff --git a/ash/wm/tablet_mode/tablet_mode_observer.h b/ash/wm/tablet_mode/tablet_mode_observer.h
index 80eec39..bcf7a45e 100644
--- a/ash/wm/tablet_mode/tablet_mode_observer.h
+++ b/ash/wm/tablet_mode/tablet_mode_observer.h
@@ -13,6 +13,9 @@
 // NOTE: Code in chrome should use TabletModeClientObserver.
 class ASH_EXPORT TabletModeObserver {
  public:
+  // Called when the tablet mode is about to start.
+  virtual void OnTabletModeStarting() {}
+
   // Called when the tablet mode has started. Windows might still be animating
   // though.
   virtual void OnTabletModeStarted() {}
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
index cb36af4..316a269 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -9,8 +9,10 @@
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/scoped_animation_disabler.h"
+#include "ash/screen_util.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desks_util.h"
@@ -36,41 +38,127 @@
 
 namespace {
 
-// Exits overview mode if it is currently active. Returns true if overview is
-// active before cancelling it.
-bool CancelOverview() {
-  OverviewController* controller = Shell::Get()->overview_controller();
-  if (controller->InOverviewSession()) {
-    controller->EndOverview();
-    return true;
-  }
-  return false;
-}
-
-// Returns true if windows[i] exists, can snap in split view, and is not ARC. A
-// window snapped in clamshell mode must meet these criteria to potentially
-// carry over into tablet mode split view. We want ARC windows to be included,
-// but there is an obstacle (see https://crbug.com/922282 and
+// This function is called to check if window[i] is eligible to be carried over
+// to split view mode during clamshell <-> tablet mode transition or multi-user
+// switch transition. Returns true if windows[i] exists, can snap in split view,
+// is not showing in overview, and is not ARC window.
+// TODO(xdai): Make it work for ARC windows. (see
+// https://crbug.com/922282 and
 // https://buganizer.corp.google.com/issues/123432223).
-bool IsCandidateForSplitView(const MruWindowTracker::WindowList& windows,
-                             size_t i) {
+bool IsCarryOverCandidateForSplitView(
+    const MruWindowTracker::WindowList& windows,
+    size_t i) {
   return windows.size() > i && CanSnapInSplitview(windows[i]) &&
+         !windows[i]->GetProperty(kIsShowingInOverviewKey) &&
          static_cast<ash::AppType>(windows[i]->GetProperty(
              aura::client::kAppType)) != AppType::ARC_APP;
 }
 
-// Iterates over |windows| and |positions| in parallel, up to the length of
-// |positions| (assumed not to exceed the length of |windows|), snapping the
-// windows in the positions.
-void DoSplitView(
-    const MruWindowTracker::WindowList& windows,
-    const std::vector<SplitViewController::SnapPosition>& positions) {
-  DCHECK_GE(windows.size(), positions.size());
+// Returns the windows that are going to be carried over to splitview during
+// clamshell <-> tablet transition or multi user switch transition.
+// TODO(xdai): Return eligible windows regardless of window zorders.
+base::flat_map<aura::Window*, WindowStateType>
+GetCarryOverWindowsInSplitView() {
+  base::flat_map<aura::Window*, WindowStateType> windows;
+  // Check the topmost window and the second topmost's window state to see if
+  // they are eligible to be carried over to splitscreen. A window must meet
+  // IsCarryOverCandidateForSplitView() to be carried over to splitscreen.
+  MruWindowTracker::WindowList mru_windows =
+      Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks);
+  if (IsCarryOverCandidateForSplitView(mru_windows, 0u)) {
+    if (wm::GetWindowState(mru_windows[0])->GetStateType() ==
+        WindowStateType::kLeftSnapped) {
+      windows.emplace(mru_windows[0], WindowStateType::kLeftSnapped);
+      if (IsCarryOverCandidateForSplitView(mru_windows, 1u) &&
+          wm::GetWindowState(mru_windows[1])->GetStateType() ==
+              WindowStateType::kRightSnapped) {
+        windows.emplace(mru_windows[1], WindowStateType::kRightSnapped);
+      }
+    } else if (wm::GetWindowState(mru_windows[0])->GetStateType() ==
+               WindowStateType::kRightSnapped) {
+      windows.emplace(mru_windows[0], WindowStateType::kRightSnapped);
+      if (IsCarryOverCandidateForSplitView(mru_windows, 1u) &&
+          wm::GetWindowState(mru_windows[1])->GetStateType() ==
+              WindowStateType::kLeftSnapped) {
+        windows.emplace(mru_windows[1], WindowStateType::kLeftSnapped);
+      }
+    }
+  }
+  return windows;
+}
+
+// Calculates the divider position of the splitscreen based on the snapped
+// window(s)'s positions. We'll try to keep the current snapped window(s)'
+// bounds as much as possible.
+int CalculateCarryOverDividerPostion(
+    base::flat_map<aura::Window*, WindowStateType> windows_in_splitview) {
+  aura::Window* left_window = nullptr;
+  aura::Window* right_window = nullptr;
+  for (auto& iter : windows_in_splitview) {
+    if (iter.second == WindowStateType::kLeftSnapped)
+      left_window = iter.first;
+    else if (iter.second == WindowStateType::kRightSnapped)
+      right_window = iter.first;
+  }
+  if (!left_window && !right_window)
+    return -1;
+
+  gfx::Rect work_area =
+      display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(left_window ? left_window : right_window)
+          .work_area();
+  gfx::Rect left_window_bounds =
+      left_window ? left_window->GetBoundsInScreen() : gfx::Rect();
+  gfx::Rect right_window_bounds =
+      right_window ? right_window->GetBoundsInScreen() : gfx::Rect();
+
+  switch (GetCurrentScreenOrientation()) {
+    case OrientationLockType::kLandscapePrimary:
+      return left_window ? left_window_bounds.width()
+                         : work_area.width() - right_window_bounds.width();
+    case OrientationLockType::kPortraitPrimary:
+      return left_window ? left_window_bounds.height()
+                         : work_area.height() - right_window_bounds.height();
+    case OrientationLockType::kLandscapeSecondary:
+      return left_window ? work_area.width() - left_window_bounds.width()
+                         : right_window_bounds.width();
+    case OrientationLockType::kPortraitSecondary:
+      return left_window ? work_area.height() - left_window_bounds.height()
+                         : right_window_bounds.height();
+    default:
+      return Shell::Get()->split_view_controller()->GetDefaultDividerPosition(
+          left_window ? left_window : right_window);
+  }
+}
+
+// Snap the carry over windows into splitview mode at |divider_position|.
+void DoSplitViewTransition(
+    base::flat_map<aura::Window*, WindowStateType> windows,
+    int divider_position) {
+  if (windows.empty())
+    return;
 
   SplitViewController* split_view_controller =
       Shell::Get()->split_view_controller();
-  for (size_t i = 0u; i < positions.size(); ++i)
-    split_view_controller->SnapWindow(windows[i], positions[i]);
+  // If split view mode is already active, use its own divider position.
+  if (!split_view_controller->InSplitViewMode())
+    split_view_controller->InitDividerPositionForTransition(divider_position);
+
+  for (auto& iter : windows) {
+    split_view_controller->SnapWindow(
+        iter.first, iter.second == WindowStateType::kLeftSnapped
+                        ? SplitViewController::LEFT
+                        : SplitViewController::RIGHT);
+  }
+
+  // For clamshell split view mode, end splitview mode if we're in single
+  // split mode or both snapped mode (in both cases overview is not active).
+  // TODO(xdai): Refactoring SplitViewController to make SplitViewController to
+  // handle this case.
+  if (split_view_controller->InClamshellSplitViewMode() &&
+      !Shell::Get()->overview_controller()->InOverviewSession()) {
+    split_view_controller->EndSplitView();
+  }
 }
 
 }  // namespace
@@ -132,9 +220,19 @@
 }
 
 void TabletModeWindowManager::Init() {
-  // The overview mode needs to be ended before the tablet mode is started. To
-  // guarantee the proper order, it will be turned off from here.
-  CancelOverview();
+  // There are 3 cases when entering tablet mode:
+  // 1) overview is active but split view is inactive: keep overview active in
+  //    tablet mode.
+  // 2) overview and splitview are both active (splitview can only be active
+  //    when overview is active in clamshell mode): keep overview and splitview
+  //    both active in tablet mode.
+  // 3) overview is inactive: keep the current behavior, i.e.,
+  //    a. if the top window is a snapped window, put it in splitview
+  //    b. if the second top window is also a snapped window and snapped to
+  //       the other side, put it in split view as well. Otherwise, open
+  //       overview on the other side of the screen
+  //    c. if the top window is not a snapped window, maximize all windows
+  //       when entering tablet mode.
 
   {
     ScopedObserveWindowAnimation scoped_observe(GetTopWindow(), this,
@@ -153,10 +251,41 @@
 }
 
 void TabletModeWindowManager::Shutdown() {
-  // Overview mode needs to be ended before exiting tablet mode to prevent
-  // transforming windows which are currently in
-  // overview: http://crbug.com/366605
-  const bool was_in_overview = CancelOverview();
+  // There are 4 cases when exiting tablet mode:
+  // 1) overview is active but split view is inactive: keep overview active in
+  //    clamshell mode.
+  // 2) overview and splitview are both active: keep overview and splitview both
+  //    active in clamshell mode, unless if it's single split state, splitview
+  //    and overview will both be ended.
+  // 3) overview is inactive but split view is active (two snapped windows):
+  //    split view is no longer active. But the two snapped windows will still
+  //    keep snapped in clamshell mode.
+  // 4) overview and splitview are both inactive: keep the current behavior,
+  //    i.e., restore all windows to its window state before entering tablet
+  //    mode.
+
+  // TODO(xdai): Instead of caching snapped windows and their state here, we
+  // should try to see if it can be done in the WindowState::State impl.
+  base::flat_map<aura::Window*, WindowStateType> windows_in_splitview =
+      GetCarryOverWindowsInSplitView();
+
+  // For case 2 and 3: End splitview mode for two snapped windows case or single
+  // split case to match the clamshell split view behavior. (there is no both
+  // snapped state or single split state in clamshell split view). The windows
+  // will still be kept snapped though.
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  if (split_view_controller->InSplitViewMode()) {
+    OverviewController* overview_controller =
+        Shell::Get()->overview_controller();
+    if (!overview_controller->InOverviewSession() ||
+        overview_controller->overview_session()->IsEmpty()) {
+      Shell::Get()->split_view_controller()->EndSplitView(
+          SplitViewController::EndReason::kExitTabletMode);
+      overview_controller->EndOverview();
+    }
+  }
+
   for (aura::Window* window : added_windows_)
     window->RemoveObserver(this);
   added_windows_.clear();
@@ -169,7 +298,7 @@
 
   ScopedObserveWindowAnimation scoped_observe(GetTopWindow(), this,
                                               /*exiting_tablet_mode=*/true);
-  ArrangeWindowsForDesktopMode(was_in_overview);
+  ArrangeWindowsForClamshellMode(windows_in_splitview);
 }
 
 int TabletModeWindowManager::GetNumberOfManagedWindows() {
@@ -208,6 +337,11 @@
     pair.second->set_ignore_wm_events(true);
 }
 
+void TabletModeWindowManager::StopWindowAnimations() {
+  for (auto& pair : window_state_map_)
+    pair.first->layer()->GetAnimator()->StopAnimating();
+}
+
 void TabletModeWindowManager::OnOverviewModeEndingAnimationComplete(
     bool canceled) {
   if (canceled)
@@ -238,6 +372,7 @@
     case SplitViewController::EndReason::kHomeLauncherPressed:
     case SplitViewController::EndReason::kActiveUserChanged:
     case SplitViewController::EndReason::kWindowDragStarted:
+    case SplitViewController::EndReason::kExitTabletMode:
       // For the case of kHomeLauncherPressed, the home launcher will minimize
       // the snapped windows after ending splitview, so avoid maximizing them
       // here. For the case of kActiveUserChanged, the snapped windows will be
@@ -376,9 +511,11 @@
   // then do the logic for carrying over snapped windows. Else recreate the
   // split view layout from the last time the current user session was active.
   if (accounts_since_entering_tablet_.count(account_id) == 0u) {
-    MruWindowTracker::WindowList windows =
-        Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks);
-    DoSplitView(windows, GetSnapPositions(windows));
+    base::flat_map<aura::Window*, WindowStateType> windows_in_splitview =
+        GetCarryOverWindowsInSplitView();
+    int divider_position =
+        CalculateCarryOverDividerPostion(windows_in_splitview);
+    DoSplitViewTransition(windows_in_splitview, divider_position);
     accounts_since_entering_tablet_.insert(account_id);
   } else {
     // Search for snapped windows to detect if the now active user session was
@@ -438,40 +575,6 @@
              : iter->second->old_state()->GetType();
 }
 
-std::vector<SplitViewController::SnapPosition>
-TabletModeWindowManager::GetSnapPositions(
-    const MruWindowTracker::WindowList& windows) const {
-  std::vector<SplitViewController::SnapPosition> result;
-  if (!IsCandidateForSplitView(windows, 0u))
-    return result;
-  switch (GetDesktopWindowStateType(windows[0])) {
-    case WindowStateType::kLeftSnapped:
-      // windows[0] was snapped on the left in desktop mode. Snap windows[0] on
-      // the left in split view. If windows[1] was snapped on the right in
-      // desktop mode, then snap windows[1] on the right in split view.
-      result.push_back(SplitViewController::LEFT);
-      if (IsCandidateForSplitView(windows, 1u) &&
-          GetDesktopWindowStateType(windows[1]) ==
-              WindowStateType::kRightSnapped) {
-        result.push_back(SplitViewController::RIGHT);
-      }
-      return result;
-    case WindowStateType::kRightSnapped:
-      // windows[0] was snapped on the right in desktop mode. Snap windows[0] on
-      // the right in split view. If windows[1] was snapped on the left in
-      // desktop mode, then snap windows[1] on the left in split view.
-      result.push_back(SplitViewController::RIGHT);
-      if (IsCandidateForSplitView(windows, 1u) &&
-          GetDesktopWindowStateType(windows[1]) ==
-              WindowStateType::kLeftSnapped) {
-        result.push_back(SplitViewController::LEFT);
-      }
-      return result;
-    default:
-      return result;
-  }
-}
-
 void TabletModeWindowManager::ArrangeWindowsForTabletMode() {
   // |split_view_eligible_windows| is for determining split view layout.
   // |activatable_windows| includes all windows to be tracked, and that includes
@@ -482,12 +585,14 @@
   MruWindowTracker::WindowList activatable_windows =
       Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal(kAllDesks);
 
-  // Determine the desired split view layout.
-  const std::vector<SplitViewController::SnapPosition> snap_positions =
-      GetSnapPositions(split_view_eligible_windows);
+  // Determine which windows are to be carried over to splitview from clamshell
+  // mode to tablet mode.
+  base::flat_map<aura::Window*, WindowStateType> windows_in_splitview =
+      GetCarryOverWindowsInSplitView();
+  int divider_position = CalculateCarryOverDividerPostion(windows_in_splitview);
 
   // If split view is not appropriate, then maximize all windows and bail out.
-  if (snap_positions.empty()) {
+  if (windows_in_splitview.empty()) {
     for (auto* window : activatable_windows)
       TrackWindow(window, /*entering_tablet_mode=*/true);
     return;
@@ -497,8 +602,8 @@
   // Maximize all other windows. Do not animate any window bounds updates.
   for (auto* window : activatable_windows) {
     bool snap = false;
-    for (size_t i = 0u; i < snap_positions.size(); ++i) {
-      if (window == split_view_eligible_windows[i]) {
+    for (auto& iter : windows_in_splitview) {
+      if (window == iter.first) {
         snap = true;
         break;
       }
@@ -507,15 +612,25 @@
                 /*animate_bounds_on_attach=*/false);
   }
 
-  // Enter split view mode.
-  DoSplitView(split_view_eligible_windows, snap_positions);
+  // Do split view mode transition.
+  DoSplitViewTransition(windows_in_splitview, divider_position);
 }
 
-void TabletModeWindowManager::ArrangeWindowsForDesktopMode(
-    bool was_in_overview) {
+void TabletModeWindowManager::ArrangeWindowsForClamshellMode(
+    base::flat_map<aura::Window*, WindowStateType> windows_in_splitview) {
+  int divider_position = CalculateCarryOverDividerPostion(windows_in_splitview);
+
   while (window_state_map_.size()) {
     aura::Window* window = window_state_map_.begin()->first;
-    ForgetWindow(window, /*destroyed=*/false, was_in_overview);
+    ForgetWindow(window, /*destroyed=*/false);
+  }
+
+  if (IsClamshellSplitViewModeEnabled()) {
+    // Arriving here the window state has changed to its clamshell window state.
+    // Since we need to keep the windows that were in splitview still be snapped
+    // in clamshell mode, change its window state to the corresponding snapped
+    // window state.
+    DoSplitViewTransition(windows_in_splitview, divider_position);
   }
 }
 
@@ -538,8 +653,7 @@
 }
 
 void TabletModeWindowManager::ForgetWindow(aura::Window* window,
-                                           bool destroyed,
-                                           bool was_in_overview) {
+                                           bool destroyed) {
   added_windows_.erase(window);
   window->RemoveObserver(this);
 
@@ -560,7 +674,7 @@
   } else {
     // By telling the state object to revert, it will switch back the old
     // State object and destroy itself, calling WindowStateDestroyed().
-    it->second->LeaveTabletMode(wm::GetWindowState(it->first), was_in_overview);
+    it->second->LeaveTabletMode(wm::GetWindowState(it->first));
     DCHECK(!IsTrackingWindow(window));
   }
 }
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h
index 42cec9a..ddd75d6 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -79,6 +79,9 @@
   // Tell all managing windows not to handle WM events.
   void SetIgnoreWmEventsForExit();
 
+  // Stops animations on windows managed by this TabletModeWindowManager.
+  void StopWindowAnimations();
+
   // OverviewObserver:
   void OnOverviewModeEndingAnimationComplete(bool canceled) override;
 
@@ -111,22 +114,15 @@
   // |window| is not yet tracked, returns the current state type of |window|.
   WindowStateType GetDesktopWindowStateType(aura::Window* window) const;
 
-  // Returns a std::vector of up to two split view snap positions, parallel to
-  // |windows|, implementing the logic for carrying over snapped window states
-  // from desktop mode to tablet mode: if the active window is snapped, then it
-  // shall carry over to split view, along with the previous window if it is
-  // snapped to the opposite side.
-  std::vector<SplitViewController::SnapPosition> GetSnapPositions(
-      const MruWindowTracker::WindowList& windows) const;
-
   // Maximize all windows, except that snapped windows shall carry over to split
   // view as determined by GetSnapPositions().
   void ArrangeWindowsForTabletMode();
 
   // Revert all windows to how they were arranged before tablet mode.
-  // |was_in_overview| indicates whether it was in overview before entering
-  // desktop mode.
-  void ArrangeWindowsForDesktopMode(bool was_in_overview = false);
+  // |windows_in_splitview| contains the windows that were in splitview before
+  // entering clamshell mode.
+  void ArrangeWindowsForClamshellMode(
+      base::flat_map<aura::Window*, WindowStateType> windows_in_splitview);
 
   // If the given window should be handled by us, this function will add it to
   // the list of known windows (remembering the initial show state).
@@ -137,13 +133,10 @@
                    bool snap = false,
                    bool animate_bounds_on_attach = true);
 
-  // Remove a window from our tracking list. |was_in_overview| used when
-  // |destroyed| is false to help handle leaving tablet mode. If the window is
-  // going to be destroyed, do not restore its old previous window state object
-  // as it will send unnecessary window state change event.
-  void ForgetWindow(aura::Window* window,
-                    bool destroyed,
-                    bool was_in_overview = false);
+  // Remove a window from our tracking list. If the window is going to be
+  // destroyed, do not restore its old previous window state object as it will
+  // send unnecessary window state change event.
+  void ForgetWindow(aura::Window* window, bool destroyed);
 
   // Returns true when the given window should be modified in any way by us.
   bool ShouldHandleWindow(aura::Window* window);
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
index b760d446..8933772f 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shelf_prefs.h"
 #include "ash/public/cpp/test/shell_test_api.h"
@@ -16,10 +17,14 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/splitview/split_view_constants.h"
+#include "ash/wm/splitview/split_view_controller.h"
+#include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/switchable_windows.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
@@ -33,6 +38,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
@@ -1302,33 +1308,6 @@
   EXPECT_EQ(first_dragged_origin.y() + 5, window->bounds().y());
 }
 
-// Test that overview is exited before entering / exiting tablet mode so that
-// the window changes made by TabletModeWindowManager do not conflict with
-// those made in WindowOverview.
-TEST_F(TabletModeWindowManagerTest, ExitsOverview) {
-  // Bounds for windows we know can be controlled.
-  gfx::Rect rect1(10, 10, 200, 50);
-  gfx::Rect rect2(10, 60, 200, 50);
-  std::unique_ptr<aura::Window> w1(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect1));
-  std::unique_ptr<aura::Window> w2(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect2));
-
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  ASSERT_TRUE(overview_controller->StartOverview());
-  ASSERT_TRUE(overview_controller->InOverviewSession());
-  TabletModeWindowManager* manager = CreateTabletModeWindowManager();
-  ASSERT_TRUE(manager);
-  EXPECT_FALSE(overview_controller->InOverviewSession());
-
-  ASSERT_TRUE(overview_controller->StartOverview());
-  ASSERT_TRUE(overview_controller->InOverviewSession());
-  // Destroy the manager again and check that the windows return to their
-  // previous state.
-  DestroyTabletModeWindowManager();
-  EXPECT_FALSE(overview_controller->InOverviewSession());
-}
-
 // Test that an edge swipe from the top will end full screen mode.
 TEST_F(TabletModeWindowManagerTest, ExitFullScreenWithEdgeSwipeFromTop) {
   gfx::Rect rect(10, 10, 200, 50);
@@ -1810,4 +1789,274 @@
   EXPECT_EQ(rect.size(), child->bounds().size());
 }
 
+// Test clamshell mode <-> tablet mode transition if clamshell splitscreen is
+// not enabled.
+TEST_F(TabletModeWindowManagerTest, ClamshellTabletTransitionTest) {
+  gfx::Rect rect(10, 10, 200, 50);
+  std::unique_ptr<aura::Window> window(
+      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
+
+  // 1. Clamshell -> tablet. If overview is active, it should still be kept
+  // active after transition.
+  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  EXPECT_TRUE(overview_controller->StartOverview());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  TabletModeWindowManager* manager = CreateTabletModeWindowManager();
+  EXPECT_TRUE(manager);
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+
+  // 2. Tablet -> Clamshell. If overview is active, it should still be kept
+  // active after transition.
+  DestroyTabletModeWindowManager();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+
+  // 3. Clamshell -> tablet. If overview is inactive, it should still be kept
+  // inactive after transition. All windows will be maximized.
+  EXPECT_TRUE(overview_controller->EndOverview());
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  CreateTabletModeWindowManager();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized());
+
+  // 4. Tablet -> Clamshell. The window should be restored to its old state.
+  DestroyTabletModeWindowManager();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(wm::GetWindowState(window.get())->IsMaximized());
+
+  // 5. Clamshell -> Tablet. If the window is snapped, it will be carried over
+  // to splitview in tablet mode.
+  const wm::WMEvent event(wm::WM_EVENT_SNAP_LEFT);
+  wm::GetWindowState(window.get())->OnWMEvent(&event);
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+  // After transition, we should be in single split screen.
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+
+  // 6. Tablet -> Clamshell. Since clamshell splitscreen is not enabled, oveview
+  // and splitview will be both ended, and the window is restored to its old
+  // state.
+  DestroyTabletModeWindowManager();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+
+  // Create another normal state window to test additional scenarios.
+  std::unique_ptr<aura::Window> window2(
+      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
+  wm::ActivateWindow(window2.get());
+
+  // 7. Clamshell -> Tablet. Since the top active window is not a snapped
+  // window, all windows will be maximized after the transition.
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized());
+  EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMaximized());
+
+  // 8. Tablet -> Clamshell. If the two windows are in splitscreen in tablet
+  // mode, after transition they will restore to their old window states.
+  Shell::Get()->split_view_controller()->SnapWindow(window.get(),
+                                                    SplitViewController::LEFT);
+  Shell::Get()->split_view_controller()->SnapWindow(window2.get(),
+                                                    SplitViewController::RIGHT);
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  DestroyTabletModeWindowManager();
+  EXPECT_FALSE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+  EXPECT_FALSE(wm::GetWindowState(window2.get())->IsSnapped());
+
+  // 9. Clamshell -> Tablet. If the top two windows are snapped to both sides of
+  // the screen, they will carry over to tablet split view mode.
+  const wm::WMEvent event2(wm::WM_EVENT_SNAP_RIGHT);
+  wm::GetWindowState(window2.get())->OnWMEvent(&event2);
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+
+  // 10. Tablet -> Clamshell. If overview and splitview are both active, they
+  // will be both ended after the transition.
+  overview_controller->StartOverview();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  DestroyTabletModeWindowManager();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(Shell::Get()->split_view_controller()->InSplitViewMode());
+}
+
+// The class to test TabletModeWindowManagerTest related functionalities when
+// clamshell split screen feature is enabled.
+class TabletModeWindowManagerWithClamshellSplitViewTest
+    : public TabletModeWindowManagerTest {
+ public:
+  TabletModeWindowManagerWithClamshellSplitViewTest() = default;
+  ~TabletModeWindowManagerWithClamshellSplitViewTest() override = default;
+
+  // AshTestBase:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        ash::features::kDragToSnapInClamshellMode);
+    TabletModeWindowManagerTest::SetUp();
+    DCHECK(ShouldAllowSplitView());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  DISALLOW_COPY_AND_ASSIGN(TabletModeWindowManagerWithClamshellSplitViewTest);
+};
+
+// Test clamshell mode <-> tablet mode transition if clamshell splitscreen is
+// enabled.
+TEST_F(TabletModeWindowManagerWithClamshellSplitViewTest,
+       ClamshellTabletTransitionTest) {
+  gfx::Rect rect(10, 10, 200, 50);
+  std::unique_ptr<aura::Window> window(
+      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
+
+  // 1. Clamshell -> tablet. If overview is active, it should still be kept
+  // active after transition.
+  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  EXPECT_TRUE(overview_controller->StartOverview());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  TabletModeWindowManager* manager = CreateTabletModeWindowManager();
+  EXPECT_TRUE(manager);
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+
+  // 2. Tablet -> Clamshell. If overview is active, it should still be kept
+  // active after transition.
+  DestroyTabletModeWindowManager();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+
+  // 3. Clamshell -> tablet. If overview is inactive, it should still be kept
+  // inactive after transition. All windows will be maximized.
+  EXPECT_TRUE(overview_controller->EndOverview());
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  CreateTabletModeWindowManager();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized());
+
+  // 4. Tablet -> Clamshell. The window should be restored to its old state.
+  DestroyTabletModeWindowManager();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(wm::GetWindowState(window.get())->IsMaximized());
+
+  // 5. Clamshell -> Tablet. If the window is snapped, it will be carried over
+  // to splitview in tablet mode.
+  const wm::WMEvent event(wm::WM_EVENT_SNAP_LEFT);
+  wm::GetWindowState(window.get())->OnWMEvent(&event);
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+  // After transition, we should be in single split screen.
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+
+  // 6. Tablet -> Clamshell. Since there is only 1 window, splitview and
+  // overview will be both ended. The window will be kept snapped.
+  DestroyTabletModeWindowManager();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+
+  // Create another normal state window to test additional scenarios.
+  std::unique_ptr<aura::Window> window2(
+      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
+  wm::ActivateWindow(window2.get());
+  // 7. Clamshell -> Tablet. Since top window is not a snapped window, all
+  // windows will be maximized.
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized());
+  EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMaximized());
+
+  // 8. Tablet -> Clamshell. If tablet splitscreen is active with two snapped
+  // windows, the two windows will remain snapped in clamshell mode.
+  Shell::Get()->split_view_controller()->SnapWindow(window.get(),
+                                                    SplitViewController::LEFT);
+  Shell::Get()->split_view_controller()->SnapWindow(window2.get(),
+                                                    SplitViewController::RIGHT);
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  DestroyTabletModeWindowManager();
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+  EXPECT_TRUE(wm::GetWindowState(window2.get())->IsSnapped());
+  EXPECT_FALSE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+
+  // 9. Clamshell -> Tablet. If two window are snapped to two sides of the
+  // screen, they will carry over to splitscreen in tablet mode.
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(wm::GetWindowState(window.get())->IsSnapped());
+  EXPECT_TRUE(wm::GetWindowState(window2.get())->IsSnapped());
+
+  // 10. Tablet -> Clamshell. If overview and splitview are both active, after
+  // transition, they will remain both active.
+  overview_controller->StartOverview();
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  DestroyTabletModeWindowManager();
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+
+  // 11. Clamshell -> Tablet. The same as 10.
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->InSplitViewMode());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+}
+
+// Test the divider position value during tablet <-> clamshell transition.
+TEST_F(TabletModeWindowManagerWithClamshellSplitViewTest,
+       ClamshellTabletTransitionDividerPositionTest) {
+  UpdateDisplay("1200x800");
+  gfx::Rect rect(10, 10, 200, 50);
+  std::unique_ptr<aura::Window> window(
+      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  OverviewController* overview_controller = Shell::Get()->overview_controller();
+
+  // First test 1 window case.
+  const wm::WMEvent left_snap_event(wm::WM_EVENT_SNAP_LEFT);
+  wm::GetWindowState(window.get())->OnWMEvent(&left_snap_event);
+  const gfx::Rect left_snapped_bounds = gfx::Rect(1200 / 2, 800 - kShelfSize);
+  EXPECT_EQ(window->bounds().width(), left_snapped_bounds.width());
+  // Change its bounds horizontally a bit and then enter tablet mode.
+  window->SetBounds(gfx::Rect(400, left_snapped_bounds.height()));
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(split_view_controller->InSplitViewMode());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(split_view_controller->IsWindowInSplitView(window.get()));
+  // Check the window is moved to 1/3 snapped position.
+  EXPECT_EQ(window->bounds().width(),
+            1200 * 0.33 - kSplitviewDividerShortSideLength / 2);
+  // Exit tablet mode and verify the window stays in the same position.
+  DestroyTabletModeWindowManager();
+  EXPECT_EQ(window->bounds().width(),
+            1200 * 0.33 - kSplitviewDividerShortSideLength / 2);
+
+  // Now test the 2 windows case.
+  std::unique_ptr<aura::Window> window2(
+      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
+  wm::GetWindowState(window.get())->OnWMEvent(&left_snap_event);
+  const wm::WMEvent right_snap_event(wm::WM_EVENT_SNAP_RIGHT);
+  wm::GetWindowState(window2.get())->OnWMEvent(&right_snap_event);
+  // Change their bounds horizontally and then enter tablet mode.
+  window->SetBounds(gfx::Rect(400, left_snapped_bounds.height()));
+  window2->SetBounds(gfx::Rect(400, 0, 800, left_snapped_bounds.height()));
+  CreateTabletModeWindowManager();
+  EXPECT_TRUE(split_view_controller->InSplitViewMode());
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(split_view_controller->IsWindowInSplitView(window.get()));
+  EXPECT_TRUE(split_view_controller->IsWindowInSplitView(window2.get()));
+  // Check |window| and |window2| is moved to 1/3 snapped position.
+  EXPECT_EQ(window->bounds().width(),
+            1200 * 0.33 - kSplitviewDividerShortSideLength / 2);
+  EXPECT_EQ(window2->bounds().width(),
+            1200 - window->bounds().width() - kSplitviewDividerShortSideLength);
+  // Exit tablet mode and verify the windows stay in the same position.
+  DestroyTabletModeWindowManager();
+  EXPECT_EQ(window->bounds().width(),
+            1200 * 0.33 - kSplitviewDividerShortSideLength / 2);
+  EXPECT_EQ(window2->bounds().width(), 1200 - window->bounds().width());
+}
+
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc
index 6cc5bcc..5b8263a 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -187,6 +187,7 @@
   DCHECK(!snap || CanSnapInSplitview(window));
   state_type_on_attach_ =
       snap ? current_state_type_ : GetMaximizedOrCenteredWindowType(state);
+  // TODO(oshima|sammiequon): consider SplitView scenario.
   if (entering_tablet_mode)
     set_enter_animation_type(IsTopWindow(window) ? DEFAULT : STEP_END);
   old_state_.reset(
@@ -197,18 +198,19 @@
   creator_->WindowStateDestroyed(window_);
 }
 
-void TabletModeWindowState::LeaveTabletMode(wm::WindowState* window_state,
-                                            bool was_in_overview) {
-  // TODO(minch): Keep the current animation if leaving tablet mode from
-  // overview. Need more investigation for windows' transform animation and
-  // updates bounds animation when overview is active.
-  bool use_default = was_in_overview || window_state->IsSnapped() ||
-                     IsTopWindow(window_state->window());
+void TabletModeWindowState::LeaveTabletMode(wm::WindowState* window_state) {
+  // Only do bounds change animation if the window is the top window or a window
+  // showing in splitview, and the window has changed its state. Otherwise,
+  // restore its bounds immediately.
+  EnterAnimationType animation_type =
+      window_state->IsSnapped() || IsTopWindow(window_state->window())
+          ? DEFAULT
+          : IMMEDIATE;
   if (old_state_->GetType() == window_state->GetStateType() &&
       !window_state->IsNormalStateType()) {
-    use_default = false;
+    animation_type = IMMEDIATE;
   }
-  old_state_->set_enter_animation_type(use_default ? DEFAULT : IMMEDIATE);
+  old_state_->set_enter_animation_type(animation_type);
   // Note: When we return we will destroy ourselves with the |our_reference|.
   std::unique_ptr<wm::WindowState::State> our_reference =
       window_state->SetStateObject(std::move(old_state_));
@@ -466,8 +468,12 @@
       window_state->SetBoundsDirect(bounds_in_parent);
     } else {
       if (enter_animation_type() == STEP_END) {
-        window_state->SetBoundsDirectCrossFade(bounds_in_parent,
-                                               gfx::Tween::ZERO);
+        // Just use the normal bounds animation with ZERO tween with long enough
+        // duration for STEP_END. The animation will be stopped when the to
+        // window's animation ends.
+        window_state->SetBoundsDirectAnimated(bounds_in_parent,
+                                              base::TimeDelta::FromSeconds(1),
+                                              gfx::Tween::ZERO);
         // Reset the |enter_animation_type_| to DEFAULT it if is STEP_END, which
         // is set for non-top windows when entering tablet mode.
         set_enter_animation_type(DEFAULT);
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.h b/ash/wm/tablet_mode/tablet_mode_window_state.h
index edbe9b99..d4b9494 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.h
@@ -41,7 +41,7 @@
   void set_ignore_wm_events(bool ignore) { ignore_wm_events_ = ignore; }
 
   // Leaves the tablet mode by reverting to previous state object.
-  void LeaveTabletMode(wm::WindowState* window_state, bool was_in_overview);
+  void LeaveTabletMode(wm::WindowState* window_state);
 
   // WindowState::State overrides:
   void OnWMEvent(wm::WindowState* window_state,
diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc
index 12db160..d1e50f7 100644
--- a/ash/wm/window_animations.cc
+++ b/ash/wm/window_animations.cc
@@ -47,9 +47,8 @@
 
 const int kLayerAnimationsForMinimizeDurationMS = 200;
 
-// Durations for the cross-fade animation, in milliseconds.
-const float kCrossFadeDurationMinMs = 200.f;
-const float kCrossFadeDurationMaxMs = 400.f;
+constexpr base::TimeDelta kCrossFadeMaxDuration =
+    base::TimeDelta::FromMilliseconds(400);
 
 // Durations for the brightness/grayscale fade animation, in milliseconds.
 const int kBrightnessGrayscaleFadeDurationMs = 1000;
@@ -68,10 +67,6 @@
 constexpr base::TimeDelta kZeroAnimationMs =
     base::TimeDelta::FromMilliseconds(300);
 
-int64_t Round64(float f) {
-  return static_cast<int64_t>(f + 0.5f);
-}
-
 base::TimeDelta GetCrossFadeDuration(aura::Window* window,
                                      const gfx::RectF& old_bounds,
                                      const gfx::Rect& new_bounds) {
@@ -83,17 +78,16 @@
   int max_area = std::max(old_area, new_area);
   // Avoid divide by zero.
   if (max_area == 0)
-    return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
+    return kCrossFadeDuration;
 
   int delta_area = std::abs(old_area - new_area);
   // If the area didn't change, the animation is instantaneous.
   if (delta_area == 0)
-    return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
+    return kCrossFadeDuration;
 
   float factor = static_cast<float>(delta_area) / static_cast<float>(max_area);
-  const float kRange = kCrossFadeDurationMaxMs - kCrossFadeDurationMinMs;
-  return base::TimeDelta::FromMilliseconds(
-      Round64(kCrossFadeDurationMinMs + (factor * kRange)));
+  const auto kRange = kCrossFadeMaxDuration - kCrossFadeDuration;
+  return kCrossFadeDuration + factor * kRange;
 }
 
 class CrossFadeMetricsReporter : public ui::AnimationMetricsReporter {
@@ -114,8 +108,6 @@
 
 }  // namespace
 
-const int kCrossFadeDurationMS = 200;
-
 void AddLayerAnimationsForMinimize(aura::Window* window, bool show) {
   // Recalculate the transform at restore time since the launcher item may have
   // moved while the window was minimized.
@@ -410,8 +402,7 @@
 
 base::TimeDelta CrossFadeAnimation(
     aura::Window* window,
-    std::unique_ptr<ui::LayerTreeOwner> old_layer_owner,
-    gfx::Tween::Type tween_type) {
+    std::unique_ptr<ui::LayerTreeOwner> old_layer_owner) {
   ui::Layer* old_layer = old_layer_owner->root();
   ui::Layer* new_layer = window->layer();
 
@@ -448,7 +439,7 @@
     settings.AddObserver(
         new CrossFadeObserver(window, std::move(old_layer_owner)));
     settings.SetTransitionDuration(duration);
-    settings.SetTweenType(tween_type);
+    settings.SetTweenType(gfx::Tween::EASE_OUT);
     // Only add reporter to |old_layer|.
     settings.SetAnimationMetricsReporter(g_reporter_cross_fade.Pointer());
     settings.DeferPaint();
@@ -495,7 +486,7 @@
     // its newly set bounds.
     ui::ScopedLayerAnimationSettings settings(new_layer->GetAnimator());
     settings.SetTransitionDuration(duration);
-    settings.SetTweenType(tween_type);
+    settings.SetTweenType(gfx::Tween::EASE_OUT);
     settings.DeferPaint();
     if (!old_on_top) {
       // Only caching render surface when there is an opacity animation and
diff --git a/ash/wm/window_animations.h b/ash/wm/window_animations.h
index e30993a..43a07e0 100644
--- a/ash/wm/window_animations.h
+++ b/ash/wm/window_animations.h
@@ -24,16 +24,15 @@
 namespace ash {
 
 // Amount of time for the cross fade animation.
-extern const int kCrossFadeDurationMS;
+constexpr base::TimeDelta kCrossFadeDuration =
+    base::TimeDelta::FromMilliseconds(200);
 
 // Implementation of cross fading. Window is the window being cross faded. It
 // should be at the target bounds. |old_layer_owner| contains the previous layer
-// from |window|.  |tween_type| specifies the tween type of the cross fade
-// animation.
+// from |window|.
 ASH_EXPORT base::TimeDelta CrossFadeAnimation(
     aura::Window* window,
-    std::unique_ptr<ui::LayerTreeOwner> old_layer_owner,
-    gfx::Tween::Type tween_type);
+    std::unique_ptr<ui::LayerTreeOwner> old_layer_owner);
 
 ASH_EXPORT bool AnimateOnChildWindowVisibilityChanged(aura::Window* window,
                                                       bool visible);
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 01e6123..d798172 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -659,7 +659,6 @@
         std::max(min_size.height(), actual_new_bounds.height()));
   }
   BoundsSetter().SetBounds(window_, actual_new_bounds);
-  ::wm::SnapWindowToPixelBoundary(window_);
 }
 
 void WindowState::SetBoundsConstrained(const gfx::Rect& bounds) {
@@ -671,7 +670,8 @@
 }
 
 void WindowState::SetBoundsDirectAnimated(const gfx::Rect& bounds,
-                                          base::TimeDelta duration) {
+                                          base::TimeDelta duration,
+                                          gfx::Tween::Type tween_type) {
   if (::wm::WindowAnimationsDisabled(window_)) {
     SetBoundsDirect(bounds);
     return;
@@ -680,12 +680,12 @@
   ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
   slide_settings.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  slide_settings.SetTweenType(tween_type);
   slide_settings.SetTransitionDuration(duration);
   SetBoundsDirect(bounds);
 }
 
-void WindowState::SetBoundsDirectCrossFade(const gfx::Rect& new_bounds,
-                                           gfx::Tween::Type animation_type) {
+void WindowState::SetBoundsDirectCrossFade(const gfx::Rect& new_bounds) {
   // Some test results in invoking CrossFadeToBounds when window is not visible.
   // No animation is necessary in that case, thus just change the bounds and
   // quit.
@@ -713,7 +713,7 @@
   // Resize the window to the new size, which will force a layout and paint.
   SetBoundsDirect(new_bounds);
 
-  CrossFadeAnimation(window_, std::move(old_layer_owner), animation_type);
+  CrossFadeAnimation(window_, std::move(old_layer_owner));
 }
 
 void WindowState::OnPrePipStateChange(WindowStateType old_window_state_type) {
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index 5f80e8b..0d753828 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -412,13 +412,12 @@
   // a scale animation, with duration specified by |duration|.
   void SetBoundsDirectAnimated(
       const gfx::Rect& bounds,
-      base::TimeDelta duration = kBoundsChangeSlideDuration);
+      base::TimeDelta duration = kBoundsChangeSlideDuration,
+      gfx::Tween::Type animation_type = gfx::Tween::LINEAR);
 
   // Sets the window's |bounds| and transition to the new bounds with
   // a cross fade animation.
-  void SetBoundsDirectCrossFade(
-      const gfx::Rect& bounds,
-      gfx::Tween::Type animation_type = gfx::Tween::EASE_OUT);
+  void SetBoundsDirectCrossFade(const gfx::Rect& bounds);
 
   // Called before the state change and update PIP related state, such as next
   // window animation type, upon state change.
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index ecaef7cc..df16373 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -41,7 +41,6 @@
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/easy_resize_window_targeter.h"
 #include "ui/wm/core/window_animations.h"
-#include "ui/wm/core/window_properties.h"
 #include "ui/wm/core/window_util.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -207,12 +206,6 @@
   return root && MoveWindowToRoot(window, root);
 }
 
-void SetSnapsChildrenToPhysicalPixelBoundary(aura::Window* container) {
-  DCHECK(!container->GetProperty(::wm::kSnapChildrenToPixelBoundary))
-      << container->GetName();
-  container->SetProperty(::wm::kSnapChildrenToPixelBoundary, true);
-}
-
 int GetNonClientComponent(aura::Window* window, const gfx::Point& location) {
   return window->delegate()
              ? window->delegate()->GetNonClientComponent(location)
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index b606f92..cbf7f0e 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -77,11 +77,6 @@
 ASH_EXPORT bool MoveWindowToEventRoot(aura::Window* window,
                                       const ui::Event& event);
 
-// Mark the container window so that InstallSnapLayoutManagerToContainers
-// installs the SnapToPixelLayoutManager.
-ASH_EXPORT void SetSnapsChildrenToPhysicalPixelBoundary(
-    aura::Window* container);
-
 // Convenience for window->delegate()->GetNonClientComponent(location) that
 // returns HTNOWHERE if window->delegate() is null.
 ASH_EXPORT int GetNonClientComponent(aura::Window* window,
diff --git a/ash/wm/wm_default_layout_manager.cc b/ash/wm/wm_default_layout_manager.cc
new file mode 100644
index 0000000..4c7e50a
--- /dev/null
+++ b/ash/wm/wm_default_layout_manager.cc
@@ -0,0 +1,50 @@
+// 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 "ash/wm/wm_default_layout_manager.h"
+
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+#include "ui/wm/core/window_properties.h"
+#include "ui/wm/core/window_util.h"
+
+namespace ash {
+namespace wm {
+
+WmDefaultLayoutManager::WmDefaultLayoutManager() = default;
+
+WmDefaultLayoutManager::~WmDefaultLayoutManager() = default;
+
+// static
+void WmDefaultLayoutManager::InstallOnContainers(aura::Window* window) {
+  for (aura::Window* child : window->children()) {
+    if (child->id() < kShellWindowId_MinContainer ||
+        child->id() > kShellWindowId_MaxContainer)  // not a container
+      continue;
+    if (!child->layout_manager())
+      child->SetLayoutManager(new WmDefaultLayoutManager());
+    InstallOnContainers(child);
+  }
+}
+
+void WmDefaultLayoutManager::OnWindowResized() {}
+
+void WmDefaultLayoutManager::OnWindowAddedToLayout(aura::Window* child) {}
+
+void WmDefaultLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
+}
+
+void WmDefaultLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {}
+
+void WmDefaultLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
+                                                            bool visible) {}
+
+void WmDefaultLayoutManager::SetChildBounds(aura::Window* child,
+                                            const gfx::Rect& requested_bounds) {
+  SetChildBoundsDirect(child, requested_bounds);
+}
+
+}  // namespace wm
+}  // namespace ash
diff --git a/ash/wm/wm_snap_to_pixel_layout_manager.h b/ash/wm/wm_default_layout_manager.h
similarity index 68%
rename from ash/wm/wm_snap_to_pixel_layout_manager.h
rename to ash/wm/wm_default_layout_manager.h
index 90766f1..9888fe9 100644
--- a/ash/wm/wm_snap_to_pixel_layout_manager.h
+++ b/ash/wm/wm_default_layout_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_WM_WM_SNAP_TO_PIXEL_LAYOUT_MANAGER_H_
-#define ASH_WM_WM_SNAP_TO_PIXEL_LAYOUT_MANAGER_H_
+#ifndef ASH_WM_WM_DEFAULT_LAYOUT_MANAGER_H_
+#define ASH_WM_WM_DEFAULT_LAYOUT_MANAGER_H_
 
 #include "ash/ash_export.h"
 #include "base/macros.h"
@@ -14,12 +14,14 @@
 
 // A layout manager that places children's layer at the physical pixel
 // boundaries.
-class ASH_EXPORT WmSnapToPixelLayoutManager : public aura::LayoutManager {
+// TODO(malaykeshav): This is essentially a skeleton class, so remove it
+// alltogether.
+class ASH_EXPORT WmDefaultLayoutManager : public aura::LayoutManager {
  public:
-  WmSnapToPixelLayoutManager();
-  ~WmSnapToPixelLayoutManager() override;
+  WmDefaultLayoutManager();
+  ~WmDefaultLayoutManager() override;
 
-  // Sets WmSnapToPixelLayoutManager as the LayoutManager on the appropriate
+  // Sets WmDefaultLayoutManager as the LayoutManager on the appropriate
   // descendants of |window|.
   static void InstallOnContainers(aura::Window* window);
 
@@ -35,10 +37,10 @@
                       const gfx::Rect& requested_bounds) override;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(WmSnapToPixelLayoutManager);
+  DISALLOW_COPY_AND_ASSIGN(WmDefaultLayoutManager);
 };
 
 }  // namespace wm
 }  // namespace ash
 
-#endif  // ASH_WM_WM_SNAP_TO_PIXEL_LAYOUT_MANAGER_H_
+#endif  // ASH_WM_WM_DEFAULT_LAYOUT_MANAGER_H_
diff --git a/ash/wm/wm_snap_to_pixel_layout_manager.cc b/ash/wm/wm_snap_to_pixel_layout_manager.cc
deleted file mode 100644
index 8d41fdee..0000000
--- a/ash/wm/wm_snap_to_pixel_layout_manager.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/wm/wm_snap_to_pixel_layout_manager.h"
-
-#include "ash/public/cpp/shell_window_ids.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/window.h"
-#include "ui/wm/core/window_properties.h"
-#include "ui/wm/core/window_util.h"
-
-namespace ash {
-namespace wm {
-
-WmSnapToPixelLayoutManager::WmSnapToPixelLayoutManager() = default;
-
-WmSnapToPixelLayoutManager::~WmSnapToPixelLayoutManager() = default;
-
-// static
-void WmSnapToPixelLayoutManager::InstallOnContainers(aura::Window* window) {
-  for (aura::Window* child : window->children()) {
-    if (child->id() < kShellWindowId_MinContainer ||
-        child->id() > kShellWindowId_MaxContainer)  // not a container
-      continue;
-    if (child->GetProperty(::wm::kSnapChildrenToPixelBoundary)) {
-      if (!child->layout_manager())
-        child->SetLayoutManager(new WmSnapToPixelLayoutManager());
-    } else {
-      InstallOnContainers(child);
-    }
-  }
-}
-
-void WmSnapToPixelLayoutManager::OnWindowResized() {}
-
-void WmSnapToPixelLayoutManager::OnWindowAddedToLayout(aura::Window* child) {}
-
-void WmSnapToPixelLayoutManager::OnWillRemoveWindowFromLayout(
-    aura::Window* child) {}
-
-void WmSnapToPixelLayoutManager::OnWindowRemovedFromLayout(
-    aura::Window* child) {}
-
-void WmSnapToPixelLayoutManager::OnChildWindowVisibilityChanged(
-    aura::Window* child,
-    bool visible) {}
-
-void WmSnapToPixelLayoutManager::SetChildBounds(
-    aura::Window* child,
-    const gfx::Rect& requested_bounds) {
-  SetChildBoundsDirect(child, requested_bounds);
-  ::wm::SnapWindowToPixelBoundary(child);
-}
-
-}  // namespace wm
-}  // namespace ash
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index fa25925..fda30d88 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -106,7 +106,6 @@
   Shell::Get()->activation_client()->AddObserver(this);
   root_window_->AddObserver(this);
   display::Screen::GetScreen()->AddObserver(this);
-  DCHECK(window->GetProperty(::wm::kSnapChildrenToPixelBoundary));
   backdrop_controller_ = std::make_unique<BackdropController>(window_);
   keyboard::KeyboardController::Get()->AddObserver(this);
   settings_bubble_container_ = window->GetRootWindow()->GetChildById(
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 0e8f1433..9115cd59 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -716,7 +716,6 @@
     "sync_socket_win.cc",
     "synchronization/atomic_flag.cc",
     "synchronization/atomic_flag.h",
-    "synchronization/cancellation_flag.h",
     "synchronization/condition_variable.h",
     "synchronization/condition_variable_win.cc",
     "synchronization/lock.cc",
@@ -1376,7 +1375,6 @@
       "android/jni_weak_ref.h",
       "android/library_loader/anchor_functions.cc",
       "android/library_loader/anchor_functions.h",
-      "android/library_loader/library_load_from_apk_status_codes.h",
       "android/library_loader/library_loader_hooks.cc",
       "android/library_loader/library_loader_hooks.h",
       "android/library_loader/library_prefetcher.cc",
@@ -3478,7 +3476,6 @@
     sources = [
       "android/application_status_listener.h",
       "android/child_process_binding_types.h",
-      "android/library_loader/library_load_from_apk_status_codes.h",
       "android/library_loader/library_loader_hooks.h",
       "android/task_scheduler/task_runner_android.h",
       "memory/memory_pressure_listener.h",
diff --git a/base/android/android_image_reader_abi.h b/base/android/android_image_reader_abi.h
index 3b0ba9b..087f03d 100644
--- a/base/android/android_image_reader_abi.h
+++ b/base/android/android_image_reader_abi.h
@@ -92,6 +92,11 @@
                        AImage** image,
                        int* acquireFenceFd);
 
+using pAImageReader_acquireNextImageAsync =
+    media_status_t (*)(AImageReader* reader,
+                       AImage** image,
+                       int* acquireFenceFd);
+
 // For ANativeWindow
 using pANativeWindow_toSurface = jobject (*)(JNIEnv* env,
                                              ANativeWindow* window);
diff --git a/base/android/android_image_reader_compat.cc b/base/android/android_image_reader_compat.cc
index 62c153ba..9e1c6cb 100644
--- a/base/android/android_image_reader_compat.cc
+++ b/base/android/android_image_reader_compat.cc
@@ -80,6 +80,7 @@
   LOAD_FUNCTION(libmediandk, AImageReader_getFormat);
   LOAD_FUNCTION(libmediandk, AImageReader_getWindow);
   LOAD_FUNCTION(libmediandk, AImageReader_acquireLatestImageAsync);
+  LOAD_FUNCTION(libmediandk, AImageReader_acquireNextImageAsync);
 
   void* libandroid = dlopen("libandroid.so", RTLD_NOW);
   if (libandroid == nullptr) {
@@ -156,6 +157,13 @@
   return AImageReader_acquireLatestImageAsync_(reader, image, acquireFenceFd);
 }
 
+media_status_t AndroidImageReader::AImageReader_acquireNextImageAsync(
+    AImageReader* reader,
+    AImage** image,
+    int* acquireFenceFd) {
+  return AImageReader_acquireNextImageAsync_(reader, image, acquireFenceFd);
+}
+
 jobject AndroidImageReader::ANativeWindow_toSurface(JNIEnv* env,
                                                     ANativeWindow* window) {
   return ANativeWindow_toSurface_(env, window);
diff --git a/base/android/android_image_reader_compat.h b/base/android/android_image_reader_compat.h
index b259175..b9b2f89 100644
--- a/base/android/android_image_reader_compat.h
+++ b/base/android/android_image_reader_compat.h
@@ -55,6 +55,9 @@
   media_status_t AImageReader_acquireLatestImageAsync(AImageReader* reader,
                                                       AImage** image,
                                                       int* acquireFenceFd);
+  media_status_t AImageReader_acquireNextImageAsync(AImageReader* reader,
+                                                    AImage** image,
+                                                    int* acquireFenceFd);
   jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window);
 
  private:
@@ -76,6 +79,7 @@
   pAImageReader_getFormat AImageReader_getFormat_;
   pAImageReader_getWindow AImageReader_getWindow_;
   pAImageReader_acquireLatestImageAsync AImageReader_acquireLatestImageAsync_;
+  pAImageReader_acquireNextImageAsync AImageReader_acquireNextImageAsync_;
   pANativeWindow_toSurface ANativeWindow_toSurface_;
 
   DISALLOW_COPY_AND_ASSIGN(AndroidImageReader);
diff --git a/base/android/java/src/org/chromium/base/BuildInfo.java b/base/android/java/src/org/chromium/base/BuildInfo.java
index 0e636934..969d2410e 100644
--- a/base/android/java/src/org/chromium/base/BuildInfo.java
+++ b/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -12,6 +12,7 @@
 import android.text.TextUtils;
 
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.compat.ApiHelperForP;
 
 /**
  * BuildInfo is a utility class providing easy access to {@link PackageInfo} information. This is
@@ -30,11 +31,11 @@
     /** The application name (e.g. "Chrome"). For WebView, this is name of the embedding app. */
     public final String hostPackageLabel;
     /** By default: same as versionCode. For WebView: versionCode of the embedding app. */
-    public final int hostVersionCode;
+    public final long hostVersionCode;
     /** The packageName of Chrome/WebView. Use application context for host app packageName. */
     public final String packageName;
     /** The versionCode of the apk. */
-    public final int versionCode;
+    public final long versionCode;
     /** The versionName of Chrome/WebView. Use application context for host app versionName. */
     public final String versionName;
     /** Result of PackageManager.getInstallerPackageName(). Never null, but may be "". */
@@ -89,6 +90,14 @@
         return seq == null ? "" : seq.toString();
     }
 
+    private static long packageVersionCode(PackageInfo pi) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            return ApiHelperForP.getLongVersionCode(pi);
+        } else {
+            return pi.versionCode;
+        }
+    }
+
     /**
      * @param packageInfo Package for Chrome/WebView (as opposed to host app).
      */
@@ -108,10 +117,10 @@
             String hostPackageName = appContext.getPackageName();
             PackageManager pm = appContext.getPackageManager();
             PackageInfo pi = pm.getPackageInfo(hostPackageName, 0);
-            hostVersionCode = pi.versionCode;
+            hostVersionCode = packageVersionCode(pi);
             if (sBrowserPackageInfo != null) {
                 packageName = sBrowserPackageInfo.packageName;
-                versionCode = sBrowserPackageInfo.versionCode;
+                versionCode = packageVersionCode(sBrowserPackageInfo);
                 versionName = nullToEmpty(sBrowserPackageInfo.versionName);
                 sBrowserPackageInfo = null;
             } else {
@@ -129,8 +138,9 @@
             } catch (NameNotFoundException e) {
                 Log.d(TAG, "GMS package is not found.", e);
             }
-            gmsVersionCode = gmsPackageInfo != null ? String.valueOf(gmsPackageInfo.versionCode)
-                                                    : "gms versionCode not available.";
+            gmsVersionCode = gmsPackageInfo != null
+                    ? String.valueOf(packageVersionCode(gmsPackageInfo))
+                    : "gms versionCode not available.";
 
             String hasCustomThemes = "true";
             try {
diff --git a/base/android/java/src/org/chromium/base/PathUtils.java b/base/android/java/src/org/chromium/base/PathUtils.java
index 3f7d29c289..b2da8591 100644
--- a/base/android/java/src/org/chromium/base/PathUtils.java
+++ b/base/android/java/src/org/chromium/base/PathUtils.java
@@ -153,6 +153,11 @@
             // inherently posts to the UI thread for onPostExecute().
             sDirPathFetchTask = new FutureTask<>(PathUtils::setPrivateDataDirectorySuffixInternal);
             AsyncTask.THREAD_POOL_EXECUTOR.execute(sDirPathFetchTask);
+        } else {
+            assert TextUtils.equals(sDataDirectorySuffix, suffix)
+                : String.format("%s != %s", suffix, sDataDirectorySuffix);
+            assert TextUtils.equals(sCacheSubDirectory, cacheSubDir)
+                : String.format("%s != %s", cacheSubDir, sCacheSubDirectory);
         }
     }
 
diff --git a/base/android/java/src/org/chromium/base/compat/ApiHelperForP.java b/base/android/java/src/org/chromium/base/compat/ApiHelperForP.java
index 6619bae..d947498 100644
--- a/base/android/java/src/org/chromium/base/compat/ApiHelperForP.java
+++ b/base/android/java/src/org/chromium/base/compat/ApiHelperForP.java
@@ -5,6 +5,7 @@
 package org.chromium.base.compat;
 
 import android.annotation.TargetApi;
+import android.content.pm.PackageInfo;
 import android.net.LinkProperties;
 import android.os.Build;
 
@@ -29,4 +30,9 @@
     public static String getPrivateDnsServerName(LinkProperties linkProperties) {
         return linkProperties.getPrivateDnsServerName();
     }
+
+    /** See {@link PackageInfo#getLongVersionCode() }. */
+    public static long getLongVersionCode(PackageInfo packageInfo) {
+        return packageInfo.getLongVersionCode();
+    }
 }
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index ae1541f..cfcbfc7 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -614,11 +614,7 @@
     private void recordBrowserProcessHistogramAlreadyLocked() {
         assert Thread.holdsLock(mLock);
         if (useCrazyLinker()) {
-            nativeRecordChromiumAndroidLinkerBrowserHistogram(mIsUsingBrowserSharedRelros,
-                    mLoadAtFixedAddressFailed,
-                    mLibraryWasLoadedFromApk ? LibraryLoadFromApkStatusCodes.SUCCESSFUL
-                                             : LibraryLoadFromApkStatusCodes.UNKNOWN,
-                    mLibraryLoadTimeMs);
+            nativeRecordChromiumAndroidLinkerBrowserHistogram(mLibraryLoadTimeMs);
         }
         if (mLibraryPreloader != null) {
             nativeRecordLibraryPreloaderBrowserHistogram(mLibraryPreloaderStatus);
@@ -629,12 +625,10 @@
     // recorded as a histogram immediately because histograms and IPC are not ready at the
     // time it are captured. This function stores a pending value, so that a later call to
     // RecordChromiumAndroidLinkerRendererHistogram() will record it correctly.
-    public void registerRendererProcessHistogram(boolean requestedSharedRelro,
-                                                 boolean loadAtFixedAddressFailed) {
+    public void registerRendererProcessHistogram() {
         synchronized (mLock) {
             if (useCrazyLinker()) {
-                nativeRegisterChromiumAndroidLinkerRendererHistogram(
-                        requestedSharedRelro, loadAtFixedAddressFailed, mLibraryLoadTimeMs);
+                nativeRegisterChromiumAndroidLinkerRendererHistogram(mLibraryLoadTimeMs);
             }
             if (mLibraryPreloader != null) {
                 nativeRegisterLibraryPreloaderRendererHistogram(mLibraryPreloaderStatus);
@@ -732,28 +726,16 @@
     // Return true on success and false on failure.
     private native boolean nativeLibraryLoaded(@LibraryProcessType int processType);
 
-    // Method called to record statistics about the Chromium linker operation for the main
-    // browser process. Indicates whether the linker attempted relro sharing for the browser,
-    // and if it did, whether the library failed to load at a fixed address. Also records
-    // support for loading a library directly from the APK file, and the number of milliseconds
-    // it took to load the libraries.
+    // Records the number of milliseconds it took to load the libraries in the browser.
     private native void nativeRecordChromiumAndroidLinkerBrowserHistogram(
-            boolean isUsingBrowserSharedRelros,
-            boolean loadAtFixedAddressFailed,
-            int libraryLoadFromApkStatus,
             long libraryLoadTime);
 
     // Method called to record the return value of NativeLibraryPreloader.loadLibrary for the main
     // browser process.
     private native void nativeRecordLibraryPreloaderBrowserHistogram(int status);
 
-    // Method called to register (for later recording) statistics about the Chromium linker
-    // operation for a renderer process. Indicates whether the linker attempted relro sharing,
-    // and if it did, whether the library failed to load at a fixed address. Also records the
-    // number of milliseconds it took to load the libraries.
+    // Records the number of milliseconds it took to load the libraries in the renderer.
     private native void nativeRegisterChromiumAndroidLinkerRendererHistogram(
-            boolean requestedSharedRelro,
-            boolean loadAtFixedAddressFailed,
             long libraryLoadTime);
 
     // Method called to register (for later recording) the return value of
diff --git a/base/android/jni_generator/sample_for_tests.h b/base/android/jni_generator/sample_for_tests.h
index 6414158..f1fc66f 100644
--- a/base/android/jni_generator/sample_for_tests.h
+++ b/base/android/jni_generator/sample_for_tests.h
@@ -48,7 +48,6 @@
 //      sources = [
 //        "java/src/org/chromium/example/jni_generator/SampleForTests.java",
 //      ]
-//      jni_package = "foo"
 //    }
 //    android_library("java") {
 //      java_files = [
diff --git a/base/android/library_loader/library_load_from_apk_status_codes.h b/base/android/library_loader/library_load_from_apk_status_codes.h
deleted file mode 100644
index 8910d48..0000000
--- a/base/android/library_loader/library_load_from_apk_status_codes.h
+++ /dev/null
@@ -1,49 +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 BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_
-#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_
-
-namespace base {
-namespace android {
-
-namespace {
-
-// This enum must be kept in sync with the LibraryLoadFromApkStatus enum in
-// tools/metrics/histograms/histograms.xml.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader
-enum LibraryLoadFromApkStatusCodes {
-  // The loader was unable to determine whether the functionality is supported.
-  LIBRARY_LOAD_FROM_APK_STATUS_CODES_UNKNOWN = 0,
-
-  // The device does not support loading a library directly from the APK file
-  // (obsolete).
-  LIBRARY_LOAD_FROM_APK_STATUS_CODES_NOT_SUPPORTED_OBSOLETE = 1,
-
-  // The device supports loading a library directly from the APK file.
-  // (obsolete).
-  LIBRARY_LOAD_FROM_APK_STATUS_CODES_SUPPORTED_OBSOLETE = 2,
-
-  // The Chromium library was successfully loaded directly from the APK file.
-  LIBRARY_LOAD_FROM_APK_STATUS_CODES_SUCCESSFUL = 3,
-
-  // The Chromium library was successfully loaded using the unpack library
-  // fallback because it was compressed or not page aligned in the APK file.
-  LIBRARY_LOAD_FROM_APK_STATUS_CODES_USED_UNPACK_LIBRARY_FALLBACK = 4,
-
-  // The Chromium library was successfully loaded using the no map executable
-  // support fallback (obsolete).
-  LIBRARY_LOAD_FROM_APK_STATUS_CODES_USED_NO_MAP_EXEC_SUPPORT_FALLBACK_OBSOLETE
-      = 5,
-
-  // End sentinel.
-  LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX = 6,
-};
-
-}  // namespace
-
-}  // namespace android
-}  // namespace base
-
-#endif  // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_
diff --git a/base/android/library_loader/library_loader_hooks.cc b/base/android/library_loader/library_loader_hooks.cc
index 2e12ab6..3201406d 100644
--- a/base/android/library_loader/library_loader_hooks.cc
+++ b/base/android/library_loader/library_loader_hooks.cc
@@ -8,7 +8,6 @@
 
 #include "base/android/jni_string.h"
 #include "base/android/library_loader/anchor_functions_buildflags.h"
-#include "base/android/library_loader/library_load_from_apk_status_codes.h"
 #include "base/android/library_loader/library_prefetcher.h"
 #include "base/android/orderfile/orderfile_buildflags.h"
 #include "base/android/sys_utils.h"
@@ -33,34 +32,7 @@
 LibraryLoadedHook* g_registration_callback = NULL;
 NativeInitializationHook* g_native_initialization_hook = NULL;
 
-enum RendererHistogramCode {
-  // Renderer load at fixed address success, fail, or not attempted.
-  // Renderers do not attempt to load at at fixed address if on a
-  // low-memory device on which browser load at fixed address has already
-  // failed.
-  LFA_SUCCESS = 0,
-  LFA_BACKOFF_USED = 1,
-  LFA_NOT_ATTEMPTED = 2,
-
-  // End sentinel, also used as nothing-pending indicator.
-  MAX_RENDERER_HISTOGRAM_CODE = 3,
-  NO_PENDING_HISTOGRAM_CODE = MAX_RENDERER_HISTOGRAM_CODE
-};
-
-enum BrowserHistogramCode {
-  // Non-low-memory random address browser loads.
-  NORMAL_LRA_SUCCESS = 0,
-
-  // Low-memory browser loads at fixed address, success or fail.
-  LOW_MEMORY_LFA_SUCCESS = 1,
-  LOW_MEMORY_LFA_BACKOFF_USED = 2,
-
-  MAX_BROWSER_HISTOGRAM_CODE = 3,
-};
-
-RendererHistogramCode g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE;
-
-// Indicate whether g_library_preloader_renderer_histogram_code is valid
+// Indicates whether g_library_preloader_renderer_histogram_code is valid.
 bool g_library_preloader_renderer_histogram_code_registered = false;
 
 // The return value of NativeLibraryPreloader.loadLibrary() in child processes,
@@ -69,18 +41,10 @@
 
 // The amount of time, in milliseconds, that it took to load the shared
 // libraries in the renderer. Set in
-// RegisterChromiumAndroidLinkerRendererHistogram.
+// JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram.
 long g_renderer_library_load_time_ms = 0;
 
 void RecordChromiumAndroidLinkerRendererHistogram() {
-  if (g_renderer_histogram_code == NO_PENDING_HISTOGRAM_CODE)
-    return;
-  // Record and release the pending histogram value.
-  UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.RendererStates",
-                            g_renderer_histogram_code,
-                            MAX_RENDERER_HISTOGRAM_CODE);
-  g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE;
-
   // Record how long it took to load the shared libraries.
   UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.RendererLoadTime",
       base::TimeDelta::FromMilliseconds(g_renderer_library_load_time_ms));
@@ -106,46 +70,14 @@
 static void JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
-    jboolean requested_shared_relro,
-    jboolean load_at_fixed_address_failed,
     jlong library_load_time_ms) {
-  // Note a pending histogram value for later recording.
-  if (requested_shared_relro) {
-    g_renderer_histogram_code = load_at_fixed_address_failed
-                                ? LFA_BACKOFF_USED : LFA_SUCCESS;
-  } else {
-    g_renderer_histogram_code = LFA_NOT_ATTEMPTED;
-  }
-
   g_renderer_library_load_time_ms = library_load_time_ms;
 }
 
 static void JNI_LibraryLoader_RecordChromiumAndroidLinkerBrowserHistogram(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
-    jboolean is_using_browser_shared_relros,
-    jboolean load_at_fixed_address_failed,
-    jint library_load_from_apk_status,
     jlong library_load_time_ms) {
-  // For low-memory devices, record whether or not we successfully loaded the
-  // browser at a fixed address. Otherwise just record a normal invocation.
-  BrowserHistogramCode histogram_code;
-  if (is_using_browser_shared_relros) {
-    histogram_code = load_at_fixed_address_failed
-                     ? LOW_MEMORY_LFA_BACKOFF_USED : LOW_MEMORY_LFA_SUCCESS;
-  } else {
-    histogram_code = NORMAL_LRA_SUCCESS;
-  }
-  UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.BrowserStates",
-                            histogram_code,
-                            MAX_BROWSER_HISTOGRAM_CODE);
-
-  // Record the device support for loading a library directly from the APK file.
-  UMA_HISTOGRAM_ENUMERATION(
-      "ChromiumAndroidLinker.LibraryLoadFromApkStatus",
-      static_cast<LibraryLoadFromApkStatusCodes>(library_load_from_apk_status),
-      LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX);
-
   // Record how long it took to load the shared libraries.
   UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.BrowserLoadTime",
                       base::TimeDelta::FromMilliseconds(library_load_time_ms));
diff --git a/base/debug/task_trace.cc b/base/debug/task_trace.cc
index ad5276a..bea5f1b 100644
--- a/base/debug/task_trace.cc
+++ b/base/debug/task_trace.cc
@@ -4,15 +4,50 @@
 
 #include "base/debug/task_trace.h"
 
+#include "build/build_config.h"
+
+#if defined(OS_ANDROID)
+#include <android/log.h>
+#endif  // OS_ANDROID
+
 #include <algorithm>
 #include <iostream>
 #include <sstream>
 
+#if defined(OS_ANDROID)
+#include "base/no_destructor.h"
+#endif
+
 #include "base/pending_task.h"
 #include "base/task/common/task_annotator.h"
 
 namespace base {
 namespace debug {
+namespace {
+#if defined(OS_ANDROID)
+// Android sends stdout and stderr to /dev/null; logging should be done through
+// the __android_log_write() function. Here we create an override of
+// std::stringbuf that writes to the Android log.
+class AndroidErrBuffer : public std::stringbuf {
+ protected:
+  int sync() override {
+    __android_log_write(ANDROID_LOG_ERROR, "chromium", str().c_str());
+    return 0;
+  }
+};
+
+std::ostream& DefaultOutputStream() {
+  static NoDestructor<AndroidErrBuffer> buf;
+  static NoDestructor<std::ostream> out(buf.get());
+  return *out;
+}
+#else
+// Use stderr by default.
+std::ostream& DefaultOutputStream() {
+  return std::cerr;
+}
+#endif  // OS_ANDROID
+}  // namespace
 
 TaskTrace::TaskTrace() {
   const PendingTask* current_task = TaskAnnotator::CurrentTaskForThread();
@@ -36,7 +71,7 @@
 }
 
 void TaskTrace::Print() const {
-  OutputToStream(&std::cerr);
+  OutputToStream(&DefaultOutputStream());
 }
 
 void TaskTrace::OutputToStream(std::ostream* os) const {
diff --git a/base/process/process_metrics_win.cc b/base/process/process_metrics_win.cc
index 45e2767..fcf69ac 100644
--- a/base/process/process_metrics_win.cc
+++ b/base/process/process_metrics_win.cc
@@ -141,6 +141,9 @@
   FILETIME kernel_time;
   FILETIME user_time;
 
+  if (!process_.IsValid())
+    return TimeDelta();
+
   if (!GetProcessTimes(process_.Get(), &creation_time, &exit_time, &kernel_time,
                        &user_time)) {
     // This should never fail because we duplicate the handle to guarantee it
@@ -154,6 +157,9 @@
 }
 
 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+  if (!process_.IsValid())
+    return false;
+
   return GetProcessIoCounters(process_.Get(), io_counters) != FALSE;
 }
 
diff --git a/base/run_loop.cc b/base/run_loop.cc
index af0ed1f..077a153 100644
--- a/base/run_loop.cc
+++ b/base/run_loop.cc
@@ -85,10 +85,12 @@
 
 RunLoop::Delegate::~Delegate() {
   DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
+  DCHECK(active_run_loops_.empty());
   // A RunLoop::Delegate may be destroyed before it is bound, if so it may still
   // be on its creation thread (e.g. a Thread that fails to start) and
   // shouldn't disrupt that thread's state.
   if (bound_) {
+    DCHECK_EQ(this, GetTlsDelegate().Get());
     GetTlsDelegate().Set(nullptr);
   }
 }
@@ -149,23 +151,12 @@
         FROM_HERE, cancelable_timeout.callback(), run_timeout->timeout());
   }
 
-  // It is okay to access this RunLoop from another sequence while Run() is
-  // active as this RunLoop won't touch its state until after that returns (if
-  // the RunLoop's state is accessed while processing Run(), it will be re-bound
-  // to the accessing sequence for the remainder of that Run() -- accessing from
-  // multiple sequences is still disallowed).
-  DETACH_FROM_SEQUENCE(sequence_checker_);
-
   DCHECK_EQ(this, delegate_->active_run_loops_.top());
   const bool application_tasks_allowed =
       delegate_->active_run_loops_.size() == 1U ||
       type_ == Type::kNestableTasksAllowed;
   delegate_->Run(application_tasks_allowed, timeout);
 
-  // Rebind this RunLoop to the current thread after Run().
-  DETACH_FROM_SEQUENCE(sequence_checker_);
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
   AfterRun();
 }
 
@@ -211,25 +202,23 @@
 }
 
 Closure RunLoop::QuitClosure() {
-  // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
-  // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Obtaining the QuitClosure() is not thread-safe; either post the
+  // QuitClosure() from the run thread or invoke Quit() directly (which is
+  // thread-safe).
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   allow_quit_current_deprecated_ = false;
 
-  // Need to use ProxyToTaskRunner() as WeakPtrs vended from
-  // |weak_factory_| may only be accessed on |origin_task_runner_|.
-  // TODO(gab): It feels wrong that QuitClosure() is bound to a WeakPtr.
   return Bind(&ProxyToTaskRunner, origin_task_runner_,
               Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr()));
 }
 
 Closure RunLoop::QuitWhenIdleClosure() {
-  // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
-  // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Obtaining the QuitWhenIdleClosure() is not thread-safe; either post the
+  // QuitWhenIdleClosure() from the run thread or invoke QuitWhenIdle() directly
+  // (which is thread-safe).
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   allow_quit_current_deprecated_ = false;
 
-  // Need to use ProxyToTaskRunner() as WeakPtrs vended from
-  // |weak_factory_| may only be accessed on |origin_task_runner_|.
-  // TODO(gab): It feels wrong that QuitWhenIdleClosure() is bound to a WeakPtr.
   return Bind(&ProxyToTaskRunner, origin_task_runner_,
               Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()));
 }
@@ -331,10 +320,10 @@
   if (quit_called_)
     return false;
 
-  auto& active_run_loops_ = delegate_->active_run_loops_;
-  active_run_loops_.push(this);
+  auto& active_run_loops = delegate_->active_run_loops_;
+  active_run_loops.push(this);
 
-  const bool is_nested = active_run_loops_.size() > 1;
+  const bool is_nested = active_run_loops.size() > 1;
 
   if (is_nested) {
     for (auto& observer : delegate_->nesting_observers_)
@@ -352,21 +341,19 @@
 
   running_ = false;
 
-  auto& active_run_loops_ = delegate_->active_run_loops_;
-  DCHECK_EQ(active_run_loops_.top(), this);
-  active_run_loops_.pop();
+  auto& active_run_loops = delegate_->active_run_loops_;
+  DCHECK_EQ(active_run_loops.top(), this);
+  active_run_loops.pop();
 
-  RunLoop* previous_run_loop =
-      active_run_loops_.empty() ? nullptr : active_run_loops_.top();
-
-  if (previous_run_loop) {
+  // Exiting a nested RunLoop?
+  if (!active_run_loops.empty()) {
     for (auto& observer : delegate_->nesting_observers_)
       observer.OnExitNestedRunLoop();
-  }
 
-  // Execute deferred Quit, if any:
-  if (previous_run_loop && previous_run_loop->quit_called_)
-    delegate_->Quit();
+    // Execute deferred Quit, if any:
+    if (active_run_loops.top()->quit_called_)
+      delegate_->Quit();
+  }
 }
 
 }  // namespace base
diff --git a/base/run_loop.h b/base/run_loop.h
index 597d5de..aa9a33af 100644
--- a/base/run_loop.h
+++ b/base/run_loop.h
@@ -101,7 +101,7 @@
   // Quit() quits an earlier call to Run() immediately. QuitWhenIdle() quits an
   // earlier call to Run() when there aren't any tasks or messages in the queue.
   //
-  // These methods are thread-safe but note that Quit() is best-effort when
+  // These methods are thread-safe but note that Quit() is asynchronous when
   // called from another thread (will quit soon but tasks that were already
   // queued on this RunLoop will get to run first).
   //
@@ -112,23 +112,35 @@
   // RunLoop has already finished running has no effect.
   //
   // WARNING: You must NEVER assume that a call to Quit() or QuitWhenIdle() will
-  // terminate the targetted message loop. If a nested RunLoop continues
+  // terminate the targeted message loop. If a nested RunLoop continues
   // running, the target may NEVER terminate. It is very easy to livelock (run
   // forever) in such a case.
   void Quit();
   void QuitWhenIdle();
 
-  // Convenience methods to get a closure that safely calls Quit() or
-  // QuitWhenIdle() (has no effect if the RunLoop instance is gone).
+  // Returns a Closure that safely calls Quit() or QuitWhenIdle() (has no effect
+  // if the RunLoop instance is gone).
   //
-  // The resulting Closure is thread-safe (note however that invoking the
-  // QuitClosure() from another thread than this RunLoop's will result in an
-  // asynchronous rather than immediate Quit()).
+  // These methods must be called from the thread on which the RunLoop was
+  // created.
+  //
+  // Returned Closures may be safely:
+  //   * Passed to other threads.
+  //   * Run() from other threads, though this will quit the RunLoop
+  //     asynchronously.
+  //   * Run() after the RunLoop has stopped or been destroyed, in which case
+  //     they are a no-op).
+  //   * Run() before RunLoop::Run(), in which case RunLoop::Run() returns
+  //     immediately."
   //
   // Example:
   //   RunLoop run_loop;
-  //   PostTask(run_loop.QuitClosure());
+  //   DoFooAsyncAndNotify(run_loop.QuitClosure());
   //   run_loop.Run();
+  //
+  // Note that Quit() itself is thread-safe and may be invoked directly if you
+  // have access to the RunLoop reference from another thread (e.g. from a
+  // capturing lambda or test observer).
   Closure QuitClosure();
   Closure QuitWhenIdleClosure();
 
@@ -339,10 +351,10 @@
   bool BeforeRun();
   void AfterRun();
 
-  // A copy of RunLoop::Delegate for the thread driven by tis RunLoop for quick
-  // access without using TLS (also allows access to state from another sequence
-  // during Run(), ref. |sequence_checker_| below).
-  Delegate* delegate_;
+  // A cached reference of RunLoop::Delegate for the thread driven by this
+  // RunLoop for quick access without using TLS (also allows access to state
+  // from another sequence during Run(), ref. |sequence_checker_| below).
+  Delegate* const delegate_;
 
   const Type type_;
 
diff --git a/base/synchronization/cancellation_flag.h b/base/synchronization/cancellation_flag.h
deleted file mode 100644
index 39094e2d..0000000
--- a/base/synchronization/cancellation_flag.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
-#define BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
-
-#include "base/synchronization/atomic_flag.h"
-
-namespace base {
-
-// Use inheritance instead of "using" to allow forward declaration of "class
-// CancellationFlag".
-// TODO(fdoray): Replace CancellationFlag with AtomicFlag throughout the
-// codebase and delete this file. crbug.com/630251
-class CancellationFlag : public AtomicFlag {};
-
-}  // namespace base
-
-#endif  // BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
diff --git a/base/task/promise/abstract_promise.cc b/base/task/promise/abstract_promise.cc
index 40c4373..71541ef 100644
--- a/base/task/promise/abstract_promise.cc
+++ b/base/task/promise/abstract_promise.cc
@@ -15,18 +15,24 @@
 
 AbstractPromise::~AbstractPromise() {
 #if DCHECK_IS_ON()
-  CheckedAutoLock lock(GetCheckedLock());
+  {
+    CheckedAutoLock lock(GetCheckedLock());
 
-  DCHECK(!must_catch_ancestor_that_could_reject_ ||
-         passed_catch_responsibility_)
-      << "Promise chain ending at " << from_here_.ToString()
-      << " didn't have a catch for potentially rejecting promise here "
-      << must_catch_ancestor_that_could_reject_->from_here().ToString();
+    DCHECK(!must_catch_ancestor_that_could_reject_ ||
+           passed_catch_responsibility_)
+        << "Promise chain ending at " << from_here_.ToString()
+        << " didn't have a catch for potentially rejecting promise here "
+        << must_catch_ancestor_that_could_reject_->from_here().ToString();
 
-  DCHECK(!this_must_catch_ || passed_catch_responsibility_)
-      << "Potentially rejecting promise at " << from_here_.ToString()
-      << " doesn't have a catch .";
+    DCHECK(!this_must_catch_ || passed_catch_responsibility_)
+        << "Potentially rejecting promise at " << from_here_.ToString()
+        << " doesn't have a catch.";
+  }
 #endif
+
+  // If we're not settled we might be retaining some promises which need to be
+  // released to prevent memory leaks. If we are settled this does nothing.
+  OnCanceled();
 }
 
 bool AbstractPromise::IsCanceled() const {
@@ -39,9 +45,10 @@
 
 const AbstractPromise* AbstractPromise::FindNonCurriedAncestor() const {
   const AbstractPromise* promise = this;
-  while (promise->IsResolvedWithPromise()) {
-    promise =
-        unique_any_cast<scoped_refptr<AbstractPromise>>(promise->value_).get();
+  while (
+      const scoped_refptr<AbstractPromise>* curried_promise =
+          unique_any_cast<scoped_refptr<AbstractPromise>>(&promise->value_)) {
+    promise = curried_promise->get();
   }
   return promise;
 }
@@ -55,18 +62,19 @@
   // although that'll be done lazily (only once they resolve/reject, so there
   // is a possibility the DCHECKs might be racy.
 
-  for (AdjacencyListNode& node : prerequisites_->prerequisite_list) {
-    node.dependent_node.dependent = this;
+  for (DependentList::Node& node : *prerequisites_->prerequisite_list()) {
+    node.dependent() = this;
 
-    // If |node.prerequisite| was canceled then early out because
+    // If |node.prerequisite()| was canceled then early out because
     // |prerequisites_->prerequisite_list| will have been cleared.
-    if (!node.prerequisite->InsertDependentOnAnyThread(&node.dependent_node))
+    DCHECK(node.prerequisite());
+    if (!node.prerequisite()->InsertDependentOnAnyThread(&node))
       break;
   }
 }
 
 bool AbstractPromise::InsertDependentOnAnyThread(DependentList::Node* node) {
-  scoped_refptr<AbstractPromise>& dependent = node->dependent;
+  scoped_refptr<AbstractPromise>& dependent = node->dependent();
 
   // Used to ensure no reference to the dependent is kept in case the Promise is
   // already settled.
@@ -75,7 +83,8 @@
 #if DCHECK_IS_ON()
   {
     CheckedAutoLock lock(GetCheckedLock());
-    node->dependent->MaybeInheritChecks(this);
+    DCHECK(node->dependent()) << from_here_.ToString();
+    node->dependent()->MaybeInheritChecks(this);
   }
 #endif
 
@@ -85,19 +94,37 @@
     case DependentList::InsertResult::SUCCESS:
       break;
 
-    case DependentList::InsertResult::FAIL_PROMISE_RESOLVED:
-      dependent_to_release = std::move(dependent);
-      dependent_to_release->OnPrerequisiteResolved(this);
+    case DependentList::InsertResult::FAIL_PROMISE_RESOLVED: {
+      AbstractPromise* curried_promise = GetCurriedPromise();
+      if (curried_promise) {
+        // Try and reinsert |node| in the curried ancestor.
+        node->SetPrerequisite(curried_promise);
+        return curried_promise->InsertDependentOnAnyThread(node);
+      } else {
+        dependent_to_release = std::move(dependent);
+        node->RetainSettledPrerequisite();
+        dependent_to_release->OnPrerequisiteResolved(this);
+      }
       break;
+    }
 
-    case DependentList::InsertResult::FAIL_PROMISE_REJECTED:
-      dependent_to_release = std::move(dependent);
-      dependent_to_release->OnPrerequisiteRejected(this);
+    case DependentList::InsertResult::FAIL_PROMISE_REJECTED: {
+      AbstractPromise* curried_promise = GetCurriedPromise();
+      if (curried_promise) {
+        // Try and reinsert |node| in the curried ancestor.
+        node->SetPrerequisite(curried_promise);
+        return curried_promise->InsertDependentOnAnyThread(node);
+      } else {
+        dependent_to_release = std::move(dependent);
+        node->RetainSettledPrerequisite();
+        dependent_to_release->OnPrerequisiteRejected(this);
+      }
       break;
+    }
 
     case DependentList::InsertResult::FAIL_PROMISE_CANCELED:
       dependent_to_release = std::move(dependent);
-      return dependent_to_release->OnPrerequisiteCancelled();
+      return dependent_to_release->OnPrerequisiteCancelled(this);
   }
 
   return true;
@@ -233,6 +260,15 @@
 
 #endif
 
+AbstractPromise* AbstractPromise::GetCurriedPromise() {
+  if (scoped_refptr<AbstractPromise>* curried_promise_refptr =
+          unique_any_cast<scoped_refptr<AbstractPromise>>(&value_)) {
+    return curried_promise_refptr->get();
+  } else {
+    return nullptr;
+  }
+}
+
 const AbstractPromise::Executor* AbstractPromise::GetExecutor() const {
   return base::unique_any_cast<Executor>(&value_);
 }
@@ -244,8 +280,8 @@
     // If there's no executor it's because the promise has already run. We
     // can't run again however. The only circumstance in which we expect
     // GetPrerequisitePolicy() to be called after execution is when it was
-    // resolved with a promise.
-    DCHECK(IsResolvedWithPromise());
+    // resolved with a promise or we're already settled.
+    DCHECK(IsSettled());
     return Executor::PrerequisitePolicy::kNever;
   }
   return executor->GetPrerequisitePolicy();
@@ -254,13 +290,15 @@
 AbstractPromise* AbstractPromise::GetFirstSettledPrerequisite() const {
   if (!prerequisites_)
     return nullptr;
-  return reinterpret_cast<AbstractPromise*>(
-      prerequisites_->first_settled_prerequisite.load(
-          std::memory_order_acquire));
+  return prerequisites_->GetFirstSettledPrerequisite();
 }
 
 void AbstractPromise::Execute() {
-  if (IsCanceled()) {
+  const Executor* executor = GetExecutor();
+  DCHECK(executor || dependents_.IsCanceled())
+      << from_here_.ToString() << " value_ contains " << value_.type();
+
+  if (!executor || executor->IsCancelled()) {
     OnCanceled();
     return;
   }
@@ -275,46 +313,31 @@
 #endif
 
   DCHECK(!IsResolvedWithPromise());
-  DCHECK(GetExecutor()) << from_here_.ToString() << " value_ contains "
-                        << value_.type();
 
   // This is likely to delete the executor.
   GetExecutor()->Execute(this);
 }
 
-bool AbstractPromise::DispatchIfNonCurriedRootSettled() {
-  AbstractPromise* curried_root = FindNonCurriedAncestor();
-  if (!curried_root->IsSettled())
-    return false;
-
-#if DCHECK_IS_ON()
-  {
-    CheckedAutoLock lock(GetCheckedLock());
-    MaybeInheritChecks(curried_root);
+void AbstractPromise::ReplaceCurriedPrerequisite(
+    AbstractPromise* curried_prerequisite,
+    AbstractPromise* replacement) {
+  DCHECK(curried_prerequisite->IsResolved() ||
+         curried_prerequisite->IsRejected());
+  DCHECK(curried_prerequisite->IsResolvedWithPromise());
+  DCHECK(replacement);
+  for (DependentList::Node& node : *prerequisites_->prerequisite_list()) {
+    if (node.prerequisite() == curried_prerequisite) {
+      node.Reset(replacement, this);
+      replacement->InsertDependentOnAnyThread(&node);
+      return;
+    }
   }
-#endif
-
-  if (curried_root->IsResolved()) {
-    OnResolveDispatchReadyDependents();
-  } else if (curried_root->IsRejected()) {
-    OnRejectDispatchReadyDependents();
-  } else {
-    DCHECK(curried_root->IsCanceled());
-    OnPrerequisiteCancelled();
-  }
-  return true;
+  NOTREACHED();
 }
 
 void AbstractPromise::OnPrerequisiteResolved(
     AbstractPromise* resolved_prerequisite) {
   DCHECK(resolved_prerequisite->IsResolved());
-  if (IsResolvedWithPromise()) {
-    // The executor has already run so we don't need to call
-    // MarkPrerequisiteAsSettling.
-    bool settled = DispatchIfNonCurriedRootSettled();
-    DCHECK(settled);
-    return;
-  }
 
   switch (GetPrerequisitePolicy()) {
     case Executor::PrerequisitePolicy::kAll:
@@ -341,13 +364,13 @@
   // it will reject as soon as any prerequisite rejects. Multiple prerequisites
   // can reject, but we wish to record only the first one. Also we can only
   // invoke executors once.
-  if (prerequisites_->MarkPrerequisiteAsSettling(rejected_prerequisite) &&
-      !DispatchIfNonCurriedRootSettled()) {
+  if (prerequisites_->MarkPrerequisiteAsSettling(rejected_prerequisite)) {
     DispatchPromise();
   }
 }
 
-bool AbstractPromise::OnPrerequisiteCancelled() {
+bool AbstractPromise::OnPrerequisiteCancelled(
+    AbstractPromise* canceled_prerequisite) {
   switch (GetPrerequisitePolicy()) {
     case Executor::PrerequisitePolicy::kAll:
       // PrerequisitePolicy::kAll should cancel immediately.
@@ -360,11 +383,13 @@
       if (prerequisites_->DecrementPrerequisiteCountAndCheckIfZero()) {
         OnCanceled();
         return false;
+      } else {
+        prerequisites_->RemoveCanceledPrerequisite(canceled_prerequisite);
       }
       return true;
 
     case Executor::PrerequisitePolicy::kNever:
-      // If we we where resolved with a promise then we can't have had
+      // If we where resolved with a promise then we can't have had
       // PrerequisitePolicy::kAny or PrerequisitePolicy::kNever before the
       // executor was replaced with the curried promise, so pass on
       // cancellation.
@@ -384,7 +409,7 @@
     void Visit(scoped_refptr<AbstractPromise> dependent) override {
       dependent->OnPrerequisiteResolved(resolved_prerequisite_);
     }
-    AbstractPromise* resolved_prerequisite_;
+    AbstractPromise* const resolved_prerequisite_;
   };
 
   Visitor visitor(this);
@@ -401,13 +426,57 @@
     void Visit(scoped_refptr<AbstractPromise> dependent) override {
       dependent->OnPrerequisiteRejected(rejected_prerequisite_);
     }
-    AbstractPromise* rejected_prerequisite_;
+    AbstractPromise* const rejected_prerequisite_;
   };
 
   Visitor visitor(this);
   dependents_.RejectAndConsumeAllDependents(&visitor);
 }
 
+void AbstractPromise::OnResolveMakeDependantsUseCurriedPrerequisite(
+    AbstractPromise* non_curried_root) {
+  class Visitor : public DependentList::Visitor {
+   public:
+    explicit Visitor(AbstractPromise* resolved_prerequisite,
+                     AbstractPromise* non_curried_root)
+        : resolved_prerequisite_(resolved_prerequisite),
+          non_curried_root_(non_curried_root) {}
+
+   private:
+    void Visit(scoped_refptr<AbstractPromise> dependent) override {
+      dependent->ReplaceCurriedPrerequisite(resolved_prerequisite_,
+                                            non_curried_root_);
+    }
+    AbstractPromise* const resolved_prerequisite_;
+    AbstractPromise* const non_curried_root_;
+  };
+
+  Visitor visitor(this, non_curried_root);
+  dependents_.ResolveAndConsumeAllDependents(&visitor);
+}
+
+void AbstractPromise::OnRejectMakeDependantsUseCurriedPrerequisite(
+    AbstractPromise* non_curried_root) {
+  class Visitor : public DependentList::Visitor {
+   public:
+    explicit Visitor(AbstractPromise* rejected_prerequisite,
+                     AbstractPromise* non_curried_root)
+        : rejected_prerequisite_(rejected_prerequisite),
+          non_curried_root_(non_curried_root) {}
+
+   private:
+    void Visit(scoped_refptr<AbstractPromise> dependent) override {
+      dependent->ReplaceCurriedPrerequisite(rejected_prerequisite_,
+                                            non_curried_root_);
+    }
+    AbstractPromise* const rejected_prerequisite_;
+    AbstractPromise* const non_curried_root_;
+  };
+
+  Visitor visitor(this, non_curried_root);
+  dependents_.RejectAndConsumeAllDependents(&visitor);
+}
+
 void AbstractPromise::DispatchPromise() {
   if (task_runner_) {
     task_runner_->PostPromiseInternal(this, TimeDelta());
@@ -418,13 +487,19 @@
 
 void AbstractPromise::OnCanceled() {
   class Visitor : public DependentList::Visitor {
+   public:
+    explicit Visitor(AbstractPromise* canceled_prerequisite)
+        : canceled_prerequisite_(canceled_prerequisite) {}
+
    private:
     void Visit(scoped_refptr<AbstractPromise> dependent) override {
-      dependent->OnPrerequisiteCancelled();
+      dependent->OnPrerequisiteCancelled(canceled_prerequisite_);
     }
+
+    AbstractPromise* const canceled_prerequisite_;
   };
 
-  Visitor visitor;
+  Visitor visitor(this);
   if (!dependents_.CancelAndConsumeAllDependents(&visitor))
     return;
 
@@ -439,23 +514,8 @@
   }
 #endif
 
-  // We need to release any AdjacencyListNodes we own to prevent memory leaks
-  // due to refcount cycles. We can't just clear |prerequisite_list| (which
-  // contains DependentList::Node) because in the case of multiple prerequisites
-  // they may not have all be settled, which means some will want to traverse
-  // their |dependent_list| which includes this promise. This is a problem
-  // because there isn't a conveniant way of removing ourself from their
-  // |dependent_list|. It's sufficient however to simply null our references.
-  if (prerequisites_) {
-    for (AdjacencyListNode& node : prerequisites_->prerequisite_list) {
-#if DCHECK_IS_ON()
-      // A settled prerequisite should not keep a reference to this.
-      if (node.prerequisite->IsSettled())
-        DCHECK(!node.dependent_node.dependent);
-#endif
-      node.prerequisite = nullptr;
-    }
-  }
+  if (prerequisites_)
+    prerequisites_->Clear();
 }
 
 void AbstractPromise::OnResolved() {
@@ -463,97 +523,95 @@
   DCHECK(executor_can_resolve_ || IsResolvedWithPromise())
       << from_here_.ToString();
 #endif
-  if (IsResolvedWithPromise()) {
-    scoped_refptr<AbstractPromise> curried_promise =
-        unique_any_cast<scoped_refptr<AbstractPromise>>(value_);
-
-    if (DispatchIfNonCurriedRootSettled()) {
-      prerequisites_->prerequisite_list.clear();
-    } else {
-      // The curried promise isn't already settled we need to throw away any
-      // existing dependencies and make |curried_promise| the only dependency of
-      // this promise.
+  if (AbstractPromise* curried_promise = GetCurriedPromise()) {
 #if DCHECK_IS_ON()
-      {
-        CheckedAutoLock lock(GetCheckedLock());
-        ancestor_that_could_resolve_ = nullptr;
-        ancestor_that_could_reject_ = nullptr;
-      }
-#endif
-      if (prerequisites_) {
-        prerequisites_->ResetWithSingleDependency(curried_promise);
-      } else {
-        prerequisites_ = std::make_unique<AdjacencyList>(curried_promise);
-      }
-      AddAsDependentForAllPrerequisites();
+    {
+      CheckedAutoLock lock(GetCheckedLock());
+      MaybeInheritChecks(curried_promise);
     }
+#endif
+
+    // If there are settled curried ancestors we can skip then do so.
+    while (curried_promise->IsSettled()) {
+      if (curried_promise->IsCanceled()) {
+        OnCanceled();
+        return;
+      }
+      const scoped_refptr<AbstractPromise>* curried_ancestor =
+          unique_any_cast<scoped_refptr<AbstractPromise>>(
+              &curried_promise->value_);
+      if (curried_ancestor) {
+        curried_promise = curried_ancestor->get();
+      } else {
+        break;
+      }
+    }
+
+    OnResolveMakeDependantsUseCurriedPrerequisite(curried_promise);
   } else {
     OnResolveDispatchReadyDependents();
-
-    // We need to release any AdjacencyListNodes we own to prevent memory leaks
-    // due to refcount cycles.
-    if (prerequisites_)
-      prerequisites_->prerequisite_list.clear();
   }
+
+  if (prerequisites_)
+    prerequisites_->Clear();
 }
 
 void AbstractPromise::OnRejected() {
-  // Rejection with a rejected promise doesn't need special handling.
-  DCHECK(!IsResolvedWithPromise() ||
-         unique_any_cast<scoped_refptr<AbstractPromise>>(value_)->IsRejected());
 #if DCHECK_IS_ON()
   DCHECK(executor_can_reject_) << from_here_.ToString();
 #endif
-  OnRejectDispatchReadyDependents();
 
-  // We need to release any AdjacencyListNodes we own to prevent memory leaks
-  // due to refcount cycles. We can't just clear |prerequisite_list| (which
-  // contains DependentList::Node) because in the case of multiple prerequisites
-  // they may not have all be settled, which means some will want to traverse
-  // their |dependent_list| which includes this promise. This is a problem
-  // because there isn't a conveniant way of removing ourself from their
-  // |dependent_list|. It's sufficient however to simply null our references.
-  if (prerequisites_) {
-    for (AdjacencyListNode& node : prerequisites_->prerequisite_list) {
+  if (AbstractPromise* curried_promise = GetCurriedPromise()) {
 #if DCHECK_IS_ON()
-      // A settled prerequisite should not keep a reference to this.
-      if (node.prerequisite->IsSettled())
-        DCHECK(!node.dependent_node.dependent);
-#endif
-      node.prerequisite = nullptr;
+    {
+      CheckedAutoLock lock(GetCheckedLock());
+      MaybeInheritChecks(curried_promise);
     }
+#endif
+
+    // If there are settled curried ancestors we can skip then do so.
+    while (curried_promise->IsSettled()) {
+      if (curried_promise->IsCanceled()) {
+        OnCanceled();
+        return;
+      }
+      const scoped_refptr<AbstractPromise>* curried_ancestor =
+          unique_any_cast<scoped_refptr<AbstractPromise>>(
+              &curried_promise->value_);
+      if (curried_ancestor) {
+        curried_promise = curried_ancestor->get();
+      } else {
+        break;
+      }
+    }
+
+    OnRejectMakeDependantsUseCurriedPrerequisite(curried_promise);
+  } else {
+    OnRejectDispatchReadyDependents();
   }
+
+  if (prerequisites_)
+    prerequisites_->Clear();
 }
 
-AbstractPromise::AdjacencyListNode::AdjacencyListNode() = default;
-
-AbstractPromise::AdjacencyListNode::AdjacencyListNode(
-    scoped_refptr<AbstractPromise> promise)
-    : prerequisite(std::move(promise)) {}
-
-AbstractPromise::AdjacencyListNode::~AdjacencyListNode() = default;
-
-AbstractPromise::AdjacencyListNode::AdjacencyListNode(
-    AdjacencyListNode&& other) noexcept = default;
-
 AbstractPromise::AdjacencyList::AdjacencyList() = default;
 
-AbstractPromise::AdjacencyList::AdjacencyList(
-    scoped_refptr<AbstractPromise> prerequisite)
-    : prerequisite_list(1), action_prerequisite_count(1) {
-  prerequisite_list[0].prerequisite = std::move(prerequisite);
+AbstractPromise::AdjacencyList::AdjacencyList(AbstractPromise* prerequisite)
+    : prerequisite_list_(1), action_prerequisite_count_(1) {
+  prerequisite_list_[0].SetPrerequisite(prerequisite);
 }
 
 AbstractPromise::AdjacencyList::AdjacencyList(
-    std::vector<AdjacencyListNode> nodes)
-    : prerequisite_list(std::move(nodes)),
-      action_prerequisite_count(prerequisite_list.size()) {}
+    std::vector<DependentList::Node> nodes)
+    : prerequisite_list_(std::move(nodes)),
+      action_prerequisite_count_(prerequisite_list_.size()) {}
 
 AbstractPromise::AdjacencyList::~AdjacencyList() = default;
 
 bool AbstractPromise::AdjacencyList::
     DecrementPrerequisiteCountAndCheckIfZero() {
-  return action_prerequisite_count.fetch_sub(1, std::memory_order_acq_rel) == 1;
+  return action_prerequisite_count_.fetch_sub(1, std::memory_order_acq_rel) ==
+         1;
 }
 
 // For PrerequisitePolicy::kAll this is called for the first rejected
@@ -563,16 +621,38 @@
     AbstractPromise* settled_prerequisite) {
   DCHECK(settled_prerequisite->IsSettled());
   uintptr_t expected = 0;
-  return first_settled_prerequisite.compare_exchange_strong(
+  return first_settled_prerequisite_.compare_exchange_strong(
       expected, reinterpret_cast<uintptr_t>(settled_prerequisite),
       std::memory_order_acq_rel);
 }
 
-void AbstractPromise::AdjacencyList::ResetWithSingleDependency(
-    scoped_refptr<AbstractPromise> prerequisite) {
-  prerequisite_list.clear();
-  prerequisite_list.push_back(AdjacencyListNode{std::move(prerequisite)});
-  action_prerequisite_count = 1;
+void AbstractPromise::AdjacencyList::RemoveCanceledPrerequisite(
+    AbstractPromise* canceled_prerequisite) {
+  DCHECK(canceled_prerequisite->IsCanceled());
+  for (DependentList::Node& node : prerequisite_list_) {
+    if (node.prerequisite() == canceled_prerequisite) {
+      node.ClearPrerequisite();
+      return;
+    }
+  }
+  NOTREACHED() << "Couldn't find canceled_prerequisite "
+               << canceled_prerequisite->from_here().ToString();
+}
+
+void AbstractPromise::AdjacencyList::Clear() {
+  // If there's only one prerequisite we can just clear |prerequisite_list_|
+  // which deals with potential refcounting cycles due to curried promises.
+  if (prerequisite_list_.size() == 1) {
+    prerequisite_list_.clear();
+  } else {
+    // If there's multiple prerequisites we can't do that because the
+    // DependentList::Nodes may still be in use by some of them. Instead we
+    // release our prerequisite references and rely on refcounting to release
+    // the owning AbstractPromise.
+    for (DependentList::Node& node : prerequisite_list_) {
+      node.ClearPrerequisite();
+    }
+  }
 }
 
 AbstractPromise::Executor::~Executor() {
diff --git a/base/task/promise/abstract_promise.h b/base/task/promise/abstract_promise.h
index 45597ed..9d56465 100644
--- a/base/task/promise/abstract_promise.h
+++ b/base/task/promise/abstract_promise.h
@@ -19,6 +19,236 @@
 namespace base {
 class TaskRunner;
 
+// AbstractPromise Memory Management.
+//
+// Consider a chain of promises: P1, P2 & P3
+//
+// Before resolve:
+// * P1 needs an external reference (such as a Promise<> handle or it has been
+//   posted) to keep it alive
+// * P2 is kept alive by P1
+// * P3 is kept alive by P2
+//
+//                                      ------------
+//  Key:                               |     P1     | P1 doesn't have an
+//  ═ Denotes a reference is held      |            | AdjacencyList
+//  ─ Denotes a raw pointer            | Dependants |
+//                                      -----|------
+//                                           |    ^
+//                          ╔════════════╗   |    |
+//                          ↓            ║   ↓    |
+//                    ------------    ---║--------|--
+//                   |     P2     |  | dependent_ |  |
+//                   |            |==|            |  | P2's AdjacencyList
+//                   | Dependants |  | prerequisite_ |
+//                    -----|------    ---------------
+//                         |    ^
+//        ╔════════════╗   |    |
+//        ↓            ║   ↓    |
+//  ------------    ---║--------|--
+// |     P3     |  | dependent_ |  |
+// |            |==|            |  | P3's AdjacencyList
+// | Dependants |  | prerequisite_ |
+//  ---|--------    ---------------
+//     |
+//     ↓
+//    null
+//
+//
+// After P1's executor runs, P2's |prerequisite_| link is upgraded by
+// OnResolveDispatchReadyDependents (which incirectly calls
+// RetainSettledPrerequisite) from a raw pointer to a reference. This is done to
+// ensure P1's |value_| is available when P2's executor runs.
+//
+//                                      ------------
+//                                     |     P1     | P1 doesn't have an
+//                                     |            | AdjacencyList
+//                                     | Dependants |
+//                                      -----|------
+//                                           |    ^
+//                          ╔════════════╗   |    ║
+//                          ↓            ║   ↓    ║
+//                    ------------    ---║--------║--
+//                   |     P2     |  | dependent_ ║  |
+//                   |            |==|            ║  | P2's AdjacencyList
+//                   | Dependants |  | prerequisite_ |
+//                    -----|------    ---------------
+//                         |    ^
+//        ╔════════════╗   |    |
+//        ↓            ║   ↓    |
+//  ------------    ---║--------|--
+// |     P3     |  | dependent_ |  |
+// |            |==|            |  | P3's AdjacencyList
+// | Dependants |  | prerequisite_ |
+//  ---|--------    ---------------
+//     |
+//     ↓
+//    null
+//
+//
+// After P2's executor runs, it's AdjacencyList is cleared. Unless there's
+// external references, at this stage P1 will be deleted. P3's |prerequisite_|
+// is from a raw pointer to a reference to ensure P2's |value_| is available
+// when P3's executor runs.
+//
+//                                      ------------
+//                                     |     P1     | P1 doesn't have an
+//                                     |            | AdjacencyList
+//                                     | Dependants |
+//                                      -----|------
+//                                           |
+//                                     null  |  null
+//                                       ^   ↓    ^
+//                    ------------    ---|--------|--
+//                   |     P2     |  | dependent_ |  |
+//                   |            |==|            |  | P2's AdjacencyList
+//                   | Dependants |  | prerequisite_ |
+//                    -----|------    ---------------
+//                         |    ^
+//        ╔════════════╗   |    ║
+//        ↓            ║   ↓    ║
+//  ------------    ---║--------║--
+// |     P3     |  | dependent_ ║  |
+// |            |==|            ║  | P3's AdjacencyList
+// | Dependants |  | prerequisite_ |
+//  ---|--------    ---------------
+//     |
+//     ↓
+//    null
+//
+// =============================================================================
+// Consider a promise P1 that is resolved with an unresolved promise P2, and P3
+// which depends on P1.
+//
+// 1) Initially P1 doesn't have an AdjacencyList and must be kept alive by an
+//    external reference.  P1 keeps P3 alive.
+//
+// 2) P1's executor resolves with P2 and P3 is modified to have P2 as a
+//    dependent instead of P1. P1 has a reference to P2, but it needs an
+//    external reference to keep alive.
+//
+// 3) When P2's executor runs, P3's executor is scheduled and P3's
+//    |prerequisite_| link to P2 is upgraded to a reference. So P3 keeps P2
+//    alive.
+//
+// 4) When P3's executor runs, its AdjacencyList is cleared. At this stage
+//    unless there are external referecnes P2 and P3 will be deleted.
+//
+//
+// 1.                   --------------
+//                     | P1  value_   |
+//                     |              | P1 doesn't have an AdjacencyList
+//                     | Dependants   |
+//                      ---|----------
+//                         |        ^
+//                         ↓        ║
+//      ------------    ------------║---
+//     | P3         |  | dependent_ ║   |
+//     |            |==|            ║   | P3's AdjacencyList
+//     | Dependants |  | prerequisite_  |
+//      ---|--------     ---------------
+//         |
+//         ↓
+//        null
+//
+// 2.                                  ------------
+//                                    |     P2     |
+//                            ╔══════>|            | P2 doesn't have an
+//                            ║       | Dependants | AdjacencyList
+//                            ║        -----|------
+//                            ║             |  ^
+//                      ------║-------      |  |
+//                     | P1  value_   |     |  |
+//                     |              |     |  |
+//                     | Dependants   |     |  |
+//                      --------------      |  |
+//               ╔═════════╗   ┌────────────┘  |
+//               ↓         ║   ↓    ┌──────────┘
+//      ------------    ---║--------|---
+//     | P3         |  | dependent_ |   |
+//     |            |==|            |   | P3's AdjacencyList
+//     | Dependants |  | prerequisite_  |
+//      ---|--------     ---------------
+//         |
+//         ↓
+//        null
+// 3.                                  ------------
+//                                    |     P2     |
+//                                    |            | P2 doesn't have an
+//                                    | Dependants | AdjacencyList
+//                                     -----|------
+//                                          |  ^
+//                                          |  ║
+//                                          |  ║
+//                                          |  ║
+//                                          |  ║
+//                                          |  ║
+//               ╔═════════╗   ┌────────────┘  ║
+//               ↓         ║   ↓    ╔══════════╝
+//      ------------    ---║--------║---
+//     | P3         |  | dependent_ ║   |
+//     |            |==|            ║   | P3's AdjacencyList
+//     | Dependants |  | prerequisite_  |
+//      ---|--------     ---------------
+//         |
+//         ↓
+//        null
+//
+//
+// 4.                                  ------------
+//                                    |     P2     |
+//                                    |            | P2 doesn't have an
+//                                    | Dependants | AdjacencyList
+//                                     ------------
+//
+//
+//
+//
+//
+//
+//
+//
+//      ------------
+//     | P3         |  P3 doesn't have an AdjacencyList anymore.
+//     |            |
+//     | Dependants |
+//      ---|--------
+//         |
+//         ↓
+//        null
+//
+// =============================================================================
+// Consider an all promise Pall with dependents P1, P2 & P3:
+//
+// Before resolve P1, P2 & P3 keep Pall alive. If say P2 rejects then Pall
+// keeps P2 alive, however all the dependents in Pall's AdjacencyList are
+// cleared. When there are no external references to P1, P2 & P3 then Pall
+// will get deleted too if it has no external references.
+//
+//                                Pall's AdjacencyList
+//  ------------             ----------------
+// |     P1     |           |                |
+// |            | <─────────── prerequisite_ |
+// | Dependants────────────>|  dependent_══════════════════╗
+//  ------------            |                |             ↓
+//                          |----------------|         ---------
+//  ------------            |                |        |         |
+// |     P2     | <─────────── prerequisite_ |        |  Pall   |
+// |            |           |  dependent_════════════>|         |
+// | Dependants────────────>|                |        |         |
+//  ------------            |                |         ---------
+//                          |----------------|             ^
+//  ------------            |                |             ║
+// |     P3     | <─────────── prerequisite_ |             ║
+// |            |           |  dependent_══════════════════╝
+// | Dependants────────────>|                |
+//  ------------             ----------------
+//
+//
+// In general a promise's AdjacencyList's only retains prerequisites after the
+// promise has resolved. It is necessary to retain the prerequisites because a
+// ThenOn or CatchOn can be added after the promise has resolved.
+
 // std::variant, std::tuple and other templates can't contain void but they can
 // contain the empty type Void. This is the same idea as std::monospace.
 struct Void {};
@@ -107,7 +337,7 @@
 class BASE_EXPORT AbstractPromise
     : public RefCountedThreadSafe<AbstractPromise> {
  public:
-  struct AdjacencyList;
+  class AdjacencyList;
 
   template <typename ConstructType, typename DerivedExecutorType>
   struct ConstructWith {};
@@ -139,14 +369,22 @@
 
   const Location& from_here() const { return from_here_; }
 
+  bool IsSettled() const { return dependents_.IsSettled(); }
   bool IsCanceled() const;
+
+  // It's an error (result will be racy) to call these if unsettled.
   bool IsRejected() const { return dependents_.IsRejected(); }
   bool IsResolved() const { return dependents_.IsResolved(); }
-  bool IsSettled() const { return dependents_.IsSettled(); }
+
+  bool IsRejectedForTesting() const {
+    return dependents_.IsRejectedForTesting();
+  }
+  bool IsResolvedForTesting() const {
+    return dependents_.IsResolvedForTesting();
+  }
 
   bool IsResolvedWithPromise() const {
-    return value_.type() ==
-           TypeId::From<scoped_refptr<internal::AbstractPromise>>();
+    return value_.type() == TypeId::From<scoped_refptr<AbstractPromise>>();
   }
 
   const unique_any& value() const { return FindNonCurriedAncestor()->value_; }
@@ -180,18 +418,25 @@
         const_cast<const AbstractPromise*>(this)->FindNonCurriedAncestor());
   }
 
+  // Returns nullptr if there isn't a curried promise.
+  const AbstractPromise* GetCurriedPromise() const;
+
   // Sets the |value_| to |t|. The caller should call OnResolved() or
   // OnRejected() afterwards.
   template <typename T>
   void emplace(T&& t) {
     DCHECK(GetExecutor() != nullptr) << "Only valid to emplace once";
     value_ = std::forward<T>(t);
+    static_assert(!std::is_same<std::decay_t<T>, AbstractPromise*>::value,
+                  "Use scoped_refptr<AbstractPromise> instead");
   }
 
   template <typename T, typename... Args>
   void emplace(in_place_type_t<T>, Args&&... args) {
     DCHECK(GetExecutor() != nullptr) << "Only valid to emplace once";
     value_.emplace<T>(std::forward<Args>(args)...);
+    static_assert(!std::is_same<std::decay_t<T>, AbstractPromise*>::value,
+                  "Use scoped_refptr<AbstractPromise> instead");
   }
 
   // Unresolved promises have an executor which invokes one of the callbacks
@@ -368,25 +613,15 @@
   // scheduled for execution.
   void OnRejected();
 
-  struct BASE_EXPORT AdjacencyListNode {
-    AdjacencyListNode();
-    explicit AdjacencyListNode(scoped_refptr<AbstractPromise> prerequisite);
-    explicit AdjacencyListNode(AdjacencyListNode&& other) noexcept;
-    ~AdjacencyListNode();
-
-    scoped_refptr<AbstractPromise> prerequisite;
-    DependentList::Node dependent_node;
-  };
-
   // This is separate from AbstractPromise to reduce the memory footprint of
   // regular PostTask without promise chains.
-  struct BASE_EXPORT AdjacencyList {
+  class BASE_EXPORT AdjacencyList {
+   public:
     AdjacencyList();
-    explicit AdjacencyList(scoped_refptr<AbstractPromise> prerequisite);
-    explicit AdjacencyList(std::vector<AdjacencyListNode> prerequisite_list);
     ~AdjacencyList();
 
-    void ResetWithSingleDependency(scoped_refptr<AbstractPromise> prerequisite);
+    explicit AdjacencyList(AbstractPromise* prerequisite);
+    explicit AdjacencyList(std::vector<DependentList::Node> prerequisite_list);
 
     bool DecrementPrerequisiteCountAndCheckIfZero();
 
@@ -396,32 +631,52 @@
     // true iff called for the first time.
     bool MarkPrerequisiteAsSettling(AbstractPromise* settled_prerequisite);
 
-    std::vector<AdjacencyListNode> prerequisite_list;
+    // Invoked when this promise is notified that |canceled_prerequisite| is
+    // cancelled. Clears the reference to |canceled_prerequisite| in this
+    // AdjacencyList to ensure the it is not accessed later when Clear() is
+    // called.
+    void RemoveCanceledPrerequisite(AbstractPromise* canceled_prerequisite);
+
+    std::vector<DependentList::Node>* prerequisite_list() {
+      return &prerequisite_list_;
+    }
+
+    AbstractPromise* GetFirstSettledPrerequisite() const {
+      return reinterpret_cast<AbstractPromise*>(
+          first_settled_prerequisite_.load(std::memory_order_acquire));
+    }
+
+    void Clear();
+
+   private:
+    std::vector<DependentList::Node> prerequisite_list_;
 
     // PrerequisitePolicy::kAny waits for at most 1 resolve or N cancellations.
     // PrerequisitePolicy::kAll waits for N resolves or at most 1 cancellation.
     // PrerequisitePolicy::kNever doesn't use this.
-    std::atomic_int action_prerequisite_count;
+    std::atomic_int action_prerequisite_count_;
 
     // For PrerequisitePolicy::kAll the address of the first rejected
     // prerequisite if any.
     // For PrerequisitePolicy::kAll the address of the first rejected or
     // resolved rerequsite if any.
-    std::atomic<uintptr_t> first_settled_prerequisite{0};
+    std::atomic<uintptr_t> first_settled_prerequisite_{0};
   };
 
-  const std::vector<AdjacencyListNode>* prerequisite_list() const {
+  const std::vector<DependentList::Node>* prerequisite_list() const {
     if (!prerequisites_)
       return nullptr;
-    return &prerequisites_->prerequisite_list;
+    return prerequisites_->prerequisite_list();
   }
 
   // Returns the first and only prerequisite AbstractPromise.  It's an error to
   // call this if the number of prerequisites isn't exactly one.
   AbstractPromise* GetOnlyPrerequisite() const {
     DCHECK(prerequisites_);
-    DCHECK_EQ(prerequisites_->prerequisite_list.size(), 1u);
-    return prerequisites_->prerequisite_list[0].prerequisite.get();
+    const std::vector<DependentList::Node>* prerequisite_list =
+        prerequisites_->prerequisite_list();
+    DCHECK_EQ(prerequisite_list->size(), 1u);
+    return (*prerequisite_list)[0].prerequisite();
   }
 
   // For PrerequisitePolicy::kAll returns the first rejected prerequisite if
@@ -484,6 +739,9 @@
 
   NOINLINE ~AbstractPromise();
 
+  // Returns the curried promise if there is one or null otherwise.
+  AbstractPromise* GetCurriedPromise();
+
   // Returns the associated Executor if there is one.
   const Executor* GetExecutor() const;
 
@@ -514,7 +772,7 @@
 
   // Returns true if we are still potentially eligible to run despite the
   // cancellation.
-  bool OnPrerequisiteCancelled();
+  bool OnPrerequisiteCancelled(AbstractPromise* canceled_prerequisite);
 
   // This promise was resolved, post any dependent promises that are now ready
   // as a result.
@@ -524,6 +782,16 @@
   // as a result.
   void OnRejectDispatchReadyDependents();
 
+  // This promise was resolved with a curried promise, make any dependent
+  // promises depend on |non_curried_root| instead.
+  void OnResolveMakeDependantsUseCurriedPrerequisite(
+      AbstractPromise* non_curried_root);
+
+  // This promise was rejected with a curried promise, make any dependent
+  // promises depend on |non_curried_root| instead.
+  void OnRejectMakeDependantsUseCurriedPrerequisite(
+      AbstractPromise* non_curried_root);
+
   void DispatchPromise();
 
   // Reverses |list| so dependents can be dispatched in the order they where
@@ -531,9 +799,8 @@
   static DependentList::Node* NonThreadSafeReverseList(
       DependentList::Node* list);
 
-  // Finds the non-curried root, and if settled ready dependents are posted.
-  // Returns true if the non-curried root was settled.
-  bool DispatchIfNonCurriedRootSettled();
+  void ReplaceCurriedPrerequisite(AbstractPromise* curried_prerequisite,
+                                  AbstractPromise* replacement);
 
   scoped_refptr<TaskRunner> task_runner_;
 
@@ -545,6 +812,18 @@
   // * Rejected<T>
   // * scoped_refptr<AbstractPromise> (for curried promises - i.e. a promise
   //   which is resolved with a promise).
+  //
+  // The state transitions which occur during Execute() (which is once only) are
+  // like so:
+  //
+  //      ┌────────── Executor ─────────┐
+  //      |               |             │
+  //      |               |             │
+  //      ↓               |             ↓
+  // Resolved<T>          |        Rejected<T>
+  //                      ↓
+  //        scoped_refptr<AbstractPromise>
+  //
   unique_any value_;
 
 #if DCHECK_IS_ON()
diff --git a/base/task/promise/abstract_promise_unittest.cc b/base/task/promise/abstract_promise_unittest.cc
index dcd00ee..e48caa22 100644
--- a/base/task/promise/abstract_promise_unittest.cc
+++ b/base/task/promise/abstract_promise_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/task/promise/abstract_promise.h"
 
+#include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/do_nothing_promise.h"
 #include "base/test/gtest_util.h"
@@ -14,76 +15,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// TODO(crbug.com/968302): Fix memory leaks in tests and re-enable on LSAN.
-#ifdef LEAK_SANITIZER
-#define MAYBE_DetectCatchCallbackDoubleMoveHazardInChainIntermediateThensCanReject \
-  DISABLED_DetectCatchCallbackDoubleMoveHazardInChainIntermediateThensCanReject
-#define MAYBE_CancelationPrerequisitePolicyALL \
-  DISABLED_CancelationPrerequisitePolicyALL
-#define MAYBE_MultipleRejectPrerequisitePolicyALL \
-  DISABLED_MultipleRejectPrerequisitePolicyALL
-#define MAYBE_DoubleMoveDoNothingPromise DISABLED_DoubleMoveDoNothingPromise
-#define MAYBE_SomeAlreadyCanceledPrerequisitePolicyANY \
-  DISABLED_SomeAlreadyCanceledPrerequisitePolicyANY
-#define MAYBE_AlreadyCanceledPrerequisitePolicyALL \
-  DISABLED_AlreadyCanceledPrerequisitePolicyALL
-#define MAYBE_MultipleNonMoveCatchCallbacksAreOK \
-  DISABLED_MultipleNonMoveCatchCallbacksAreOK
-#define MAYBE_MutipleThreadsAddingDependants \
-  DISABLED_MutipleThreadsAddingDependants
-#define MAYBE_DetectCatchCallbackDoubleMoveHazard \
-  DISABLED_DetectCatchCallbackDoubleMoveHazard
-#define MAYBE_AllAlreadyCanceledPrerequisitePolicyANY \
-  DISABLED_AllAlreadyCanceledPrerequisitePolicyANY
-#define MAYBE_DetectMixedCatchCallbackMoveAndNonMoveHazard \
-  DISABLED_DetectMixedCatchCallbackMoveAndNonMoveHazard
-#define MAYBE_DetectCatchCallbackDoubleMoveHazardInChain \
-  DISABLED_DetectCatchCallbackDoubleMoveHazardInChain
-#define MAYBE_SingleRejectPrerequisitePolicyANY \
-  DISABLED_SingleRejectPrerequisitePolicyANY
-#define MAYBE_SingleRejectPrerequisitePolicyALL \
-  DISABLED_SingleRejectPrerequisitePolicyALL
-#define MAYBE_DetectResolveDoubleMoveHazard \
-  DISABLED_DetectResolveDoubleMoveHazard
-#define MAYBE_MoveAtEndOfChain DISABLED_MoveAtEndOfChain
-#define MAYBE_DetectMixedResolveCallbackMoveAndNonMoveHazard \
-  DISABLED_DetectMixedResolveCallbackMoveAndNonMoveHazard
-#define MAYBE_DetectThenCallbackDoubleMoveHazardInChain \
-  DISABLED_DetectThenCallbackDoubleMoveHazardInChain
-#else
-#define MAYBE_DetectCatchCallbackDoubleMoveHazardInChainIntermediateThensCanReject \
-  DetectCatchCallbackDoubleMoveHazardInChainIntermediateThensCanReject
-#define MAYBE_CancelationPrerequisitePolicyALL CancelationPrerequisitePolicyALL
-#define MAYBE_MultipleRejectPrerequisitePolicyALL \
-  MultipleRejectPrerequisitePolicyALL
-#define MAYBE_DoubleMoveDoNothingPromise DoubleMoveDoNothingPromise
-#define MAYBE_SomeAlreadyCanceledPrerequisitePolicyANY \
-  SomeAlreadyCanceledPrerequisitePolicyANY
-#define MAYBE_AlreadyCanceledPrerequisitePolicyALL \
-  AlreadyCanceledPrerequisitePolicyALL
-#define MAYBE_MultipleNonMoveCatchCallbacksAreOK \
-  MultipleNonMoveCatchCallbacksAreOK
-#define MAYBE_MutipleThreadsAddingDependants MutipleThreadsAddingDependants
-#define MAYBE_DetectCatchCallbackDoubleMoveHazard \
-  DetectCatchCallbackDoubleMoveHazard
-#define MAYBE_AllAlreadyCanceledPrerequisitePolicyANY \
-  AllAlreadyCanceledPrerequisitePolicyANY
-#define MAYBE_DetectMixedCatchCallbackMoveAndNonMoveHazard \
-  DetectMixedCatchCallbackMoveAndNonMoveHazard
-#define MAYBE_DetectCatchCallbackDoubleMoveHazardInChain \
-  DetectCatchCallbackDoubleMoveHazardInChain
-#define MAYBE_SingleRejectPrerequisitePolicyANY \
-  SingleRejectPrerequisitePolicyANY
-#define MAYBE_SingleRejectPrerequisitePolicyALL \
-  SingleRejectPrerequisitePolicyALL
-#define MAYBE_DetectResolveDoubleMoveHazard DetectResolveDoubleMoveHazard
-#define MAYBE_MoveAtEndOfChain MoveAtEndOfChain
-#define MAYBE_DetectMixedResolveCallbackMoveAndNonMoveHazard \
-  DetectMixedResolveCallbackMoveAndNonMoveHazard
-#define MAYBE_DetectThenCallbackDoubleMoveHazardInChain \
-  DetectThenCallbackDoubleMoveHazardInChain
-#endif
-
 // Even trivial DCHECK_DEATH_TESTs like
 // AbstractPromiseTest.CantRejectIfpromiseDeclaredAsNonRejecting can flakily
 // timeout on the chromeos bots.
@@ -288,9 +219,9 @@
   PromiseSettingsBuilder ThenPromise(Location from_here,
                                      scoped_refptr<AbstractPromise> parent) {
     PromiseSettingsBuilder builder(
-        from_here, parent ? std::make_unique<AbstractPromise::AdjacencyList>(
-                                std::move(parent))
-                          : std::make_unique<AbstractPromise::AdjacencyList>());
+        from_here,
+        parent ? std::make_unique<AbstractPromise::AdjacencyList>(parent.get())
+               : std::make_unique<AbstractPromise::AdjacencyList>());
     builder.With(BindOnce([](AbstractPromise* p) {
       AbstractPromise* prerequisite = p->GetOnlyPrerequisite();
       if (prerequisite->IsResolved()) {
@@ -300,6 +231,8 @@
         // Consistent with BaseThenAndCatchExecutor::ProcessNullExecutor.
         p->emplace(scoped_refptr<AbstractPromise>(prerequisite));
         p->OnResolved();
+      } else {
+        NOTREACHED();
       }
     }));
     return builder;
@@ -308,9 +241,9 @@
   PromiseSettingsBuilder CatchPromise(Location from_here,
                                       scoped_refptr<AbstractPromise> parent) {
     PromiseSettingsBuilder builder(
-        from_here, parent ? std::make_unique<AbstractPromise::AdjacencyList>(
-                                std::move(parent))
-                          : std::make_unique<AbstractPromise::AdjacencyList>());
+        from_here,
+        parent ? std::make_unique<AbstractPromise::AdjacencyList>(parent.get())
+               : std::make_unique<AbstractPromise::AdjacencyList>());
     builder.With(CallbackResultType::kNoCallback)
         .With(CallbackResultType::kCanResolve)
         .WithResolve(ArgumentPassingType::kNoCallback)
@@ -324,6 +257,8 @@
           } else if (prerequisite->IsRejected()) {
             p->emplace(Resolved<void>());
             p->OnResolved();
+          } else {
+            NOTREACHED();
           }
         }));
     return builder;
@@ -331,22 +266,19 @@
 
   PromiseSettingsBuilder AllPromise(
       Location from_here,
-      std::vector<internal::AbstractPromise::AdjacencyListNode>
-          prerequisite_list) {
+      std::vector<internal::DependentList::Node> prerequisite_list) {
     PromiseSettingsBuilder builder(
         from_here, std::make_unique<AbstractPromise::AdjacencyList>(
                        std::move(prerequisite_list)));
     builder.With(PrerequisitePolicy::kAll)
         .With(BindOnce([](AbstractPromise* p) {
-          // Reject if any prerequisites rejected.
-          for (const AbstractPromise::AdjacencyListNode& node :
-               *p->prerequisite_list()) {
-            if (node.prerequisite->IsRejected()) {
-              p->emplace(Rejected<void>());
-              p->OnRejected();
-              return;
-            }
+          AbstractPromise* first_settled = p->GetFirstSettledPrerequisite();
+          if (first_settled && first_settled->IsRejected()) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+            return;
           }
+
           p->emplace(Resolved<void>());
           p->OnResolved();
         }));
@@ -355,22 +287,19 @@
 
   PromiseSettingsBuilder AnyPromise(
       Location from_here,
-      std::vector<internal::AbstractPromise::AdjacencyListNode>
-          prerequisite_list) {
+      std::vector<internal::DependentList::Node> prerequisite_list) {
     PromiseSettingsBuilder builder(
         from_here, std::make_unique<AbstractPromise::AdjacencyList>(
                        std::move(prerequisite_list)));
     builder.With(PrerequisitePolicy::kAny)
         .With(BindOnce([](AbstractPromise* p) {
-          // Reject if any prerequisites rejected.
-          for (const AbstractPromise::AdjacencyListNode& node :
-               *p->prerequisite_list()) {
-            if (node.prerequisite->IsRejected()) {
-              p->emplace(Rejected<void>());
-              p->OnRejected();
-              return;
-            }
+          AbstractPromise* first_settled = p->GetFirstSettledPrerequisite();
+          if (first_settled && first_settled->IsRejected()) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+            return;
           }
+
           p->emplace(Resolved<void>());
           p->OnResolved();
         }));
@@ -383,26 +312,26 @@
 TEST_F(AbstractPromiseTest, UnfulfilledPromise) {
   scoped_refptr<AbstractPromise> promise =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
-  EXPECT_FALSE(promise->IsResolved());
-  EXPECT_FALSE(promise->IsRejected());
+  EXPECT_FALSE(promise->IsResolvedForTesting());
+  EXPECT_FALSE(promise->IsRejectedForTesting());
   EXPECT_FALSE(promise->IsCanceled());
 }
 
 TEST_F(AbstractPromiseTest, OnResolve) {
   scoped_refptr<AbstractPromise> promise =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
-  EXPECT_FALSE(promise->IsResolved());
+  EXPECT_FALSE(promise->IsResolvedForTesting());
   promise->OnResolved();
-  EXPECT_TRUE(promise->IsResolved());
+  EXPECT_TRUE(promise->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, OnReject) {
   scoped_refptr<AbstractPromise> promise =
       DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetRejectPolicy(
           RejectPolicy::kCatchNotRequired);
-  EXPECT_FALSE(promise->IsRejected());
+  EXPECT_FALSE(promise->IsRejectedForTesting());
   promise->OnRejected();
-  EXPECT_TRUE(promise->IsRejected());
+  EXPECT_TRUE(promise->IsRejectedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, ExecuteOnResolve) {
@@ -412,9 +341,9 @@
         p->OnResolved();
       }));
 
-  EXPECT_FALSE(promise->IsResolved());
+  EXPECT_FALSE(promise->IsResolvedForTesting());
   promise->Execute();
-  EXPECT_TRUE(promise->IsResolved());
+  EXPECT_TRUE(promise->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, ExecuteOnReject) {
@@ -427,9 +356,9 @@
             p->OnRejected();
           }));
 
-  EXPECT_FALSE(promise->IsRejected());
+  EXPECT_FALSE(promise->IsRejectedForTesting());
   promise->Execute();
-  EXPECT_TRUE(promise->IsRejected());
+  EXPECT_TRUE(promise->IsRejectedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, ExecutionChain) {
@@ -442,15 +371,15 @@
 
   p1->OnResolved();
 
-  EXPECT_FALSE(p2->IsResolved());
-  EXPECT_FALSE(p3->IsResolved());
-  EXPECT_FALSE(p4->IsResolved());
-  EXPECT_FALSE(p5->IsResolved());
+  EXPECT_FALSE(p2->IsResolvedForTesting());
+  EXPECT_FALSE(p3->IsResolvedForTesting());
+  EXPECT_FALSE(p4->IsResolvedForTesting());
+  EXPECT_FALSE(p5->IsResolvedForTesting());
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p1->IsResolved());
-  EXPECT_TRUE(p3->IsResolved());
-  EXPECT_TRUE(p4->IsResolved());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(p1->IsResolvedForTesting());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
+  EXPECT_TRUE(p4->IsResolvedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, MoveExecutionChain) {
@@ -471,15 +400,15 @@
 
   p1->OnResolved();
 
-  EXPECT_FALSE(p2->IsResolved());
-  EXPECT_FALSE(p3->IsResolved());
-  EXPECT_FALSE(p4->IsResolved());
-  EXPECT_FALSE(p5->IsResolved());
+  EXPECT_FALSE(p2->IsResolvedForTesting());
+  EXPECT_FALSE(p3->IsResolvedForTesting());
+  EXPECT_FALSE(p4->IsResolvedForTesting());
+  EXPECT_FALSE(p5->IsResolvedForTesting());
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p1->IsResolved());
-  EXPECT_TRUE(p3->IsResolved());
-  EXPECT_TRUE(p4->IsResolved());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(p1->IsResolvedForTesting());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
+  EXPECT_TRUE(p4->IsResolvedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, MoveResolveCatchExecutionChain) {
@@ -524,15 +453,15 @@
 
   p1->OnResolved();
 
-  EXPECT_FALSE(p2->IsRejected());
-  EXPECT_FALSE(p3->IsResolved());
-  EXPECT_FALSE(p4->IsRejected());
-  EXPECT_FALSE(p5->IsResolved());
+  EXPECT_FALSE(p2->IsRejectedForTesting());
+  EXPECT_FALSE(p3->IsResolvedForTesting());
+  EXPECT_FALSE(p4->IsRejectedForTesting());
+  EXPECT_FALSE(p5->IsResolvedForTesting());
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p2->IsRejected());
-  EXPECT_TRUE(p3->IsResolved());
-  EXPECT_TRUE(p4->IsRejected());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(p2->IsRejectedForTesting());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
+  EXPECT_TRUE(p4->IsRejectedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, MoveResolveCatchExecutionChainType2) {
@@ -612,23 +541,23 @@
           }));
   p1->OnResolved();
 
-  EXPECT_FALSE(p2->IsRejected());
-  EXPECT_FALSE(p3->IsRejected());
-  EXPECT_FALSE(p4->IsResolved());
-  EXPECT_FALSE(p5->IsResolved());
-  EXPECT_FALSE(p6->IsRejected());
-  EXPECT_FALSE(p7->IsRejected());
-  EXPECT_FALSE(p8->IsResolved());
-  EXPECT_FALSE(p9->IsResolved());
+  EXPECT_FALSE(p2->IsRejectedForTesting());
+  EXPECT_FALSE(p3->IsRejectedForTesting());
+  EXPECT_FALSE(p4->IsResolvedForTesting());
+  EXPECT_FALSE(p5->IsResolvedForTesting());
+  EXPECT_FALSE(p6->IsRejectedForTesting());
+  EXPECT_FALSE(p7->IsRejectedForTesting());
+  EXPECT_FALSE(p8->IsResolvedForTesting());
+  EXPECT_FALSE(p9->IsResolvedForTesting());
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p2->IsRejected());
-  EXPECT_TRUE(p3->IsRejected());
-  EXPECT_TRUE(p4->IsResolved());
-  EXPECT_TRUE(p5->IsResolved());
-  EXPECT_TRUE(p6->IsRejected());
-  EXPECT_TRUE(p7->IsRejected());
-  EXPECT_TRUE(p8->IsResolved());
-  EXPECT_TRUE(p9->IsResolved());
+  EXPECT_TRUE(p2->IsRejectedForTesting());
+  EXPECT_TRUE(p3->IsRejectedForTesting());
+  EXPECT_TRUE(p4->IsResolvedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
+  EXPECT_TRUE(p6->IsRejectedForTesting());
+  EXPECT_TRUE(p7->IsRejectedForTesting());
+  EXPECT_TRUE(p8->IsResolvedForTesting());
+  EXPECT_TRUE(p9->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, MixedMoveAndNormalExecutionChain) {
@@ -647,18 +576,18 @@
 
   p1->OnResolved();
 
-  EXPECT_FALSE(p2->IsResolved());
-  EXPECT_FALSE(p3->IsResolved());
-  EXPECT_FALSE(p4->IsResolved());
-  EXPECT_FALSE(p5->IsResolved());
+  EXPECT_FALSE(p2->IsResolvedForTesting());
+  EXPECT_FALSE(p3->IsResolvedForTesting());
+  EXPECT_FALSE(p4->IsResolvedForTesting());
+  EXPECT_FALSE(p5->IsResolvedForTesting());
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p1->IsResolved());
-  EXPECT_TRUE(p3->IsResolved());
-  EXPECT_TRUE(p4->IsResolved());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(p1->IsResolvedForTesting());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
+  EXPECT_TRUE(p4->IsResolvedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_MoveAtEndOfChain) {
+TEST_F(AbstractPromiseTest, MoveAtEndOfChain) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
   scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
@@ -676,15 +605,15 @@
 
   p1->OnResolved();
 
-  EXPECT_FALSE(p2->IsResolved());
-  EXPECT_FALSE(p3->IsResolved());
-  EXPECT_FALSE(p4->IsResolved());
-  EXPECT_FALSE(p5->IsResolved());
+  EXPECT_FALSE(p2->IsResolvedForTesting());
+  EXPECT_FALSE(p3->IsResolvedForTesting());
+  EXPECT_FALSE(p4->IsResolvedForTesting());
+  EXPECT_FALSE(p5->IsResolvedForTesting());
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p2->IsResolved());
-  EXPECT_TRUE(p3->IsResolved());
-  EXPECT_TRUE(p4->IsResolved());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(p2->IsResolvedForTesting());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
+  EXPECT_TRUE(p4->IsResolvedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, PrerequisiteAlreadyResolved) {
@@ -694,9 +623,9 @@
 
   scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
 
-  EXPECT_FALSE(p2->IsResolved());
+  EXPECT_FALSE(p2->IsResolvedForTesting());
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p2->IsResolved());
+  EXPECT_TRUE(p2->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, PrerequisiteAlreadyRejected) {
@@ -708,15 +637,16 @@
   scoped_refptr<AbstractPromise> p2 =
       CatchPromise(FROM_HERE, p1)
           .With(BindLambdaForTesting([&](AbstractPromise* p) {
-            EXPECT_TRUE(p->GetFirstSettledPrerequisite()->IsRejected());
+            EXPECT_TRUE(
+                p->GetFirstSettledPrerequisite()->IsRejectedForTesting());
             EXPECT_EQ(p->GetFirstSettledPrerequisite(), p1);
             p->emplace(Resolved<void>());
             p->OnResolved();
           }));
 
-  EXPECT_FALSE(p2->IsResolved());
+  EXPECT_FALSE(p2->IsResolvedForTesting());
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p2->IsResolved());
+  EXPECT_TRUE(p2->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, MultipleResolvedPrerequisitePolicyALL) {
@@ -729,12 +659,11 @@
   scoped_refptr<AbstractPromise> p4 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      4);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
-  prerequisite_list[3].prerequisite = p4;
+  std::vector<internal::DependentList::Node> prerequisite_list(4);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
+  prerequisite_list[3].SetPrerequisite(p4.get());
 
   scoped_refptr<AbstractPromise> all_promise =
       AllPromise(FROM_HERE, std::move(prerequisite_list));
@@ -744,11 +673,11 @@
   p3->OnResolved();
   RunLoop().RunUntilIdle();
 
-  EXPECT_FALSE(all_promise->IsResolved());
+  EXPECT_FALSE(all_promise->IsResolvedForTesting());
   p4->OnResolved();
 
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(all_promise->IsResolved());
+  EXPECT_TRUE(all_promise->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest,
@@ -763,11 +692,10 @@
   }
 
   scoped_refptr<AbstractPromise> promise[num_promises];
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      num_promises);
+  std::vector<internal::DependentList::Node> prerequisite_list(num_promises);
   for (int i = 0; i < num_promises; i++) {
     promise[i] = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
-    prerequisite_list[i].prerequisite = promise[i];
+    prerequisite_list[i].SetPrerequisite(promise[i].get());
   }
 
   scoped_refptr<AbstractPromise> all_promise =
@@ -776,15 +704,17 @@
   RunLoop run_loop;
   scoped_refptr<AbstractPromise> p2 =
       ThenPromise(FROM_HERE, all_promise)
-          .With(BindLambdaForTesting(
-              [&](AbstractPromise* p) { run_loop.Quit(); }));
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_loop.Quit();
+            p->OnResolved();
+          }));
 
   for (int i = 0; i < num_promises; i++) {
     thread[i % num_threads]->task_runner()->PostTask(
         FROM_HERE, BindOnce(
                        [](scoped_refptr<AbstractPromise> all_promise,
                           scoped_refptr<AbstractPromise> promise) {
-                         EXPECT_FALSE(all_promise->IsResolved());
+                         EXPECT_FALSE(all_promise->IsResolvedForTesting());
                          promise->OnResolved();
                        },
                        all_promise, promise[i]));
@@ -793,17 +723,17 @@
   run_loop.Run();
 
   for (int i = 0; i < num_promises; i++) {
-    EXPECT_TRUE(promise[i]->IsResolved());
+    EXPECT_TRUE(promise[i]->IsResolvedForTesting());
   }
 
-  EXPECT_TRUE(all_promise->IsResolved());
+  EXPECT_TRUE(all_promise->IsResolvedForTesting());
 
   for (int i = 0; i < num_threads; i++) {
     thread[i]->Stop();
   }
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_SingleRejectPrerequisitePolicyALL) {
+TEST_F(AbstractPromiseTest, SingleRejectPrerequisitePolicyALL) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
           false);
@@ -817,18 +747,18 @@
       DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
           false);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      4);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
-  prerequisite_list[3].prerequisite = p4;
+  std::vector<internal::DependentList::Node> prerequisite_list(4);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
+  prerequisite_list[3].SetPrerequisite(p4.get());
 
   scoped_refptr<AbstractPromise> all_promise =
       AllPromise(FROM_HERE, std::move(prerequisite_list))
           .With(CallbackResultType::kCanResolveOrReject)
           .With(BindLambdaForTesting([&](AbstractPromise* p) {
-            EXPECT_TRUE(p->GetFirstSettledPrerequisite()->IsRejected());
+            EXPECT_TRUE(
+                p->GetFirstSettledPrerequisite()->IsRejectedForTesting());
             EXPECT_EQ(p->GetFirstSettledPrerequisite(), p3);
             p->emplace(Rejected<void>());
             p->OnRejected();
@@ -838,11 +768,11 @@
 
   p3->OnRejected();
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(all_promise->IsRejected());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(all_promise->IsRejectedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_MultipleRejectPrerequisitePolicyALL) {
+TEST_F(AbstractPromiseTest, MultipleRejectPrerequisitePolicyALL) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
           false);
@@ -856,12 +786,11 @@
       DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
           false);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      4);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
-  prerequisite_list[3].prerequisite = p4;
+  std::vector<internal::DependentList::Node> prerequisite_list(4);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
+  prerequisite_list[3].SetPrerequisite(p4.get());
 
   scoped_refptr<AbstractPromise> all_promise =
       AllPromise(FROM_HERE, std::move(prerequisite_list))
@@ -881,7 +810,8 @@
       CatchPromise(FROM_HERE, all_promise)
           .With(BindLambdaForTesting([&](AbstractPromise* p) {
             EXPECT_FALSE(p->IsSettled());  // Should only happen once.
-            EXPECT_TRUE(p->GetFirstSettledPrerequisite()->IsRejected());
+            EXPECT_TRUE(
+                p->GetFirstSettledPrerequisite()->IsRejectedForTesting());
             EXPECT_EQ(p->GetFirstSettledPrerequisite(), all_promise);
             p->emplace(Resolved<void>());
             p->OnResolved();
@@ -891,8 +821,8 @@
   p1->OnRejected();
   p3->OnRejected();
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(all_promise->IsRejected());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(all_promise->IsRejectedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, SingleResolvedPrerequisitePolicyANY) {
@@ -905,19 +835,18 @@
   scoped_refptr<AbstractPromise> p4 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      4);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
-  prerequisite_list[3].prerequisite = p4;
+  std::vector<internal::DependentList::Node> prerequisite_list(4);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
+  prerequisite_list[3].SetPrerequisite(p4.get());
 
   scoped_refptr<AbstractPromise> any_promise =
       AnyPromise(FROM_HERE, std::move(prerequisite_list));
 
   p2->OnResolved();
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(any_promise->IsResolved());
+  EXPECT_TRUE(any_promise->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, MultipleResolvedPrerequisitePolicyANY) {
@@ -930,12 +859,11 @@
   scoped_refptr<AbstractPromise> p4 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      4);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
-  prerequisite_list[3].prerequisite = p4;
+  std::vector<internal::DependentList::Node> prerequisite_list(4);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
+  prerequisite_list[3].SetPrerequisite(p4.get());
 
   scoped_refptr<AbstractPromise> any_promise =
       AnyPromise(FROM_HERE, std::move(prerequisite_list));
@@ -943,10 +871,10 @@
   p1->OnResolved();
   p2->OnResolved();
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(any_promise->IsResolved());
+  EXPECT_TRUE(any_promise->IsResolvedForTesting());
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_SingleRejectPrerequisitePolicyANY) {
+TEST_F(AbstractPromiseTest, SingleRejectPrerequisitePolicyANY) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
           false);
@@ -960,12 +888,11 @@
       DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
           false);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      4);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
-  prerequisite_list[3].prerequisite = p4;
+  std::vector<internal::DependentList::Node> prerequisite_list(4);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
+  prerequisite_list[3].SetPrerequisite(p4.get());
 
   scoped_refptr<AbstractPromise> any_promise =
       AnyPromise(FROM_HERE, std::move(prerequisite_list))
@@ -975,8 +902,8 @@
 
   p3->OnRejected();
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(any_promise->IsRejected());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(any_promise->IsRejectedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, SingleResolvePrerequisitePolicyANY) {
@@ -989,12 +916,11 @@
   scoped_refptr<AbstractPromise> p4 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      4);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
-  prerequisite_list[3].prerequisite = p4;
+  std::vector<internal::DependentList::Node> prerequisite_list(4);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
+  prerequisite_list[3].SetPrerequisite(p4.get());
 
   scoped_refptr<AbstractPromise> any_promise =
       AnyPromise(FROM_HERE, std::move(prerequisite_list));
@@ -1003,8 +929,8 @@
 
   p3->OnResolved();
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(any_promise->IsResolved());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(any_promise->IsResolvedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, IsCanceled) {
@@ -1086,7 +1012,7 @@
   RunLoop().RunUntilIdle();
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_CancelationPrerequisitePolicyALL) {
+TEST_F(AbstractPromiseTest, CancelationPrerequisitePolicyALL) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
   scoped_refptr<AbstractPromise> p2 =
@@ -1094,11 +1020,10 @@
   scoped_refptr<AbstractPromise> p3 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      3);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
+  std::vector<internal::DependentList::Node> prerequisite_list(3);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
 
   scoped_refptr<AbstractPromise> all_promise =
       AllPromise(FROM_HERE, std::move(prerequisite_list));
@@ -1115,11 +1040,10 @@
   scoped_refptr<AbstractPromise> p3 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      3);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
+  std::vector<internal::DependentList::Node> prerequisite_list(3);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
 
   scoped_refptr<AbstractPromise> any_promise =
       AnyPromise(FROM_HERE, std::move(prerequisite_list));
@@ -1132,7 +1056,7 @@
   EXPECT_TRUE(any_promise->IsCanceled());
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_AlreadyCanceledPrerequisitePolicyALL) {
+TEST_F(AbstractPromiseTest, AlreadyCanceledPrerequisitePolicyALL) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
   scoped_refptr<AbstractPromise> p2 =
@@ -1140,11 +1064,10 @@
   scoped_refptr<AbstractPromise> p3 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      3);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
+  std::vector<internal::DependentList::Node> prerequisite_list(3);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
   p2->OnCanceled();
 
   scoped_refptr<AbstractPromise> all_promise =
@@ -1153,7 +1076,7 @@
   EXPECT_TRUE(all_promise->IsCanceled());
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_SomeAlreadyCanceledPrerequisitePolicyANY) {
+TEST_F(AbstractPromiseTest, SomeAlreadyCanceledPrerequisitePolicyANY) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
   scoped_refptr<AbstractPromise> p2 =
@@ -1161,11 +1084,10 @@
   scoped_refptr<AbstractPromise> p3 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      3);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
+  std::vector<internal::DependentList::Node> prerequisite_list(3);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
   p2->OnCanceled();
 
   scoped_refptr<AbstractPromise> any_promise =
@@ -1174,7 +1096,7 @@
   EXPECT_FALSE(any_promise->IsCanceled());
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_AllAlreadyCanceledPrerequisitePolicyANY) {
+TEST_F(AbstractPromiseTest, AllAlreadyCanceledPrerequisitePolicyANY) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
   scoped_refptr<AbstractPromise> p2 =
@@ -1182,11 +1104,10 @@
   scoped_refptr<AbstractPromise> p3 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
-  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
-      3);
-  prerequisite_list[0].prerequisite = p1;
-  prerequisite_list[1].prerequisite = p2;
-  prerequisite_list[2].prerequisite = p3;
+  std::vector<internal::DependentList::Node> prerequisite_list(3);
+  prerequisite_list[0].SetPrerequisite(p1.get());
+  prerequisite_list[1].SetPrerequisite(p2.get());
+  prerequisite_list[2].SetPrerequisite(p3.get());
   p1->OnCanceled();
   p2->OnCanceled();
   p3->OnCanceled();
@@ -1254,7 +1175,7 @@
 }
 
 TEST_F(AbstractPromiseTest,
-       ABSTRACT_PROMISE_DEATH_TEST(MAYBE_DetectResolveDoubleMoveHazard)) {
+       ABSTRACT_PROMISE_DEATH_TEST(DetectResolveDoubleMoveHazard)) {
   scoped_refptr<AbstractPromise> p0 = ThenPromise(FROM_HERE, nullptr);
 
   scoped_refptr<AbstractPromise> p1 =
@@ -1268,7 +1189,7 @@
 
 TEST_F(AbstractPromiseTest,
        ABSTRACT_PROMISE_DEATH_TEST(
-           MAYBE_DetectMixedResolveCallbackMoveAndNonMoveHazard)) {
+           DetectMixedResolveCallbackMoveAndNonMoveHazard)) {
   scoped_refptr<AbstractPromise> p0 = ThenPromise(FROM_HERE, nullptr);
 
   scoped_refptr<AbstractPromise> p1 =
@@ -1278,7 +1199,7 @@
       { scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0); });
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_MultipleNonMoveCatchCallbacksAreOK) {
+TEST_F(AbstractPromiseTest, MultipleNonMoveCatchCallbacksAreOK) {
   /*
    * Key:  T = Then, C = Catch
    *
@@ -1303,7 +1224,7 @@
 }
 
 TEST_F(AbstractPromiseTest,
-       ABSTRACT_PROMISE_DEATH_TEST(MAYBE_DetectCatchCallbackDoubleMoveHazard)) {
+       ABSTRACT_PROMISE_DEATH_TEST(DetectCatchCallbackDoubleMoveHazard)) {
   /*
    * Key:  T = Then, C = Catch
    *
@@ -1329,9 +1250,9 @@
   });
 }
 
-TEST_F(AbstractPromiseTest,
-       ABSTRACT_PROMISE_DEATH_TEST(
-           MAYBE_DetectCatchCallbackDoubleMoveHazardInChain)) {
+TEST_F(
+    AbstractPromiseTest,
+    ABSTRACT_PROMISE_DEATH_TEST(DetectCatchCallbackDoubleMoveHazardInChain)) {
   /*
    * Key:  T = Then, C = Catch
    *
@@ -1367,7 +1288,7 @@
 TEST_F(
     AbstractPromiseTest,
     ABSTRACT_PROMISE_DEATH_TEST(
-        MAYBE_DetectCatchCallbackDoubleMoveHazardInChainIntermediateThensCanReject)) {
+        DetectCatchCallbackDoubleMoveHazardInChainIntermediateThensCanReject)) {
   /*
    * Key:  T = Then, C = Catch
    *
@@ -1403,9 +1324,9 @@
   });
 }
 
-TEST_F(AbstractPromiseTest,
-       ABSTRACT_PROMISE_DEATH_TEST(
-           MAYBE_DetectMixedCatchCallbackMoveAndNonMoveHazard)) {
+TEST_F(
+    AbstractPromiseTest,
+    ABSTRACT_PROMISE_DEATH_TEST(DetectMixedCatchCallbackMoveAndNonMoveHazard)) {
   /*
    * Key:  T = Then, C = Catch
    *
@@ -1438,8 +1359,7 @@
 }
 
 TEST_F(AbstractPromiseTest,
-       ABSTRACT_PROMISE_DEATH_TEST(
-           MAYBE_DetectThenCallbackDoubleMoveHazardInChain)) {
+       ABSTRACT_PROMISE_DEATH_TEST(DetectThenCallbackDoubleMoveHazardInChain)) {
   /*
    * Key:  T = Then, C = Catch
    *
@@ -1918,7 +1838,7 @@
 }
 
 TEST_F(AbstractPromiseTest,
-       ABSTRACT_PROMISE_DEATH_TEST(MAYBE_DoubleMoveDoNothingPromise)) {
+       ABSTRACT_PROMISE_DEATH_TEST(DoubleMoveDoNothingPromise)) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
@@ -2052,6 +1972,43 @@
   EXPECT_THAT(run_order, ElementsAre(3, 4));
 }
 
+TEST_F(AbstractPromiseTest, NeverResolvedCurriedPromise) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  std::vector<int> run_order;
+
+  // Promise |p3| will be resolved with.
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p1)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(3);
+            // Resolve with a promise.
+            ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, BindOnce(&AbstractPromise::Execute, p2));
+            p->emplace(p2);
+            p->OnResolved();
+
+            EXPECT_TRUE(p3->IsResolvedWithPromise());
+          }));
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(4);
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  p1->OnResolved();
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(run_order, ElementsAre(3));
+
+  // This shouldn't leak.
+}
+
 TEST_F(AbstractPromiseTest, CanceledCurriedPromise) {
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
@@ -2067,8 +2024,10 @@
             // Resolve with a promise.
             ThreadTaskRunnerHandle::Get()->PostTask(
                 FROM_HERE, BindOnce(&AbstractPromise::Execute, p2));
+            EXPECT_TRUE(p2->IsCanceled());
             p->emplace(p2);
             p->OnResolved();
+            EXPECT_TRUE(p->IsCanceled());
           }));
 
   scoped_refptr<AbstractPromise> p4 =
@@ -2173,7 +2132,7 @@
   p1->OnResolved();
   RunLoop().RunUntilIdle();
 
-  EXPECT_TRUE(p4->IsResolved());
+  EXPECT_TRUE(p4->IsResolvedForTesting());
   EXPECT_EQ(p1.get(), p4->FindNonCurriedAncestor());
 }
 
@@ -2233,10 +2192,10 @@
   scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p2);
 
   p0->OnResolved();
-  EXPECT_FALSE(p3->IsResolved());
+  EXPECT_FALSE(p3->IsResolvedForTesting());
 
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p3->IsResolved());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, ManuallyResolveWithNonSettledCurriedPromise) {
@@ -2246,14 +2205,17 @@
   scoped_refptr<AbstractPromise> p1 =
       DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
 
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+
   p1->emplace(p0);
   p1->OnResolved();
   RunLoop().RunUntilIdle();
-  EXPECT_FALSE(p1->IsResolved());
+  EXPECT_TRUE(p1->IsResolvedForTesting());
+  EXPECT_FALSE(p2->IsResolvedForTesting());
 
   p0->OnResolved();
   RunLoop().RunUntilIdle();
-  EXPECT_TRUE(p1->IsResolved());
+  EXPECT_TRUE(p2->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, ExecuteCalledOnceForLateResolvedCurriedPromise) {
@@ -2279,12 +2241,12 @@
   p0->OnResolved();
   // |p2| should run but not |p3|.
   EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner));
-  EXPECT_FALSE(p3->IsResolved());
+  EXPECT_FALSE(p3->IsResolvedForTesting());
 
   p1->OnResolved();
   // |p3| should run.
   EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner));
-  EXPECT_TRUE(p3->IsResolved());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, ExecuteCalledOnceForLateRejectedCurriedPromise) {
@@ -2306,17 +2268,17 @@
           .With(task_runner);
 
   scoped_refptr<AbstractPromise> p3 =
-      ThenPromise(FROM_HERE, p1).With(task_runner);
+      CatchPromise(FROM_HERE, p1).With(task_runner);
 
   p0->OnResolved();
   // |p2| should run but not |p3|.
   EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner));
-  EXPECT_FALSE(p3->IsRejected());
+  EXPECT_FALSE(p3->IsResolvedForTesting());
 
   p1->OnRejected();
   // |p3| should run.
   EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner));
-  EXPECT_TRUE(p3->IsRejected());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
 }
 
 TEST_F(AbstractPromiseTest, ThreadHopping) {
@@ -2372,19 +2334,19 @@
 
   p1->OnResolved();
 
-  EXPECT_FALSE(p5->IsResolved());
+  EXPECT_FALSE(p5->IsResolvedForTesting());
   run_loop.Run();
-  EXPECT_TRUE(p2->IsResolved());
-  EXPECT_TRUE(p3->IsResolved());
-  EXPECT_TRUE(p4->IsResolved());
-  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(p2->IsResolvedForTesting());
+  EXPECT_TRUE(p3->IsResolvedForTesting());
+  EXPECT_TRUE(p4->IsResolvedForTesting());
+  EXPECT_TRUE(p5->IsResolvedForTesting());
 
   thread_a->Stop();
   thread_b->Stop();
   thread_c->Stop();
 }
 
-TEST_F(AbstractPromiseTest, MAYBE_MutipleThreadsAddingDependants) {
+TEST_F(AbstractPromiseTest, MutipleThreadsAddingDependants) {
   constexpr int num_threads = 4;
   constexpr int num_promises = 100000;
 
@@ -2405,6 +2367,7 @@
     int count = pending_count.fetch_sub(1, std::memory_order_acq_rel);
     if (count == 1)
       run_loop.Quit();
+    p->OnResolved();
   });
 
   // Post a bunch of tasks on multiple threads that create Then promises
@@ -2431,5 +2394,58 @@
   }
 }
 
+TEST_F(AbstractPromiseTest, SingleRejectPrerequisitePolicyALLModified) {
+  // Regression test to ensure cross thread rejection works as intended. Loop
+  // increaces chances of catching any bugs.
+  for (size_t i = 0; i < 1000; ++i) {
+    scoped_refptr<AbstractPromise> p1 =
+        DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+            false);
+    scoped_refptr<AbstractPromise> p2 =
+        DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+            false);
+    scoped_refptr<AbstractPromise> p3 =
+        DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+            false);
+    scoped_refptr<AbstractPromise> p4 =
+        DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+            false);
+
+    std::vector<internal::DependentList::Node> prerequisite_list(4);
+    prerequisite_list[0].SetPrerequisite(p1.get());
+    prerequisite_list[1].SetPrerequisite(p2.get());
+    prerequisite_list[2].SetPrerequisite(p3.get());
+    prerequisite_list[3].SetPrerequisite(p4.get());
+
+    scoped_refptr<AbstractPromise> all_promise =
+        AllPromise(FROM_HERE, std::move(prerequisite_list))
+            .With(CallbackResultType::kCanResolveOrReject)
+            .With(BindLambdaForTesting([&](AbstractPromise* p) {
+              p->emplace(Rejected<void>());
+              p->OnRejected();
+            }));
+
+    base::PostTaskWithTraits(
+        FROM_HERE, {},
+        base::Bind([](scoped_refptr<AbstractPromise> p2) { p2->OnRejected(); },
+                   p2));
+
+    RunLoop run_loop;
+    scoped_refptr<AbstractPromise> p5 =
+        CatchPromise(FROM_HERE, all_promise)
+            .With(BindLambdaForTesting([&](AbstractPromise* p) {
+              p->emplace(Resolved<void>());
+              p->OnResolved();
+              run_loop.Quit();
+            }));
+
+    p3->OnRejected();
+    run_loop.Run();
+    EXPECT_TRUE(all_promise->IsRejected());
+    EXPECT_TRUE(p5->IsResolved());
+    scoped_task_environment_.RunUntilIdle();
+  }
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/promise/all_container_executor.h b/base/task/promise/all_container_executor.h
index 786f5b0..400d0b8 100644
--- a/base/task/promise/all_container_executor.h
+++ b/base/task/promise/all_container_executor.h
@@ -74,17 +74,17 @@
     using NonVoidResolveType = ToNonVoidT<ResolveType>;
     Resolved<std::vector<NonVoidResolveType>> result;
 
-    const std::vector<AbstractPromise::AdjacencyListNode>* prerequisite_list =
+    const std::vector<DependentList::Node>* prerequisite_list =
         promise->prerequisite_list();
     DCHECK(prerequisite_list);
     result.value.reserve(prerequisite_list->size());
 
     for (const auto& node : *prerequisite_list) {
-      DCHECK(node.prerequisite->IsResolved());
+      DCHECK(node.prerequisite()->IsResolved());
       result.value.push_back(
           ArgMoveSemanticsHelper<
               NonVoidResolveType,
-              Resolved<NonVoidResolveType>>::Get(node.prerequisite.get()));
+              Resolved<NonVoidResolveType>>::Get(node.prerequisite()));
     }
 
     promise->emplace(std::move(result));
@@ -106,10 +106,9 @@
 
   static PromiseType All(const Location& from_here, const Container& promises) {
     size_t i = 0;
-    std::vector<AbstractPromise::AdjacencyListNode> prerequisite_list(
-        promises.size());
+    std::vector<DependentList::Node> prerequisite_list(promises.size());
     for (auto& promise : promises) {
-      prerequisite_list[i++].prerequisite = promise.abstract_promise_;
+      prerequisite_list[i++].SetPrerequisite(promise.abstract_promise_.get());
     }
     return PromiseType(AbstractPromise::Create(
         nullptr, from_here,
diff --git a/base/task/promise/all_tuple_executor.h b/base/task/promise/all_tuple_executor.h
index 69a3fa8..b28c6c7 100644
--- a/base/task/promise/all_tuple_executor.h
+++ b/base/task/promise/all_tuple_executor.h
@@ -29,13 +29,13 @@
   // Resolves |result| with a std::tuple of the promise results of the dependent
   // promises.
   static void ConstructTuple(
-      const std::vector<AbstractPromise::AdjacencyListNode>* prerequisite_list,
+      const std::vector<DependentList::Node>* prerequisite_list,
       AbstractPromise* result) {
     DCHECK_EQ(sizeof...(Indices), prerequisite_list->size());
     result->emplace(
         in_place_type_t<Resolved<Tuple>>(),
         GetResolvedValueFromPromise<std::tuple_element_t<Indices, Tuple>>(
-            (*prerequisite_list)[Indices].prerequisite.get())...);
+            (*prerequisite_list)[Indices].prerequisite())...);
   }
 };
 
@@ -69,7 +69,7 @@
       return;
     }
 
-    const std::vector<AbstractPromise::AdjacencyListNode>* prerequisite_list =
+    const std::vector<DependentList::Node>* prerequisite_list =
         promise->prerequisite_list();
     DCHECK(prerequisite_list);
     TupleConstructor<ResolveTuple>::ConstructTuple(prerequisite_list, promise);
diff --git a/base/task/promise/dependent_list.cc b/base/task/promise/dependent_list.cc
index 9afaf46..81cc763 100644
--- a/base/task/promise/dependent_list.cc
+++ b/base/task/promise/dependent_list.cc
@@ -13,42 +13,55 @@
 
 namespace base {
 namespace internal {
-namespace {
 
-DependentList::Node* ReverseList(DependentList::Node* list) {
+// static
+DependentList::Node* DependentList::ReverseList(DependentList::Node* list) {
   DependentList::Node* prev = nullptr;
   while (list) {
-    DependentList::Node* next = list->next;
-    list->next = prev;
+    DependentList::Node* next = list->next_;
+    list->next_ = prev;
     prev = list;
     list = next;
   }
   return prev;
 }
 
-// Goes through the list starting at |head| consuming node->dependent and
-// passing it to the provided |visitor|.
-void DispatchAll(DependentList::Node* head, DependentList::Visitor* visitor) {
+// static
+void DependentList::DispatchAll(DependentList::Node* head,
+                                DependentList::Visitor* visitor,
+                                bool retain_prerequsites) {
   head = ReverseList(head);
   DependentList::Node* next = nullptr;
   while (head) {
-    next = head->next;
-    // consume_fn might delete the node, so no access to node past this
+    next = head->next_;
+    if (retain_prerequsites)
+      head->RetainSettledPrerequisite();
+    // |visitor| might delete the node, so no access to node past this
     // call!
-    visitor->Visit(std::move(head->dependent));
+    visitor->Visit(std::move(head->dependent_));
     head = next;
   }
 }
 
-}  // namespace
-
 DependentList::Visitor::~Visitor() = default;
 
 DependentList::Node::Node() = default;
-DependentList::Node::Node(DependentList::Node&& other) = default;
-DependentList::Node& DependentList::Node::operator=(
-    DependentList::Node&& other) = default;
-DependentList::Node::~Node() = default;
+
+DependentList::Node::Node(Node&& other) {
+  prerequisite_ = other.prerequisite_.load(std::memory_order_relaxed);
+  other.prerequisite_ = 0;
+  dependent_ = std::move(other.dependent_);
+  DCHECK_EQ(other.next_, nullptr);
+}
+
+DependentList::Node::Node(AbstractPromise* prerequisite,
+                          scoped_refptr<AbstractPromise> dependent)
+    : prerequisite_(reinterpret_cast<intptr_t>(prerequisite)),
+      dependent_(std::move(dependent)) {}
+
+DependentList::Node::~Node() {
+  ClearPrerequisite();
+}
 
 DependentList::DependentList(State initial_state)
     : data_(CreateData(nullptr,
@@ -67,8 +80,50 @@
 
 DependentList::~DependentList() = default;
 
+void DependentList::Node::Reset(AbstractPromise* prerequisite,
+                                scoped_refptr<AbstractPromise> dependent) {
+  SetPrerequisite(prerequisite);
+  dependent_ = std::move(dependent);
+  next_ = nullptr;
+}
+
+void DependentList::Node::SetPrerequisite(AbstractPromise* prerequisite) {
+  DCHECK(prerequisite);
+  intptr_t prev_value = prerequisite_.exchange(
+      reinterpret_cast<intptr_t>(prerequisite), std::memory_order_acq_rel);
+
+  if (prev_value & kIsRetained)
+    reinterpret_cast<AbstractPromise*>(prev_value & ~kIsRetained)->Release();
+}
+
+AbstractPromise* DependentList::Node::prerequisite() const {
+  return reinterpret_cast<AbstractPromise*>(
+      prerequisite_.load(std::memory_order_acquire) & ~kIsRetained);
+}
+
+void DependentList::Node::RetainSettledPrerequisite() {
+  intptr_t prerequisite = prerequisite_.load(std::memory_order_acquire);
+  DCHECK((prerequisite & kIsRetained) == 0) << "May only be called once";
+  if (!prerequisite)
+    return;
+
+  // Mark as retained, note we could have another thread trying to call
+  // ClearPrerequisite.
+  if (prerequisite_.compare_exchange_strong(
+          prerequisite, prerequisite | kIsRetained, std::memory_order_release,
+          std::memory_order_acquire)) {
+    reinterpret_cast<AbstractPromise*>(prerequisite)->AddRef();
+  }
+}
+
+void DependentList::Node::ClearPrerequisite() {
+  intptr_t prerequisite = prerequisite_.exchange(0, std::memory_order_acq_rel);
+  if (prerequisite & kIsRetained)
+    reinterpret_cast<AbstractPromise*>(prerequisite & ~kIsRetained)->Release();
+}
+
 DependentList::InsertResult DependentList::Insert(Node* node) {
-  DCHECK(!node->next);
+  DCHECK(!node->next_);
 
   // std::memory_order_acquire for hapens-after relation with
   // SettleAndDispatchAllDependents completing and thus this this call returning
@@ -76,7 +131,7 @@
   uintptr_t prev_data = data_.load(std::memory_order_acquire);
   bool did_insert = false;
   while (IsAllowingInserts(prev_data) && !did_insert) {
-    node->next = ExtractHead(prev_data);
+    node->next_ = ExtractHead(prev_data);
 
     // On success std::memory_order_release so that all memory operations become
     // visible in SettleAndDispatchAllDependents when iterating the list.
@@ -90,11 +145,11 @@
     // new node but with the same address so node->next is still valid).
     if (data_.compare_exchange_weak(
             prev_data, CreateData(node, ExtractState(prev_data), kAllowInserts),
-            std::memory_order_release, std::memory_order_acquire)) {
+            std::memory_order_seq_cst, std::memory_order_seq_cst)) {
       did_insert = true;
     } else {
       // Cleanup in case the loop terminates
-      node->next = nullptr;
+      node->next_ = nullptr;
     }
   }
 
@@ -132,7 +187,7 @@
   // This load, and the ones in for compare_exchange_weak failures can be
   // std::memory_order_relaxed as we do not make any ordering guarantee when
   // this method returns false.
-  uintptr_t prev_data = data_.load(std::memory_order_relaxed);
+  uintptr_t prev_data = data_.load(std::memory_order_seq_cst);
   while (true) {
     if (!did_set_state && ExtractState(prev_data) != State::kUnresolved) {
       // Somebody else set the state.
@@ -156,12 +211,14 @@
       // On success std::memory_order_acquire for happens-after relation with
       // with the last successful Insert().
       if (!data_.compare_exchange_weak(prev_data, new_data,
-                                       std::memory_order_acquire,
+                                       std::memory_order_seq_cst,
                                        std::memory_order_relaxed)) {
         continue;
       }
       did_set_state = true;
-      DispatchAll(ExtractHead(prev_data), visitor);
+      // We don't want to retain prerequisites when cancelling.
+      DispatchAll(ExtractHead(prev_data), visitor,
+                  settled_state != State::kCanceled);
       prev_data = new_data;
     }
 
@@ -175,7 +232,7 @@
     // Insert returning an error.
     if (data_.compare_exchange_weak(
             prev_data, CreateData(nullptr, settled_state, kBlockInserts),
-            std::memory_order_release, std::memory_order_relaxed)) {
+            std::memory_order_seq_cst, std::memory_order_relaxed)) {
       // Inserts no longer allowed, state settled and list is empty. We are
       // done!
       return true;
@@ -190,24 +247,36 @@
 // std::memory_order_relaxed.
 
 bool DependentList::IsSettled() const {
-  return ExtractState(data_.load(std::memory_order_relaxed)) !=
+  return ExtractState(data_.load(std::memory_order_seq_cst)) !=
          State::kUnresolved;
 }
 
 bool DependentList::IsResolved() const {
-  return ExtractState(data_.load(std::memory_order_relaxed)) ==
+  DCHECK(IsSettled()) << "This check is racy";
+  return ExtractState(data_.load(std::memory_order_seq_cst)) ==
          State::kResolved;
 }
 
 bool DependentList::IsRejected() const {
-  return ExtractState(data_.load(std::memory_order_relaxed)) ==
+  DCHECK(IsSettled()) << "This check is racy";
+  return ExtractState(data_.load(std::memory_order_seq_cst)) ==
          State::kRejected;
 }
 
 bool DependentList::IsCanceled() const {
-  return ExtractState(data_.load(std::memory_order_relaxed)) ==
+  return ExtractState(data_.load(std::memory_order_seq_cst)) ==
          State::kCanceled;
 }
 
+bool DependentList::IsResolvedForTesting() const {
+  return ExtractState(data_.load(std::memory_order_seq_cst)) ==
+         State::kResolved;
+}
+
+bool DependentList::IsRejectedForTesting() const {
+  return ExtractState(data_.load(std::memory_order_seq_cst)) ==
+         State::kRejected;
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/promise/dependent_list.h b/base/task/promise/dependent_list.h
index 8083969b..020bdbfc 100644
--- a/base/task/promise/dependent_list.h
+++ b/base/task/promise/dependent_list.h
@@ -59,14 +59,68 @@
 
   // Align Node on an 8-byte boundary to ensure the first 3 bits are 0 and can
   // be used to store additional state (see static_asserts below).
-  struct BASE_EXPORT alignas(8) Node {
+  class BASE_EXPORT alignas(8) Node {
+   public:
     Node();
     explicit Node(Node&& other) noexcept;
-    Node& operator=(Node&& other) noexcept;
+
+    // Constructs a Node, |prerequisite| will not be retained unless
+    // RetainSettledPrerequisite is called.
+    Node(AbstractPromise* prerequisite,
+         scoped_refptr<AbstractPromise> dependent);
     ~Node();
 
-    scoped_refptr<AbstractPromise> dependent;
-    Node* next = nullptr;
+    // Caution this is not thread safe.
+    void Reset(AbstractPromise* prerequisite,
+               scoped_refptr<AbstractPromise> dependent);
+
+    // Expected prerequisite usage:
+    // 1. prerequisite = null on creation (or is constructed with a value)
+    // 2. (optional, once only) SetPrerequisite(value)
+    // 3. (maybe, once only) RetainSettledPrerequisite();
+    // 4. (maybe) ClearPrerequisite()
+    // 5. Destructor called
+
+    // Can be called on any thread.
+    void SetPrerequisite(AbstractPromise* prerequisite);
+
+    // Can be called on any thread.
+    AbstractPromise* prerequisite() const;
+
+    scoped_refptr<AbstractPromise>& dependent() { return dependent_; }
+
+    const scoped_refptr<AbstractPromise>& dependent() const {
+      return dependent_;
+    }
+
+    Node* next() const { return next_; }
+
+    // Calls AddRef on |prerequisite()| and marks the prerequisite as being
+    // retained. The |prerequisite()| will be released by Node's destructor or
+    // a call to ClearPrerequisite. Does nothing if called more than once.
+    // Can be called on any thread at any time. Can be called once only.
+    void RetainSettledPrerequisite();
+
+    // Calls Release() if the rerequsite was retained and then sets
+    // |prerequisite_| to zero. Can be called on any thread at any time. Can be
+    // called more than once.
+    void ClearPrerequisite();
+
+   private:
+    friend class DependentList;
+
+    void MarkAsRetained() { prerequisite_ |= kIsRetained; }
+
+    // An AbstractPromise* where the LSB is a flag which specified if it's
+    // retained or not.
+    // A reference for |prerequisite_| is acquired with an explicit call to
+    // AddRef() if it's resolved or rejected.
+    std::atomic<intptr_t> prerequisite_{0};
+
+    scoped_refptr<AbstractPromise> dependent_;
+    Node* next_ = nullptr;
+
+    static constexpr intptr_t kIsRetained = 1;
   };
 
   // Insert will only succeed if neither ResolveAndConsumeAllDependents nor
@@ -128,10 +182,18 @@
   //
   // ATTENTION: No guarantees are made as of whether the
   // (Resolve/Reject/Cancel)AndConsumeAllDependents method is still executing.
-  bool IsResolved() const;
-  bool IsRejected() const;
   bool IsCanceled() const;
 
+  // DCHECKs if not settled.
+  bool IsResolved() const;
+
+  // DCHECKs if not settled.
+  bool IsRejected() const;
+
+  // Like the above but doesn't DCHECK if unsettled.
+  bool IsResolvedForTesting() const;
+  bool IsRejectedForTesting() const;
+
  private:
   // The data for this class is:
   //   * head: Pointer to the head of the list of Node instances
@@ -224,6 +286,14 @@
   // already settled it does nothing and returns false, true otherwise.
   bool SettleAndDispatchAllDependents(State settled_state, Visitor* visitor);
 
+  static DependentList::Node* ReverseList(DependentList::Node* list);
+
+  // Goes through the list starting at |head| consuming node->dependent and
+  // passing it to the provided |visitor|.
+  static void DispatchAll(DependentList::Node* head,
+                          DependentList::Visitor* visitor,
+                          bool retain_prerequsites);
+
   std::atomic<uintptr_t> data_;
 };
 
diff --git a/base/task/promise/dependent_list_unittest.cc b/base/task/promise/dependent_list_unittest.cc
index 6e9d507..9373aff 100644
--- a/base/task/promise/dependent_list_unittest.cc
+++ b/base/task/promise/dependent_list_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/task/promise/abstract_promise.h"
+#include "base/test/do_nothing_promise.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -45,9 +46,9 @@
   DependentList list(DependentList::ConstructUnresolved{});
   DependentList::Node node;
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node));
-  EXPECT_FALSE(list.IsRejected());
+  EXPECT_FALSE(list.IsRejectedForTesting());
   EXPECT_FALSE(list.IsCanceled());
-  EXPECT_FALSE(list.IsResolved());
+  EXPECT_FALSE(list.IsResolvedForTesting());
   EXPECT_FALSE(list.IsSettled());
 }
 
@@ -82,11 +83,12 @@
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node2));
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
 
-  EXPECT_FALSE(list.IsResolved());
+  EXPECT_FALSE(list.IsResolvedForTesting());
   EXPECT_FALSE(list.IsSettled());
 
-  std::vector<AbstractPromise*> expected_dependants = {
-      node1.dependent.get(), node2.dependent.get(), node3.dependent.get()};
+  std::vector<AbstractPromise*> expected_dependants = {node1.dependent().get(),
+                                                       node2.dependent().get(),
+                                                       node3.dependent().get()};
 
   PushBackVisitor visitor;
   list.ResolveAndConsumeAllDependents(&visitor);
@@ -110,10 +112,11 @@
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node2));
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
 
-  EXPECT_FALSE(list.IsResolved());
+  EXPECT_FALSE(list.IsResolvedForTesting());
   EXPECT_FALSE(list.IsSettled());
-  std::vector<AbstractPromise*> expected_dependants = {
-      node1.dependent.get(), node2.dependent.get(), node3.dependent.get()};
+  std::vector<AbstractPromise*> expected_dependants = {node1.dependent().get(),
+                                                       node2.dependent().get(),
+                                                       node3.dependent().get()};
 
   PushBackVisitor visitor;
   list.RejectAndConsumeAllDependents(&visitor);
@@ -137,10 +140,11 @@
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node2));
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
 
-  EXPECT_FALSE(list.IsResolved());
+  EXPECT_FALSE(list.IsResolvedForTesting());
   EXPECT_FALSE(list.IsSettled());
-  std::vector<AbstractPromise*> expected_dependants = {
-      node1.dependent.get(), node2.dependent.get(), node3.dependent.get()};
+  std::vector<AbstractPromise*> expected_dependants = {node1.dependent().get(),
+                                                       node2.dependent().get(),
+                                                       node3.dependent().get()};
 
   PushBackVisitor visitor;
   EXPECT_TRUE(list.CancelAndConsumeAllDependents(&visitor));
@@ -181,5 +185,25 @@
                 "");
 }
 
+TEST(DependentListNode, Simple) {
+  DependentList::Node node;
+  EXPECT_EQ(nullptr, node.prerequisite());
+
+  scoped_refptr<AbstractPromise> p = DoNothingPromiseBuilder(FROM_HERE);
+  EXPECT_TRUE(p->HasOneRef());
+  node.SetPrerequisite(p.get());
+  EXPECT_EQ(p.get(), node.prerequisite());
+  EXPECT_TRUE(p->HasOneRef());
+
+  EXPECT_TRUE(p->HasOneRef());
+  node.RetainSettledPrerequisite();
+  EXPECT_EQ(p.get(), node.prerequisite());
+  EXPECT_FALSE(p->HasOneRef());
+
+  node.ClearPrerequisite();
+  EXPECT_EQ(nullptr, node.prerequisite());
+  EXPECT_TRUE(p->HasOneRef());
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/promise/promise.h b/base/task/promise/promise.h
index f32b627..0990708 100644
--- a/base/task/promise/promise.h
+++ b/base/task/promise/promise.h
@@ -116,12 +116,12 @@
 
   bool IsResolvedForTesting() const {
     DCHECK(abstract_promise_);
-    return abstract_promise_->IsResolved();
+    return abstract_promise_->IsResolvedForTesting();
   }
 
   bool IsRejectedForTesting() const {
     DCHECK(abstract_promise_);
-    return abstract_promise_->IsRejected();
+    return abstract_promise_->IsRejectedForTesting();
   }
 
   // A task to execute |on_reject| is posted on |task_runner| as soon as this
@@ -173,7 +173,7 @@
                    ReturnedPromiseRejectT>(internal::AbstractPromise::Create(
         std::move(task_runner), from_here,
         std::make_unique<internal::AbstractPromise::AdjacencyList>(
-            abstract_promise_),
+            abstract_promise_.get()),
         RejectPolicy::kMustCatchRejection,
         internal::AbstractPromise::ConstructWith<
             internal::DependentList::ConstructUnresolved,
@@ -250,7 +250,7 @@
         internal::AbstractPromise::Create(
             std::move(task_runner), from_here,
             std::make_unique<internal::AbstractPromise::AdjacencyList>(
-                abstract_promise_),
+                abstract_promise_.get()),
             RejectPolicy::kMustCatchRejection,
             internal::AbstractPromise::ConstructWith<
                 internal::DependentList::ConstructUnresolved,
@@ -346,7 +346,7 @@
                    ReturnedPromiseRejectT>(internal::AbstractPromise::Create(
         std::move(task_runner), from_here,
         std::make_unique<internal::AbstractPromise::AdjacencyList>(
-            abstract_promise_),
+            abstract_promise_.get()),
         RejectPolicy::kMustCatchRejection,
         internal::AbstractPromise::ConstructWith<
             internal::DependentList::ConstructUnresolved,
@@ -406,7 +406,7 @@
         internal::AbstractPromise::Create(
             std::move(task_runner), from_here,
             std::make_unique<internal::AbstractPromise::AdjacencyList>(
-                abstract_promise_),
+                abstract_promise_.get()),
             RejectPolicy::kMustCatchRejection,
             internal::AbstractPromise::ConstructWith<
                 internal::DependentList::ConstructUnresolved,
@@ -514,17 +514,16 @@
       RejectPolicy reject_policy = RejectPolicy::kMustCatchRejection)
       : promise_(SequencedTaskRunnerHandle::Get(), from_here, reject_policy) {}
 
-  ~ManualPromiseResolver() {
-    // If the promise wasn't resolved or rejected, then cancel it to make sure
-    // we don't leak memory.
-    if (!promise_.abstract_promise_->IsSettled())
-      promise_.abstract_promise_->OnCanceled();
+  ~ManualPromiseResolver() = default;
+
+  void Resolve(Promise<ResolveType, RejectType> promise) noexcept {
+    promise_.abstract_promise_->emplace(std::move(promise.abstract_promise_));
+    promise_.abstract_promise_->OnResolved();
   }
 
   template <typename... Args>
   void Resolve(Args&&... arg) noexcept {
-    DCHECK(!promise_.abstract_promise_->IsResolved());
-    DCHECK(!promise_.abstract_promise_->IsRejected());
+    DCHECK(!promise_.abstract_promise_->IsSettled());
     static_assert(!std::is_same<NoResolve, ResolveType>::value,
                   "Can't resolve a NoResolve promise.");
     promise_.abstract_promise_->emplace(
@@ -534,8 +533,7 @@
 
   template <typename... Args>
   void Reject(Args&&... arg) noexcept {
-    DCHECK(!promise_.abstract_promise_->IsResolved());
-    DCHECK(!promise_.abstract_promise_->IsRejected());
+    DCHECK(!promise_.abstract_promise_->IsSettled());
     static_assert(!std::is_same<NoReject, RejectType>::value,
                   "Can't reject a NoReject promise.");
     promise_.abstract_promise_->emplace(
@@ -637,11 +635,11 @@
         std::tuple<internal::ToNonVoidT<Resolve>...>;
     using ReturnedPromiseRejectT = Reject;
 
-    std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+    std::vector<internal::DependentList::Node> prerequisite_list(
         sizeof...(promises));
     int i = 0;
     for (auto&& p : {promises.abstract_promise_...}) {
-      prerequisite_list[i++].prerequisite = std::move(p);
+      prerequisite_list[i++].SetPrerequisite(p.get());
     }
     return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
         internal::AbstractPromise::Create(
diff --git a/base/task/promise/promise_unittest.cc b/base/task/promise/promise_unittest.cc
index 7e917df..0d10fb9e 100644
--- a/base/task/promise/promise_unittest.cc
+++ b/base/task/promise/promise_unittest.cc
@@ -15,27 +15,11 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// TODO(crbug.com/968302): Fix memory leaks in tests and re-enable on LSAN.
-#ifdef LEAK_SANITIZER
-#define MAYBE_ThenRejectWithTuple DISABLED_ThenRejectWithTuple
-#define MAYBE_TargetTaskRunnerClearsTasks DISABLED_TargetTaskRunnerClearsTasks
-#define MAYBE_MoveOnlyTypeMultipleThensNotAllowed \
-  DISABLED_MoveOnlyTypeMultipleThensNotAllowed
-#define MAYBE_MoveOnlyTypeMultipleCatchesNotAllowed \
-  DISABLED_MoveOnlyTypeMultipleCatchesNotAllowed
-#else
-#define MAYBE_ThenRejectWithTuple ThenRejectWithTuple
-#define MAYBE_TargetTaskRunnerClearsTasks TargetTaskRunnerClearsTasks
-#define MAYBE_MoveOnlyTypeMultipleThensNotAllowed \
-  MoveOnlyTypeMultipleThensNotAllowed
-#define MAYBE_MoveOnlyTypeMultipleCatchesNotAllowed \
-  MoveOnlyTypeMultipleCatchesNotAllowed
-#endif
-
 using testing::ElementsAre;
 
 namespace base {
@@ -93,7 +77,7 @@
   test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
-TEST(PromiseMemoryLeakTest, MAYBE_TargetTaskRunnerClearsTasks) {
+TEST(PromiseMemoryLeakTest, TargetTaskRunnerClearsTasks) {
   scoped_refptr<TestMockTimeTaskRunner> post_runner =
       MakeRefCounted<TestMockTimeTaskRunner>();
   scoped_refptr<TestMockTimeTaskRunner> reply_runner =
@@ -277,7 +261,7 @@
   run_loop.Run();
 }
 
-TEST_F(PromiseTest, MAYBE_ThenRejectWithTuple) {
+TEST_F(PromiseTest, ThenRejectWithTuple) {
   ManualPromiseResolver<void> p(FROM_HERE);
   p.Resolve();
 
@@ -1048,6 +1032,95 @@
   run_loop.Run();
 }
 
+TEST_F(PromiseTest, CurriedIntPromiseChain) {
+  Promise<int> p = Promise<int>::CreateResolved(FROM_HERE, 1000);
+
+  ManualPromiseResolver<int> promise_resolver_1(FROM_HERE);
+  ManualPromiseResolver<int> promise_resolver_2(FROM_HERE);
+  promise_resolver_2.Resolve(promise_resolver_1.promise());
+  promise_resolver_1.Resolve(123);
+
+  RunLoop run_loop;
+  p.ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+               EXPECT_EQ(1000, result);
+               return promise_resolver_2.promise();
+             }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(123, result);
+                  run_loop.Quit();
+                }));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CurriedIntPromiseChain2) {
+  Promise<int> p1 = Promise<int>::CreateResolved(FROM_HERE, 1000);
+  Promise<int> p2 = Promise<int>::CreateResolved(FROM_HERE, 789);
+  Promise<int> then2;
+
+  {
+    Promise<int> then1 =
+        Promise<int>::CreateResolved(FROM_HERE, 789)
+            .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { return p2; }));
+    then2 = Promise<int>::CreateResolved(FROM_HERE, 789)
+                .ThenHere(
+                    FROM_HERE,
+                    BindOnce([&](Promise<int> then1) { return then1; }, then1));
+  }
+
+  RunLoop run_loop;
+  p1.ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                EXPECT_EQ(1000, result);
+                return then2;
+              }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(789, result);
+                  run_loop.Quit();
+                }));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CurriedIntPromiseChainThenAddedAfterInitialResolve) {
+  ManualPromiseResolver<int> promise_resolver_1(FROM_HERE);
+  ManualPromiseResolver<int> promise_resolver_2(FROM_HERE);
+  ManualPromiseResolver<int> promise_resolver_3(FROM_HERE);
+  promise_resolver_2.Resolve(promise_resolver_1.promise());
+  promise_resolver_3.Resolve(promise_resolver_2.promise());
+
+  RunLoop run_loop;
+  promise_resolver_3.promise().ThenHere(FROM_HERE,
+                                        BindLambdaForTesting([&](int result) {
+                                          EXPECT_EQ(123, result);
+                                          run_loop.Quit();
+                                        }));
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      BindLambdaForTesting([&]() { promise_resolver_1.Resolve(123); }));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CurriedVoidPromiseModified) {
+  for (size_t i = 0; i < 1000; ++i) {
+    Promise<void> p = Promise<void>::CreateResolved(FROM_HERE);
+    std::unique_ptr<ManualPromiseResolver<int>> promise_resolver =
+        std::make_unique<ManualPromiseResolver<int>>(FROM_HERE);
+    RunLoop run_loop;
+    p.ThenHere(FROM_HERE, BindOnce([](Promise<int> promise) { return promise; },
+                                   promise_resolver->promise()))
+        .ThenHere(FROM_HERE, base::BindOnce([](int v) { EXPECT_EQ(v, 42); }))
+        .ThenHere(FROM_HERE, run_loop.QuitClosure());
+    base::PostTaskWithTraits(FROM_HERE, {}, base::BindLambdaForTesting([&]() {
+                               promise_resolver->Resolve(42);
+                               promise_resolver.reset();
+                             }));
+    run_loop.Run();
+    scoped_task_environment_.RunUntilIdle();
+  }
+}
+
 TEST_F(PromiseTest, PromiseResultReturningAPromise) {
   Promise<int> p = Promise<int>::CreateResolved(FROM_HERE, 1000);
   ManualPromiseResolver<int> promise_resolver(FROM_HERE);
@@ -1476,7 +1549,7 @@
   run_loop.Run();
 }
 
-TEST_F(PromiseTest, MAYBE_MoveOnlyTypeMultipleThensNotAllowed) {
+TEST_F(PromiseTest, MoveOnlyTypeMultipleThensNotAllowed) {
 #if DCHECK_IS_ON()
   Promise<std::unique_ptr<int>> p =
       Promise<std::unique_ptr<int>>::CreateResolved(FROM_HERE,
@@ -1492,7 +1565,7 @@
 #endif
 }
 
-TEST_F(PromiseTest, MAYBE_MoveOnlyTypeMultipleCatchesNotAllowed) {
+TEST_F(PromiseTest, MoveOnlyTypeMultipleCatchesNotAllowed) {
 #if DCHECK_IS_ON()
   auto p = Promise<void, std::unique_ptr<int>>::CreateRejected(
       FROM_HERE, std::make_unique<int>(123));
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index 13182e9..9baffbd 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -2282,7 +2282,8 @@
  public:
   ~MockTaskQueueObserver() override = default;
 
-  MOCK_METHOD2(OnQueueNextWakeUpChanged, void(TaskQueue*, TimeTicks));
+  MOCK_METHOD2(OnPostTask, void(Location, TimeDelta));
+  MOCK_METHOD1(OnQueueNextWakeUpChanged, void(TimeTicks));
 };
 
 }  // namespace
@@ -2293,14 +2294,17 @@
   MockTaskQueueObserver observer;
   queue->SetObserver(&observer);
 
-  // We should get a notification when a task is posted on an empty queue.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(queue.get(), _));
+  // We should get a OnQueueNextWakeUpChanged notification when a task is posted
+  // on an empty queue.
+  EXPECT_CALL(observer, OnPostTask(_, TimeDelta()));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_));
   queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
   sequence_manager()->ReloadEmptyWorkQueues();
   Mock::VerifyAndClearExpectations(&observer);
 
   // But not subsequently.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+  EXPECT_CALL(observer, OnPostTask(_, TimeDelta()));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_)).Times(0);
   queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
   sequence_manager()->ReloadEmptyWorkQueues();
   Mock::VerifyAndClearExpectations(&observer);
@@ -2310,7 +2314,8 @@
   sequence_manager()->DidRunTask();
   sequence_manager()->TakeTask();
   sequence_manager()->DidRunTask();
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(queue.get(), _));
+  EXPECT_CALL(observer, OnPostTask(_, TimeDelta()));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_));
   queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask));
   sequence_manager()->ReloadEmptyWorkQueues();
   Mock::VerifyAndClearExpectations(&observer);
@@ -2330,23 +2335,25 @@
   MockTaskQueueObserver observer;
   queue->SetObserver(&observer);
 
-  // We should get a notification when a delayed task is posted on an empty
-  // queue.
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queue.get(), start_time + delay10s));
+  // We should get OnQueueNextWakeUpChanged notification when a delayed task is
+  // is posted on an empty queue.
+  EXPECT_CALL(observer, OnPostTask(_, delay10s));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(start_time + delay10s));
   queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
                                         delay10s);
   Mock::VerifyAndClearExpectations(&observer);
 
-  // We should not get a notification for a longer delay.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+  // We should not get an OnQueueNextWakeUpChanged notification for a longer
+  // delay.
+  EXPECT_CALL(observer, OnPostTask(_, delay100s));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_)).Times(0);
   queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
                                         delay100s);
   Mock::VerifyAndClearExpectations(&observer);
 
-  // We should get a notification for a shorter delay.
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queue.get(), start_time + delay1s));
+  // We should get an OnQueueNextWakeUpChanged notification for a shorter delay.
+  EXPECT_CALL(observer, OnPostTask(_, delay1s));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(start_time + delay1s));
   queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask), delay1s);
   Mock::VerifyAndClearExpectations(&observer);
 
@@ -2357,8 +2364,8 @@
 
   // When a queue has been enabled, we may get a notification if the
   // TimeDomain's next scheduled wake-up has changed.
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queue.get(), start_time + delay1s));
+  EXPECT_CALL(observer, OnPostTask(_, _)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(start_time + delay1s));
   voter->SetVoteToEnable(true);
   Mock::VerifyAndClearExpectations(&observer);
 
@@ -2369,25 +2376,27 @@
 TEST_P(SequenceManagerTest, TaskQueueObserver_DelayedTaskMultipleQueues) {
   auto queues = CreateTaskQueues(2u);
 
-  MockTaskQueueObserver observer;
-  queues[0]->SetObserver(&observer);
-  queues[1]->SetObserver(&observer);
+  MockTaskQueueObserver observer0;
+  MockTaskQueueObserver observer1;
+  queues[0]->SetObserver(&observer0);
+  queues[1]->SetObserver(&observer1);
 
   TimeTicks start_time = sequence_manager()->NowTicks();
   TimeDelta delay1s(TimeDelta::FromSeconds(1));
   TimeDelta delay10s(TimeDelta::FromSeconds(10));
 
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queues[0].get(), start_time + delay1s))
+  EXPECT_CALL(observer0, OnPostTask(_, delay1s));
+  EXPECT_CALL(observer0, OnQueueNextWakeUpChanged(start_time + delay1s))
       .Times(1);
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queues[1].get(), start_time + delay10s))
+  EXPECT_CALL(observer1, OnPostTask(_, delay10s));
+  EXPECT_CALL(observer1, OnQueueNextWakeUpChanged(start_time + delay10s))
       .Times(1);
   queues[0]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
                                             delay1s);
   queues[1]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask),
                                             delay10s);
-  testing::Mock::VerifyAndClearExpectations(&observer);
+  testing::Mock::VerifyAndClearExpectations(&observer0);
+  testing::Mock::VerifyAndClearExpectations(&observer1);
 
   std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 =
       queues[0]->CreateQueueEnabledVoter();
@@ -2395,29 +2404,33 @@
       queues[1]->CreateQueueEnabledVoter();
 
   // Disabling a queue should not trigger a notification.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+  EXPECT_CALL(observer0, OnPostTask(_, _)).Times(0);
+  EXPECT_CALL(observer0, OnQueueNextWakeUpChanged(_)).Times(0);
   voter0->SetVoteToEnable(false);
-  Mock::VerifyAndClearExpectations(&observer);
+  Mock::VerifyAndClearExpectations(&observer0);
 
-  // Re-enabling it should should also trigger a notification.
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queues[0].get(), start_time + delay1s));
+  // But re-enabling it should should trigger an OnQueueNextWakeUpChanged
+  // notification.
+  EXPECT_CALL(observer0, OnPostTask(_, _)).Times(0);
+  EXPECT_CALL(observer0, OnQueueNextWakeUpChanged(start_time + delay1s));
   voter0->SetVoteToEnable(true);
-  Mock::VerifyAndClearExpectations(&observer);
+  Mock::VerifyAndClearExpectations(&observer0);
 
   // Disabling a queue should not trigger a notification.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+  EXPECT_CALL(observer1, OnPostTask(_, _)).Times(0);
+  EXPECT_CALL(observer1, OnQueueNextWakeUpChanged(_)).Times(0);
   voter1->SetVoteToEnable(false);
-  Mock::VerifyAndClearExpectations(&observer);
+  Mock::VerifyAndClearExpectations(&observer0);
 
-  // Re-enabling it should should trigger a notification.
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queues[1].get(), start_time + delay10s));
+  // But re-enabling it should should trigger a notification.
+  EXPECT_CALL(observer1, OnPostTask(_, _)).Times(0);
+  EXPECT_CALL(observer1, OnQueueNextWakeUpChanged(start_time + delay10s));
   voter1->SetVoteToEnable(true);
-  Mock::VerifyAndClearExpectations(&observer);
+  Mock::VerifyAndClearExpectations(&observer1);
 
   // Tidy up.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(AnyNumber());
+  EXPECT_CALL(observer0, OnQueueNextWakeUpChanged(_)).Times(AnyNumber());
+  EXPECT_CALL(observer1, OnQueueNextWakeUpChanged(_)).Times(AnyNumber());
   queues[0]->ShutdownTaskQueue();
   queues[1]->ShutdownTaskQueue();
 }
@@ -2440,7 +2453,8 @@
 
   // We should get a notification when a delayed task is posted on an empty
   // queue.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _));
+  EXPECT_CALL(observer, OnPostTask(_, _));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_));
   queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask), delay1s);
   Mock::VerifyAndClearExpectations(&observer);
 
@@ -2450,7 +2464,8 @@
 
   AdvanceMockTickClock(delay10s);
 
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _));
+  EXPECT_CALL(observer, OnPostTask(_, _)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_));
   queue->SetTimeDomain(mock_time_domain.get());
   Mock::VerifyAndClearExpectations(&observer);
 
@@ -2481,9 +2496,8 @@
   TimeDelta delay1(TimeDelta::FromSeconds(5));
   TimeDelta delay2(TimeDelta::FromSeconds(10));
 
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queue.get(), start_time + delay1))
-      .Times(1);
+  EXPECT_CALL(observer, OnPostTask(_, _)).Times(AnyNumber());
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(start_time + delay1)).Times(1);
 
   CancelableTask task1(mock_tick_clock());
   CancelableTask task2(mock_tick_clock());
@@ -2502,9 +2516,7 @@
   task1.weak_factory_.InvalidateWeakPtrs();
 
   // Sweeping away canceled delayed tasks should trigger a notification.
-  EXPECT_CALL(observer,
-              OnQueueNextWakeUpChanged(queue.get(), start_time + delay2))
-      .Times(1);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(start_time + delay2)).Times(1);
   sequence_manager()->ReclaimMemory();
 }
 
@@ -3366,7 +3378,7 @@
   voter->SetVoteToEnable(false);
 
   // We should not get a notification for a disabled queue.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_)).Times(0);
 
   std::unique_ptr<MockTimeDomain> domain =
       std::make_unique<MockTimeDomain>(sequence_manager()->NowTicks());
@@ -3485,7 +3497,8 @@
   main_tq->SetObserver(&observer);
 
   // We don't expect the observer to fire if the TaskQueue gets destructed.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+  EXPECT_CALL(observer, OnPostTask(_, _)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_)).Times(0);
   auto task_runner = main_tq->task_runner();
   main_tq = nullptr;
   task_runner->PostTask(FROM_HERE, BindOnce(&NopTask));
@@ -3493,7 +3506,8 @@
   FastForwardUntilNoTasksRemain();
 }
 
-TEST_P(SequenceManagerTest, ObserverNotFiredForDisabledQueuePostTask) {
+TEST_P(SequenceManagerTest,
+       OnQueueNextWakeUpChangedNotFiredForDisabledQueuePostTask) {
   scoped_refptr<TestTaskQueue> main_tq = CreateTaskQueue();
   auto task_runner = main_tq->task_runner();
 
@@ -3504,8 +3518,11 @@
       main_tq->CreateQueueEnabledVoter();
   voter->SetVoteToEnable(false);
 
-  // We don't expect the observer to fire if the TaskQueue gets disabled.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+  EXPECT_CALL(observer, OnPostTask(_, _));
+
+  // We don't expect the OnQueueNextWakeUpChanged to fire if the TaskQueue gets
+  // disabled.
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_)).Times(0);
 
   // Should not fire the observer.
   task_runner->PostTask(FROM_HERE, BindOnce(&NopTask));
@@ -3517,7 +3534,7 @@
 }
 
 TEST_P(SequenceManagerTest,
-       ObserverNotFiredForCrossThreadDisabledQueuePostTask) {
+       OnQueueNextWakeUpChangedNotFiredForCrossThreadDisabledQueuePostTask) {
   scoped_refptr<TestTaskQueue> main_tq = CreateTaskQueue();
   auto task_runner = main_tq->task_runner();
 
@@ -3528,8 +3545,10 @@
       main_tq->CreateQueueEnabledVoter();
   voter->SetVoteToEnable(false);
 
+  EXPECT_CALL(observer, OnPostTask(_, _));
+
   // We don't expect the observer to fire if the TaskQueue gets blocked.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_)).Times(0);
 
   WaitableEvent done_event;
   Thread thread("TestThread");
diff --git a/base/task/sequence_manager/task_queue.cc b/base/task/sequence_manager/task_queue.cc
index dfae4895..a69f9d30 100644
--- a/base/task/sequence_manager/task_queue.cc
+++ b/base/task/sequence_manager/task_queue.cc
@@ -136,7 +136,7 @@
 
   // If we've not been unregistered then this must occur on the main thread.
   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
-  impl_->SetOnNextWakeUpChangedCallback(RepeatingCallback<void(TimeTicks)>());
+  impl_->SetObserver(nullptr);
   impl_->sequence_manager()->ShutdownTaskQueueGracefully(TakeTaskQueueImpl());
 }
 
@@ -323,15 +323,10 @@
   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
   if (!impl_)
     return;
-  if (observer) {
-    // Observer is guaranteed to outlive TaskQueue and TaskQueueImpl lifecycle
-    // is controlled by |this|.
-    impl_->SetOnNextWakeUpChangedCallback(
-        BindRepeating(&TaskQueue::Observer::OnQueueNextWakeUpChanged,
-                      Unretained(observer), Unretained(this)));
-  } else {
-    impl_->SetOnNextWakeUpChangedCallback(RepeatingCallback<void(TimeTicks)>());
-  }
+
+  // Observer is guaranteed to outlive TaskQueue and TaskQueueImpl lifecycle is
+  // controlled by |this|.
+  impl_->SetObserver(observer);
 }
 
 bool TaskQueue::IsOnMainThread() const {
diff --git a/base/task/sequence_manager/task_queue.h b/base/task/sequence_manager/task_queue.h
index 673503b..f57b4a1 100644
--- a/base/task/sequence_manager/task_queue.h
+++ b/base/task/sequence_manager/task_queue.h
@@ -49,6 +49,10 @@
    public:
     virtual ~Observer() = default;
 
+    // Notify observer that a task has been posted on the TaskQueue. Can be
+    // called on any thread.
+    virtual void OnPostTask(Location from_here, TimeDelta delay) = 0;
+
     // Notify observer that the time at which this queue wants to run
     // the next task has changed. |next_wakeup| can be in the past
     // (e.g. TimeTicks() can be used to notify about immediate work).
@@ -58,8 +62,7 @@
     //
     // TODO(altimin): Make it Optional<TimeTicks> to tell
     // observer about cancellations.
-    virtual void OnQueueNextWakeUpChanged(TaskQueue* queue,
-                                          TimeTicks next_wake_up) = 0;
+    virtual void OnQueueNextWakeUpChanged(TimeTicks next_wake_up) = 0;
   };
 
   // Shuts down the queue. All tasks currently queued will be discarded.
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index 27402369..0ad98e8 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -171,6 +171,7 @@
     any_thread_.unregistered = true;
     any_thread_.time_domain = nullptr;
     immediate_incoming_queue.swap(any_thread_.immediate_incoming_queue);
+    any_thread_.task_queue_observer = nullptr;
   }
 
   if (main_thread_only().time_domain)
@@ -178,8 +179,7 @@
 
   main_thread_only().on_task_completed_handler = OnTaskCompletedHandler();
   main_thread_only().time_domain = nullptr;
-  main_thread_only().on_next_wake_up_changed_callback =
-      OnNextWakeUpChangedCallback();
+  main_thread_only().task_queue_observer = nullptr;
   empty_queues_to_reload_handle_.ReleaseAtomicFlag();
 
   // It is possible for a task to hold a scoped_refptr to this, which
@@ -264,6 +264,8 @@
     // See https://crbug.com/901800
     base::internal::CheckedAutoLock lock(any_thread_lock_);
     TimeTicks now;
+    if (any_thread_.task_queue_observer)
+      any_thread_.task_queue_observer->OnPostTask(task.location, TimeDelta());
     bool add_queue_time_to_tasks = sequence_manager_->GetAddQueueTimeToTasks();
     if (delayed_fence_allowed_ || add_queue_time_to_tasks) {
       now = any_thread_.time_domain->Now();
@@ -341,9 +343,12 @@
 
     TimeTicks time_domain_now = main_thread_only().time_domain->Now();
     TimeTicks time_domain_delayed_run_time = time_domain_now + task.delay;
-    if (sequence_manager_->GetAddQueueTimeToTasks()) {
-      task.queue_time = time_domain_now;
+    if (main_thread_only().task_queue_observer) {
+      main_thread_only().task_queue_observer->OnPostTask(task.location,
+                                                         task.delay);
     }
+    if (sequence_manager_->GetAddQueueTimeToTasks())
+      task.queue_time = time_domain_now;
 
     PushOntoDelayedIncomingQueueFromMainThread(
         Task(std::move(task), time_domain_delayed_run_time, sequence_number,
@@ -360,11 +365,12 @@
     {
       base::internal::CheckedAutoLock lock(any_thread_lock_);
       time_domain_now = any_thread_.time_domain->Now();
+      if (any_thread_.task_queue_observer)
+        any_thread_.task_queue_observer->OnPostTask(task.location, task.delay);
     }
     TimeTicks time_domain_delayed_run_time = time_domain_now + task.delay;
-    if (sequence_manager_->GetAddQueueTimeToTasks()) {
+    if (sequence_manager_->GetAddQueueTimeToTasks())
       task.queue_time = time_domain_now;
-    }
 
     PushOntoDelayedIncomingQueue(
         Task(std::move(task), time_domain_delayed_run_time, sequence_number,
@@ -433,9 +439,9 @@
   DCHECK(main_thread_only().immediate_work_queue->Empty());
   main_thread_only().immediate_work_queue->TakeImmediateIncomingQueueTasks();
 
-  if (!main_thread_only().on_next_wake_up_changed_callback.is_null() &&
-      IsQueueEnabled()) {
-    main_thread_only().on_next_wake_up_changed_callback.Run(TimeTicks());
+  if (main_thread_only().task_queue_observer && IsQueueEnabled()) {
+    main_thread_only().task_queue_observer->OnQueueNextWakeUpChanged(
+        TimeTicks());
   }
 }
 
@@ -897,10 +903,10 @@
   }
 
   if (enable) {
-    if (has_pending_immediate_work &&
-        !main_thread_only().on_next_wake_up_changed_callback.is_null()) {
+    if (has_pending_immediate_work && main_thread_only().task_queue_observer) {
       // Delayed work notification will be issued via time domain.
-      main_thread_only().on_next_wake_up_changed_callback.Run(TimeTicks());
+      main_thread_only().task_queue_observer->OnQueueNextWakeUpChanged(
+          TimeTicks());
     }
 
     // Note the selector calls SequenceManager::OnTaskQueueEnabled which posts
@@ -915,10 +921,10 @@
   any_thread_.immediate_work_queue_empty =
       main_thread_only().immediate_work_queue->Empty();
 
-  if (main_thread_only().on_next_wake_up_changed_callback) {
-    // If there's a callback we need a DoWork for the callback to be issued by
-    // ReloadEmptyImmediateWorkQueue. The callback isn't
-    // sent for disabled queues.
+  if (main_thread_only().task_queue_observer) {
+    // If there's an observer we need a DoWork for the callback to be issued by
+    // ReloadEmptyImmediateWorkQueue. The callback isn't sent for disabled
+    // queues.
     any_thread_.post_immediate_task_should_schedule_work = IsQueueEnabled();
   } else {
     // Otherwise we need PostImmediateTaskImpl to ScheduleWork unless the queue
@@ -987,16 +993,17 @@
   }
 }
 
-void TaskQueueImpl::SetOnNextWakeUpChangedCallback(
-    TaskQueueImpl::OnNextWakeUpChangedCallback callback) {
-#if DCHECK_IS_ON()
-  if (callback) {
-    DCHECK(main_thread_only().on_next_wake_up_changed_callback.is_null())
+void TaskQueueImpl::SetObserver(TaskQueue::Observer* observer) {
+  if (observer) {
+    DCHECK(!main_thread_only().task_queue_observer)
         << "Can't assign two different observers to "
-           "blink::scheduler::TaskQueue";
+           "base::sequence_manager:TaskQueue";
   }
-#endif
-  main_thread_only().on_next_wake_up_changed_callback = callback;
+
+  main_thread_only().task_queue_observer = observer;
+
+  base::internal::CheckedAutoLock lock(any_thread_lock_);
+  any_thread_.task_queue_observer = observer;
 }
 
 void TaskQueueImpl::UpdateDelayedWakeUp(LazyNow* lazy_now) {
@@ -1009,10 +1016,10 @@
     return;
   main_thread_only().scheduled_wake_up = wake_up;
 
-  if (wake_up &&
-      !main_thread_only().on_next_wake_up_changed_callback.is_null() &&
+  if (wake_up && main_thread_only().task_queue_observer &&
       !HasPendingImmediateWork()) {
-    main_thread_only().on_next_wake_up_changed_callback.Run(wake_up->time);
+    main_thread_only().task_queue_observer->OnQueueNextWakeUpChanged(
+        wake_up->time);
   }
 
   WakeUpResolution resolution = has_pending_high_resolution_tasks()
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index 24ee810..d5a4ea6 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -121,7 +121,7 @@
   bool BlockedByFence() const;
 
   // Implementation of TaskQueue::SetObserver.
-  void SetOnNextWakeUpChangedCallback(OnNextWakeUpChangedCallback callback);
+  void SetObserver(TaskQueue::Observer* observer);
 
   void UnregisterTaskQueue();
 
@@ -333,8 +333,7 @@
     // See description inside struct AnyThread for details.
     TimeDomain* time_domain;
 
-    // Callback corresponding to TaskQueue::Observer::OnQueueNextChanged.
-    OnNextWakeUpChangedCallback on_next_wake_up_changed_callback;
+    TaskQueue::Observer* task_queue_observer = nullptr;
 
     std::unique_ptr<WorkQueue> delayed_work_queue;
     std::unique_ptr<WorkQueue> immediate_work_queue;
@@ -430,6 +429,8 @@
     // locked before accessing from other threads.
     TimeDomain* time_domain;
 
+    TaskQueue::Observer* task_queue_observer = nullptr;
+
     TaskDeque immediate_incoming_queue;
 
     // True if main_thread_only().immediate_work_queue is empty.
diff --git a/base/task/task_features.cc b/base/task/task_features.cc
index 97428587..55bba7e3 100644
--- a/base/task/task_features.cc
+++ b/base/task/task_features.cc
@@ -28,4 +28,7 @@
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
+const Feature kUseFiveMinutesThreadReclaimTime = {
+    "UseFiveMinutesThreadReclaimTime", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace base
diff --git a/base/task/task_features.h b/base/task/task_features.h
index ac1ae97..be1f70a5 100644
--- a/base/task/task_features.h
+++ b/base/task/task_features.h
@@ -37,6 +37,10 @@
 extern const BASE_EXPORT Feature kUseNativeThreadPool;
 #endif
 
+// Whether threads in the ThreadPool should be reclaimed after being idle for 5
+// minutes, instead of 30 seconds.
+extern const BASE_EXPORT Feature kUseFiveMinutesThreadReclaimTime;
+
 }  // namespace base
 
 #endif  // BASE_TASK_TASK_FEATURES_H_
diff --git a/base/task/task_traits.h b/base/task/task_traits.h
index 32be5e3..cd3f601b 100644
--- a/base/task/task_traits.h
+++ b/base/task/task_traits.h
@@ -66,6 +66,8 @@
   HIGHEST = USER_BLOCKING
 };
 
+using TaskPriorityType = std::underlying_type<TaskPriority>::type;
+
 // Valid shutdown behaviors supported by the thread pool.
 enum class TaskShutdownBehavior : uint8_t {
   // Tasks posted with this mode which have not started executing before
diff --git a/base/task/thread_pool/task_tracker.cc b/base/task/thread_pool/task_tracker.cc
index f75ecd6..2419bc3 100644
--- a/base/task/thread_pool/task_tracker.cc
+++ b/base/task/thread_pool/task_tracker.cc
@@ -306,9 +306,11 @@
                              "UserBlockingTaskPriority_MayBlock")}},
       tracked_ref_factory_(this) {
   // Confirm that all |task_latency_histograms_| have been initialized above.
-  DCHECK(*(&task_latency_histograms_[static_cast<int>(TaskPriority::HIGHEST) +
-                                     1][0] -
-           1));
+  for (TaskPriorityType i = 0; i < kNumTaskPriorities; ++i) {
+    for (TaskPriorityType j = 0; j < kNumBlockingModes; ++j) {
+      DCHECK(task_latency_histograms_[i][j]);
+    }
+  }
 }
 
 TaskTracker::~TaskTracker() = default;
diff --git a/base/task/thread_pool/task_tracker.h b/base/task/thread_pool/task_tracker.h
index f5f717c..346b2dc94 100644
--- a/base/task/thread_pool/task_tracker.h
+++ b/base/task/thread_pool/task_tracker.h
@@ -281,12 +281,16 @@
   // blocking tasks. Intentionally leaked.
   // TODO(scheduler-dev): Consider using STATIC_HISTOGRAM_POINTER_GROUP for
   // these.
-  static constexpr int kNumTaskPriorities =
-      static_cast<int>(TaskPriority::HIGHEST) + 1;
-  HistogramBase* const task_latency_histograms_[kNumTaskPriorities][2];
-  HistogramBase* const heartbeat_latency_histograms_[kNumTaskPriorities][2];
+  static constexpr auto kNumTaskPriorities =
+      static_cast<TaskPriorityType>(TaskPriority::HIGHEST) + 1;
+  static constexpr TaskPriorityType kNumBlockingModes = 2;
+  HistogramBase* const task_latency_histograms_[kNumTaskPriorities]
+                                               [kNumBlockingModes];
+  HistogramBase* const heartbeat_latency_histograms_[kNumTaskPriorities]
+                                                    [kNumBlockingModes];
   HistogramBase* const
-      num_tasks_run_while_queuing_histograms_[kNumTaskPriorities][2];
+      num_tasks_run_while_queuing_histograms_[kNumTaskPriorities]
+                                             [kNumBlockingModes];
 
   // Ensures all state (e.g. dangling cleaned up workers) is coalesced before
   // destroying the TaskTracker (e.g. in test environments).
diff --git a/base/task/thread_pool/thread_pool_impl.cc b/base/task/thread_pool/thread_pool_impl.cc
index 90f05273..7a1a081 100644
--- a/base/task/thread_pool/thread_pool_impl.cc
+++ b/base/task/thread_pool/thread_pool_impl.cc
@@ -176,6 +176,11 @@
 #endif
   }
 
+  const base::TimeDelta suggested_reclaim_time =
+      FeatureList::IsEnabled(kUseFiveMinutesThreadReclaimTime)
+          ? base::TimeDelta::FromMinutes(5)
+          : init_params.suggested_reclaim_time;
+
 #if HAS_NATIVE_THREAD_POOL()
   if (FeatureList::IsEnabled(kUseNativeThreadPool)) {
     static_cast<ThreadGroupNative*>(foreground_thread_group_.get())
@@ -190,15 +195,14 @@
     // of best-effort tasks.
     static_cast<ThreadGroupImpl*>(foreground_thread_group_.get())
         ->Start(init_params.max_num_foreground_threads, max_best_effort_tasks,
-                init_params.suggested_reclaim_time, service_thread_task_runner,
+                suggested_reclaim_time, service_thread_task_runner,
                 worker_thread_observer, worker_environment);
   }
 
   if (background_thread_group_) {
     background_thread_group_->Start(
-        max_best_effort_tasks, max_best_effort_tasks,
-        init_params.suggested_reclaim_time, service_thread_task_runner,
-        worker_thread_observer,
+        max_best_effort_tasks, max_best_effort_tasks, suggested_reclaim_time,
+        service_thread_task_runner, worker_thread_observer,
 #if defined(OS_WIN)
         // COM STA is a backward-compatibility feature for the foreground thread
         // group only.
diff --git a/base/test/mock_callback.h b/base/test/mock_callback.h
index 1715f45..24ad8a3b 100644
--- a/base/test/mock_callback.h
+++ b/base/test/mock_callback.h
@@ -8,7 +8,9 @@
 
 // Analogous to GMock's built-in MockFunction, but for base::Callback instead of
 // std::function. It takes the full callback type as a parameter, so that it can
-// support both OnceCallback and RepeatingCallback.
+// support both OnceCallback and RepeatingCallback. Furthermore, this file
+// defines convenience typedefs in the form of MockOnceCallback<Signature>,
+// MockRepeatingCallback<Signature>, MockOnceClosure and MockRepeatingClosure.
 //
 // Use:
 //   using FooCallback = base::RepeatingCallback<int(std::string)>;
@@ -19,6 +21,15 @@
 //     Foo(callback.Get());
 //   }
 //
+// Or equivalently:
+//
+//   TEST(FooTest, RunsCallbackWithBarArgument) {
+//     base::MockRepeatingCallback<int(std::string)> callback;
+//     EXPECT_CALL(callback, Run("bar")).WillOnce(Return(1));
+//     Foo(callback.Get());
+//   }
+//
+//
 // Can be used with StrictMock and NiceMock. Caller must ensure that it outlives
 // any base::Callback obtained from it.
 
@@ -37,6 +48,14 @@
 template <typename F>
 class MockCallback;
 
+template <typename Signature>
+using MockOnceCallback = MockCallback<OnceCallback<Signature>>;
+template <typename Signature>
+using MockRepeatingCallback = MockCallback<RepeatingCallback<Signature>>;
+
+using MockOnceClosure = MockCallback<OnceClosure>;
+using MockRepeatingClosure = MockCallback<RepeatingClosure>;
+
 template <typename R>
 class MockCallback<RepeatingCallback<R()>> {
  public:
diff --git a/base/test/mock_callback.h.pump b/base/test/mock_callback.h.pump
index 933d9ae..59155276 100644
--- a/base/test/mock_callback.h.pump
+++ b/base/test/mock_callback.h.pump
@@ -14,7 +14,9 @@
 
 // Analogous to GMock's built-in MockFunction, but for base::Callback instead of
 // std::function. It takes the full callback type as a parameter, so that it can
-// support both OnceCallback and RepeatingCallback.
+// support both OnceCallback and RepeatingCallback. Furthermore, this file
+// defines convenience typedefs in the form of MockOnceCallback<Signature>,
+// MockRepeatingCallback<Signature>, MockOnceClosure and MockRepeatingClosure.
 //
 // Use:
 //   using FooCallback = base::RepeatingCallback<int(std::string)>;
@@ -25,6 +27,15 @@
 //     Foo(callback.Get());
 //   }
 //
+// Or equivalently:
+//
+//   TEST(FooTest, RunsCallbackWithBarArgument) {
+//     base::MockRepeatingCallback<int(std::string)> callback;
+//     EXPECT_CALL(callback, Run("bar")).WillOnce(Return(1));
+//     Foo(callback.Get());
+//   }
+//
+//
 // Can be used with StrictMock and NiceMock. Caller must ensure that it outlives
 // any base::Callback obtained from it.
 
@@ -43,6 +54,14 @@
 template <typename F>
 class MockCallback;
 
+template <typename Signature>
+using MockOnceCallback = MockCallback<OnceCallback<Signature>>;
+template <typename Signature>
+using MockRepeatingCallback = MockCallback<RepeatingCallback<Signature>>;
+
+using MockOnceClosure = MockCallback<OnceClosure>;
+using MockRepeatingClosure = MockCallback<RepeatingClosure>;
+
 $range i 0..MAX_ARITY
 $for i [[
 $range j 1..i
diff --git a/base/test/mock_callback_unittest.cc b/base/test/mock_callback_unittest.cc
index b232499..efab282 100644
--- a/base/test/mock_callback_unittest.cc
+++ b/base/test/mock_callback_unittest.cc
@@ -56,5 +56,26 @@
   EXPECT_EQ(42, std::move(two_int_callback).Run(1, 2));
 }
 
+TEST(MockCallbackTest, Typedefs) {
+  static_assert(std::is_same<MockCallback<RepeatingCallback<int()>>,
+                             MockRepeatingCallback<int()>>::value,
+                "Repeating typedef differs for zero args");
+  static_assert(std::is_same<MockCallback<RepeatingCallback<int(int, int)>>,
+                             MockRepeatingCallback<int(int, int)>>::value,
+                "Repeating typedef differs for multiple args");
+  static_assert(std::is_same<MockCallback<RepeatingCallback<void()>>,
+                             MockRepeatingClosure>::value,
+                "Repeating typedef differs for closure");
+  static_assert(std::is_same<MockCallback<OnceCallback<int()>>,
+                             MockOnceCallback<int()>>::value,
+                "Once typedef differs for zero args");
+  static_assert(std::is_same<MockCallback<OnceCallback<int(int, int)>>,
+                             MockOnceCallback<int(int, int)>>::value,
+                "Once typedef differs for multiple args");
+  static_assert(std::is_same<MockCallback<RepeatingCallback<void()>>,
+                             MockRepeatingClosure>::value,
+                "Once typedef differs for closure");
+}
+
 }  // namespace
 }  // namespace base
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index 9ec2eb6..ed48703c 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -327,7 +327,8 @@
               ? nullptr
               : std::make_unique<RunLoop::ScopedRunTimeoutForTest>(
                     TestTimeouts::action_max_timeout(),
-                    MakeExpectedNotRunClosure(FROM_HERE, "Run() timed out."))) {
+                    MakeExpectedNotRunClosure(FROM_HERE,
+                                              "RunLoop::Run() timed out."))) {
   CHECK(now_source == NowSource::REAL_TIME || mock_time_domain_)
       << "NowSource must be REAL_TIME unless we're using mock time";
 
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc
index cb6d5f39..5182fbfd 100644
--- a/base/test/scoped_task_environment_unittest.cc
+++ b/base/test/scoped_task_environment_unittest.cc
@@ -498,13 +498,15 @@
     ScopedTaskEnvironment scoped_task_environment;
 
     // ScopedTaskEnvironment should set a default Run() timeout that fails the
-    // calling test.
+    // calling test (before test_launcher_timeout()).
+
     const RunLoop::ScopedRunTimeoutForTest* run_timeout =
         RunLoop::ScopedRunTimeoutForTest::Current();
-    ASSERT_NE(run_timeout, old_run_timeout);
-    EXPECT_EQ(run_timeout->timeout(), TestTimeouts::action_max_timeout());
+    EXPECT_NE(run_timeout, old_run_timeout);
+    EXPECT_TRUE(run_timeout);
+    EXPECT_LT(run_timeout->timeout(), TestTimeouts::test_launcher_timeout());
     EXPECT_NONFATAL_FAILURE({ run_timeout->on_timeout().Run(); },
-                            "Run() timed out");
+                            "RunLoop::Run() timed out");
   }
 
   EXPECT_EQ(RunLoop::ScopedRunTimeoutForTest::Current(), old_run_timeout);
diff --git a/base/test/test_timeouts.cc b/base/test/test_timeouts.cc
index 5b43672..beee123 100644
--- a/base/test/test_timeouts.cc
+++ b/base/test/test_timeouts.cc
@@ -77,12 +77,7 @@
 // static
 int TestTimeouts::tiny_timeout_ms_ = 100;
 int TestTimeouts::action_timeout_ms_ = 10000;
-#ifndef NDEBUG
-int TestTimeouts::action_max_timeout_ms_ = 45000;
-#else
 int TestTimeouts::action_max_timeout_ms_ = 30000;
-#endif  // NDEBUG
-
 int TestTimeouts::test_launcher_timeout_ms_ = 45000;
 
 // static
diff --git a/base/test/test_timeouts.h b/base/test/test_timeouts.h
index 71983ed..1bdda2a 100644
--- a/base/test/test_timeouts.h
+++ b/base/test/test_timeouts.h
@@ -32,9 +32,11 @@
     return base::TimeDelta::FromMilliseconds(action_timeout_ms_);
   }
 
-  // Timeout longer than the above, but still suitable to use
-  // multiple times in a single test. Use if the timeout above
-  // is not sufficient.
+  // Timeout longer than the above, suitable to wait on success conditions which
+  // can take a while to achieve but still should expire on failure before
+  // |test_launcher_timeout()| terminates the process. Note that
+  // test_launcher_timeout() can be reached nonetheless when multiple such
+  // actions are compounded in the same test.
   static base::TimeDelta action_max_timeout() {
     DCHECK(initialized_);
     return base::TimeDelta::FromMilliseconds(action_max_timeout_ms_);
diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn
index 5aa61d19..67fa0f0 100644
--- a/build/android/BUILD.gn
+++ b/build/android/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
 import("//build/config/python.gni")
+import("//build_overrides/build.gni")
 
 if (enable_java_templates) {
   sun_tools_jar_path = "$root_gen_dir/sun_tools_jar/tools.jar"
@@ -87,8 +88,11 @@
   ]
   data_deps = [
     ":devil_chromium_py",
-    "//third_party/android_platform/development/scripts:stack_py",
   ]
+  if (build_with_chromium) {
+    data_deps +=
+        [ "//third_party/android_platform/development/scripts:stack_py" ]
+  }
   if (is_asan) {
     data_deps += [ "//tools/android/asan/third_party:asan_device_setup" ]
   }
diff --git a/build/android/gradle/android.jinja b/build/android/gradle/android.jinja
index 4a7da29..ade3d50 100644
--- a/build/android/gradle/android.jinja
+++ b/build/android/gradle/android.jinja
@@ -15,11 +15,11 @@
             ]
 {% endif %}
 {% if variables.java_excludes is defined %}
-            java.filter.exclude(
+            java.filter.exclude([
 {% for path in variables.java_excludes %}
                 "{{ path }}",
 {% endfor %}
-            )
+            ])
 {% endif %}
 {% if variables.jni_libs is defined %}
             jniLibs.srcDirs = [
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py
index 77f7350d..7b5fcea 100755
--- a/build/android/gradle/generate_gradle.py
+++ b/build/android/gradle/generate_gradle.py
@@ -240,11 +240,12 @@
 
   def IsValid(self):
     return self.GetType() in (
-      'android_apk',
-      'java_library',
-      "java_annotation_processor",
-      'java_binary',
-      'junit_binary',
+        'android_apk',
+        'android_app_bundle_module',
+        'java_library',
+        "java_annotation_processor",
+        'java_binary',
+        'junit_binary',
     )
 
   def ResZips(self):
@@ -708,7 +709,7 @@
 
 
 def _ExtractFile(zip_path, extracted_path):
-  logging.info('Extracting %s to %s', zip_path, extracted_path)
+  logging.debug('Extracting %s to %s', zip_path, extracted_path)
   with zipfile.ZipFile(zip_path) as z:
     z.extractall(extracted_path)
 
@@ -898,11 +899,15 @@
 
   if args.all:
     # There are many unused libraries, so restrict to those that are actually
-    # used by apks/binaries/tests or that are explicitly mentioned in --targets.
-    main_entries = [e for e in main_entries if (
-        e.GetType() in ('android_apk', 'java_binary', 'junit_binary') or
-        e.GnTarget() in targets_from_args or
-        e.GnTarget().endswith('_test_apk__apk'))]
+    # used by apks/bundles/binaries/tests or that are explicitly mentioned in
+    # --targets.
+    BASE_TYPES = ('android_apk', 'android_app_bundle_module', 'java_binary',
+                  'junit_binary')
+    main_entries = [
+        e for e in main_entries
+        if (e.GetType() in BASE_TYPES or e.GnTarget() in targets_from_args
+            or e.GnTarget().endswith('_test_apk__apk'))
+    ]
 
   if args.split_projects:
     main_entries = _FindAllProjectEntries(main_entries)
diff --git a/build/android/gradle/java.jinja b/build/android/gradle/java.jinja
index 92fe575..7626f61 100644
--- a/build/android/gradle/java.jinja
+++ b/build/android/gradle/java.jinja
@@ -16,11 +16,11 @@
 {% endfor %}
         ]
 {% if main.java_excludes is defined %}
-        java.filter.exclude(
+        java.filter.exclude([
 {% for path in main.java_excludes %}
             "{{ path }}",
 {% endfor %}
-        )
+        ])
 {% endif %}
     }
 }
diff --git a/build/android/gyp/compile_resources.py b/build/android/gyp/compile_resources.py
index 6df8fc37..dff69254 100755
--- a/build/android/gyp/compile_resources.py
+++ b/build/android/gyp/compile_resources.py
@@ -738,6 +738,7 @@
     link_command += ['--version-name', options.version_name]
   if options.proguard_file:
     link_command += ['--proguard', build.proguard_path]
+    link_command += ['--proguard-minimal-keep-rules']
   if options.proguard_file_main_dex:
     link_command += ['--proguard-main-dex', build.proguard_main_dex_path]
   if options.emit_ids_out:
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 24a62ee7..c3d1ff8 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -245,20 +245,6 @@
     <ignore path="AndroidManifest.xml"/>
   </issue>
   <issue id="Overdraw" severity="ignore"/>
-  <issue id="Override">
-    <!-- TODO(crbug.com/635567): Fix this properly. -->
-    <ignore regexp="org/chromium/content/browser/input/ThreadedInputConnection.class"/>
-    <!-- TODO(crbug.com/635567): Fix this properly. -->
-    <ignore regexp="com/android/webview/chromium/ContentSettingsAdapter.class"/>
-    <!-- TODO(crbug.com/635567): Fix this properly. -->
-    <ignore regexp="com/android/webview/chromium/ServiceWorkerControllerAdapter.class"/>
-    <!-- TODO(crbug.com/635567): Fix this properly. -->
-    <ignore regexp="com/android/webview/chromium/ServiceWorkerSettingsAdapter.class"/>
-    <!-- TODO(crbug.com/635567): Fix this properly. -->
-    <ignore regexp="org/chromium/chrome/browser/ChromeActivity.class"/>
-    <!-- TODO(crbug.com/635567): Fix this properly. -->
-    <ignore regexp="org/chromium/chrome/browser/ChromeTabbedActivity.class"/>
-  </issue>
   <issue id="PackageManagerGetSignatures">
     <ignore regexp="chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java"/>
     <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java"/>
diff --git a/build/android/pylib/dex/dex_parser.py b/build/android/pylib/dex/dex_parser.py
index bce6a06..b4f9471 100644
--- a/build/android/pylib/dex/dex_parser.py
+++ b/build/android/pylib/dex/dex_parser.py
@@ -54,24 +54,37 @@
 _TypeItem = collections.namedtuple('TypeItem', 'type_idx')
 _StringDataItem = collections.namedtuple('StringItem', 'utf16_size,data')
 
+DexMethodFullSignature = collections.namedtuple(
+    'DexMethodFullSignature',
+    'class_type,return_type,method_name,parameter_types')
+
 
 class _MemoryItemList(object):
-  """Base class for sections that are composed of repeated memory items."""
+  """Base class for repeated memory items."""
 
-  def __init__(self, reader, offset, size, factory, alignment=None):
+  def __init__(self,
+               reader,
+               offset,
+               size,
+               factory,
+               alignment=None,
+               first_item_offset=None):
     """Creates the item list using the specific item factory.
 
     Args:
       reader: _DexReader used for decoding the memory item.
-      offset: Offset from start of the file to the item list.
+      offset: Offset from start of the file to the item list, serving as the
+        key for some item types.
       size: Number of memory items in the list.
       factory: Function to extract each memory item from a _DexReader.
       alignment: Optional integer specifying the alignment for the memory
         section represented by this list.
+      first_item_offset: Optional, specifies a different offset to use for
+        extracting memory items (default is to use offset).
     """
-    self._offset = offset
-    self._size = size
-    reader.Seek(self._offset)
+    self.offset = offset
+    self.size = size
+    reader.Seek(first_item_offset or offset)
     self._items = [factory(reader) for _ in xrange(size)]
 
     if alignment:
@@ -88,12 +101,12 @@
 
   def __repr__(self):
     item_type_part = ''
-    if self._size != 0:
+    if self.size != 0:
       item_type = type(self._items[0])
       item_type_part = ', item type={}'.format(item_type.__name__)
 
     return '{}(offset={:#x}, size={}{})'.format(
-        type(self).__name__, self._offset, self._size, item_type_part)
+        type(self).__name__, self.offset, self.size, item_type_part)
 
 
 class _TypeIdItemList(_MemoryItemList):
@@ -143,11 +156,19 @@
 class _TypeListItem(_MemoryItemList):
 
   def __init__(self, reader):
-    size = reader.ReadUInt()
     offset = reader.Tell()
+    size = reader.ReadUInt()
     factory = lambda x: _TypeItem(x.ReadUShort())
+    # This is necessary because we need to extract the size of the type list
+    # (in other cases the list size is provided in the header).
+    first_item_offset = reader.Tell()
     super(_TypeListItem, self).__init__(
-        reader, offset, size, factory, alignment=4)
+        reader,
+        offset,
+        size,
+        factory,
+        alignment=4,
+        first_item_offset=first_item_offset)
 
 
 class _TypeListItemList(_MemoryItemList):
@@ -321,8 +342,8 @@
     method_item_list: _MethodIdItemList containing method_id_items.
     string_item_list: _StringItemList containing string_data_items that are
       referenced by index in other sections.
-    type_list_item_list: _TypeListItemList containing lists of _TypeListItems.
-      _TypeListItems are referenced by their offset.
+    type_list_item_list: _TypeListItemList containing _TypeListItems.
+      _TypeListItems are referenced by their offsets from other dex items.
   """
 
   def __init__(self, data):
@@ -340,12 +361,20 @@
 
     type_list_key = _DexMapList.TYPE_TYPE_LIST
     if type_list_key in self.map_list:
+      map_list_item = self.map_list[type_list_key]
       self.type_list_item_list = _TypeListItemList(
-          self.reader, self.map_list[type_list_key].offset,
-          self.map_list[type_list_key].size)
+          self.reader, map_list_item.offset, map_list_item.size)
     else:
       self.type_list_item_list = _TypeListItemList(self.reader, 0, 0)
 
+  def GetString(self, string_item_idx):
+    string_item = self.string_item_list[string_item_idx]
+    return string_item.data
+
+  def GetTypeString(self, type_item_idx):
+    type_item = self.type_item_list[type_item_idx]
+    return self.GetString(type_item.descriptor_idx)
+
   def __repr__(self):
     items = [
         self.reader.header,
@@ -358,7 +387,50 @@
     return '\n'.join(str(item) for item in items)
 
 
-if __name__ == '__main__':
+def _MethodSignaturesFromDexFile(dexfile):
+  methods = []
+  type_lists_by_offset = {
+      type_list.offset: type_list
+      for type_list in dexfile.type_list_item_list
+  }
+
+  for method_item in dexfile.method_item_list:
+    class_name_string = dexfile.GetTypeString(method_item.type_idx)
+    method_name_string = dexfile.GetString(method_item.name_idx)
+
+    proto_item = dexfile.proto_item_list[method_item.proto_idx]
+    return_type_string = dexfile.GetTypeString(proto_item.return_type_idx)
+    parameter_types = ()
+    if proto_item.parameters_off:
+      type_list = type_lists_by_offset[proto_item.parameters_off]
+      parameter_types = tuple(
+          dexfile.GetTypeString(item.type_idx) for item in type_list)
+
+    methods.append(
+        DexMethodFullSignature(class_name_string, return_type_string,
+                               method_name_string, parameter_types))
+
+  return methods
+
+
+def CountUniqueDexMethods(dexfiles):
+  """Returns the number of unique methods given an iterable of dex files.
+
+  For method counts, most tools count the total number of defined dex methods.
+  In the multi-dex case, some method items are duplicated across dex files.
+  This function dedupes method definitions by converting dex method items into
+  string representations of the full method signatures.
+
+  Args:
+    dexfiles: Iterable of DexFile objects to count unique methods for.
+  """
+  unique_methods = set()
+  for dexfile in dexfiles:
+    unique_methods.update(_MethodSignaturesFromDexFile(dexfile))
+  return len(unique_methods)
+
+
+def main():
   parser = argparse.ArgumentParser(description='Dump dex contents to stdout.')
   parser.add_argument('dexfile', help='Input dex file path.')
   args = parser.parse_args()
@@ -366,3 +438,7 @@
   with open(args.dexfile) as f:
     dexfile = DexFile(bytearray(f.read()))
     print('{} contents:\n\n{}'.format(args.dexfile, dexfile))
+
+
+if __name__ == '__main__':
+  main()
diff --git a/build/android/pylib/symbols/stack_symbolizer.py b/build/android/pylib/symbols/stack_symbolizer.py
index 123b726..4173741 100644
--- a/build/android/pylib/symbols/stack_symbolizer.py
+++ b/build/android/pylib/symbols/stack_symbolizer.py
@@ -18,13 +18,13 @@
 
 
 def _DeviceAbiToArch(device_abi):
-    # The order of this list is significant to find the more specific match
-    # (e.g., arm64) before the less specific (e.g., arm).
-    arches = ['arm64', 'arm', 'x86_64', 'x86_64', 'x86', 'mips']
-    for arch in arches:
-      if arch in device_abi:
-        return arch
-    raise RuntimeError('Unknown device ABI: %s' % device_abi)
+  # The order of this list is significant to find the more specific match
+  # (e.g., arm64) before the less specific (e.g., arm).
+  arches = ['arm64', 'arm', 'x86_64', 'x86_64', 'x86', 'mips']
+  for arch in arches:
+    if arch in device_abi:
+      return arch
+  raise RuntimeError('Unknown device ABI: %s' % device_abi)
 
 
 class Symbolizer(object):
@@ -58,6 +58,11 @@
     Yields:
       A string for each line of resolved stack output.
     """
+    if not os.path.exists(_STACK_TOOL):
+      logging.warning('%s missing. Unable to resolve native stack traces.',
+                      _STACK_TOOL)
+      return
+
     arch = _DeviceAbiToArch(device_abi)
     if not arch:
       logging.warning('No device_abi can be found.')
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 1acd1c43..b0931c2 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -41,7 +41,6 @@
 # Targets that match the whitelist but are not actually java targets.
 _java_target_blacklist = [ "*:*_unpack_aar" ]
 
-_default_proguard_jar_path = "//third_party/proguard/lib/proguard.jar"
 _r8_path = "//third_party/r8/lib/r8.jar"
 
 _dexdump_path = "$android_sdk_build_tools/dexdump"
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index eb2052a..70f4929 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -198,20 +198,8 @@
   # JNI target implementation. See generate_jni or generate_jar_jni for usage.
   template("generate_jni_impl") {
     set_sources_assignment_filter([])
-    forward_variables_from(invoker, [ "testonly" ])
 
     _jni_output_dir = "${target_gen_dir}/${target_name}"
-
-    # TODO(crbug.com/964169): Stop generating long output paths, remove the
-    # jni_package variable, and remove the config target below once includes
-    # have been updated to use full paths.
-    _use_long_paths = defined(invoker.jni_package)
-    if (_use_long_paths) {
-      _base_output_dir = "${target_gen_dir}/${target_name}"
-      _package_output_dir = "${_base_output_dir}/${invoker.jni_package}"
-      _jni_output_dir = "${_package_output_dir}/jni"
-    }
-
     if (defined(invoker.jni_generator_include)) {
       _jni_generator_include = invoker.jni_generator_include
       _jni_generator_include_deps = []
@@ -228,11 +216,21 @@
       ]
     }
 
-    _jni_target_name = "${target_name}__jni"
-    action_with_pydeps(_jni_target_name) {
+    action_with_pydeps(target_name) {
       # The sources aren't compiled so don't check their dependencies.
       check_includes = false
       script = "//base/android/jni_generator/jni_generator.py"
+      forward_variables_from(invoker,
+                             [
+                               "deps",
+                               "public_deps",
+                               "visibility",
+                               "testonly",
+                             ])
+      if (!defined(public_deps)) {
+        public_deps = []
+      }
+      public_deps += _jni_generator_include_deps
       inputs = []
       args = [
         "--ptr_type=long",
@@ -293,32 +291,6 @@
         args += [ "--enable_tracing" ]
       }
     }
-
-    if (_use_long_paths) {
-      config("jni_includes_${target_name}") {
-        include_dirs = [
-          _base_output_dir,
-          _package_output_dir,
-        ]
-      }
-    }
-
-    group(target_name) {
-      forward_variables_from(invoker,
-                             [
-                               "deps",
-                               "public_deps",
-                               "visibility",
-                             ])
-      if (!defined(public_deps)) {
-        public_deps = []
-      }
-      public_deps += [ ":$_jni_target_name" ]
-      public_deps += _jni_generator_include_deps
-      if (_use_long_paths) {
-        public_configs = [ ":jni_includes_${target_name}" ]
-      }
-    }
   }
 
   # Declare a jni target
@@ -330,16 +302,18 @@
   #
   # Variables
   #   sources: list of .java files to generate jni for
-  #   jni_package: subdirectory path for generated bindings
   #   namespace: Specify the namespace for the generated header file.
+  #   deps, public_deps: As normal
   #
   # Example
+  #   # Target located in base/BUILD.gn.
   #   generate_jni("foo_jni") {
+  #     # Generates gen/base/foo_jni/Foo_jni.h
+  #     # To use: #include "base/foo_jni/Foo_jni.h"
   #     sources = [
   #       "android/java/src/org/chromium/foo/Foo.java",
-  #       "android/java/src/org/chromium/foo/FooUtil.java",
+  #       ...,
   #     ]
-  #     jni_package = "foo"
   #   }
   template("generate_jni") {
     generate_jni_impl(target_name) {
@@ -357,20 +331,20 @@
   # Variables
   #   classes: list of .class files in the jar to generate jni for. These should
   #     include the full path to the .class file.
-  #   jni_package: subdirectory path for generated bindings
   #   jar_file: the path to the .jar. If not provided, will default to the sdk's
   #     android.jar
   #   always_mangle: Mangle all generated method names. By default, the script
   #     only mangles methods that cause ambiguity due to method overload.
-  #
   #   deps, public_deps: As normal
   #
   # Example
+  #   # Target located in base/BUILD.gn.
   #   generate_jar_jni("foo_jni") {
+  #     # Generates gen/base/foo_jni/Runnable_jni.h
+  #     # To use: #include "base/foo_jni/Runnable_jni.h"
   #     classes = [
   #       "android/view/Foo.class",
   #     ]
-  #     jni_package = "foo"
   #   }
   template("generate_jar_jni") {
     generate_jni_impl(target_name) {
@@ -4695,7 +4669,6 @@
     }
 
     # Generate a wrapper script for the bundle.
-    _android_aapt2_dir = android_sdk_tools_bundle_aapt2_dir
     _android_aapt2_path = android_sdk_tools_bundle_aapt2
 
     _bundle_apks_path = "$_bundle_base_path/$_bundle_name.apks"
@@ -4715,7 +4688,6 @@
       data = [
         _bundle_wrapper_script_path,
         _android_aapt2_path,
-        _android_aapt2_dir + "/lib64/libc++.so",
         _keystore_path,
       ]
       data_deps = [
diff --git a/build/config/fuchsia/testing_sandbox_policy b/build/config/fuchsia/testing_sandbox_policy
index 060dbb4..39c72ef 100644
--- a/build/config/fuchsia/testing_sandbox_policy
+++ b/build/config/fuchsia/testing_sandbox_policy
@@ -15,6 +15,7 @@
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
+      "fuchsia.posix.socket.Provider",
       "fuchsia.process.Launcher",
       "fuchsia.sys.Environment",
       "fuchsia.sys.Launcher",
diff --git a/build/config/ios/asset_catalog.gni b/build/config/ios/asset_catalog.gni
index 460cd962..68979b07 100644
--- a/build/config/ios/asset_catalog.gni
+++ b/build/config/ios/asset_catalog.gni
@@ -134,6 +134,12 @@
     asset_type = "appiconset"
   }
 }
+template("colorset") {
+  asset_catalog(target_name) {
+    forward_variables_from(invoker, "*", [ "asset_type" ])
+    asset_type = "colorset"
+  }
+}
 template("imageset") {
   asset_catalog(target_name) {
     forward_variables_from(invoker, "*", [ "asset_type" ])
diff --git a/build/fuchsia/device_target.py b/build/fuchsia/device_target.py
index c989b16..0d7b4473 100644
--- a/build/fuchsia/device_target.py
+++ b/build/fuchsia/device_target.py
@@ -153,7 +153,7 @@
                             stdout=subprocess.PIPE,
                             stderr=open(os.devnull, 'w'))
 
-    output = proc.communicate()[0].strip().split('\n')
+    output = set(proc.communicate()[0].strip().split('\n'))
 
     if proc.returncode != 0:
       return False
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index edd30db..e23209f2 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8909632060298347488
\ No newline at end of file
+8909409475142840640
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 4783789..0502f82 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8909635704923071440
\ No newline at end of file
+8909412159513317936
\ No newline at end of file
diff --git a/build/linux/sysroot_scripts/generated_package_lists/sid.amd64 b/build/linux/sysroot_scripts/generated_package_lists/sid.amd64
index 5b11066e..91a8d94 100644
--- a/build/linux/sysroot_scripts/generated_package_lists/sid.amd64
+++ b/build/linux/sysroot_scripts/generated_package_lists/sid.amd64
@@ -1,329 +1,346 @@
-http://ftp.us.debian.org/debian/pool/main/a/alsa-lib/libasound2_1.1.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/alsa-lib/libasound2-dev_1.1.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/atk1.0/libatk1.0-0_2.30.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/atk1.0/libatk1.0-dev_2.30.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/at-spi2-atk/libatk-bridge2.0-0_2.30.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/at-spi2-atk/libatk-bridge2.0-dev_2.30.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/at-spi2-core/libatspi2.0-0_2.30.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/at-spi2-core/libatspi2.0-dev_2.30.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/attr/libattr1_2.4.47-2+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/audit/libaudit1_2.8.4-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/avahi/libavahi-client3_0.7-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/a/avahi/libavahi-common3_0.7-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/b/bluez/libbluetooth3_5.50-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/b/bluez/libbluetooth-dev_5.50-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/b/brltty/libbrlapi0.6_5.6-5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/b/brltty/libbrlapi-dev_5.6-5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/cairo/libcairo2_1.15.12-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/cairo/libcairo2-dev_1.15.12-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/cairo/libcairo-gobject2_1.15.12-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/cairo/libcairo-script-interpreter2_1.15.12-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/colord/libcolord2_1.4.3-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/cups/libcups2_2.2.8-5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/cups/libcups2-dev_2.2.8-5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/cups/libcupsimage2_2.2.8-5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/c/cups/libcupsimage2-dev_2.2.8-5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/d/dbus-glib/libdbus-glib-1-2_0.110-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/d/dbus/libdbus-1-3_1.12.10-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/d/dbus/libdbus-1-dev_1.12.10-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/e/e2fsprogs/comerr-dev_2.1-1.44.4-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/e/e2fsprogs/libcom-err2_1.44.4-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/e/elfutils/libelf1_0.170-0.5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/e/elfutils/libelf-dev_0.170-0.5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/e/expat/libexpat1_2.2.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/e/expat/libexpat1-dev_2.2.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/f/flac/libflac8_1.3.2-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/f/flac/libflac-dev_1.3.2-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/f/fontconfig/libfontconfig1_2.13.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/f/fontconfig/libfontconfig1-dev_2.13.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/f/freetype/libfreetype6_2.8.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/f/freetype/libfreetype6-dev_2.8.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/f/fribidi/libfribidi0_1.0.5-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/f/fribidi/libfribidi-dev_1.0.5-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-6/libasan3_6.4.0-22_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-6/libgcc-6-dev_6.4.0-22_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-6/libstdc++-6-dev_6.4.0-22_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-7/libcilkrts5_7.3.0-29_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-7/libubsan0_7.3.0-29_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/libatomic1_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/libgcc1_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/libgomp1_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/libitm1_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/liblsan0_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/libmpx2_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/libquadmath0_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/libstdc++6_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gcc-8/libtsan0_8.2.0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gdk-pixbuf/libgdk-pixbuf2.0-0_2.38.0+dfsg-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gdk-pixbuf/libgdk-pixbuf2.0-dev_2.38.0+dfsg-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/glib2.0/libglib2.0-0_2.58.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/glib2.0/libglib2.0-dev_2.58.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/glibc/libc6_2.27-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/glibc/libc6-dev_2.27-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gmp/libgmp10_6.1.2+dfsg-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gnutls28/libgnutls28-dev_3.5.19-1+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gnutls28/libgnutls30_3.5.19-1+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gnutls28/libgnutls-dane0_3.5.19-1+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gnutls28/libgnutls-openssl27_3.5.19-1+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gnutls28/libgnutlsxx28_3.5.19-1+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/graphene/libgraphene-1.0-0_1.8.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/graphene/libgraphene-1.0-dev_1.8.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/graphite2/libgraphite2-3_1.3.12-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/graphite2/libgraphite2-dev_1.3.12-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gtk+2.0/libgtk2.0-0_2.24.32-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gtk+3.0/libgtk-3-0_3.24.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gtk+3.0/libgtk-3-dev_3.24.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gtk+4.0/libgtk-4-0_3.91.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/g/gtk+4.0/libgtk-4-dev_3.91.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/h/harfbuzz/libharfbuzz0b_1.9.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/h/harfbuzz/libharfbuzz-dev_1.9.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/h/harfbuzz/libharfbuzz-gobject0_1.9.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/h/harfbuzz/libharfbuzz-icu0_1.9.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/i/icu-le-hb/libicu-le-hb0_1.0.3+git161113-5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/i/icu/libicu57_57.1-9_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/i/icu/libicu60_60.2-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/j/jbigkit/libjbig0_2.1-3.1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/j/json-glib/libjson-glib-1.0-0_1.4.2-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/keyutils/libkeyutils1_1.5.9-9.3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/krb5-multidev_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libgssapi-krb5-2_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libgssrpc4_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libk5crypto3_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libkadm5clnt-mit11_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libkadm5srv-mit11_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libkdb5-9_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libkrb5-3_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libkrb5-dev_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/k/krb5/libkrb5support0_1.16.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/liba/libappindicator/libappindicator1_0.4.92-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/liba/libappindicator/libappindicator3-1_0.4.92-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/liba/libappindicator/libappindicator3-dev_0.4.92-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/liba/libappindicator/libappindicator-dev_0.4.92-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/liba/libasyncns/libasyncns0_0.8-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libb/libbsd/libbsd0_0.9.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libc/libcap2/libcap2_2.25-1.2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libc/libcap2/libcap-dev_2.25-1.2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libc/libcap-ng/libcap-ng0_0.7.9-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdatrie/libdatrie1_0.2.12-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr490+repack1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdbusmenu/libdbusmenu-glib-dev_18.10.20180917~bzr490+repack1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr490+repack1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdbusmenu/libdbusmenu-gtk4_18.10.20180917~bzr490+repack1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdrm/libdrm2_2.4.94-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.94-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdrm/libdrm-dev_2.4.94-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdrm/libdrm-intel1_2.4.94-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdrm/libdrm-nouveau2_2.4.94-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libd/libdrm/libdrm-radeon1_2.4.94-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libe/libepoxy/libepoxy0_1.5.2-0.3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libe/libepoxy/libepoxy-dev_1.5.2-0.3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libe/libevent/libevent-2.1-6_2.1.8-stable-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libf/libffi/libffi6_3.2.1-8_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libf/libffi/libffi7_3.3~rc0-7_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libf/libffi/libffi-dev_3.2.1-8_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libgcrypt20/libgcrypt20_1.8.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libgcrypt20/libgcrypt20-dev_1.8.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libglvnd/libegl1_1.1.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libglvnd/libgl1_1.1.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libglvnd/libgles1_1.1.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libglvnd/libgles2_1.1.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libglvnd/libglvnd0_1.1.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libglvnd/libglvnd-dev_1.1.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libglvnd/libglx0_1.1.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libglvnd/libopengl0_1.1.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libgnome-keyring/libgnome-keyring0_3.12.0-1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libgnome-keyring/libgnome-keyring-dev_3.12.0-1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libgpg-error/libgpg-error0_1.32-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libg/libgpg-error/libgpg-error-dev_1.32-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libi/libice/libice6_1.0.9-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libi/libidl/libidl-2-0_0.8.14-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libi/libidn2/libidn2-0_2.0.5-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libi/libidn/libidn11_1.33-2.2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libi/libindicator/libindicator3-7_0.5.0-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libi/libindicator/libindicator7_0.5.0-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libj/libjpeg-turbo/libjpeg62-turbo_1.5.2-2+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libj/libjpeg-turbo/libjpeg62-turbo-dev_1.5.2-2+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libj/libjsoncpp/libjsoncpp1_1.7.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libj/libjsoncpp/libjsoncpp-dev_1.7.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libn/libnss-db/libnss-db_2.2.3pre1-6+b5_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libo/libogg/libogg0_1.3.2-1+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libo/libogg/libogg-dev_1.3.2-1+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libp/libpciaccess/libpciaccess0_0.14-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libp/libpng1.6/libpng16-16_1.6.34-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libp/libpng1.6/libpng-dev_1.6.34-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libp/libpsl/libpsl5_0.20.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.3-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libr/librest/librest-0.7-0_0.8.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libs/libselinux/libselinux1_2.8-1+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libs/libsm/libsm6_1.2.2-1+b3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libs/libsndfile/libsndfile1_1.0.28-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libs/libsoup2.4/libsoup2.4-1_2.64.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libs/libsoup2.4/libsoup-gnome2.4-1_2.64.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libt/libtasn1-6/libtasn1-6_4.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libt/libthai/libthai0_0.1.28-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libt/libtool/libltdl7_2.4.6-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libu/libunistring/libunistring2_0.9.10-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libva/libva2_2.3.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libva/libva-dev_2.3.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libva/libva-drm2_2.3.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libva/libva-glx2_2.3.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libva/libva-wayland2_2.3.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libva/libva-x11-2_2.3.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libvorbis/libvorbis0a_1.3.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libvorbis/libvorbisenc2_1.3.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libvpx/libvpx5_1.7.0-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libv/libvpx/libvpx-dev_1.7.0-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libw/libwebp/libwebp6_0.6.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libw/libwebp/libwebpdemux2_0.6.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libw/libwebp/libwebp-dev_0.6.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libw/libwebp/libwebpmux3_0.6.1-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libx11/libx11-6_1.6.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libx11/libx11-dev_1.6.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libx11/libx11-xcb1_1.6.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libx11/libx11-xcb-dev_1.6.6-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxau/libxau6_1.0.8-1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxau/libxau-dev_1.0.8-1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb1_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb1-dev_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-dri2-0_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-dri2-0-dev_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-dri3-0_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-glx0_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-glx0-dev_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-present0_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-render0_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-render0-dev_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-shm0_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-shm0-dev_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-sync1_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcb/libxcb-xfixes0_1.13-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcomposite/libxcomposite1_0.4.4-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcomposite/libxcomposite-dev_0.4.4-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcursor/libxcursor1_1.1.15-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxcursor/libxcursor-dev_1.1.15-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxdamage/libxdamage1_1.1.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxdamage/libxdamage-dev_1.1.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxdmcp/libxdmcp6_1.1.2-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxdmcp/libxdmcp-dev_1.1.2-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxext/libxext6_1.3.3-1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxext/libxext-dev_1.3.3-1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxfixes/libxfixes3_5.0.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxfixes/libxfixes-dev_5.0.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxi/libxi6_1.7.9-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxi/libxi-dev_1.7.9-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxinerama/libxinerama1_1.1.4-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxinerama/libxinerama-dev_1.1.4-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxkbcommon/libxkbcommon0_0.8.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxkbcommon/libxkbcommon-dev_0.8.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxml2/libxml2_2.9.4+dfsg1-7+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxml2/libxml2-dev_2.9.4+dfsg1-7+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxrandr/libxrandr2_1.5.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxrandr/libxrandr-dev_1.5.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxrender/libxrender1_0.9.10-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxrender/libxrender-dev_0.9.10-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxshmfence/libxshmfence1_1.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxslt/libxslt1.1_1.1.32-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxslt/libxslt1-dev_1.1.32-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxss/libxss1_1.2.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxss/libxss-dev_1.2.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxt/libxt6_1.1.5-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxt/libxt-dev_1.1.5-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxtst/libxtst6_1.2.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxtst/libxtst-dev_1.2.3-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxxf86vm/libxxf86vm1_1.1.4-1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/libx/libxxf86vm/libxxf86vm-dev_1.1.4-1+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/l/lcms2/liblcms2-2_2.9-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/l/linux/linux-libc-dev_4.18.10-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/l/lz4/liblz4-1_1.8.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/l/lzo2/liblzo2-2_2.10-0.1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/libegl1-mesa_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/libegl1-mesa-dev_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/libgbm1_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/libgbm-dev_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/libgl1-mesa-dev_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/libgl1-mesa-glx_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/libglapi-mesa_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/libwayland-egl1-mesa_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/mesa/mesa-common-dev_18.1.8-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/minizip/libminizip1_1.1-8+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/m/minizip/libminizip-dev_1.1-8+b1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/n/nettle/libhogweed4_3.4-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/n/nettle/libnettle6_3.4-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/n/nspr/libnspr4_4.20-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/n/nspr/libnspr4-dev_4.20-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/n/nss/libnss3_3.39-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/n/nss/libnss3-dev_3.39-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/o/openssl1.0/libssl1.0.2_1.0.2o-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/o/openssl/libssl1.1_1.1.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/o/openssl/libssl-dev_1.1.1-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/o/opus/libopus0_1.3~beta+20180518-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/o/opus/libopus-dev_1.3~beta+20180518-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/o/orbit2/liborbit-2-0_2.14.19-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/o/orbit2/liborbit2_2.14.19-4_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/p11-kit/libp11-kit0_0.23.14-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pam/libpam0g_1.1.8-3.8_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pam/libpam0g-dev_1.1.8-3.8_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pango1.0/libpango-1.0-0_1.42.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pango1.0/libpango1.0-dev_1.42.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pango1.0/libpangocairo-1.0-0_1.42.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pango1.0/libpangoft2-1.0-0_1.42.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pango1.0/libpangoxft-1.0-0_1.42.4-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pangox-compat/libpangox-1.0-0_0.0.2-5+b2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pciutils/libpci3_3.5.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pciutils/libpci-dev_3.5.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pcre3/libpcre16-3_8.39-11_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pcre3/libpcre32-3_8.39-11_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pcre3/libpcre3_8.39-11_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pcre3/libpcre3-dev_8.39-11_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pcre3/libpcrecpp0v5_8.39-11_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pipewire/libpipewire-0.2-1_0.2.3-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pipewire/libpipewire-0.2-dev_0.2.3-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pipewire/libspa-lib-0.1-dev_0.2.3-3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pixman/libpixman-1-0_0.34.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pixman/libpixman-1-dev_0.34.0-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pulseaudio/libpulse0_12.2-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pulseaudio/libpulse-dev_12.2-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/p/pulseaudio/libpulse-mainloop-glib0_12.2-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/r/re2/libre2-4_20180901+dfsg-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/r/re2/libre2-dev_20180901+dfsg-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/shared-mime-info/shared-mime-info_1.10-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/snappy/libsnappy1v5_1.1.7-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/snappy/libsnappy-dev_1.1.7-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/speech-dispatcher/libspeechd2_0.8.8-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/speech-dispatcher/libspeechd-dev_0.8.8-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/speech-dispatcher/speech-dispatcher_0.8.8-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/sqlite3/libsqlite3-0_3.25.2-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/systemd/libsystemd0_239-10_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/systemd/libudev1_239-10_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/s/systemd/libudev-dev_239-10_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/t/tcp-wrappers/libwrap0_7.6.q-27_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/t/tiff/libtiff5_4.0.9-6_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/u/unbound/libunbound8_1.8.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/u/util-linux/libblkid1_2.32.1-0.1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/u/util-linux/libmount1_2.32.1-0.1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/u/util-linux/libuuid1_2.32.1-0.1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/u/util-linux/uuid-dev_2.32.1-0.1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/v/vulkan/libvulkan1_1.1.73+dfsg-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/w/wayland/libwayland-client0_1.16.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/w/wayland/libwayland-cursor0_1.16.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/w/wayland/libwayland-dev_1.16.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/w/wayland/libwayland-egl1_1.16.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/w/wayland/libwayland-server0_1.16.0-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/w/wayland-protocols/wayland-protocols_1.16-1_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xft/libxft2_2.3.2-2_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-composite-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-damage-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-fixes-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-input-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-kb-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-randr-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-record-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-render-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-scrnsaver-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-xext-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xorgproto/x11proto-xinerama-dev_2018.4-4_all.deb
-http://ftp.us.debian.org/debian/pool/main/x/xz-utils/liblzma5_5.2.2-1.3_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/z/zlib/zlib1g_1.2.11.dfsg-1_amd64.deb
-http://ftp.us.debian.org/debian/pool/main/z/zlib/zlib1g-dev_1.2.11.dfsg-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/alsa-lib/libasound2_1.1.7-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/alsa-lib/libasound2-dev_1.1.7-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/atk1.0/libatk1.0-0_2.30.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/atk1.0/libatk1.0-dev_2.30.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/at-spi2-atk/libatk-bridge2.0-0_2.30.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/at-spi2-atk/libatk-bridge2.0-dev_2.30.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/at-spi2-core/libatspi2.0-0_2.30.0-5_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/at-spi2-core/libatspi2.0-dev_2.30.0-5_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/attr/libattr1_2.4.47-2+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/audit/libaudit1_2.8.4-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/avahi/libavahi-client3_0.7-4+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/a/avahi/libavahi-common3_0.7-4+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/b/bluez/libbluetooth3_5.50-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/b/bluez/libbluetooth-dev_5.50-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/b/brltty/libbrlapi0.6_5.6-7+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/b/brltty/libbrlapi-dev_5.6-7+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/cairo/libcairo2_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/cairo/libcairo2-dev_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/cairo/libcairo-gobject2_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/cairo/libcairo-script-interpreter2_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/colord/libcolord2_1.4.3-3+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/cups/libcups2_2.2.10-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/cups/libcups2-dev_2.2.10-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/cups/libcupsimage2_2.2.10-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/c/cups/libcupsimage2-dev_2.2.10-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/d/dbus-glib/libdbus-glib-1-2_0.110-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/d/dbus/libdbus-1-3_1.12.12-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/d/dbus/libdbus-1-dev_1.12.12-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/e/e2fsprogs/comerr-dev_2.1-1.44.4-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/e/e2fsprogs/libcom-err2_1.44.4-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/e/elfutils/libelf1_0.175-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/e/elfutils/libelf-dev_0.175-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/e/expat/libexpat1_2.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/e/expat/libexpat1-dev_2.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/f/flac/libflac8_1.3.2-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/f/flac/libflac-dev_1.3.2-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/f/fontconfig/libfontconfig1_2.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/f/fontconfig/libfontconfig1-dev_2.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/f/freetype/libfreetype6_2.9.1-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/f/freetype/libfreetype6-dev_2.9.1-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/f/fribidi/libfribidi0_1.0.5-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/f/fribidi/libfribidi-dev_1.0.5-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-6/lib32asan3_6.5.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-6/lib32gcc-6-dev_6.5.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-6/lib32stdc++-6-dev_6.5.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-6/libasan3_6.5.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-6/libgcc-6-dev_6.5.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-6/libstdc++-6-dev_6.5.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-7/lib32cilkrts5_7.4.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-7/lib32ubsan0_7.4.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-7/libcilkrts5_7.4.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-7/libubsan0_7.4.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/lib32atomic1_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/lib32gcc1_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/lib32gomp1_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/lib32itm1_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/lib32mpx2_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/lib32quadmath0_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/lib32stdc++6_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/libatomic1_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/libgcc1_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/libgomp1_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/libitm1_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/liblsan0_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/libmpx2_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/libquadmath0_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/libstdc++6_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gcc-8/libtsan0_8.2.0-12_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf2.0-0_2.38.0+dfsg-6_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gdk-pixbuf/libgdk-pixbuf2.0-dev_2.38.0+dfsg-6_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/glib2.0/libglib2.0-0_2.58.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/glib2.0/libglib2.0-dev_2.58.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/glibc/libc6_2.28-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/glibc/libc6-dev_2.28-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/glibc/libc6-i386_2.28-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gmp/libgmp10_6.1.2+dfsg-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gnutls28/libgnutls28-dev_3.5.19-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gnutls28/libgnutls30_3.5.19-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gnutls28/libgnutls-dane0_3.5.19-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gnutls28/libgnutls-openssl27_3.5.19-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gnutls28/libgnutlsxx28_3.5.19-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/graphene/libgraphene-1.0-0_1.8.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/graphene/libgraphene-1.0-dev_1.8.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/graphite2/libgraphite2-3_1.3.12-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/graphite2/libgraphite2-dev_1.3.12-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gtk+2.0/libgtk2.0-0_2.24.32-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gtk+3.0/libgtk-3-0_3.24.2-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gtk+3.0/libgtk-3-dev_3.24.2-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gtk+4.0/libgtk-4-0_3.91.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/g/gtk+4.0/libgtk-4-dev_3.91.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/h/harfbuzz/libharfbuzz0b_2.1.1-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/h/harfbuzz/libharfbuzz-dev_2.1.1-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/h/harfbuzz/libharfbuzz-gobject0_2.1.1-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/h/harfbuzz/libharfbuzz-icu0_2.1.1-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/i/icu-le-hb/libicu-le-hb0_1.0.3+git180724-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/i/icu/libicu57_57.1-9_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/i/icu/libicu60_60.2-6_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/i/icu/libicu63_63.1-5_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/j/jbigkit/libjbig0_2.1-3.1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/j/json-glib/libjson-glib-1.0-0_1.4.4-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/keyutils/libkeyutils1_1.5.9-9.3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/krb5-multidev_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libgssapi-krb5-2_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libgssrpc4_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libk5crypto3_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libkadm5clnt-mit11_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libkadm5srv-mit11_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libkdb5-9_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libkrb5-3_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libkrb5-dev_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/k/krb5/libkrb5support0_1.16.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/liba/libappindicator/libappindicator1_0.4.92-7_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/liba/libappindicator/libappindicator3-1_0.4.92-7_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/liba/libappindicator/libappindicator3-dev_0.4.92-7_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/liba/libappindicator/libappindicator-dev_0.4.92-7_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/liba/libasyncns/libasyncns0_0.8-6_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libb/libbsd/libbsd0_0.9.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libc/libcap2/libcap2_2.25-1.2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libc/libcap2/libcap-dev_2.25-1.2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libc/libcap-ng/libcap-ng0_0.7.9-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdatrie/libdatrie1_0.2.12-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr490+repack1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdbusmenu/libdbusmenu-glib-dev_18.10.20180917~bzr490+repack1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr490+repack1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdbusmenu/libdbusmenu-gtk4_18.10.20180917~bzr490+repack1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdrm/libdrm2_2.4.95-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.95-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdrm/libdrm-dev_2.4.95-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdrm/libdrm-intel1_2.4.95-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdrm/libdrm-nouveau2_2.4.95-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libd/libdrm/libdrm-radeon1_2.4.95-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libe/libepoxy/libepoxy0_1.5.3-0.1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libe/libepoxy/libepoxy-dev_1.5.3-0.1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libe/libevent/libevent-2.1-6_2.1.8-stable-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libf/libffi/libffi6_3.2.1-9_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libf/libffi/libffi7_3.3~rc0-7_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libf/libffi/libffi-dev_3.2.1-9_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libgcrypt20/libgcrypt20_1.8.4-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libgcrypt20/libgcrypt20-dev_1.8.4-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libglvnd/libegl1_1.1.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libglvnd/libgl1_1.1.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libglvnd/libgles1_1.1.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libglvnd/libgles2_1.1.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libglvnd/libglvnd0_1.1.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libglvnd/libglvnd-dev_1.1.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libglvnd/libglx0_1.1.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libglvnd/libopengl0_1.1.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libgnome-keyring/libgnome-keyring0_3.12.0-1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libgnome-keyring/libgnome-keyring-dev_3.12.0-1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libgpg-error/libgpg-error0_1.32-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libg/libgpg-error/libgpg-error-dev_1.32-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libi/libice/libice6_1.0.9-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libi/libidl/libidl-2-0_0.8.14-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libi/libidn2/libidn2-0_2.0.5-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libi/libidn/libidn11_1.33-2.2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libi/libindicator/libindicator3-7_0.5.0-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libi/libindicator/libindicator7_0.5.0-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libj/libjpeg-turbo/libjpeg62-turbo_1.5.2-2+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libj/libjpeg-turbo/libjpeg62-turbo-dev_1.5.2-2+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libj/libjsoncpp/libjsoncpp1_1.7.4-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libj/libjsoncpp/libjsoncpp-dev_1.7.4-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libn/libnss-db/libnss-db_2.2.3pre1-6+b6_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libo/libogg/libogg0_1.3.2-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libo/libogg/libogg-dev_1.3.2-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libp/libpciaccess/libpciaccess0_0.14-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libp/libpng1.6/libpng16-16_1.6.34-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libp/libpng1.6/libpng-dev_1.6.34-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libp/libpsl/libpsl5_0.20.2-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.3-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libr/librest/librest-0.7-0_0.8.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libs/libselinux/libselinux1_2.8-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libs/libsm/libsm6_1.2.2-1+b3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libs/libsndfile/libsndfile1_1.0.28-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libs/libsoup2.4/libsoup2.4-1_2.64.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libs/libsoup2.4/libsoup-gnome2.4-1_2.64.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libt/libtasn1-6/libtasn1-6_4.13-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libt/libthai/libthai0_0.1.28-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libt/libtool/libltdl7_2.4.6-6_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libu/libunistring/libunistring2_0.9.10-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libva/libva2_2.3.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libva/libva-dev_2.3.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libva/libva-drm2_2.3.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libva/libva-glx2_2.3.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libva/libva-wayland2_2.3.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libva/libva-x11-2_2.3.0-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libvorbis/libvorbis0a_1.3.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libvorbis/libvorbisenc2_1.3.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libvpx/libvpx5_1.7.0-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libv/libvpx/libvpx-dev_1.7.0-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libw/libwebp/libwebp6_0.6.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libw/libwebp/libwebpdemux2_0.6.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libw/libwebp/libwebp-dev_0.6.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libw/libwebp/libwebpmux3_0.6.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libx11/libx11-6_1.6.7-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libx11/libx11-dev_1.6.7-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libx11/libx11-xcb1_1.6.7-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libx11/libx11-xcb-dev_1.6.7-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxau/libxau6_1.0.8-1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxau/libxau-dev_1.0.8-1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb1_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb1-dev_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-dri2-0_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-dri2-0-dev_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-dri3-0_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-glx0_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-glx0-dev_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-present0_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-render0_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-render0-dev_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-shm0_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-shm0-dev_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-sync1_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcb/libxcb-xfixes0_1.13.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcomposite/libxcomposite1_0.4.4-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcomposite/libxcomposite-dev_0.4.4-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcursor/libxcursor1_1.1.15-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxcursor/libxcursor-dev_1.1.15-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxdamage/libxdamage1_1.1.4-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxdamage/libxdamage-dev_1.1.4-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxdmcp/libxdmcp6_1.1.2-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxdmcp/libxdmcp-dev_1.1.2-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxext/libxext6_1.3.3-1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxext/libxext-dev_1.3.3-1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxfixes/libxfixes3_5.0.3-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxfixes/libxfixes-dev_5.0.3-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxi/libxi6_1.7.9-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxi/libxi-dev_1.7.9-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxinerama/libxinerama1_1.1.4-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxinerama/libxinerama-dev_1.1.4-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxkbcommon/libxkbcommon0_0.8.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxkbcommon/libxkbcommon-dev_0.8.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxml2/libxml2_2.9.4+dfsg1-7+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxml2/libxml2-dev_2.9.4+dfsg1-7+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxrandr/libxrandr2_1.5.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxrandr/libxrandr-dev_1.5.1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxrender/libxrender1_0.9.10-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxrender/libxrender-dev_0.9.10-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxshmfence/libxshmfence1_1.3-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxslt/libxslt1.1_1.1.32-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxslt/libxslt1-dev_1.1.32-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxss/libxss1_1.2.3-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxss/libxss-dev_1.2.3-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxt/libxt6_1.1.5-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxt/libxt-dev_1.1.5-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxtst/libxtst6_1.2.3-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxtst/libxtst-dev_1.2.3-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxxf86vm/libxxf86vm1_1.1.4-1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libx/libxxf86vm/libxxf86vm-dev_1.1.4-1+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/libz/libzstd/libzstd1_1.3.5+dfsg-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/l/lcms2/liblcms2-2_2.9-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/l/linux/linux-libc-dev_4.18.20-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/l/lz4/liblz4-1_1.8.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/l/lzo2/liblzo2-2_2.10-0.1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/libegl1-mesa_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/libegl1-mesa-dev_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/libgbm1_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/libgbm-dev_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/libgl1-mesa-dev_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/libgl1-mesa-glx_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/libglapi-mesa_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/libwayland-egl1-mesa_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/mesa/mesa-common-dev_18.2.6-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/minizip/libminizip1_1.1-8+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/m/minizip/libminizip-dev_1.1-8+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/n/nettle/libhogweed4_3.4.1~rc1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/n/nettle/libnettle6_3.4.1~rc1-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/n/nspr/libnspr4_4.20-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/n/nspr/libnspr4-dev_4.20-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/n/nss/libnss3_3.41-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/n/nss/libnss3-dev_3.41-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/o/openssl1.0/libssl1.0.2_1.0.2q-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/o/openssl/libssl1.1_1.1.1a-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/o/openssl/libssl-dev_1.1.1a-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/o/opus/libopus0_1.3~beta+20180518-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/o/opus/libopus-dev_1.3~beta+20180518-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/o/orbit2/liborbit-2-0_2.14.19-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/o/orbit2/liborbit2_2.14.19-4_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/p11-kit/libp11-kit0_0.23.14-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pam/libpam0g_1.1.8-3.8_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pam/libpam0g-dev_1.1.8-3.8_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pango1.0/libpango-1.0-0_1.42.4-5_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pango1.0/libpango1.0-dev_1.42.4-5_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pango1.0/libpangocairo-1.0-0_1.42.4-5_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.42.4-5_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pango1.0/libpangoxft-1.0-0_1.42.4-5_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pangox-compat/libpangox-1.0-0_0.0.2-5+b2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pciutils/libpci3_3.5.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pciutils/libpci-dev_3.5.2-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pcre3/libpcre16-3_8.39-11_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pcre3/libpcre32-3_8.39-11_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pcre3/libpcre3_8.39-11_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pcre3/libpcre3-dev_8.39-11_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pcre3/libpcrecpp0v5_8.39-11_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pipewire/libpipewire-0.2-1_0.2.3-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pipewire/libpipewire-0.2-dev_0.2.3-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pipewire/libspa-lib-0.1-dev_0.2.3-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pixman/libpixman-1-0_0.36.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pixman/libpixman-1-dev_0.36.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pulseaudio/libpulse0_12.2-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pulseaudio/libpulse-dev_12.2-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/p/pulseaudio/libpulse-mainloop-glib0_12.2-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/r/re2/libre2-4_20180901+dfsg-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/r/re2/libre2-dev_20180901+dfsg-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/shared-mime-info/shared-mime-info_1.10-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/snappy/libsnappy1v5_1.1.7-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/snappy/libsnappy-dev_1.1.7-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/speech-dispatcher/libspeechd2_0.8.8-7_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/speech-dispatcher/libspeechd-dev_0.8.8-7_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/speech-dispatcher/speech-dispatcher_0.8.8-7_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/sqlite3/libsqlite3-0_3.26.0-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/systemd/libsystemd0_239-15_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/systemd/libudev1_239-15_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/s/systemd/libudev-dev_239-15_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/t/tcp-wrappers/libwrap0_7.6.q-27_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/t/tiff/libtiff5_4.0.10-3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/u/unbound/libunbound8_1.8.1-1+b1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/u/util-linux/libblkid1_2.33-0.2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/u/util-linux/libmount1_2.33-0.2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/u/util-linux/libuuid1_2.33-0.2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/u/util-linux/uuid-dev_2.33-0.2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/v/vulkan/libvulkan1_1.1.73+dfsg-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/w/wayland/libwayland-client0_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/w/wayland/libwayland-cursor0_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/w/wayland/libwayland-dev_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/w/wayland/libwayland-egl1_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/w/wayland/libwayland-server0_1.16.0-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/w/wayland-protocols/wayland-protocols_1.16-1_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xft/libxft2_2.3.2-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-composite-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-damage-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-fixes-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-input-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-kb-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-randr-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-record-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-render-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-scrnsaver-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-xext-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xorgproto/x11proto-xinerama-dev_2018.4-4_all.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/x/xz-utils/liblzma5_5.2.2-1.3_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/z/zlib/lib32z1_1.2.11.dfsg-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/z/zlib/lib32z1-dev_1.2.11.dfsg-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/z/zlib/zlib1g_1.2.11.dfsg-1_amd64.deb
+https://snapshot.debian.org/archive/debian/20181214T150526Z/pool/main/z/zlib/zlib1g-dev_1.2.11.dfsg-1_amd64.deb
diff --git a/build/linux/sysroot_scripts/sysroot-creator-sid.sh b/build/linux/sysroot_scripts/sysroot-creator-sid.sh
index 6e21e97..6f471d83 100755
--- a/build/linux/sysroot_scripts/sysroot-creator-sid.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator-sid.sh
@@ -26,6 +26,7 @@
 KEYRING_FILE="${SCRIPT_DIR}/debian-archive-sid-stable.gpg"
 
 HAS_ARCH_AMD64=1
+HAS_ARCH_AMD64MULTILIB=1
 HAS_ARCH_I386=1
 HAS_ARCH_ARM=1
 HAS_ARCH_ARM64=1
@@ -160,7 +161,7 @@
   libhogweed4
   libice6
   libicu-le-hb0
-  libicu57
+  libicu63
   libicu60
   libidl-2-0
   libidn11
@@ -336,6 +337,7 @@
   libxtst6
   libxxf86vm-dev
   libxxf86vm1
+  libzstd1
   linux-libc-dev
   mesa-common-dev
   shared-mime-info
@@ -363,6 +365,27 @@
   libtsan0
 "
 
+DEBIAN_PACKAGES_AMD64MULTILIB="
+  gcc-multilib
+  lib32asan3
+  lib32atomic1
+  lib32cilkrts5
+  lib32gcc1
+  lib32gcc-6-dev
+  lib32gomp1
+  lib32itm1
+  lib32mpx2
+  lib32quadmath0
+  lib32stdc++6
+  lib32stdc++-6-dev
+  lib32ubsan0
+  lib32z1
+  lib32z1-dev
+  libc6-dev-i386
+  libc6-dev-i386-cross
+  libc6-i386
+"
+
 DEBIAN_PACKAGES_X86="
   libasan3
   libcilkrts5
diff --git a/build/linux/sysroot_scripts/sysroot-creator.sh b/build/linux/sysroot_scripts/sysroot-creator.sh
index 36f440b..fa15adef 100644
--- a/build/linux/sysroot_scripts/sysroot-creator.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator.sh
@@ -45,6 +45,7 @@
 fi
 
 readonly HAS_ARCH_AMD64=${HAS_ARCH_AMD64:=0}
+readonly HAS_ARCH_AMD64MULTILIB=${HAS_ARCH_AMD64MULTILIB:=0}
 readonly HAS_ARCH_I386=${HAS_ARCH_I386:=0}
 readonly HAS_ARCH_ARM=${HAS_ARCH_ARM:=0}
 readonly HAS_ARCH_ARM64=${HAS_ARCH_ARM64:=0}
@@ -62,6 +63,7 @@
 readonly RELEASE_FILE_GPG="Release.gpg"
 
 readonly DEBIAN_DEP_LIST_AMD64="generated_package_lists/${DIST}.amd64"
+readonly DEBIAN_DEP_LIST_AMD64MULTILIB="generated_package_lists/${DIST}.amd64-multilib"
 readonly DEBIAN_DEP_LIST_I386="generated_package_lists/${DIST}.i386"
 readonly DEBIAN_DEP_LIST_ARM="generated_package_lists/${DIST}.arm"
 readonly DEBIAN_DEP_LIST_ARM64="generated_package_lists/${DIST}.arm64"
@@ -116,27 +118,33 @@
 
 
 SetEnvironmentVariables() {
-  ARCH=""
-  echo $1 | grep -qs Amd64$ && ARCH=AMD64
-  if [ -z "$ARCH" ]; then
-    echo $1 | grep -qs I386$ && ARCH=I386
-  fi
-  if [ -z "$ARCH" ]; then
-    echo $1 | grep -qs Mips64el$ && ARCH=MIPS64EL
-  fi
-  if [ -z "$ARCH" ]; then
-    echo $1 | grep -qs Mips$ && ARCH=MIPS
-  fi
-  if [ -z "$ARCH" ]; then
-    echo $1 | grep -qs ARM$ && ARCH=ARM
-  fi
-  if [ -z "$ARCH" ]; then
-    echo $1 | grep -qs ARM64$ && ARCH=ARM64
-  fi
-  if [ -z "${ARCH}" ]; then
-    echo "ERROR: Unable to determine architecture based on: $1"
-    exit 1
-  fi
+  case $1 in
+    *Amd64)
+      ARCH=AMD64
+      ;;
+    *Amd64Multilib)
+      ARCH=AMD64MULTILIB
+      ;;
+    *I386)
+      ARCH=I386
+      ;;
+    *Mips64el)
+      ARCH=MIPS64EL
+      ;;
+    *Mips)
+      ARCH=MIPS
+      ;;
+    *ARM)
+      ARCH=ARM
+      ;;
+    *ARM64)
+      ARCH=ARM64
+      ;;
+    *)
+      echo "ERROR: Unable to determine architecture based on: $1"
+      exit 1
+      ;;
+  esac
   ARCH_LOWER=$(echo $ARCH | tr '[:upper:]' '[:lower:]')
 }
 
@@ -236,6 +244,12 @@
     ${DEBIAN_PACKAGES_X86:=} ${DEBIAN_PACKAGES_AMD64:=}"
 }
 
+GeneratePackageListAmd64Multilib() {
+  GeneratePackageListCommon "$1" amd64 "${DEBIAN_PACKAGES}
+    ${DEBIAN_PACKAGES_X86:=} ${DEBIAN_PACKAGES_AMD64:=}
+    ${DEBIAN_PACKAGES_AMD64MULTILIB:=}"
+}
+
 GeneratePackageListI386() {
   GeneratePackageListCommon "$1" i386 "${DEBIAN_PACKAGES}
     ${DEBIAN_PACKAGES_X86:=}"
@@ -325,6 +339,11 @@
 }
 
 
+HacksAndPatchesAmd64Multilib() {
+  HacksAndPatchesCommon x86_64 linux-gnu strip
+}
+
+
 HacksAndPatchesI386() {
   HacksAndPatchesCommon i386 linux-gnu strip
 }
@@ -455,6 +474,11 @@
 }
 
 
+VerifyLibraryDepsAmd64Multilib() {
+  VerifyLibraryDepsCommon x86_64 linux-gnu
+}
+
+
 VerifyLibraryDepsI386() {
   VerifyLibraryDepsCommon i386 linux-gnu
 }
@@ -501,6 +525,26 @@
 }
 
 #@
+#@ BuildSysrootAmd64Multilib
+#@
+#@    Build everything and package it
+BuildSysrootAmd64Multilib() {
+  if [ "$HAS_ARCH_AMD64MULTILIB" = "0" ]; then
+    return
+  fi
+  ClearInstallDir
+  local package_file="${DEBIAN_DEP_LIST_AMD64MULTILIB}"
+  GeneratePackageListAmd64Multilib "$package_file"
+  local files_and_sha256sums="$(cat ${package_file})"
+  StripChecksumsFromPackageList "$package_file"
+  InstallIntoSysroot ${files_and_sha256sums}
+  CleanupJailSymlinks
+  HacksAndPatchesAmd64Multilib
+  VerifyLibraryDepsAmd64Multilib
+  CreateTarBall
+}
+
+#@
 #@ BuildSysrootI386
 #@
 #@    Build everything and package it
@@ -606,6 +650,7 @@
 #@    Build sysroot images for all architectures
 BuildSysrootAll() {
   RunCommand BuildSysrootAmd64
+  RunCommand BuildSysrootAmd64Multilib
   RunCommand BuildSysrootI386
   RunCommand BuildSysrootARM
   RunCommand BuildSysrootARM64
@@ -616,7 +661,7 @@
 UploadSysroot() {
   local sha=$(sha1sum "${TARBALL}" | awk '{print $1;}')
   set -x
-  gsutil cp -a public-read "${TARBALL}" \
+  gsutil.py cp -a public-read "${TARBALL}" \
       "gs://chrome-linux-sysroot/toolchain/$sha/"
   set +x
 }
@@ -632,6 +677,16 @@
 }
 
 #@
+#@ UploadSysrootAmd64Multilib
+#@
+UploadSysrootAmd64Multilib() {
+  if [ "$HAS_ARCH_AMD64MULTILIB" = "0" ]; then
+    return
+  fi
+  UploadSysroot "$@"
+}
+
+#@
 #@ UploadSysrootI386
 #@
 UploadSysrootI386() {
@@ -687,6 +742,7 @@
 #@    Upload sysroot image for all architectures
 UploadSysrootAll() {
   RunCommand UploadSysrootAmd64 "$@"
+  RunCommand UploadSysrootAmd64Multilib "$@"
   RunCommand UploadSysrootI386 "$@"
   RunCommand UploadSysrootARM "$@"
   RunCommand UploadSysrootARM64 "$@"
@@ -787,6 +843,9 @@
   if [ "$HAS_ARCH_AMD64" = "1" ]; then
     echo Amd64
   fi
+  if [ "$HAS_ARCH_AMD64MULTILIB" = "1" ]; then
+    echo Amd64Multilib
+  fi
   if [ "$HAS_ARCH_I386" = "1" ]; then
     echo I386
   fi
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index ef52f0e..cce364e 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -43,17 +43,6 @@
     // http://crbug.com/244755
     "race:v8::internal::Zone::NewExpand\n"
 
-    // http://crbug.com/244774
-    "race:webrtc::RTPReceiver::ProcessBitrate\n"
-    "race:webrtc::RTPSender::ProcessBitrate\n"
-    "race:webrtc::VideoCodingModuleImpl::Decode\n"
-    "race:webrtc::RTPSender::SendOutgoingData\n"
-    "race:webrtc::LibvpxVp8Encoder::GetEncodedPartitions\n"
-    "race:webrtc::LibvpxVp8Encoder::Encode\n"
-    "race:webrtc::ViEEncoder::DeliverFrame\n"
-    "race:webrtc::vcm::VideoReceiver::Decode\n"
-    "race:webrtc::VCMReceiver::FrameForDecoding\n"
-
     // http://crbug.com/244856
     "race:libpulsecommon*.so\n"
 
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 25e01d1..d5d3bac 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -88,6 +88,10 @@
     "layers/layer_position_constraint.h",
     "layers/layer_sticky_position_constraint.cc",
     "layers/layer_sticky_position_constraint.h",
+    "layers/mirror_layer.cc",
+    "layers/mirror_layer.h",
+    "layers/mirror_layer_impl.cc",
+    "layers/mirror_layer_impl.h",
     "layers/nine_patch_generator.cc",
     "layers/nine_patch_generator.h",
     "layers/nine_patch_layer.cc",
@@ -605,6 +609,7 @@
     "layers/layer_list_iterator_unittest.cc",
     "layers/layer_position_constraint_unittest.cc",
     "layers/layer_unittest.cc",
+    "layers/mirror_layer_unittest.cc",
     "layers/nine_patch_generator_unittest.cc",
     "layers/nine_patch_layer_impl_unittest.cc",
     "layers/nine_patch_layer_unittest.cc",
@@ -681,6 +686,7 @@
     "trees/layer_tree_host_pixeltest_blending.cc",
     "trees/layer_tree_host_pixeltest_filters.cc",
     "trees/layer_tree_host_pixeltest_masks.cc",
+    "trees/layer_tree_host_pixeltest_mirror.cc",
     "trees/layer_tree_host_pixeltest_readback.cc",
     "trees/layer_tree_host_pixeltest_scrollbars.cc",
     "trees/layer_tree_host_pixeltest_synchronous.cc",
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 35f58477c..95248a07 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -71,7 +71,8 @@
       has_will_change_transform_hint(false),
       trilinear_filtering(false),
       hide_layer_and_subtree(false),
-      overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto) {}
+      overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto),
+      mirror_count(0) {}
 
 Layer::Inputs::~Inputs() = default;
 
@@ -549,9 +550,14 @@
   if (clip_tree_index() != ClipTree::kInvalidNodeId && !force_rebuild) {
     PropertyTrees* property_trees = layer_tree_host_->property_trees();
     if (ClipNode* node = property_trees->clip_tree.Node(clip_tree_index())) {
-      node->clip = gfx::RectF(
-          gfx::PointF(clip_rect.origin()) + offset_to_transform_parent(),
-          gfx::SizeF(clip_rect.size()));
+      node->clip = gfx::RectF(clip_rect);
+      // This mirrors the logic in PropertyTreeBuilder's AddClipNodeIfNeeded
+      // method.
+      if (masks_to_bounds() || mask_layer() ||
+          filters().HasFilterThatMovesPixels()) {
+        node->clip.Intersect(gfx::RectF(gfx::SizeF(bounds())));
+      }
+      node->clip += offset_to_transform_parent();
       property_trees->clip_tree.set_needs_update(true);
     }
   } else {
@@ -1500,6 +1506,7 @@
   layer->SetMasksToBounds(inputs_.masks_to_bounds);
   layer->SetNonFastScrollableRegion(inputs_.non_fast_scrollable_region);
   layer->SetTouchActionRegion(inputs_.touch_action_region);
+  layer->SetMirrorCount(inputs_.mirror_count);
   // TODO(sunxd): Pass the correct region for wheel event handlers, see
   // https://crbug.com/841364.
   EventListenerProperties mouse_wheel_props =
@@ -1693,6 +1700,27 @@
   SetNeedsCommit();
 }
 
+void Layer::IncrementMirrorCount() {
+  SetMirrorCount(mirror_count() + 1);
+}
+
+void Layer::DecrementMirrorCount() {
+  SetMirrorCount(mirror_count() - 1);
+}
+
+void Layer::SetMirrorCount(int mirror_count) {
+  if (inputs_.mirror_count == mirror_count)
+    return;
+
+  DCHECK_LE(0, mirror_count);
+  bool was_mirrored = inputs_.mirror_count > 0;
+  inputs_.mirror_count = mirror_count;
+  bool is_mirrored = inputs_.mirror_count > 0;
+  if (was_mirrored != is_mirrored)
+    SetPropertyTreesNeedRebuild();
+  SetNeedsPushProperties();
+}
+
 ElementListType Layer::GetElementTypeForAnimation() const {
   return ElementListType::ACTIVE;
 }
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 58631ace3..fd5c08aec 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -651,6 +651,11 @@
   void SetTrilinearFiltering(bool trilinear_filtering);
   bool trilinear_filtering() const { return inputs_.trilinear_filtering; }
 
+  // Increments/decrements/gets number of layers mirroring this layer.
+  void IncrementMirrorCount();
+  void DecrementMirrorCount();
+  int mirror_count() const { return inputs_.mirror_count; }
+
   // Called on the scroll layer to trigger showing the overlay scrollbars.
   void ShowScrollbars() { needs_show_scrollbars_ = true; }
 
@@ -930,6 +935,8 @@
   // they are marked as needing to be rebuilt.
   void UpdateScrollOffset(const gfx::ScrollOffset&);
 
+  void SetMirrorCount(int mirror_count);
+
   // Encapsulates all data, callbacks or interfaces received from the embedder.
   struct Inputs {
     explicit Inputs(int layer_id);
@@ -1048,6 +1055,8 @@
     OverscrollBehavior overscroll_behavior;
 
     base::Optional<SnapContainerData> snap_container_data;
+
+    int mirror_count;
   };
 
   Layer* parent_;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 86ccf0fc..5acf62e 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -80,7 +80,8 @@
       scrollbars_hidden_(false),
       needs_show_scrollbars_(false),
       raster_even_if_not_drawn_(false),
-      has_transform_node_(false) {
+      has_transform_node_(false),
+      mirror_count_(0) {
   DCHECK_GT(layer_id_, 0);
 
   DCHECK(layer_tree_impl_);
@@ -345,6 +346,7 @@
   layer->clip_tree_index_ = clip_tree_index_;
   layer->scroll_tree_index_ = scroll_tree_index_;
   layer->has_will_change_transform_hint_ = has_will_change_transform_hint_;
+  layer->mirror_count_ = mirror_count_;
   layer->scrollbars_hidden_ = scrollbars_hidden_;
   if (needs_show_scrollbars_)
     layer->needs_show_scrollbars_ = needs_show_scrollbars_;
@@ -516,7 +518,6 @@
   needs_push_properties_ = false;
 
   update_rect_.SetRect(0, 0, 0, 0);
-  damage_rect_.SetRect(0, 0, 0, 0);
 }
 
 bool LayerImpl::IsActive() const {
@@ -696,12 +697,16 @@
   layer_tree_impl_->AddToElementLayerList(element_id_, this);
 }
 
+void LayerImpl::SetMirrorCount(int mirror_count) {
+  mirror_count_ = mirror_count;
+}
+
 void LayerImpl::SetUpdateRect(const gfx::Rect& update_rect) {
   update_rect_ = update_rect;
 }
 
-void LayerImpl::AddDamageRect(const gfx::Rect& damage_rect) {
-  damage_rect_.Union(damage_rect);
+gfx::Rect LayerImpl::GetDamageRect() const {
+  return gfx::Rect();
 }
 
 void LayerImpl::SetCurrentScrollOffset(const gfx::ScrollOffset& scroll_offset) {
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index b98cfd6..423eb7b 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -194,6 +194,9 @@
   void SetElementId(ElementId element_id);
   ElementId element_id() const { return element_id_; }
 
+  void SetMirrorCount(int mirror_count);
+  int mirror_count() const { return mirror_count_; }
+
   bool IsAffectedByPageScale() const;
 
   bool Is3dSorted() const { return GetSortingContextId() != 0; }
@@ -332,8 +335,10 @@
   void SetUpdateRect(const gfx::Rect& update_rect);
   const gfx::Rect& update_rect() const { return update_rect_; }
 
-  void AddDamageRect(const gfx::Rect& damage_rect);
-  const gfx::Rect& damage_rect() const { return damage_rect_; }
+  // Denotes an area that is damaged and needs redraw. This is in the layer's
+  // space. By default returns empty rect, but can be overridden by subclasses
+  // as appropriate.
+  virtual gfx::Rect GetDamageRect() const;
 
   virtual std::unique_ptr<base::DictionaryValue> LayerAsJson() const;
   // TODO(pdr): This should be removed because there is no longer a tree
@@ -349,7 +354,7 @@
   // from property_trees changes in animaiton.
   bool LayerPropertyChangedNotFromPropertyTrees() const;
 
-  void ResetChangeTracking();
+  virtual void ResetChangeTracking();
 
   virtual SimpleEnclosedRegion VisibleOpaqueRegion() const;
 
@@ -437,8 +442,6 @@
   // PopulateScaledSharedQuadStateQuadState() for more details.
   gfx::Rect GetScaledEnclosingRectInTargetSpace(float scale) const;
 
-  void UpdatePropertyTreeForAnimationIfNeeded(ElementId element_id);
-
   float GetIdealContentsScale() const;
 
   void NoteLayerPropertyChanged();
@@ -569,10 +572,6 @@
   // This is in the layer's space.
   gfx::Rect update_rect_;
 
-  // Denotes an area that is damaged and needs redraw. This is in the layer's
-  // space.
-  gfx::Rect damage_rect_;
-
   // Group of properties that need to be computed based on the layer tree
   // hierarchy before layers can be drawn.
   DrawProperties draw_properties_;
@@ -598,6 +597,10 @@
   bool raster_even_if_not_drawn_ : 1;
 
   bool has_transform_node_ : 1;
+
+  // Number of layers mirroring this layer. If greater than zero, forces a
+  // render pass for the layer so it can be embedded by the mirroring layer.
+  int mirror_count_;
 };
 
 }  // namespace cc
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index be4d26a..1d1064d 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -27,6 +27,7 @@
 #include "cc/test/layer_test_common.h"
 #include "cc/test/stub_layer_tree_host_single_thread_client.h"
 #include "cc/test/test_task_graph_runner.h"
+#include "cc/trees/clip_node.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/single_thread_proxy.h"
 #include "cc/trees/transform_node.h"
@@ -1011,6 +1012,7 @@
       1, test_layer->SetTransformOrigin(gfx::Point3F(1.23f, 4.56f, 0.f)));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBackgroundColor(SK_ColorLTGRAY));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetMasksToBounds(true));
+  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetClipRect(gfx::Rect(1, 2, 3, 4)));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetRoundedCorner({1, 2, 3, 4}));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetIsFastRoundedCorner(true));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetOpacity(0.5f));
@@ -1654,6 +1656,186 @@
   test_layer->SetLayerTreeHost(nullptr);
 }
 
+// Verifies that mirror count is pushed to the LayerImpl.
+TEST_F(LayerTest, MirrorCountIsPushed) {
+  scoped_refptr<Layer> test_layer = Layer::Create();
+  std::unique_ptr<LayerImpl> impl_layer =
+      LayerImpl::Create(host_impl_.active_tree(), 1);
+  test_layer->SetLayerTreeHost(layer_tree_host_.get());
+  EXPECT_EQ(0, test_layer->mirror_count());
+  EXPECT_EQ(0, impl_layer->mirror_count());
+
+  test_layer->IncrementMirrorCount();
+  EXPECT_EQ(1, test_layer->mirror_count());
+  EXPECT_EQ(0, impl_layer->mirror_count());
+
+  test_layer->PushPropertiesTo(impl_layer.get());
+  EXPECT_EQ(1, test_layer->mirror_count());
+  EXPECT_EQ(1, impl_layer->mirror_count());
+
+  test_layer->SetLayerTreeHost(nullptr);
+}
+
+// Verifies that when mirror count of the layer is incremented or decremented,
+// SetPropertyTreesNeedRebuild() and SetNeedsPushProperties() are called
+// appropriately.
+TEST_F(LayerTest, UpdateMirrorCount) {
+  scoped_refptr<Layer> test_layer = Layer::Create();
+  test_layer->SetLayerTreeHost(layer_tree_host_.get());
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+  EXPECT_EQ(0, test_layer->mirror_count());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_EQ(0u, layer_tree_host_->LayersThatShouldPushProperties().size());
+
+  // Incrementing mirror count from zero should trigger property trees rebuild.
+  test_layer->IncrementMirrorCount();
+  EXPECT_EQ(1, test_layer->mirror_count());
+  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             test_layer.get()));
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Incrementing mirror count from non-zero should not trigger property trees
+  // rebuild.
+  test_layer->IncrementMirrorCount();
+  EXPECT_EQ(2, test_layer->mirror_count());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             test_layer.get()));
+
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Decrementing mirror count to non-zero should not trigger property trees
+  // rebuild.
+  test_layer->DecrementMirrorCount();
+  EXPECT_EQ(1, test_layer->mirror_count());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             test_layer.get()));
+
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Decrementing mirror count to zero should trigger property trees rebuild.
+  test_layer->DecrementMirrorCount();
+  EXPECT_EQ(0, test_layer->mirror_count());
+  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             test_layer.get()));
+
+  test_layer->SetLayerTreeHost(nullptr);
+}
+
+TEST_F(LayerTest, UpdatingClipRect) {
+  const gfx::Size kRootSize(200, 200);
+  const gfx::Vector2dF kParentOffset(10.f, 20.f);
+  const gfx::Size kLayerSize(100, 100);
+  const gfx::Rect kClipRect(50, 25, 100, 100);
+  const gfx::Rect kUpdatedClipRect_1(10, 20, 150, 200);
+  const gfx::Rect kUpdatedClipRect_2(20, 20, 50, 100);
+  const gfx::Rect kUpdatedClipRect_3(50, 25, 100, 80);
+  const gfx::Rect kUpdatedClipRect_4(10, 10, 200, 200);
+
+  scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<Layer> parent = Layer::Create();
+  scoped_refptr<Layer> clipped_1 = Layer::Create();
+  scoped_refptr<Layer> clipped_2 = Layer::Create();
+  scoped_refptr<Layer> clipped_3 = Layer::Create();
+  scoped_refptr<Layer> clipped_4 = Layer::Create();
+
+  EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AtLeast(1));
+  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+  layer_tree_host_->SetRootLayer(root);
+  root->AddChild(parent);
+  parent->AddChild(clipped_1);
+  parent->AddChild(clipped_2);
+  parent->AddChild(clipped_3);
+  parent->AddChild(clipped_4);
+
+  root->SetBounds(kRootSize);
+  parent->SetBounds(kRootSize);
+  clipped_1->SetBounds(kLayerSize);
+  clipped_2->SetBounds(kLayerSize);
+  clipped_3->SetBounds(kLayerSize);
+  clipped_4->SetBounds(kLayerSize);
+
+  // This should introduce the |offset_from_transform_parent| component.
+  parent->SetPosition(gfx::PointF() + kParentOffset);
+
+  clipped_1->SetClipRect(kClipRect);
+  clipped_2->SetClipRect(kClipRect);
+  clipped_3->SetClipRect(kClipRect);
+  clipped_4->SetClipRect(kClipRect);
+  EXPECT_EQ(clipped_1->clip_rect(), kClipRect);
+  EXPECT_EQ(clipped_2->clip_rect(), kClipRect);
+  EXPECT_EQ(clipped_3->clip_rect(), kClipRect);
+  EXPECT_EQ(clipped_4->clip_rect(), kClipRect);
+
+  root->layer_tree_host()->BuildPropertyTreesForTesting();
+  ClipNode* node_1 = layer_tree_host_->property_trees()->clip_tree.Node(
+      clipped_1->clip_tree_index());
+  ClipNode* node_2 = layer_tree_host_->property_trees()->clip_tree.Node(
+      clipped_2->clip_tree_index());
+  ClipNode* node_3 = layer_tree_host_->property_trees()->clip_tree.Node(
+      clipped_3->clip_tree_index());
+  ClipNode* node_4 = layer_tree_host_->property_trees()->clip_tree.Node(
+      clipped_4->clip_tree_index());
+
+  EXPECT_EQ(gfx::RectF(kClipRect) + kParentOffset, node_1->clip);
+  EXPECT_EQ(gfx::RectF(kClipRect) + kParentOffset, node_2->clip);
+  EXPECT_EQ(gfx::RectF(kClipRect) + kParentOffset, node_3->clip);
+  EXPECT_EQ(gfx::RectF(kClipRect) + kParentOffset, node_4->clip);
+
+  // The following layer properties should result in the layer being clipped to
+  // its bounds along with being clipped by the clip rect. Check if the final
+  // rect on the clip node is set correctly.
+
+  // Setting clip to layer bounds.
+  clipped_1->SetMasksToBounds(true);
+
+  // Setting a mask.
+  FakeContentLayerClient client;
+  scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client);
+  clipped_2->SetMaskLayer(mask.get());
+
+  // Setting a filter that moves pixels.
+  FilterOperations move_pixel_filters;
+  move_pixel_filters.Append(
+      FilterOperation::CreateBlurFilter(2, SkBlurImageFilter::kClamp_TileMode));
+  ASSERT_TRUE(move_pixel_filters.HasFilterThatMovesPixels());
+  clipped_3->SetFilters(move_pixel_filters);
+
+  clipped_1->SetClipRect(kUpdatedClipRect_1);
+  clipped_2->SetClipRect(kUpdatedClipRect_2);
+  clipped_3->SetClipRect(kUpdatedClipRect_3);
+  clipped_4->SetClipRect(kUpdatedClipRect_4);
+
+  node_1 = layer_tree_host_->property_trees()->clip_tree.Node(
+      clipped_1->clip_tree_index());
+  node_2 = layer_tree_host_->property_trees()->clip_tree.Node(
+      clipped_2->clip_tree_index());
+  node_3 = layer_tree_host_->property_trees()->clip_tree.Node(
+      clipped_3->clip_tree_index());
+  node_4 = layer_tree_host_->property_trees()->clip_tree.Node(
+      clipped_4->clip_tree_index());
+
+  EXPECT_EQ(node_1->clip,
+            gfx::IntersectRects(gfx::RectF(kUpdatedClipRect_1),
+                                gfx::RectF(gfx::SizeF(kLayerSize))) +
+                kParentOffset);
+  EXPECT_EQ(node_2->clip,
+            gfx::IntersectRects(gfx::RectF(kUpdatedClipRect_2),
+                                gfx::RectF(gfx::SizeF(kLayerSize))) +
+                kParentOffset);
+  EXPECT_EQ(node_3->clip,
+            gfx::IntersectRects(gfx::RectF(kUpdatedClipRect_3),
+                                gfx::RectF(gfx::SizeF(kLayerSize))) +
+                kParentOffset);
+  EXPECT_EQ(node_4->clip, gfx::RectF(kUpdatedClipRect_4) + kParentOffset);
+}
+
 class LayerTestWithLayerLists : public LayerTest {
  protected:
   void SetUp() override {
diff --git a/cc/layers/mirror_layer.cc b/cc/layers/mirror_layer.cc
new file mode 100644
index 0000000..b89e300d
--- /dev/null
+++ b/cc/layers/mirror_layer.cc
@@ -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.
+
+#include "cc/layers/mirror_layer.h"
+
+#include "cc/layers/mirror_layer_impl.h"
+
+namespace cc {
+
+std::unique_ptr<LayerImpl> MirrorLayer::CreateLayerImpl(
+    LayerTreeImpl* tree_impl) {
+  return MirrorLayerImpl::Create(tree_impl, id());
+}
+
+void MirrorLayer::PushPropertiesTo(LayerImpl* layer) {
+  Layer::PushPropertiesTo(layer);
+
+  auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer);
+  mirror_layer->SetMirroredLayerId(mirrored_layer_->id());
+}
+
+void MirrorLayer::SetLayerTreeHost(LayerTreeHost* host) {
+#if DCHECK_IS_ON()
+  if (host && host != layer_tree_host()) {
+    for (auto* p = parent(); p; p = p->parent())
+      DCHECK_NE(p, mirrored_layer_.get());
+  }
+#endif
+
+  Layer::SetLayerTreeHost(host);
+}
+
+scoped_refptr<MirrorLayer> MirrorLayer::Create(
+    scoped_refptr<Layer> mirrored_layer) {
+  return base::WrapRefCounted(new MirrorLayer(std::move(mirrored_layer)));
+}
+
+MirrorLayer::MirrorLayer(scoped_refptr<Layer> mirrored_layer)
+    : mirrored_layer_(std::move(mirrored_layer)) {
+  DCHECK(mirrored_layer_);
+  mirrored_layer_->IncrementMirrorCount();
+}
+
+MirrorLayer::~MirrorLayer() {
+  mirrored_layer_->DecrementMirrorCount();
+}
+
+}  // namespace cc
diff --git a/cc/layers/mirror_layer.h b/cc/layers/mirror_layer.h
new file mode 100644
index 0000000..9915dcf5
--- /dev/null
+++ b/cc/layers/mirror_layer.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 CC_LAYERS_MIRROR_LAYER_H_
+#define CC_LAYERS_MIRROR_LAYER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "cc/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+// A layer that can mirror contents of another Layer.
+class CC_EXPORT MirrorLayer : public Layer {
+ public:
+  static scoped_refptr<MirrorLayer> Create(scoped_refptr<Layer> mirrored_layer);
+
+  MirrorLayer(const MirrorLayer&) = delete;
+  MirrorLayer& operator=(const MirrorLayer&) = delete;
+
+  Layer* mirrored_layer() const { return mirrored_layer_.get(); }
+
+  // Layer overrides.
+  std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
+  void PushPropertiesTo(LayerImpl* layer) override;
+  void SetLayerTreeHost(LayerTreeHost* host) override;
+
+ protected:
+  explicit MirrorLayer(scoped_refptr<Layer> mirrored_layer);
+
+ private:
+  ~MirrorLayer() override;
+
+  // A reference to a layer that is mirrored by this layer. |mirrored_layer_|
+  // cannot be an ancestor of |this|.
+  scoped_refptr<Layer> mirrored_layer_;
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_MIRROR_LAYER_H_
diff --git a/cc/layers/mirror_layer_impl.cc b/cc/layers/mirror_layer_impl.cc
new file mode 100644
index 0000000..00de04dc
--- /dev/null
+++ b/cc/layers/mirror_layer_impl.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 "cc/layers/mirror_layer_impl.h"
+
+#include "cc/trees/effect_node.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/occlusion.h"
+#include "components/viz/common/quads/render_pass_draw_quad.h"
+
+namespace cc {
+
+MirrorLayerImpl::MirrorLayerImpl(LayerTreeImpl* tree_impl, int id)
+    : LayerImpl(tree_impl, id) {}
+
+MirrorLayerImpl::~MirrorLayerImpl() = default;
+
+std::unique_ptr<LayerImpl> MirrorLayerImpl::CreateLayerImpl(
+    LayerTreeImpl* tree_impl) {
+  return MirrorLayerImpl::Create(tree_impl, id());
+}
+
+void MirrorLayerImpl::AppendQuads(viz::RenderPass* render_pass,
+                                  AppendQuadsData* append_quads_data) {
+  // TODO(mohsen): Currently, effects on the mirrored layer (e.g mask and
+  // opacity) are ignored. Consider applying them here.
+
+  auto* mirrored_layer = layer_tree_impl()->LayerById(mirrored_layer_id_);
+  auto* mirrored_render_surface =
+      GetEffectTree().GetRenderSurface(mirrored_layer->effect_tree_index());
+  gfx::Rect content_rect = mirrored_render_surface->content_rect();
+
+  gfx::Rect unoccluded_content_rect =
+      draw_properties().occlusion_in_content_space.GetUnoccludedContentRect(
+          content_rect);
+  if (unoccluded_content_rect.IsEmpty())
+    return;
+
+  viz::SharedQuadState* shared_quad_state =
+      render_pass->CreateAndAppendSharedQuadState();
+  bool contents_opaque = false;
+  auto* effect_node = GetEffectTree().Node(effect_tree_index());
+  shared_quad_state->SetAll(
+      draw_properties().target_space_transform, content_rect, content_rect,
+      draw_properties().rounded_corner_bounds, draw_properties().clip_rect,
+      draw_properties().is_clipped, contents_opaque, draw_properties().opacity,
+      effect_node->HasRenderSurface() ? SkBlendMode::kSrcOver
+                                      : effect_node->blend_mode,
+      GetSortingContextId());
+  shared_quad_state->is_fast_rounded_corner =
+      draw_properties().is_fast_rounded_corner;
+
+  AppendDebugBorderQuad(render_pass, content_rect, shared_quad_state,
+                        append_quads_data);
+
+  viz::ResourceId mask_resource_id = 0;
+  gfx::RectF mask_uv_rect;
+  gfx::Size mask_texture_size;
+
+  auto* mirrored_effect_node = mirrored_render_surface->OwningEffectNode();
+  auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
+  quad->SetNew(shared_quad_state, content_rect, unoccluded_content_rect,
+               mirrored_layer_id_, mask_resource_id, mask_uv_rect,
+               mask_texture_size, mirrored_effect_node->surface_contents_scale,
+               mirrored_effect_node->filters_origin,
+               gfx::RectF(gfx::Rect(content_rect.size())),
+               !layer_tree_impl()->settings().enable_edge_anti_aliasing, 0.f);
+}
+
+void MirrorLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+  LayerImpl::PushPropertiesTo(layer);
+
+  auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer);
+  mirror_layer->SetMirroredLayerId(mirrored_layer_id_);
+}
+
+gfx::Rect MirrorLayerImpl::GetDamageRect() const {
+  // TOOD(mohsen): Currently, the whole layer is marked as damaged. We should
+  // only consider the damage from the mirrored layer.
+  return gfx::Rect(bounds());
+}
+
+const char* MirrorLayerImpl::LayerTypeAsString() const {
+  return "cc::MirrorLayerImpl";
+}
+
+}  // namespace cc
diff --git a/cc/layers/mirror_layer_impl.h b/cc/layers/mirror_layer_impl.h
new file mode 100644
index 0000000..0ab1a270
--- /dev/null
+++ b/cc/layers/mirror_layer_impl.h
@@ -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.
+
+#ifndef CC_LAYERS_MIRROR_LAYER_IMPL_H_
+#define CC_LAYERS_MIRROR_LAYER_IMPL_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "cc/cc_export.h"
+#include "cc/layers/layer_impl.h"
+
+namespace cc {
+
+// This type of layer is used to mirror contents of another layer (specified by
+// |mirrored_layer_id_|) by forcing a render pass for the mirrored layer and
+// adding a RenderPassDrawQuad in the compositor frame for this layer referring
+// to that render pass. The mirroring layer should not be a descendant of the
+// mirrored layer (in terms of the effect tree). Due to ordering requirements
+// for render passes in the compositor frame, the render pass containing
+// mirroring layer should appear after the render pass created for the mirrored
+// layer. Currently, render passes are in reverse-draw order of the effect tree,
+// so we should be careful that this reverse-draw order does not conflict with
+// render pass ordering requirement mentioned above.
+// TODO(mohsen): If necessary, reorder render passes in compositor frame such
+// that the render pass containing mirroring layer appears after the render pass
+// created for the mirrored layer.
+class CC_EXPORT MirrorLayerImpl : public LayerImpl {
+ public:
+  static std::unique_ptr<MirrorLayerImpl> Create(LayerTreeImpl* tree_impl,
+                                                 int id) {
+    return base::WrapUnique(new MirrorLayerImpl(tree_impl, id));
+  }
+
+  MirrorLayerImpl(const MirrorLayerImpl&) = delete;
+  MirrorLayerImpl& operator=(const MirrorLayerImpl&) = delete;
+
+  ~MirrorLayerImpl() override;
+
+  void SetMirroredLayerId(int id) { mirrored_layer_id_ = id; }
+  int mirrored_layer_id() const { return mirrored_layer_id_; }
+
+  // LayerImpl overrides.
+  std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
+  void AppendQuads(viz::RenderPass* render_pass,
+                   AppendQuadsData* append_quads_data) override;
+  void PushPropertiesTo(LayerImpl* layer) override;
+  gfx::Rect GetDamageRect() const override;
+
+ protected:
+  MirrorLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ private:
+  const char* LayerTypeAsString() const override;
+
+  int mirrored_layer_id_ = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_MIRROR_LAYER_IMPL_H_
diff --git a/cc/layers/mirror_layer_unittest.cc b/cc/layers/mirror_layer_unittest.cc
new file mode 100644
index 0000000..9630d80
--- /dev/null
+++ b/cc/layers/mirror_layer_unittest.cc
@@ -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.
+
+#include <memory>
+
+#include "cc/animation/animation_host.h"
+#include "cc/layers/mirror_layer.h"
+#include "cc/layers/mirror_layer_impl.h"
+#include "cc/test/fake_impl_task_runner_provider.h"
+#include "cc/test/fake_layer_tree_host.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/test_task_graph_runner.h"
+#include "cc/trees/tree_synchronizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class MirrorLayerTest : public testing::Test {
+ public:
+  MirrorLayerTest() : host_impl_(&task_runner_provider_, &task_graph_runner_) {}
+
+  // Synchronizes |layer_tree_host_| and |host_impl_| and pushes surface ids.
+  void SynchronizeTrees() {
+    TreeSynchronizer::PushLayerProperties(layer_tree_host_.get(),
+                                          host_impl_.pending_tree());
+  }
+
+ protected:
+  void SetUp() override {
+    animation_host_ = AnimationHost::CreateForTesting(ThreadInstance::MAIN);
+    layer_tree_host_ = FakeLayerTreeHost::Create(
+        &fake_client_, &task_graph_runner_, animation_host_.get());
+    layer_tree_host_->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f,
+                                              viz::LocalSurfaceIdAllocation());
+    host_impl_.CreatePendingTree();
+  }
+
+  void TearDown() override {
+    layer_tree_host_->SetRootLayer(nullptr);
+    layer_tree_host_ = nullptr;
+  }
+
+  FakeLayerTreeHostClient fake_client_;
+  FakeImplTaskRunnerProvider task_runner_provider_;
+  TestTaskGraphRunner task_graph_runner_;
+  std::unique_ptr<AnimationHost> animation_host_;
+  std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
+  FakeLayerTreeHostImpl host_impl_;
+};
+
+// This test verifies that MirrorLayer properties are pushed across to
+// MirrorLayerImpl.
+TEST_F(MirrorLayerTest, PushProperties) {
+  auto root = Layer::Create();
+  layer_tree_host_->SetRootLayer(root);
+
+  auto mirrored = Layer::Create();
+  root->AddChild(mirrored);
+  auto mirror = MirrorLayer::Create(mirrored);
+  root->AddChild(mirror);
+
+  EXPECT_EQ(1, mirrored->mirror_count());
+  EXPECT_EQ(mirrored.get(), mirror->mirrored_layer());
+
+  auto root_impl = LayerImpl::Create(host_impl_.pending_tree(), root->id());
+  auto mirrored_impl =
+      LayerImpl::Create(host_impl_.pending_tree(), mirrored->id());
+  auto mirror_impl =
+      MirrorLayerImpl::Create(host_impl_.pending_tree(), mirror->id());
+
+  // Verify that impl layers have default property values.
+  EXPECT_EQ(0, mirrored_impl->mirror_count());
+  EXPECT_EQ(0, mirror_impl->mirrored_layer_id());
+
+  SynchronizeTrees();
+
+  // Verify that property values are pushed to impl layers.
+  EXPECT_EQ(1, mirrored_impl->mirror_count());
+  EXPECT_EQ(mirrored_impl->id(), mirror_impl->mirrored_layer_id());
+}
+
+// This test verifies adding/removing mirror layers updates mirror count
+// properly and sets appropriate bits on the layer tree host.
+TEST_F(MirrorLayerTest, MirrorCount) {
+  auto mirrored = Layer::Create();
+  mirrored->SetLayerTreeHost(layer_tree_host_.get());
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+  EXPECT_EQ(0, mirrored->mirror_count());
+
+  // Creating the first mirror layer should trigger property trees rebuild.
+  auto mirror1 = MirrorLayer::Create(mirrored);
+  EXPECT_EQ(1, mirrored->mirror_count());
+  EXPECT_EQ(mirrored.get(), mirror1->mirrored_layer());
+  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             mirrored.get()));
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Creating a second mirror layer should not trigger property trees rebuild.
+  auto mirror2 = MirrorLayer::Create(mirrored);
+  EXPECT_EQ(2, mirrored->mirror_count());
+  EXPECT_EQ(mirrored.get(), mirror2->mirrored_layer());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             mirrored.get()));
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Destroying one of the mirror layers should not trigger property trees
+  // rebuild.
+  mirror1->RemoveFromParent();
+  mirror1 = nullptr;
+  EXPECT_EQ(1, mirrored->mirror_count());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_EQ(1u, layer_tree_host_->LayersThatShouldPushProperties().size());
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Destroying the only remaining mirror layer should trigger property trees
+  // rebuild.
+  mirror2->RemoveFromParent();
+  mirror2 = nullptr;
+  EXPECT_EQ(0, mirrored->mirror_count());
+  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             mirrored.get()));
+
+  mirrored->SetLayerTreeHost(nullptr);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 644a34b..fa04946c 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -764,7 +764,7 @@
 
 void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) {
   if (layer_tree_impl()->IsActiveTree())
-    AddDamageRect(tile->enclosing_layer_rect());
+    damage_rect_.Union(tile->enclosing_layer_rect());
   if (tile->draw_info().NeedsRaster()) {
     PictureLayerTiling* tiling =
         tilings_->FindTilingWithScaleKey(tile->contents_scale_key());
@@ -773,6 +773,15 @@
   }
 }
 
+gfx::Rect PictureLayerImpl::GetDamageRect() const {
+  return damage_rect_;
+}
+
+void PictureLayerImpl::ResetChangeTracking() {
+  LayerImpl::ResetChangeTracking();
+  damage_rect_.SetRect(0, 0, 0, 0);
+}
+
 void PictureLayerImpl::DidBeginTracing() {
   raster_source_->DidBeginTracing();
 }
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 6d564e1..f122754 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -52,6 +52,8 @@
   void AppendQuads(viz::RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
   void NotifyTileStateChanged(const Tile* tile) override;
+  gfx::Rect GetDamageRect() const override;
+  void ResetChangeTracking() override;
   void ResetRasterScale();
   void DidBeginTracing() override;
   void ReleaseResources() override;
@@ -242,6 +244,10 @@
 
   gfx::Size content_bounds_;
   TileSizeCalculator tile_size_calculator_;
+
+  // Denotes an area that is damaged and needs redraw. This is in the layer's
+  // space.
+  gfx::Rect damage_rect_;
 };
 
 }  // namespace cc
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index 6acd6825c..50da1d7 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -15,7 +15,6 @@
 #include "cc/layers/append_quads_data.h"
 #include "cc/paint/filter_operations.h"
 #include "cc/trees/damage_tracker.h"
-#include "cc/trees/draw_property_utils.h"
 #include "cc/trees/effect_node.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/occlusion.h"
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 30ce3e4..c8ccc89 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -1661,7 +1661,6 @@
   }
 
   TRACE_EVENT0("cc,benchmark", "GpuImageDecodeCache::DecodeImage");
-  RecordImageMipLevelUMA(image_data->upload_scale_mip_level);
 
   image_data->decode.ResetData();
   std::unique_ptr<base::DiscardableMemory> backing_memory;
diff --git a/cc/tiles/image_decode_cache.cc b/cc/tiles/image_decode_cache.cc
index 7c83ee4..9c68e3c 100644
--- a/cc/tiles/image_decode_cache.cc
+++ b/cc/tiles/image_decode_cache.cc
@@ -4,7 +4,6 @@
 
 #include "cc/tiles/image_decode_cache.h"
 
-#include "base/metrics/histogram_macros.h"
 #include "cc/raster/tile_task.h"
 
 namespace cc {
@@ -19,11 +18,4 @@
 
 ImageDecodeCache::TaskResult::~TaskResult() = default;
 
-void ImageDecodeCache::RecordImageMipLevelUMA(int mip_level) {
-  DCHECK_GE(mip_level, 0);
-  DCHECK_LT(mip_level, 32);
-  UMA_HISTOGRAM_EXACT_LINEAR("Renderer4.ImageDecodeMipLevel", mip_level + 1,
-                             33);
-}
-
 }  // namespace cc
diff --git a/cc/tiles/image_decode_cache.h b/cc/tiles/image_decode_cache.h
index 6d7d339..241f577 100644
--- a/cc/tiles/image_decode_cache.h
+++ b/cc/tiles/image_decode_cache.h
@@ -143,9 +143,6 @@
   // image can directly be used for raster (for instance bitmaps in a software
   // draw).
   virtual bool UseCacheForDrawImage(const DrawImage& image) const = 0;
-
- protected:
-  void RecordImageMipLevelUMA(int mip_level);
 };
 
 }  // namespace cc
diff --git a/cc/tiles/software_image_decode_cache.cc b/cc/tiles/software_image_decode_cache.cc
index f858a18..6bfa52cd 100644
--- a/cc/tiles/software_image_decode_cache.cc
+++ b/cc/tiles/software_image_decode_cache.cc
@@ -318,8 +318,6 @@
 
   DecodeImageIfNecessary(key, paint_image, cache_entry);
   DCHECK(cache_entry->decode_failed || cache_entry->is_locked);
-  RecordImageMipLevelUMA(
-      MipMapUtil::GetLevelForSize(key.src_rect().size(), key.target_size()));
 }
 
 void SoftwareImageDecodeCache::DecodeImageIfNecessary(
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index f3ddee21..743b681 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -386,7 +386,7 @@
     // If the layer properties haven't changed, then the the target surface is
     // only affected by the layer's damaged area, which could be empty.
     gfx::Rect damage_rect =
-        gfx::UnionRects(layer->update_rect(), layer->damage_rect());
+        gfx::UnionRects(layer->update_rect(), layer->GetDamageRect());
     damage_rect.Intersect(gfx::Rect(layer->bounds()));
 
     if (!damage_rect.IsEmpty()) {
@@ -416,7 +416,7 @@
 
   if (layer_is_new || !layer->update_rect().IsEmpty() ||
       layer->LayerPropertyChangedNotFromPropertyTrees() ||
-      !layer->damage_rect().IsEmpty() || property_change_on_non_target_node) {
+      !layer->GetDamageRect().IsEmpty() || property_change_on_non_target_node) {
     has_damage_from_contributing_content_ |= !damage_for_this_update_.IsEmpty();
   }
 }
diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc
index fcd75ca..b9046c66 100644
--- a/cc/trees/damage_tracker_unittest.cc
+++ b/cc/trees/damage_tracker_unittest.cc
@@ -26,6 +26,36 @@
 namespace cc {
 namespace {
 
+class TestLayerImpl : public LayerImpl {
+ public:
+  TestLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+  void AddDamageRect(const gfx::Rect& damage_rect);
+
+  // LayerImpl overrides.
+  gfx::Rect GetDamageRect() const override;
+  void ResetChangeTracking() override;
+
+ private:
+  gfx::Rect damage_rect_;
+};
+
+TestLayerImpl::TestLayerImpl(LayerTreeImpl* tree_impl, int id)
+    : LayerImpl(tree_impl, id) {}
+
+void TestLayerImpl::AddDamageRect(const gfx::Rect& damage_rect) {
+  damage_rect_.Union(damage_rect);
+}
+
+gfx::Rect TestLayerImpl::GetDamageRect() const {
+  return damage_rect_;
+}
+
+void TestLayerImpl::ResetChangeTracking() {
+  LayerImpl::ResetChangeTracking();
+  damage_rect_.SetRect(0, 0, 0, 0);
+}
+
 void ExecuteCalculateDrawProperties(LayerImpl* root,
                                     float device_scale_factor,
                                     RenderSurfaceList* render_surface_list) {
@@ -71,16 +101,15 @@
 
   LayerImpl* CreateTestTreeWithOneSurface(int number_of_children) {
     host_impl_.active_tree()->DetachLayers();
-    std::unique_ptr<LayerImpl> root =
-        LayerImpl::Create(host_impl_.active_tree(), 1);
+    auto root = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 1);
 
     root->SetBounds(gfx::Size(500, 500));
     root->SetDrawsContent(true);
     root->test_properties()->force_render_surface = true;
 
     for (int i = 0; i < number_of_children; ++i) {
-      std::unique_ptr<LayerImpl> child =
-          LayerImpl::Create(host_impl_.active_tree(), 2 + i);
+      auto child =
+          std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 2 + i);
       child->test_properties()->position = gfx::PointF(100.f, 100.f);
       child->SetBounds(gfx::Size(30, 30));
       child->SetDrawsContent(true);
@@ -98,16 +127,13 @@
     // two children of its own.
 
     host_impl_.active_tree()->DetachLayers();
-    std::unique_ptr<LayerImpl> root =
-        LayerImpl::Create(host_impl_.active_tree(), 1);
-    std::unique_ptr<LayerImpl> child1 =
-        LayerImpl::Create(host_impl_.active_tree(), 2);
-    std::unique_ptr<LayerImpl> child2 =
-        LayerImpl::Create(host_impl_.active_tree(), 3);
-    std::unique_ptr<LayerImpl> grand_child1 =
-        LayerImpl::Create(host_impl_.active_tree(), 4);
-    std::unique_ptr<LayerImpl> grand_child2 =
-        LayerImpl::Create(host_impl_.active_tree(), 5);
+    auto root = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 1);
+    auto child1 = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 2);
+    auto child2 = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 3);
+    auto grand_child1 =
+        std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 4);
+    auto grand_child2 =
+        std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 5);
 
     root->SetBounds(gfx::Size(500, 500));
     root->SetDrawsContent(true);
@@ -276,7 +302,8 @@
 
 TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) {
   LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface();
-  LayerImpl* child = root->test_properties()->children[0];
+  auto* child =
+      static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
 
   // CASE 1: Adding the layer damage rect should cause the corresponding damage
   // to the surface.
@@ -336,7 +363,8 @@
 
 TEST_F(DamageTrackerTest, VerifyDamageForLayerUpdateAndDamageRects) {
   LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface();
-  LayerImpl* child = root->test_properties()->children[0];
+  auto* child =
+      static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
 
   // CASE 1: Adding the layer damage rect and update rect should cause the
   // corresponding damage to the surface.
@@ -547,9 +575,12 @@
 TEST_F(DamageTrackerTest,
        VerifyDamageForUpdateAndDamageRectsFromContributingContents) {
   LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
-  LayerImpl* child1 = root->test_properties()->children[0];
-  LayerImpl* child2 = root->test_properties()->children[1];
-  LayerImpl* grandchild1 = child1->test_properties()->children[0];
+  auto* child1 =
+      static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
+  auto* child2 =
+      static_cast<TestLayerImpl*>(root->test_properties()->children[1]);
+  auto* grandchild1 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
 
   // CASE 1: Adding the layer1's damage rect and update rect should cause the
   // corresponding damage to the surface.
@@ -1912,8 +1943,10 @@
 TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) {
   LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
   LayerImpl* child1 = root->test_properties()->children[0];
-  LayerImpl* grandchild1 = child1->test_properties()->children[0];
-  LayerImpl* grandchild2 = child1->test_properties()->children[1];
+  auto* grandchild1 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
+  auto* grandchild2 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[1]);
 
   // Really far left.
   grandchild1->test_properties()->position =
@@ -2002,8 +2035,10 @@
 TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) {
   LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
   LayerImpl* child1 = root->test_properties()->children[0];
-  LayerImpl* grandchild1 = child1->test_properties()->children[0];
-  LayerImpl* grandchild2 = child1->test_properties()->children[1];
+  auto* grandchild1 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
+  auto* grandchild2 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[1]);
 
   // Set up a moving pixels filter on the child.
   FilterOperations filters;
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h
index 340305e..dd77eab0 100644
--- a/cc/trees/effect_node.h
+++ b/cc/trees/effect_node.h
@@ -40,6 +40,7 @@
   kTrilinearFiltering,
   kCache,
   kCopyRequest,
+  kMirrored,
   // This must be the last value because it's used in tracing code to know the
   // number of reasons.
   kTest,
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index cc3f57d..31aff20 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -359,7 +359,8 @@
       is_animating_for_snap_(false),
       paint_image_generator_client_id_(PaintImage::GetNextGeneratorClientId()),
       scrollbar_controller_(std::make_unique<ScrollbarController>(this)),
-      scroll_gesture_did_end_(false) {
+      scroll_gesture_did_end_(false),
+      weak_factory_(this) {
   DCHECK(mutator_host_);
   mutator_host_->SetMutatorHostClient(this);
 
@@ -3231,7 +3232,7 @@
   tile_manager_.decoded_image_tracker().QueueImageDecode(
       image, GetRasterColorSpace(),
       base::BindOnce(&LayerTreeHostImpl::ImageDecodeFinished,
-                     base::Unretained(this), request_id));
+                     weak_factory_.GetWeakPtr(), request_id));
   tile_manager_.checker_image_tracker().DisallowCheckeringForImage(image);
 }
 
@@ -5982,4 +5983,8 @@
   client_->NeedsImplSideInvalidation(needs_first_draw_on_activation);
 }
 
+base::WeakPtr<LayerTreeHostImpl> LayerTreeHostImpl::AsWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 7a955787..be13cc11 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -170,16 +170,14 @@
 
 // LayerTreeHostImpl owns the LayerImpl trees as well as associated rendering
 // state.
-class CC_EXPORT LayerTreeHostImpl
-    : public InputHandler,
-      public TileManagerClient,
-      public LayerTreeFrameSinkClient,
-      public BrowserControlsOffsetManagerClient,
-      public ScrollbarAnimationControllerClient,
-      public VideoFrameControllerClient,
-      public MutatorHostClient,
-      public ImageAnimationController::Client,
-      public base::SupportsWeakPtr<LayerTreeHostImpl> {
+class CC_EXPORT LayerTreeHostImpl : public InputHandler,
+                                    public TileManagerClient,
+                                    public LayerTreeFrameSinkClient,
+                                    public BrowserControlsOffsetManagerClient,
+                                    public ScrollbarAnimationControllerClient,
+                                    public VideoFrameControllerClient,
+                                    public MutatorHostClient,
+                                    public ImageAnimationController::Client {
  public:
   // This structure is used to build all the state required for producing a
   // single CompositorFrame. The |render_passes| list becomes the set of
@@ -312,6 +310,8 @@
   void RequestBeginFrameForAnimatedImages() override;
   void RequestInvalidationForAnimatedImages() override;
 
+  base::WeakPtr<LayerTreeHostImpl> AsWeakPtr();
+
   void UpdateViewportContainerSizes();
 
   void set_resourceless_software_draw_for_testing() {
@@ -1190,6 +1190,10 @@
   // animation completion. ScrollEnd will get called with this deferred state
   // once the animation is over.
   base::Optional<ScrollState> deferred_scroll_end_state_;
+
+  // Must be the last member to ensure this is destroyed first in the
+  // destruction order and invalidates all weak pointers.
+  base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_;
 };
 
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc
index 1e9f267..8c7abdd9 100644
--- a/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -41,6 +41,8 @@
     {LayerTreeTest::RENDERER_GL, ONE_COPY},
     {LayerTreeTest::RENDERER_GL, ZERO_COPY},
     {LayerTreeTest::RENDERER_SKIA_GL, GPU},
+    {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY},
+    {LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY},
 };
 
 std::vector<PixelResourceTestCase> const kTestCasesNonSkia = {
@@ -446,14 +448,7 @@
       &property_trees);
 }
 
-using LayerTreeHostLayerListPixelTestNonSkia = LayerTreeHostLayerListPixelTest;
-
-// TODO(crbug.com/948128): Enable this test for Skia.
-INSTANTIATE_TEST_SUITE_P(PixelResourceTest,
-                         LayerTreeHostLayerListPixelTestNonSkia,
-                         CombineWithLayerMaskTypes(kTestCasesNonSkia));
-
-TEST_P(LayerTreeHostLayerListPixelTestNonSkia, ScaledMaskWithEffect) {
+TEST_P(LayerTreeHostLayerListPixelTest, ScaledMaskWithEffect) {
   PropertyTrees property_trees;
   scoped_refptr<Layer> root_layer;
   InitializeForLayerListMode(&root_layer, &property_trees);
@@ -519,6 +514,11 @@
   float average_error_allowed_in_bad_pixels = 100.0f;
   int large_error_allowed = 256;
   int small_error_allowed = 0;
+
+  // Skia has a larger number of pixels that are off by one.
+  if (renderer_type() == RENDERER_SKIA_GL)
+    percentage_pixels_large_error = 3.8f;
+
   pixel_comparator_ = std::make_unique<FuzzyPixelComparator>(
       true,  // discard_alpha
       percentage_pixels_large_error, percentage_pixels_small_error,
@@ -983,13 +983,13 @@
     int large_error_allowed = 0;
     int small_error_allowed = 0;
     if (use_antialiasing_) {
-      percentage_pixels_small_error = 0.9f;
+      percentage_pixels_small_error = 2.f;
       percentage_pixels_error = 6.7f;
       average_error_allowed_in_bad_pixels = 3.5f;
       large_error_allowed = 15;
       small_error_allowed = 1;
     } else if (raster_type() != SOFTWARE) {
-      percentage_pixels_small_error = 0.9f;
+      percentage_pixels_small_error = 2.f;
       percentage_pixels_error = 6.5f;
       average_error_allowed_in_bad_pixels = 3.5f;
       large_error_allowed = 15;
@@ -1095,7 +1095,7 @@
   bool force_shaders_;
 };
 
-// TODO(crbug.com/948128): Enable these tests for Skia.
+// TODO(crbug.com/963446): Enable these tests for Vulkan.
 MaskTestConfig const kTestConfigs[] = {
     MaskTestConfig{{LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE}, 0},
     MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, 0},
@@ -1103,6 +1103,9 @@
     MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, kForceShaders},
     MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY},
                    kUseAntialiasing | kForceShaders},
+    MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY}, 0},
+    MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY},
+                   kUseAntialiasing},
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
diff --git a/cc/trees/layer_tree_host_pixeltest_mirror.cc b/cc/trees/layer_tree_host_pixeltest_mirror.cc
new file mode 100644
index 0000000..ed011f43
--- /dev/null
+++ b/cc/trees/layer_tree_host_pixeltest_mirror.cc
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium 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 "build/build_config.h"
+#include "cc/layers/mirror_layer.h"
+#include "cc/layers/solid_color_layer.h"
+#include "cc/test/layer_tree_pixel_test.h"
+#include "cc/test/pixel_comparator.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform_util.h"
+
+#if !defined(OS_ANDROID)
+
+namespace cc {
+namespace {
+
+class LayerTreeHostMirrorPixelTest
+    : public LayerTreePixelTest,
+      public ::testing::WithParamInterface<LayerTreeTest::RendererType> {
+ protected:
+  RendererType renderer_type() { return GetParam(); }
+};
+
+const LayerTreeTest::RendererType kRendererTypes[] = {
+    LayerTreeTest::RENDERER_GL,
+    LayerTreeTest::RENDERER_SKIA_GL,
+    LayerTreeTest::RENDERER_SOFTWARE,
+#if defined(ENABLE_CC_VULKAN_TESTS)
+    LayerTreeTest::RENDERER_SKIA_VK,
+#endif
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         LayerTreeHostMirrorPixelTest,
+                         ::testing::ValuesIn(kRendererTypes));
+
+// Verifies that a mirror layer with a scale mirrors another layer correctly.
+TEST_P(LayerTreeHostMirrorPixelTest, MirrorLayer) {
+  const float scale = 2.f;
+  gfx::Rect background_bounds(120, 180);
+  gfx::Rect mirrored_bounds(10, 10, 50, 50);
+  gfx::Rect mirror_bounds(10, 70, 100, 100);
+
+  auto background = CreateSolidColorLayer(background_bounds, SK_ColorWHITE);
+
+  auto mirrored_layer = CreateSolidColorLayerWithBorder(
+      mirrored_bounds, SK_ColorGREEN, 5, SK_ColorBLUE);
+
+  auto mirror_layer = MirrorLayer::Create(mirrored_layer);
+  mirror_layer->SetIsDrawable(true);
+  mirror_layer->SetBounds(mirror_bounds.size());
+  mirror_layer->SetPosition(gfx::PointF(mirror_bounds.origin()));
+  mirror_layer->SetTransform(gfx::GetScaleTransform(gfx::Point(), scale));
+  background->AddChild(mirrored_layer);
+  background->AddChild(mirror_layer);
+
+  if (renderer_type() == RENDERER_SOFTWARE) {
+    const bool discard_alpha = true;
+    const float error_pixels_percentage_limit = 3.f;
+    const float small_error_pixels_percentage_limit = 0.f;
+    const float avg_abs_error_limit = 65.f;
+    const int max_abs_error_limit = 120;
+    const int small_error_threshold = 0;
+    pixel_comparator_ = std::make_unique<FuzzyPixelComparator>(
+        discard_alpha, error_pixels_percentage_limit,
+        small_error_pixels_percentage_limit, avg_abs_error_limit,
+        max_abs_error_limit, small_error_threshold);
+  }
+
+#if defined(ENABLE_CC_VULKAN_TESTS) && defined(OS_LINUX)
+  if (renderer_type() == RENDERER_SKIA_VK)
+    pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true);
+#endif
+
+  RunPixelTest(renderer_type(), background,
+               base::FilePath(FILE_PATH_LITERAL("mirror_layer.png")));
+}
+
+}  // namespace
+}  // namespace cc
+
+#endif  // !defined(OS_ANDROID)
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 7d20ffa..cf905755 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -867,6 +867,14 @@
   return !layer->test_properties()->copy_requests.empty();
 }
 
+static inline int MirrorCount(Layer* layer) {
+  return layer->mirror_count();
+}
+
+static inline int MirrorCount(LayerImpl* layer) {
+  return 0;
+}
+
 static inline bool PropertyChanged(Layer* layer) {
   return layer->subtree_property_changed();
 }
@@ -979,6 +987,10 @@
   if (HasCopyRequest(layer))
     return RenderSurfaceReason::kCopyRequest;
 
+  // If the layer is mirrored.
+  if (MirrorCount(layer))
+    return RenderSurfaceReason::kMirrored;
+
   return RenderSurfaceReason::kNone;
 }
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 28ec3cd9..ad6b2a2 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=77
 MINOR=0
-BUILD=3836
+BUILD=3838
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index da467e8..3c3caef 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2612,6 +2612,7 @@
     "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java",
     "java/src/org/chromium/chrome/browser/offlinepages/OfflinePagesDownloadManagerBridge.java",
     "java/src/org/chromium/chrome/browser/offlinepages/PublishPageCallback.java",
+    "java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java",
     "java/src/org/chromium/chrome/browser/offlinepages/SavePageRequest.java",
     "java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java",
     "java/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTask.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 1049e9e..285c489e 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1006,6 +1006,7 @@
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePagesDownloadManagerBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/PublishPageCallback.java",
+  "java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/SavePageAndShareCallback.java",
   "java/src/org/chromium/chrome/browser/offlinepages/SavePageRequest.java",
   "java/src/org/chromium/chrome/browser/offlinepages/TaskExtrasPacker.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index c4d8001f..30dc99644 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -242,6 +242,7 @@
   "javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java",
   "javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java",
+  "javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.java",
   "javatests/src/org/chromium/chrome/browser/offlinepages/indicator/ConnectivityDetectorDelegateStub.java",
   "javatests/src/org/chromium/chrome/browser/offlinepages/indicator/ConnectivityDetectorTest.java",
   "javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 86d8638..ad83630 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -212,27 +212,20 @@
         }
     }
 
-    @CalledByNative
-    private void setSuggestions(int[] icons, String[] texts, boolean[] disabled) {
-        assert texts.length == icons.length;
-        assert texts.length == disabled.length;
-        List<AssistantChip> chips = new ArrayList<>();
-        for (int i = 0; i < texts.length; i++) {
-            final int suggestionIndex = i;
-            chips.add(new AssistantChip(AssistantChip.Type.CHIP_ASSISTIVE, icons[i], texts[i],
-                    disabled[i], /* sticky= */ false,
-                    () -> safeNativeOnSuggestionSelected(suggestionIndex)));
-        }
-        AssistantCarouselModel model = getModel().getSuggestionsModel();
-        setChips(model, chips);
-    }
-
     /** Creates an empty list of chips. */
     @CalledByNative
     private static List<AssistantChip> createChipList() {
         return new ArrayList<>();
     }
 
+    /** Adds a suggestion to the chip list, which executes the action {@code actionIndex}. */
+    @CalledByNative
+    private void addSuggestion(
+            List<AssistantChip> chips, String text, int actionIndex, int icon, boolean disabled) {
+        chips.add(new AssistantChip(AssistantChip.Type.CHIP_ASSISTIVE, icon, text, disabled,
+                /* sticky= */ false, () -> safeNativeOnUserActionSelected(actionIndex)));
+    }
+
     /**
      * Adds an action button to the chip list, which executes the action {@code actionIndex}.
      */
@@ -240,7 +233,7 @@
     private void addActionButton(List<AssistantChip> chips, int icon, String text, int actionIndex,
             boolean disabled, boolean sticky) {
         chips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled,
-                sticky, () -> safeNativeOnActionSelected(actionIndex)));
+                sticky, () -> safeNativeOnUserActionSelected(actionIndex)));
     }
 
     /**
@@ -251,7 +244,7 @@
     private void addHighlightedActionButton(List<AssistantChip> chips, int icon, String text,
             int actionIndex, boolean disabled, boolean sticky) {
         chips.add(new AssistantChip(Type.BUTTON_FILLED_BLUE, icon, text, disabled, sticky,
-                () -> safeNativeOnActionSelected(actionIndex)));
+                () -> safeNativeOnUserActionSelected(actionIndex)));
     }
 
     /**
@@ -282,6 +275,11 @@
         setHeaderChip(chips);
     }
 
+    @CalledByNative
+    private void setSuggestions(List<AssistantChip> chips) {
+        setChips(getModel().getSuggestionsModel(), chips);
+    }
+
     private void setHeaderChip(List<AssistantChip> chips) {
         // The header chip is the first sticky chip found in the actions.
         AssistantChip headerChip = null;
@@ -331,15 +329,10 @@
     private native void nativeOnFatalError(
             long nativeUiControllerAndroid, String message, @DropOutReason int reason);
 
-    private void safeNativeOnSuggestionSelected(int index) {
-        if (mNativeUiController != 0) nativeOnSuggestionSelected(mNativeUiController, index);
+    private void safeNativeOnUserActionSelected(int index) {
+        if (mNativeUiController != 0) nativeOnUserActionSelected(mNativeUiController, index);
     }
-    private native void nativeOnSuggestionSelected(long nativeUiControllerAndroid, int index);
-
-    private void safeNativeOnActionSelected(int index) {
-        if (mNativeUiController != 0) nativeOnActionSelected(mNativeUiController, index);
-    }
-    private native void nativeOnActionSelected(long nativeUiControllerAndroid, int index);
+    private native void nativeOnUserActionSelected(long nativeUiControllerAndroid, int index);
 
     private void safeNativeOnCancelButtonClicked(int index) {
         if (mNativeUiController != 0) nativeOnCancelButtonClicked(mNativeUiController, index);
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
index cd2870f..4df071e 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
@@ -19,6 +19,9 @@
     public static final WritableObjectPropertyKey<String> STATUS_MESSAGE =
             new WritableObjectPropertyKey<>();
 
+    public static final WritableObjectPropertyKey<String> BUBBLE_MESSAGE =
+            new WritableObjectPropertyKey<>();
+
     static final WritableIntPropertyKey PROGRESS = new WritableIntPropertyKey();
 
     @VisibleForTesting
@@ -36,8 +39,8 @@
     public static final WritableBooleanPropertyKey CHIP_VISIBLE = new WritableBooleanPropertyKey();
 
     public AssistantHeaderModel() {
-        super(STATUS_MESSAGE, PROGRESS, PROGRESS_VISIBLE, SPIN_POODLE, FEEDBACK_BUTTON_CALLBACK,
-                CHIP, CHIP_VISIBLE);
+        super(STATUS_MESSAGE, BUBBLE_MESSAGE, PROGRESS, PROGRESS_VISIBLE, SPIN_POODLE,
+                FEEDBACK_BUTTON_CALLBACK, CHIP, CHIP_VISIBLE);
     }
 
     @CalledByNative
@@ -46,6 +49,11 @@
     }
 
     @CalledByNative
+    private void setBubbleMessage(String bubbleMessage) {
+        set(BUBBLE_MESSAGE, bubbleMessage);
+    }
+
+    @CalledByNative
     private void setProgress(int progress) {
         set(PROGRESS, progress);
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
index 4db357b..8e6fc00 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
@@ -16,8 +16,10 @@
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipViewHolder;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.autofill_assistant.AutofillAssistantPreferences;
+import org.chromium.chrome.browser.widget.textbubble.TextBubble;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.widget.ViewRectProvider;
 
 /**
  * This class is responsible for pushing updates to the Autofill Assistant header view. These
@@ -31,6 +33,7 @@
      * A wrapper class that holds the different views of the header.
      */
     static class ViewHolder {
+        final Context mContext;
         final AnimatedPoodle mPoodle;
         final ViewGroup mHeader;
         final TextView mStatusMessage;
@@ -39,8 +42,11 @@
         final PopupMenu mProfileIconMenu;
         @Nullable
         AssistantChipViewHolder mChip;
+        @Nullable
+        TextBubble mTextBubble;
 
         public ViewHolder(Context context, View bottomBarView, AnimatedPoodle poodle) {
+            mContext = context;
             mPoodle = poodle;
             mHeader = bottomBarView.findViewById(R.id.header);
             mStatusMessage = bottomBarView.findViewById(R.id.status_message);
@@ -75,6 +81,8 @@
             maybeShowChip(model, view);
         } else if (AssistantHeaderModel.CHIP_VISIBLE == propertyKey) {
             maybeShowChip(model, view);
+        } else if (AssistantHeaderModel.BUBBLE_MESSAGE == propertyKey) {
+            showOrDismissBubble(model, view);
         } else {
             assert false : "Unhandled property detected in AssistantHeaderViewBinder!";
         }
@@ -135,4 +143,20 @@
             return false;
         });
     }
+
+    private void showOrDismissBubble(AssistantHeaderModel model, ViewHolder view) {
+        String message = model.get(AssistantHeaderModel.BUBBLE_MESSAGE);
+        if (message.isEmpty() && view.mTextBubble != null) {
+            view.mTextBubble.dismiss();
+            view.mTextBubble = null;
+            return;
+        }
+        View poodle = view.mPoodle.getView();
+        view.mTextBubble = new TextBubble(
+                /*context = */ view.mContext, /*rootView = */ poodle, /*contentString = */ message,
+                /*accessibilityString = */ message, /*showArrow = */ true,
+                /*anchorRectProvider = */ new ViewRectProvider(poodle));
+        view.mTextBubble.setDismissOnTouchInteraction(true);
+        view.mTextBubble.show();
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index 7d1f674..540c4b6 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -4,6 +4,13 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.not;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.verify;
 
@@ -29,7 +36,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import org.chromium.base.PathUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
@@ -84,7 +90,6 @@
         mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
         mTestPage = mTestServer.getURL(UrlUtils.getIsolatedTestFilePath(
                 "components/test/data/autofill_assistant/autofill_assistant_target_website.html"));
-        PathUtils.setPrivateDataDirectorySuffix("chrome");
         LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
     }
 
@@ -275,4 +280,39 @@
                 () -> { chipsViewContainer.getChildAt(1).performClick(); });
         inOrder.verify(mRunnableMock).run();
     }
+
+    @Test
+    @MediumTest
+    public void testTooltipBubble() throws Exception {
+        InOrder inOrder = inOrder(mRunnableMock);
+
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent());
+        BottomSheetController bottomSheetController =
+                ThreadUtils.runOnUiThreadBlocking(this::initializeBottomSheet);
+        AssistantCoordinator assistantCoordinator = ThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> new AssistantCoordinator(getActivity(), bottomSheetController,
+                                /* overlayCoordinator= */ null));
+
+        // Bottom sheet is shown in the BottomSheet when creating the AssistantCoordinator.
+        ViewGroup bottomSheetContent =
+                bottomSheetController.getBottomSheet().findViewById(R.id.autofill_assistant);
+        Assert.assertNotNull(bottomSheetContent);
+
+        // Disable bottom sheet content animations. This is a workaround for http://crbug/943483.
+        TestThreadUtils.runOnUiThreadBlocking(() -> bottomSheetContent.setLayoutTransition(null));
+
+        // Show and check the bubble message.
+        String testBubbleMessage = "Bubble message.";
+        ThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> assistantCoordinator.getModel().getHeaderModel().set(
+                                AssistantHeaderModel.BUBBLE_MESSAGE, testBubbleMessage));
+
+        // Bubbles are opened as popups and espresso needs to be instructed to not match views in
+        // the main window's root.
+        onView(withText(testBubbleMessage))
+                .inRoot(withDecorView(not(getActivity().getWindow().getDecorView())))
+                .check(matches(isDisplayed()));
+    }
 }
diff --git a/chrome/android/features/keyboard_accessory/BUILD.gn b/chrome/android/features/keyboard_accessory/BUILD.gn
index b245a1b..228c5c4 100644
--- a/chrome/android/features/keyboard_accessory/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/BUILD.gn
@@ -42,6 +42,7 @@
     "javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewTest.java",
     "javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java",
+    "javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java",
     "javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
index 6309f7eb..4dbcb82 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
@@ -5,6 +5,7 @@
 
 <android.support.v7.widget.RecyclerView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/credit_card_sheet"
     android:fillViewport="true"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
index e55ce82..60cb374 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
@@ -132,7 +132,8 @@
 
     @CalledByNative
     private void addFieldToUserInfo(Object objUserInfo, @AccessoryTabType int sheetType,
-            String displayText, String a11yDescription, boolean isObfuscated, boolean selectable) {
+            String displayText, String a11yDescription, String guid, boolean isObfuscated,
+            boolean selectable) {
         Callback<UserInfoField> callback = null;
         if (selectable) {
             callback = (field) -> {
@@ -144,7 +145,7 @@
         }
         ((UserInfo) objUserInfo)
                 .getFields()
-                .add(new UserInfoField(displayText, a11yDescription, isObfuscated, callback));
+                .add(new UserInfoField(displayText, a11yDescription, guid, isObfuscated, callback));
     }
 
     @CalledByNative
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMetricsRecorder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMetricsRecorder.java
index 6edd12d..575f4eb 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMetricsRecorder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMetricsRecorder.java
@@ -21,6 +21,8 @@
     private static final String UMA_KEYBOARD_ACCESSORY_SHEET_SUGGESTION_SELECTED =
             "KeyboardAccessory.AccessorySheetSuggestionsSelected";
     private static final String UMA_KEYBOARD_ACCESSORY_SHEET_TYPE_SUFFIX_PASSWORDS = "Passwords";
+    private static final String UMA_KEYBOARD_ACCESSORY_SHEET_TYPE_SUFFIX_CREDIT_CARDS =
+            "CreditCards";
     private static final String UMA_KEYBOARD_ACCESSORY_SHEET_TYPE_SUFFIX_ADDRESSES = "Addresses";
     private static final String UMA_KEYBOARD_ACCESSORY_SHEET_TYPE_SUFFIX_TOUCH_TO_FILL =
             "TouchToFill";
@@ -42,6 +44,8 @@
                 return baseHistogram;
             case AccessoryTabType.PASSWORDS:
                 return baseHistogram + "." + UMA_KEYBOARD_ACCESSORY_SHEET_TYPE_SUFFIX_PASSWORDS;
+            case AccessoryTabType.CREDIT_CARDS:
+                return baseHistogram + "." + UMA_KEYBOARD_ACCESSORY_SHEET_TYPE_SUFFIX_CREDIT_CARDS;
             case AccessoryTabType.ADDRESSES:
                 return baseHistogram + "." + UMA_KEYBOARD_ACCESSORY_SHEET_TYPE_SUFFIX_ADDRESSES;
             case AccessoryTabType.TOUCH_TO_FILL:
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java
index 6247ee3..8f59c0c 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java
@@ -46,6 +46,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.AccessorySheetData;
 import org.chromium.chrome.browser.keyboard_accessory.data.PropertyProvider;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AddressAccessorySheetCoordinator;
+import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.CreditCardAccessorySheetCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.PasswordAccessorySheetCoordinator;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.content_public.browser.ImeAdapter;
@@ -277,6 +278,12 @@
                 .getOrCreateSheet(AccessoryTabType.ADDRESSES);
     }
 
+    public CreditCardAccessorySheetCoordinator getOrCreateCreditCardAccessorySheet() {
+        return (CreditCardAccessorySheetCoordinator) getManualFillingCoordinator()
+                .getMediatorForTesting()
+                .getOrCreateSheet(AccessoryTabType.CREDIT_CARDS);
+    }
+
     // ----------------------------------
     // Helpers to set up the native side.
     // ----------------------------------
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java
index dbaf77c..75800bb4 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java
@@ -136,7 +136,7 @@
         // Scroll to last element and click the second icon:
         whenDisplayed(withId(R.id.bar_items_view))
                 .perform(scrollTo(isKeyboardAccessoryTabLayout()),
-                        actionOnItem(isKeyboardAccessoryTabLayout(), selectTabAtPosition(1)));
+                        actionOnItem(isKeyboardAccessoryTabLayout(), selectTabAtPosition(2)));
 
         // Wait for the sheet to come up and be stable.
         whenDisplayed(withId(R.id.addresses_sheet));
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewTest.java
index 04886db..20fec9f 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetViewTest.java
@@ -168,30 +168,30 @@
             String addressHomeCountry, String phoneHomeWholeNumber, String emailAddress,
             AtomicBoolean clickRecorder) {
         UserInfo info = new UserInfo(null);
+        info.addField(new UserInfoField(
+                nameFirst, nameFirst, "", false, item -> clickRecorder.set(true)));
+        info.addField(new UserInfoField(
+                nameMiddle, nameMiddle, "", false, item -> clickRecorder.set(true)));
         info.addField(
-                new UserInfoField(nameFirst, nameFirst, false, item -> clickRecorder.set(true)));
-        info.addField(
-                new UserInfoField(nameMiddle, nameMiddle, false, item -> clickRecorder.set(true)));
-        info.addField(
-                new UserInfoField(nameLast, nameLast, false, item -> clickRecorder.set(true)));
+                new UserInfoField(nameLast, nameLast, "", false, item -> clickRecorder.set(true)));
         info.addField(new UserInfoField(
-                companyName, companyName, false, item -> clickRecorder.set(true)));
+                companyName, companyName, "", false, item -> clickRecorder.set(true)));
         info.addField(new UserInfoField(
-                addressHomeLine1, addressHomeLine1, false, item -> clickRecorder.set(true)));
+                addressHomeLine1, addressHomeLine1, "", false, item -> clickRecorder.set(true)));
         info.addField(new UserInfoField(
-                addressHomeLine2, addressHomeLine2, false, item -> clickRecorder.set(true)));
+                addressHomeLine2, addressHomeLine2, "", false, item -> clickRecorder.set(true)));
         info.addField(new UserInfoField(
-                addressHomeZip, addressHomeZip, false, item -> clickRecorder.set(true)));
+                addressHomeZip, addressHomeZip, "", false, item -> clickRecorder.set(true)));
         info.addField(new UserInfoField(
-                addressHomeCity, addressHomeCity, false, item -> clickRecorder.set(true)));
+                addressHomeCity, addressHomeCity, "", false, item -> clickRecorder.set(true)));
         info.addField(new UserInfoField(
-                addressHomeState, addressHomeState, false, item -> clickRecorder.set(true)));
-        info.addField(new UserInfoField(
-                addressHomeCountry, addressHomeCountry, false, item -> clickRecorder.set(true)));
-        info.addField(new UserInfoField(phoneHomeWholeNumber, phoneHomeWholeNumber, false,
+                addressHomeState, addressHomeState, "", false, item -> clickRecorder.set(true)));
+        info.addField(new UserInfoField(addressHomeCountry, addressHomeCountry, "", false,
+                item -> clickRecorder.set(true)));
+        info.addField(new UserInfoField(phoneHomeWholeNumber, phoneHomeWholeNumber, "", false,
                 item -> clickRecorder.set(true)));
         info.addField(new UserInfoField(
-                emailAddress, emailAddress, false, item -> clickRecorder.set(true)));
+                emailAddress, emailAddress, "", false, item -> clickRecorder.set(true)));
         return info;
     }
 
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryIntegrationTest.java
new file mode 100644
index 0000000..c62d98f
--- /dev/null
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryIntegrationTest.java
@@ -0,0 +1,157 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.keyboard_accessory.sheet_tabs;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItem;
+import static android.support.test.espresso.contrib.RecyclerViewActions.scrollTo;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+
+import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.selectTabAtPosition;
+import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.whenDisplayed;
+import static org.chromium.chrome.browser.keyboard_accessory.tab_layout_component.KeyboardAccessoryTabTestHelper.isKeyboardAccessoryTabLayout;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.widget.TextView;
+
+import org.junit.After;
+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.RetryOnFailure;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeWindow;
+import org.chromium.chrome.browser.autofill.AutofillTestHelper;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
+import org.chromium.chrome.browser.keyboard_accessory.FakeKeyboard;
+import org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper;
+import org.chromium.chrome.browser.keyboard_accessory.R;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.test.util.DOMUtils;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Integration tests for credit card accessory views.
+ */
+
+@RunWith(ChromeJUnit4ClassRunner.class)
+@RetryOnFailure
+@EnableFeatures({ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY,
+        ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY})
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class CreditCardAccessoryIntegrationTest {
+    @Rule
+    public final ChromeTabbedActivityTestRule mActivityTestRule =
+            new ChromeTabbedActivityTestRule();
+
+    private final ManualFillingTestHelper mHelper = new ManualFillingTestHelper(mActivityTestRule);
+
+    @After
+    public void tearDown() {
+        mHelper.clear();
+    }
+
+    private void loadTestPage(ChromeWindow.KeyboardVisibilityDelegateFactory keyboardDelegate)
+            throws InterruptedException, TimeoutException {
+        mHelper.loadTestPage("/chrome/test/data/autofill/autofill_creditcard_form.html", false,
+                false, keyboardDelegate);
+        CreditCard card = new CreditCard();
+        card.setName("Kirby Puckett");
+        card.setNumber("4111111111111111");
+        card.setMonth("03");
+        card.setYear("2034");
+
+        new AutofillTestHelper().setCreditCard(card);
+        DOMUtils.waitForNonZeroNodeBounds(mHelper.getWebContents(), "CREDIT_CARD_NAME_FULL");
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures({ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID})
+    public void testCreditCardSheetAvailable() throws InterruptedException {
+        mHelper.loadTestPage(false);
+
+        CriteriaHelper.pollUiThread(() -> {
+            return mHelper.getOrCreateCreditCardAccessorySheet() != null;
+        }, "Credit Card sheet should be bound to accessory sheet.");
+    }
+
+    @Test
+    @SmallTest
+    @DisableFeatures({ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID})
+    public void testCreditCardSheetUnavailableWithoutFeature() throws InterruptedException {
+        mHelper.loadTestPage(false);
+
+        Assert.assertNull("Credit Card sheet should not have been created.",
+                mHelper.getOrCreateCreditCardAccessorySheet());
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures({ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID})
+    public void testDisplaysEmptyStateMessageWithoutSavedCards()
+            throws InterruptedException, TimeoutException {
+        mHelper.loadTestPage(false);
+
+        // Focus the field to bring up the accessory.
+        mHelper.focusPasswordField();
+        mHelper.waitForKeyboardAccessoryToBeShown();
+
+        // Click the tab to show the sheet and hide the keyboard.
+        whenDisplayed(allOf(withContentDescription(R.string.credit_card_accessory_sheet_toggle),
+                              not(isAssignableFrom(TextView.class))))
+                .perform(click());
+        mHelper.waitForKeyboardToDisappear();
+        whenDisplayed(withId(R.id.credit_card_sheet));
+        onView(withText(containsString("No saved payment methods"))).check(matches(isDisplayed()));
+    }
+
+    @Test
+    @MediumTest
+    @EnableFeatures({ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID})
+    public void testFillsSuggestionOnClick()
+            throws ExecutionException, InterruptedException, TimeoutException {
+        loadTestPage(FakeKeyboard::new);
+        mHelper.clickNodeAndShowKeyboard("CREDIT_CARD_NAME_FULL");
+        mHelper.waitForKeyboardAccessoryToBeShown();
+        DOMUtils.focusNode(mActivityTestRule.getWebContents(), "CREDIT_CARD_NAME_FULL");
+
+        // Scroll to last element and click the second icon:
+        whenDisplayed(withId(R.id.bar_items_view))
+                .perform(scrollTo(isKeyboardAccessoryTabLayout()),
+                        actionOnItem(isKeyboardAccessoryTabLayout(), selectTabAtPosition(1)));
+
+        // Wait for the sheet to come up and be stable.
+        whenDisplayed(withId(R.id.credit_card_sheet));
+
+        // Click a suggestion.
+        whenDisplayed(withId(R.id.cc_number)).perform(click());
+
+        CriteriaHelper.pollInstrumentationThread(() -> {
+            return mHelper.getFieldText("CREDIT_CARD_NAME_FULL").equals("4111111111111111");
+        });
+    }
+}
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java
index e1e28901..add50ba 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java
@@ -161,10 +161,11 @@
     private UserInfo createInfo(
             String number, String month, String year, String name, AtomicBoolean clickRecorder) {
         UserInfo info = new UserInfo(null);
-        info.addField(new UserInfoField(number, number, false, item -> clickRecorder.set(true)));
-        info.addField(new UserInfoField(month, month, false, item -> clickRecorder.set(true)));
-        info.addField(new UserInfoField(year, year, false, item -> clickRecorder.set(true)));
-        info.addField(new UserInfoField(name, name, false, item -> clickRecorder.set(true)));
+        info.addField(
+                new UserInfoField(number, number, "", false, item -> clickRecorder.set(true)));
+        info.addField(new UserInfoField(month, month, "", false, item -> clickRecorder.set(true)));
+        info.addField(new UserInfoField(year, year, "", false, item -> clickRecorder.set(true)));
+        info.addField(new UserInfoField(name, name, "", false, item -> clickRecorder.set(true)));
         return info;
     }
 
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java
index 3d3a5eb..0e11f68 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java
@@ -14,6 +14,7 @@
 import static org.hamcrest.Matchers.containsString;
 
 import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.isTransformed;
+import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.scrollToLastElement;
 import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.selectTabAtPosition;
 import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.whenDisplayed;
 
@@ -40,6 +41,7 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 
 import java.util.concurrent.TimeoutException;
+
 /**
  * Integration tests for password accessory views.
  */
@@ -126,6 +128,7 @@
         mHelper.focusPasswordField();
         mHelper.waitForKeyboardAccessoryToBeShown();
         whenDisplayed(withId(R.id.tabs)).perform(selectTabAtPosition(0));
+        whenDisplayed(withId(R.id.passwords_sheet)).perform(scrollToLastElement());
 
         // Click the suggestion.
         whenDisplayed(withText("ShorterPassword")).perform(click());
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java
index 12d8579f..1f3d6c1 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewTest.java
@@ -114,9 +114,9 @@
 
         UserInfo testInfo = new UserInfo(null);
         testInfo.addField(new UserInfoField(
-                "Name Suggestion", "Name Suggestion", false, item -> clicked.set(true)));
+                "Name Suggestion", "Name Suggestion", "", false, item -> clicked.set(true)));
         testInfo.addField(new UserInfoField(
-                "Password Suggestion", "Password Suggestion", true, item -> clicked.set(true)));
+                "Password Suggestion", "Password Suggestion", "", true, item -> clicked.set(true)));
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.add(new AccessorySheetDataPiece(
                     testInfo, AccessorySheetDataPiece.Type.PASSWORD_INFO));
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java
index dbd5836..ea48be0 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetViewTest.java
@@ -126,9 +126,9 @@
 
         UserInfo testInfo = new UserInfo(null);
         testInfo.addField(new UserInfoField(
-                "Name Suggestion", "Name Suggestion", false, item -> clicked.set(true)));
+                "Name Suggestion", "Name Suggestion", "", false, item -> clicked.set(true)));
         testInfo.addField(new UserInfoField(
-                "Password Suggestion", "Password Suggestion", true, item -> clicked.set(true)));
+                "Password Suggestion", "Password Suggestion", "", true, item -> clicked.set(true)));
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.add(new AccessorySheetDataPiece(
                     testInfo, AccessorySheetDataPiece.Type.PASSWORD_INFO));
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index 88c32f8..3f46af9 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -205,8 +205,9 @@
             AccessorySheetData sheetData =
                     new AccessorySheetData(AccessoryTabType.PASSWORDS, "Passwords");
             UserInfo userInfo = new UserInfo(null);
-            userInfo.addField(new UserInfoField("(No username)", "No username", false, null));
-            userInfo.addField(new UserInfoField(passwordString, "Password", true, null));
+            userInfo.addField(
+                    new UserInfoField("(No username)", "No username", /*id=*/"", false, null));
+            userInfo.addField(new UserInfoField(passwordString, "Password", /*id=*/"", true, null));
             sheetData.getUserInfoList().add(userInfo);
             mAccessorySheetDataProvider.notifyObservers(sheetData);
         }
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetControllerTest.java
index a8af505..be346150 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetControllerTest.java
@@ -122,9 +122,10 @@
         final AccessorySheetData testData =
                 new AccessorySheetData(AccessoryTabType.ADDRESSES, "Addresses for this site");
         testData.getUserInfoList().add(new UserInfo(null));
-        testData.getUserInfoList().get(0).addField(new UserInfoField("Name", "Name", false, null));
         testData.getUserInfoList().get(0).addField(
-                new UserInfoField("Street", "Street", true, field -> {}));
+                new UserInfoField("Name", "Name", "", false, null));
+        testData.getUserInfoList().get(0).addField(
+                new UserInfoField("Street", "Street", "", true, field -> {}));
 
         mCoordinator.registerDataProvider(testProvider);
         testProvider.notifyObservers(testData);
@@ -149,9 +150,10 @@
 
         // As soon UserInfo is available, discard the title.
         testData.getUserInfoList().add(new UserInfo(null));
-        testData.getUserInfoList().get(0).addField(new UserInfoField("Name", "Name", false, null));
         testData.getUserInfoList().get(0).addField(
-                new UserInfoField("Address", "Address for Name", true, field -> {}));
+                new UserInfoField("Name", "Name", "", false, null));
+        testData.getUserInfoList().get(0).addField(
+                new UserInfoField("Address", "Address for Name", "", true, field -> {}));
         testProvider.notifyObservers(testData);
 
         assertThat(mSheetDataPieces.size(), is(1));
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
index 3d0387d2..1004255 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
@@ -126,9 +126,10 @@
         final AccessorySheetData testData =
                 new AccessorySheetData(AccessoryTabType.PASSWORDS, "Passwords for this site");
         testData.getUserInfoList().add(new UserInfo(null));
-        testData.getUserInfoList().get(0).addField(new UserInfoField("Name", "Name", false, null));
         testData.getUserInfoList().get(0).addField(
-                new UserInfoField("Password", "Password for Name", true, field -> {}));
+                new UserInfoField("Name", "Name", "", false, null));
+        testData.getUserInfoList().get(0).addField(
+                new UserInfoField("Password", "Password for Name", "", true, field -> {}));
         testData.getFooterCommands().add(new FooterCommand("Manage passwords", result -> {}));
 
         mCoordinator.registerDataProvider(testProvider);
@@ -162,9 +163,10 @@
 
         // As soon UserInfo is available, discard the title.
         testData.getUserInfoList().add(new UserInfo(null));
-        testData.getUserInfoList().get(0).addField(new UserInfoField("Name", "Name", false, null));
         testData.getUserInfoList().get(0).addField(
-                new UserInfoField("Password", "Password for Name", true, field -> {}));
+                new UserInfoField("Name", "Name", "", false, null));
+        testData.getUserInfoList().get(0).addField(
+                new UserInfoField("Password", "Password for Name", "", true, field -> {}));
         testProvider.notifyObservers(testData);
 
         assertThat(mSheetDataPieces.size(), is(2));
@@ -205,16 +207,16 @@
 
         // If the tab is shown with X interactive item, record "X" samples.
         UserInfo userInfo1 = new UserInfo(null);
-        userInfo1.addField(new UserInfoField("Interactive 1", "", false, (v) -> {}));
-        userInfo1.addField(new UserInfoField("Non-Interactive 1", "", true, null));
+        userInfo1.addField(new UserInfoField("Interactive 1", "", "", false, (v) -> {}));
+        userInfo1.addField(new UserInfoField("Non-Interactive 1", "", "", true, null));
         accessorySheetData.getUserInfoList().add(userInfo1);
         UserInfo userInfo2 = new UserInfo(null);
-        userInfo2.addField(new UserInfoField("Interactive 2", "", false, (v) -> {}));
-        userInfo2.addField(new UserInfoField("Non-Interactive 2", "", true, null));
+        userInfo2.addField(new UserInfoField("Interactive 2", "", "", false, (v) -> {}));
+        userInfo2.addField(new UserInfoField("Non-Interactive 2", "", "", true, null));
         accessorySheetData.getUserInfoList().add(userInfo2);
         UserInfo userInfo3 = new UserInfo(null);
-        userInfo3.addField(new UserInfoField("Interactive 3", "", false, (v) -> {}));
-        userInfo3.addField(new UserInfoField("Non-Interactive 3", "", true, null));
+        userInfo3.addField(new UserInfoField("Interactive 3", "", "", false, (v) -> {}));
+        userInfo3.addField(new UserInfoField("Non-Interactive 3", "", "", true, null));
         accessorySheetData.getUserInfoList().add(userInfo3);
         testProvider.notifyObservers(accessorySheetData);
         mCoordinator.onTabShown();
diff --git a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java
index 0e44b3a..c618a9ad 100644
--- a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java
+++ b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/UserInfoField.java
@@ -14,19 +14,22 @@
 public final class UserInfoField {
     private final String mDisplayText;
     private final String mA11yDescription;
+    private final String mId;
     private final boolean mIsObfuscated;
     private final Callback<UserInfoField> mCallback;
 
     /**
      * @param displayText The text to display. Plain text if |isObfuscated| is false.
      * @param a11yDescription The description used for accessibility.
+     * @param id An ID representing this object for filling purposes. May be empty.
      * @param isObfuscated If true, the displayed caption is transformed into stars.
      * @param callback Called when the user taps the suggestions.
      */
-    public UserInfoField(String displayText, String a11yDescription, boolean isObfuscated,
-            Callback<UserInfoField> callback) {
+    public UserInfoField(String displayText, String a11yDescription, String id,
+            boolean isObfuscated, Callback<UserInfoField> callback) {
         mDisplayText = displayText;
         mA11yDescription = a11yDescription;
+        mId = id;
         mIsObfuscated = isObfuscated;
         mCallback = callback;
     }
@@ -48,6 +51,14 @@
     }
 
     /**
+     * Returns an ID representing this object for filling purposes. May be empty.
+     */
+    @CalledByNative
+    public String getId() {
+        return mId;
+    }
+
+    /**
      * Returns whether the user can interact with the selected suggestion. For example,
      * this is false if this is a password suggestion on a non-password input field.
      */
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
index 6cb71de..ca2b033 100644
--- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
@@ -12,66 +12,70 @@
         android:background="@drawable/popup_bg"
         android:layout_margin="3dp"
         android:visibility="visible"/>
-    <RelativeLayout
+    <FrameLayout
         android:id="@+id/content_view"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:background="@drawable/tab_grid_card_background"
-        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_height="match_parent">
+        <RelativeLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_toEndOf="@id/tab_favicon"
-            android:layout_marginEnd="32dp"
-            android:gravity="center_vertical"
-            android:minHeight="32dp"
-            android:requiresFadingEdge="horizontal"
-            android:fadingEdgeLength="24dp"
-            android:ellipsize="none"
-            android:singleLine="true"
-            android:textAppearance="@style/TextAppearance.BlackTitle2"/>
-        <org.chromium.ui.widget.RoundedCornerImageView
-            android:id="@+id/tab_thumbnail"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/tab_title"
-            android:gravity="center_horizontal"
-            android:adjustViewBounds="true"
-            android:scaleType="fitCenter"
-            android:importantForAccessibility="no"
-            android:src="@color/thumbnail_placeholder_on_primary_bg"
-            app:cornerRadiusBottomStart="@dimen/default_rounded_corner_radius"
-            app:cornerRadiusBottomEnd="@dimen/default_rounded_corner_radius"
-            app:roundedfillColor="@color/default_bg_color_light"/>
-        <View
-            style="@style/HorizontalDivider"
-            android:layout_below="@id/tab_title"/>
-        <org.chromium.ui.widget.ButtonCompat
-            android:id="@+id/create_group_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            android:layout_centerHorizontal="true"
-            android:layout_margin="8dp"
-            android:elevation="4dp"
-            android:text="@string/tabswitcher_create_group"
-            android:visibility="gone"
-            style="@style/FilledButton"/>
-    </RelativeLayout>
-    <org.chromium.ui.widget.ChromeImageView
-        android:id="@+id/action_button"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:scaleType="center"
-        android:layout_gravity="end"
-        android:tint="@color/modern_grey_800"/>
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:background="@drawable/tab_grid_card_background"
+            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:layout_toEndOf="@id/tab_favicon"
+                android:layout_marginEnd="32dp"
+                android:gravity="center_vertical"
+                android:minHeight="32dp"
+                android:requiresFadingEdge="horizontal"
+                android:fadingEdgeLength="24dp"
+                android:ellipsize="none"
+                android:singleLine="true"
+                android:textAppearance="@style/TextAppearance.BlackTitle2"/>
+            <org.chromium.ui.widget.RoundedCornerImageView
+                android:id="@+id/tab_thumbnail"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/tab_title"
+                android:gravity="center_horizontal"
+                android:adjustViewBounds="true"
+                android:scaleType="fitCenter"
+                android:importantForAccessibility="no"
+                android:src="@color/thumbnail_placeholder_on_primary_bg"
+                app:cornerRadiusBottomStart="@dimen/default_rounded_corner_radius"
+                app:cornerRadiusBottomEnd="@dimen/default_rounded_corner_radius"
+                app:roundedfillColor="@color/default_bg_color_light"/>
+            <View
+                style="@style/HorizontalDivider"
+                android:layout_below="@id/tab_title"/>
+            <org.chromium.ui.widget.ButtonCompat
+                android:id="@+id/create_group_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_centerHorizontal="true"
+                android:layout_margin="8dp"
+                android:elevation="4dp"
+                android:text="@string/tabswitcher_create_group"
+                android:visibility="gone"
+                style="@style/FilledButton"/>
+        </RelativeLayout>
+        <org.chromium.ui.widget.ChromeImageView
+            android:id="@+id/action_button"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:scaleType="center"
+            android:layout_gravity="end"
+            android:tint="@color/modern_grey_800"/>
+    </FrameLayout>
 </merge>
\ No newline at end of file
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 168ffa0..0dddf52 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -501,8 +501,13 @@
             {# We can only use blocks once in Jinja, for future substitutions we use
             self.supports_video_persistence(). #}
             {% block supports_video_persistence %}
+            {% if notouch_build == "true" %}
+            android:supportsPictureInPicture="false"
+            android:resizeableActivity="false"
+            {% else %}
             android:supportsPictureInPicture="true"
             android:resizeableActivity="true"
+            {% endif %}
             {% endblock %}
             {% block chrome_activity_common %}
             android:windowSoftInputMode="adjustResize"
diff --git a/chrome/android/java/DEPS b/chrome/android/java/DEPS
index 99b62b2..51131e9 100644
--- a/chrome/android/java/DEPS
+++ b/chrome/android/java/DEPS
@@ -1,7 +1,9 @@
 include_rules = [
-  "-chrome/android/features/keyboard_accessory/internal"
+  "-chrome/android/features/keyboard_accessory/internal",
 
-  "+components/embedder_support/android",
+  "+chrome/lib/lifecycle/public",
+  "+chrome/lib/image_fetcher/public",
+  "+chrome/lib/util/public",
   "+components/embedder_support/android",
   "+components/autofill/android/java/src/org/chromium/components/autofill",
   "+components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler",
@@ -32,6 +34,7 @@
 specific_include_rules = {
   # Special-case where monochrome composes chrome+webview
   "MonochromeApplication\.java": [
+    "+android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java",
     "+android_webview/glue/java/src/com/android/webview/chromium/MonochromeLibraryPreloader.java",
   ]
 }
diff --git a/chrome/android/java/monochrome_public_bundle.proguard_flags.expected b/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
index 3dd9c32f..ac8e953 100644
--- a/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
+++ b/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
@@ -293,7 +293,7 @@
 -keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
   public <init>();
 }
--keep public class org.chromium.chrome.browser.** extends android.support.v7.preference.PreferenceFragmentCompat {
+-keep public class org.chromium.chrome.browser.preferences.** extends android.support.v4.app.Fragment {
   public <init>();
 }
 
diff --git a/chrome/android/java/proguard.flags b/chrome/android/java/proguard.flags
index 8704907..e1d86756 100644
--- a/chrome/android/java/proguard.flags
+++ b/chrome/android/java/proguard.flags
@@ -10,7 +10,7 @@
 -keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
   public <init>();
 }
--keep public class org.chromium.chrome.browser.** extends android.support.v7.preference.PreferenceFragmentCompat {
+-keep public class org.chromium.chrome.browser.preferences.** extends android.support.v4.app.Fragment {
   public <init>();
 }
 
diff --git a/chrome/android/java/res/OWNERS b/chrome/android/java/res/OWNERS
index 5d7443b..3b3a3945 100644
--- a/chrome/android/java/res/OWNERS
+++ b/chrome/android/java/res/OWNERS
@@ -5,6 +5,8 @@
 
 file://ui/android/java/res/OWNERS
 
+bsazonov@chromium.org
+
 # New Tab Page changes only:
 mvanouwerkerk@chromium.org
 peconn@chromium.org
diff --git a/chrome/android/java/res/drawable/bookmark_big.xml b/chrome/android/java/res/drawable/bookmark_big.xml
deleted file mode 100644
index a00bdd2f..0000000
--- a/chrome/android/java/res/drawable/bookmark_big.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
-    android:height="64dp"
-    android:width="64dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0" >
-    <path
-        android:fillColor="@color/black_alpha_38"
-        android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z" />
-</vector>
diff --git a/chrome/android/java/res/drawable/contacts_big.xml b/chrome/android/java/res/drawable/contacts_big.xml
deleted file mode 100644
index 6a85a4b7..0000000
--- a/chrome/android/java/res/drawable/contacts_big.xml
+++ /dev/null
@@ -1,18 +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"
-        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/downloads_big.xml b/chrome/android/java/res/drawable/downloads_big.xml
deleted file mode 100644
index 2c69e2ce..0000000
--- a/chrome/android/java/res/drawable/downloads_big.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<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="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"
-        android:fillColor="@color/black_alpha_38"/>
-</vector>
diff --git a/chrome/android/java/res/drawable/history_big.xml b/chrome/android/java/res/drawable/history_big.xml
deleted file mode 100644
index 1706921..0000000
--- a/chrome/android/java/res/drawable/history_big.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<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:fillColor="@color/black_alpha_38"
-        android:pathData="M11.991,3C16.968,3 21,7.032 21,12C21,16.968 16.968,21 11.991,21C7.023,21 3,16.968 3,12C3,7.032 7.023,3 11.991,3ZM12.45,7.5L11.1,7.5L11.1,12.9L15.825,15.735L16.5,14.628L12.45,12.225L12.45,7.5Z" />
-</vector>
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 aa4963de..551f4a3 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
@@ -27,7 +27,7 @@
             android:layout_alignWithParentIfMissing="true"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
-            android:padding="@dimen/snippets_padding"
+            android:padding="@dimen/card_padding"
             android:orientation="vertical">
 
             <org.chromium.ui.widget.TextViewWithLeading
diff --git a/chrome/android/java/res/layout/content_suggestions_status_card_modern.xml b/chrome/android/java/res/layout/content_suggestions_status_card_modern.xml
index e54b48cd..cd08e98 100644
--- a/chrome/android/java/res/layout/content_suggestions_status_card_modern.xml
+++ b/chrome/android/java/res/layout/content_suggestions_status_card_modern.xml
@@ -12,7 +12,7 @@
     android:layout_height="wrap_content"
     android:gravity="end"
     android:orientation="vertical"
-    android:padding="@dimen/snippets_padding" >
+    android:padding="@dimen/card_padding" >
 
     <org.chromium.ui.widget.TextViewWithLeading
         style="@style/SuggestionCardTitleModern"
diff --git a/chrome/android/java/res/layout/new_tab_page_snippets_expandable_header.xml b/chrome/android/java/res/layout/new_tab_page_snippets_expandable_header.xml
index 7b8b754..31c9394d 100644
--- a/chrome/android/java/res/layout/new_tab_page_snippets_expandable_header.xml
+++ b/chrome/android/java/res/layout/new_tab_page_snippets_expandable_header.xml
@@ -9,8 +9,8 @@
     android:layout_height="wrap_content"
     android:minHeight="@dimen/snippets_article_header_height"
     android:orientation="horizontal"
-    android:paddingStart="@dimen/snippets_padding"
-    android:paddingEnd="@dimen/snippets_padding"
+    android:paddingStart="@dimen/card_padding"
+    android:paddingEnd="@dimen/card_padding"
     android:gravity="center_vertical">
 
     <TextView
diff --git a/chrome/android/java/res/layout/personalized_signin_promo_view_settings.xml b/chrome/android/java/res/layout/personalized_signin_promo_view_settings.xml
index d7a408d..adaacbf 100644
--- a/chrome/android/java/res/layout/personalized_signin_promo_view_settings.xml
+++ b/chrome/android/java/res/layout/personalized_signin_promo_view_settings.xml
@@ -44,10 +44,5 @@
         android:id="@android:id/widget_frame"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"/>
-
-    <View
-        android:id="@+id/divider"
-        style="@style/HorizontalDivider"
-        android:importantForAccessibility="no"/>
 </LinearLayout>
 
diff --git a/chrome/android/java/res/layout/revamped_context_menu.xml b/chrome/android/java/res/layout/revamped_context_menu.xml
index d4acc0a4..f5d2fc4b 100644
--- a/chrome/android/java/res/layout/revamped_context_menu.xml
+++ b/chrome/android/java/res/layout/revamped_context_menu.xml
@@ -8,18 +8,31 @@
     android:layout_height="match_parent"
     android:background="@color/modal_dialog_scrim_color">
 
-    <!-- Setting listSelector to transparent prevents the list items that have
-         selectableItemBackground from having a double highlight effect. -->
-    <org.chromium.chrome.browser.contextmenu.RevampedContextMenuListView
-        android:id="@+id/context_menu_list_view"
-        android:layout_width="match_parent"
+    <!-- We need 2 FrameLayouts here because the outer one is the scrim that covers the whole screen
+         while the inner one is a frame for the ListView. We use this frame as a background for the
+         list because the background is a 9-patch and has extra padding automatically. The extra
+         padding conflicts with the padding and clipToPadding attributes of the list, so they are
+         separated. -->
+    <FrameLayout
+        android:id="@+id/context_menu_frame"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:background="@drawable/popup_bg_tinted"
         android:layout_marginStart="@dimen/revamped_context_menu_lateral_margin"
         android:layout_marginEnd="@dimen/revamped_context_menu_lateral_margin"
         android:layout_marginTop="@dimen/revamped_context_menu_vertical_margin"
         android:layout_marginBottom="@dimen/revamped_context_menu_vertical_margin"
-        android:background="@drawable/popup_bg_tinted"
-        android:divider="@null"
-        android:listSelector="@android:color/transparent"/>
+        android:layout_gravity="center">
+
+        <!-- Setting listSelector to transparent prevents the list items that have
+             selectableItemBackground from having a double highlight effect. -->
+        <org.chromium.chrome.browser.contextmenu.RevampedContextMenuListView
+            android:id="@+id/context_menu_list_view"
+            android:layout_width="@dimen/context_menu_max_width"
+            android:layout_height="wrap_content"
+            android:paddingBottom="@dimen/revamped_context_menu_bottom_padding"
+            android:divider="@null"
+            android:listSelector="@android:color/transparent"
+            android:clipToPadding="false" />
+    </FrameLayout>
 </FrameLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/selectable_list_layout.xml b/chrome/android/java/res/layout/selectable_list_layout.xml
index 50b62c9..ea88b09 100644
--- a/chrome/android/java/res/layout/selectable_list_layout.xml
+++ b/chrome/android/java/res/layout/selectable_list_layout.xml
@@ -34,20 +34,28 @@
             android:clipToPadding="false"
             android:visibility="gone" />
 
+
         <FrameLayout
+            style="@style/Card"
             android:id="@+id/empty_view_wrapper"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="gone" >
-            <org.chromium.chrome.browser.widget.TextViewWithCompoundDrawables
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginStart="@dimen/selectable_list_layout_row_padding"
+            android:layout_marginEnd="@dimen/selectable_list_layout_row_padding"
+            android:visibility="gone">
+
+            <org.chromium.ui.widget.TextViewWithLeading
                 android:id="@+id/empty_view"
-                android:layout_width="wrap_content"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center"
-                android:drawablePadding="3dp"
+                android:textAlignment="center"
                 android:visibility="gone"
-                android:textAppearance="@style/TextAppearance.BlackDisabledText1"
-                app:chromeDrawableTint="@color/default_icon_color_secondary"/>
+                android:padding="@dimen/card_padding"
+                android:textAppearance="@style/TextAppearance.BlackBody"
+                app:leading="@dimen/text_size_medium_leading" />
+
         </FrameLayout>
 
         <org.chromium.chrome.browser.widget.LoadingView
diff --git a/chrome/android/java/res/layout/sync_error_widget.xml b/chrome/android/java/res/layout/sync_error_widget.xml
deleted file mode 100644
index b7c8e9d..0000000
--- a/chrome/android/java/res/layout/sync_error_widget.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<!-- The widget at the tail of the SignInPreference in settings screen to indicate sync error -->
-<ImageView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:src="@drawable/sync_error"
-    android:contentDescription="@string/sign_in_sync_error_widget"
-    app:tint="@color/default_red"/>
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 7c788f8..8f9ac197 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -915,11 +915,14 @@
         <item name="android:windowExitAnimation">@null</item>
     </style>
 
-    <!-- Content and Site Suggestions -->
-    <style name="SuggestionCardModern">
-        <item name="android:layout_marginBottom">@dimen/content_suggestions_card_modern_margin</item>
+
+    <!-- Card for General Use -->
+    <style name="Card">
         <item name="android:background">@drawable/hairline_border_card_background</item>
-        <item name="android:foreground">@drawable/button_borderless_compat</item>
+      </style>
+    <!-- Content and Site Suggestions -->
+    <style name="SuggestionCardModern" parent="Card">
+        <item name="android:layout_marginBottom">@dimen/content_suggestions_card_modern_margin</item>
     </style>
     <!-- TODO(twellington): Use standard line height for SuggestionsCard*. Updating the leading
      for these requires updating the NTP article suggestions thumbnail size as well. -->
diff --git a/chrome/android/java/res/values-v21/styles.xml b/chrome/android/java/res/values-v21/styles.xml
index f2d09eb..48b11ae 100644
--- a/chrome/android/java/res/values-v21/styles.xml
+++ b/chrome/android/java/res/values-v21/styles.xml
@@ -150,4 +150,11 @@
              half is way brighter than the bottom), so we use our own. -->
         <item name="android:textCursorDrawable">@drawable/text_cursor_lowend</item>
     </style>
-</resources>
+
+    <!-- This overrides the v17 style in order to use button_borderless_compat foreground -->
+    <!-- Card for General Use -->
+    <style name="Card">
+        <item name="android:background">@drawable/hairline_border_card_background</item>
+        <item name="android:foreground">@drawable/button_borderless_compat</item>
+    </style>
+  </resources>
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index fd1cfc05..94e8ca49 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -60,9 +60,9 @@
     <color name="update_menu_item_text_color">@color/default_red</color>
 
     <!-- URL Emphasizer colors -->
-    <color name="url_emphasis_non_emphasized_text">#80333333</color>
+    <color name="url_emphasis_non_emphasized_text">@color/default_text_color_dark_secondary</color>
     <color name="url_emphasis_light_non_emphasized_text">@color/white_alpha_50</color>
-    <color name="url_emphasis_domain_and_registry">#333333</color>
+    <color name="url_emphasis_domain_and_registry">@color/default_text_color_dark</color>
     <color name="url_emphasis_light_domain_and_registry">@android:color/white</color>
 
     <!-- Omnibox Suggestion colors -->
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 28717c0..b2efd46 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -355,7 +355,7 @@
     <dimen name="snippets_thumbnail_size">124dp</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>
+    <dimen name="card_padding">16dp</dimen>
     <dimen name="snippets_article_header_height">40dp</dimen>
     <dimen name="snippets_publisher_margin_top">8dp</dimen>
     <!-- This is in sp because we want the icon to scale with the TextView it sits alongside. -->
@@ -552,6 +552,7 @@
     <dimen name="revamped_context_menu_lateral_margin">40dp</dimen>
     <dimen name="revamped_context_menu_vertical_margin">20dp</dimen>
     <dimen name="revamped_context_menu_list_lateral_padding">16dp</dimen>
+    <dimen name="revamped_context_menu_bottom_padding">8dp</dimen>
     <dimen name="revamped_context_menu_header_vertical_padding">16dp</dimen>
     <dimen name="revamped_context_menu_divider_padding">8dp</dimen>
     <dimen name="revamped_context_menu_header_image_max_size">60dp</dimen>
diff --git a/chrome/android/java/res_download/layout/downloads_empty_view.xml b/chrome/android/java/res_download/layout/downloads_empty_view.xml
index 44a23f9..58f8214 100644
--- a/chrome/android/java/res_download/layout/downloads_empty_view.xml
+++ b/chrome/android/java/res_download/layout/downloads_empty_view.xml
@@ -3,31 +3,30 @@
     Use of this source code is governed by a BSD-style license that can be
     found in the LICENSE file. -->
 
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<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="match_parent" >
 
-    <LinearLayout
-        style="@style/SuggestionCardModern"
+    <FrameLayout
+        style="@style/Card"
         android:id="@+id/empty_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginStart="16dp"
-        android:layout_marginEnd="16dp"
+        android:layout_marginEnd="@dimen/selectable_list_layout_row_padding"
+        android:layout_marginStart="@dimen/selectable_list_layout_row_padding"
         android:gravity="center"
-        android:padding="@dimen/snippets_padding" >
+        android:padding="@dimen/card_padding" >
 
         <org.chromium.ui.widget.TextViewWithLeading
-            style="@style/SuggestionCardBodyModern"
             android:id="@+id/empty"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="0dp"
             android:textAlignment="center"
+            android:textAppearance="@style/TextAppearance.BlackBody"
             app:leading="@dimen/text_size_medium_leading"/>
-    </LinearLayout>
+
+    </FrameLayout>
 
     <org.chromium.chrome.browser.widget.LoadingView
         android:id="@+id/loading"
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 2739342b..af23a70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -22,6 +22,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.JNIUtils;
 import org.chromium.base.Log;
+import org.chromium.base.PathUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.annotations.MainDex;
 import org.chromium.base.library_loader.ProcessInitException;
@@ -56,6 +57,8 @@
 public class ChromeApplication extends Application {
     private static final String COMMAND_LINE_FILE = "chrome-command-line";
     private static final String TAG = "ChromiumApplication";
+    // Public to allow use in ChromeBackupAgent
+    public static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
 
     /** Lock on creation of sComponent. */
     private static final Object sLock = new Object();
@@ -86,6 +89,7 @@
             }
             checkAppBeingReplaced();
 
+            PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
             // Renderer and GPU processes have command line passed to them via IPC
             // (see ChildProcessService.java).
             CommandLineInitUtil.initCommandLine(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java
index c0228e0a..ae406fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java
@@ -319,7 +319,7 @@
         PostTask.runSynchronously(UiThreadTaskTraits.DEFAULT, () -> {
             // Chrome library loading depends on PathUtils.
             PathUtils.setPrivateDataDirectorySuffix(
-                    ChromeBrowserInitializer.PRIVATE_DATA_DIRECTORY_SUFFIX);
+                    ChromeApplication.PRIVATE_DATA_DIRECTORY_SUFFIX);
             createAsyncInitTaskRunner(latch).startBackgroundTasks(
                     false /* allocateChildConnection */, true /* initVariationSeed */);
         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
index 5f35548..c27367f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
@@ -7,7 +7,6 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.View;
@@ -143,8 +142,6 @@
                 mMainView.findViewById(R.id.selectable_list);
         mSelectableListLayout = selectableList;
         mEmptyView = mSelectableListLayout.initializeEmptyView(
-                VectorDrawableCompat.create(
-                        mActivity.getResources(), R.drawable.bookmark_big, mActivity.getTheme()),
                 R.string.bookmarks_folder_empty, R.string.bookmark_no_result);
 
         mAdapter = new BookmarkItemsAdapter(activity);
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 75c15b2..11bb422 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,7 +7,6 @@
 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.view.LayoutInflater;
@@ -136,8 +135,6 @@
         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);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
index ea1f996..b362a1a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
@@ -143,7 +143,7 @@
      * Returns the fully complete dialog based off the params and the itemGroups.
      *
      * @param activity Used to inflate the dialog.
-     * @param view The inflated view that contains the list view.
+     * @param view The inflated view, including the scrim, that contains the list view.
      * @param touchPointYPx The x-coordinate of the touch that triggered the context menu.
      * @param touchPointXPx The y-coordinate of the touch that triggered the context menu.
      * @return Returns a final dialog that does not have a background can be displayed using
@@ -151,11 +151,11 @@
      */
     private ContextMenuDialog createContextMenuDialog(
             Activity activity, View view, float touchPointXPx, float touchPointYPx) {
-        View listView = view.findViewById(R.id.context_menu_list_view);
+        View frame = view.findViewById(R.id.context_menu_frame);
         // TODO(sinansahin): Refactor ContextMenuDialog as well.
         final ContextMenuDialog dialog =
                 new ContextMenuDialog(activity, R.style.Theme_Chromium_DialogWhenLarge,
-                        touchPointXPx, touchPointYPx, mTopContentOffsetPx, listView);
+                        touchPointXPx, touchPointYPx, mTopContentOffsetPx, frame);
         dialog.setContentView(view);
 
         return dialog;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuListView.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuListView.java
index 1480788..5097ba5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuListView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuListView.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.ListView;
 
 import org.chromium.chrome.R;
@@ -32,11 +33,19 @@
      * @return The width of the context menu in pixels
      */
     private int calculateWidth() {
-        int windowWidthPx = getResources().getDisplayMetrics().widthPixels;
-        int maxWidth = getResources().getDimensionPixelSize(R.dimen.context_menu_max_width);
-        int lateralMargin =
+        final int windowWidthPx = getResources().getDisplayMetrics().widthPixels;
+        final int maxWidth = getResources().getDimensionPixelSize(R.dimen.context_menu_max_width);
+        final int lateralMargin =
                 getResources().getDimensionPixelSize(R.dimen.revamped_context_menu_lateral_margin);
 
-        return Math.min(maxWidth, windowWidthPx - 2 * lateralMargin);
+        // This ListView should be inside a FrameLayout with the popup_bg_tinted background. Since
+        // the background is a 9-patch, it gets some extra padding automatically, and we should
+        // take it into account when calculating the width here.
+        final View frame = ((View) getParent());
+        assert frame.getId() == R.id.context_menu_frame;
+        final int parentLateralPadding = frame.getPaddingLeft();
+
+        return Math.min(maxWidth - 2 * parentLateralPadding,
+                windowWidthPx - 2 * lateralMargin - 2 * parentLateralPadding);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index bab21314..c3ae4c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -181,6 +181,9 @@
     // Whether any ChromeActivity is launched.
     private boolean mActivityLaunched;
 
+    // Disabling call to DownloadManager.addCompletedDownload() for test.
+    private boolean mDisableAddCompletedDownloadForTesting;
+
     /**
      * Interface to intercept download request to Android DownloadManager. This is implemented by
      * tests so that we don't need to actually enqueue a download into the Android DownloadManager.
@@ -539,8 +542,8 @@
         AsyncTask<Pair<Boolean, Boolean>> task = new AsyncTask<Pair<Boolean, Boolean>>() {
             @Override
             public Pair<Boolean, Boolean> doInBackground() {
-                boolean success =
-                        ContentUriUtils.isContentUri(item.getDownloadInfo().getFilePath());
+                boolean success = mDisableAddCompletedDownloadForTesting
+                        || ContentUriUtils.isContentUri(item.getDownloadInfo().getFilePath());
                 if (!success
                         && !ChromeFeatureList.isEnabled(
                                 ChromeFeatureList.DOWNLOAD_OFFLINE_CONTENT_PROVIDER)) {
@@ -2024,6 +2027,10 @@
                 getNativeDownloadManagerService(), url, guid, targetPath);
     }
 
+    void disableAddCompletedDownloadToDownloadManager() {
+        mDisableAddCompletedDownloadForTesting = true;
+    }
+
     /**
      * Updates the last access time of a download.
      * @param downloadGuid Download GUID.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 92932ff..fb521c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -54,6 +54,7 @@
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
 import org.chromium.chrome.browser.util.ConversionUtils;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.components.download.DownloadState;
@@ -152,6 +153,7 @@
      */
     public static boolean showDownloadManager(
             @Nullable Activity activity, @Nullable Tab tab, boolean showPrefetchedContent) {
+        if (FeatureUtilities.isNoTouchModeEnabled()) return false;
         // Figure out what tab was last being viewed by the user.
         if (activity == null) activity = ApplicationStatus.getLastTrackedFocusedActivity();
         Context appContext = ContextUtils.getApplicationContext();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index f9c3128..3862c80 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -9,7 +9,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Handler;
-import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.Toolbar.OnMenuItemClickListener;
@@ -203,9 +202,7 @@
                 mMainView.findViewById(R.id.selectable_list);
 
         mSelectableListLayout.initializeEmptyView(
-                VectorDrawableCompat.create(
-                        mActivity.getResources(), R.drawable.downloads_big, mActivity.getTheme()),
-                R.string.download_manager_ui_empty, R.string.download_manager_no_results);
+                R.string.download_manager_no_downloads, R.string.download_manager_no_results);
 
         mHistoryAdapter = new DownloadHistoryAdapter(isOffTheRecord, parentComponent);
         mRecyclerView = mSelectableListLayout.initializeRecyclerView(mHistoryAdapter);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java
index 2da59d0..c22ceb1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java
@@ -21,7 +21,6 @@
 import org.chromium.base.Log;
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.base.metrics.CachedMetrics.SparseHistogramSample;
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -39,9 +38,6 @@
 
     private static final ExternalAuthUtils sInstance = AppHooks.get().createExternalAuthUtils();
 
-    private final SparseHistogramSample mConnectionResultHistogramSample =
-            new SparseHistogramSample("GooglePlayServices.ConnectionResult");
-
     /**
      * Returns the singleton instance of ExternalAuthUtils, creating it if needed.
      */
@@ -185,7 +181,6 @@
 
         Context context = ContextUtils.getApplicationContext();
         final int resultCode = checkGooglePlayServicesAvailable(context);
-        recordConnectionResult(resultCode);
         if (resultCode == ConnectionResult.SUCCESS) return true;
         // resultCode is some kind of error.
         Log.v(TAG, "Unable to use Google Play Services: %s", describeError(resultCode));
@@ -247,15 +242,6 @@
     }
 
     /**
-     * Record the result of a connection attempt. The default implementation records via a UMA
-     * histogram.
-     * @param resultCode the result from {@link #checkGooglePlayServicesAvailable(Context)}
-     */
-    protected void recordConnectionResult(final int resultCode) {
-        mConnectionResultHistogramSample.record(resultCode);
-    }
-
-    /**
      * Invokes whatever external code is necessary to check if Google Play Services is available
      * and returns the code produced by the attempt. Subclasses can override to force the behavior
      * one way or another, or to change the way that the check is performed.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java
index c2c918c3..0a7f0d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java
@@ -8,16 +8,12 @@
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.support.annotation.IntDef;
 
 import com.google.android.gms.common.GoogleApiAvailability;
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.CachedMetrics.ActionEvent;
-import org.chromium.base.metrics.CachedMetrics.EnumeratedHistogramSample;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -44,23 +40,6 @@
  * subclassing this class.
  */
 public abstract class UserRecoverableErrorHandler {
-    private static final String ERROR_HANDLER_ACTION_HISTOGRAM_NAME =
-            "GooglePlayServices.ErrorHandlerAction";
-    // Never remove or reorder histogram values. It is safe to append new values to the end.
-    @IntDef({ErrorHandlerAction.SILENT, ErrorHandlerAction.SYSTEM_NOTIFICATION,
-            ErrorHandlerAction.MODAL_DIALOG, ErrorHandlerAction.IGNORED_AS_REDUNDANT})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface ErrorHandlerAction {
-        int SILENT = 0;
-        int SYSTEM_NOTIFICATION = 1;
-        int MODAL_DIALOG = 2;
-        int IGNORED_AS_REDUNDANT = 3;
-        int NUM_ENTRIES = 4;
-    }
-
-    private static final EnumeratedHistogramSample sErrorHandlerActionHistogramSample =
-            new EnumeratedHistogramSample(
-                    ERROR_HANDLER_ACTION_HISTOGRAM_NAME, ErrorHandlerAction.NUM_ENTRIES);
 
     private static final ActionEvent sModalDialogShownActionEvent =
             new ActionEvent("Signin_Android_GmsUserRecoverableDialogShown");
@@ -95,7 +74,6 @@
     public static final class Silent extends UserRecoverableErrorHandler {
         @Override
         protected final void handle(final Context context, final int errorCode) {
-            sErrorHandlerActionHistogramSample.record(ErrorHandlerAction.SILENT);
         }
     }
 
@@ -117,11 +95,9 @@
         @Override
         protected void handle(final Context context, final int errorCode) {
             if (!sNotificationShown.getAndSet(true)) {
-                sErrorHandlerActionHistogramSample.record(ErrorHandlerAction.IGNORED_AS_REDUNDANT);
                 return;
             }
             GoogleApiAvailability.getInstance().showErrorNotification(context, errorCode);
-            sErrorHandlerActionHistogramSample.record(ErrorHandlerAction.SYSTEM_NOTIFICATION);
         }
     }
 
@@ -226,7 +202,6 @@
                 mDialog.show();
                 sModalDialogShownActionEvent.record();
             }
-            sErrorHandlerActionHistogramSample.record(ErrorHandlerAction.MODAL_DIALOG);
         }
 
         /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
index 66f8b64..b3e03f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
@@ -18,7 +18,6 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.CachedMetrics.EnumeratedHistogramSample;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.datareduction.DataReductionPromoUtils;
 import org.chromium.chrome.browser.datareduction.DataReductionProxyUma;
@@ -155,11 +154,7 @@
 
         // An optional sign-in page.
         if (mFreProperties.getBoolean(SHOW_SIGNIN_PAGE)) {
-            if (ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT)) {
-                mPages.add(SigninFirstRunFragment::new);
-            } else {
-                mPages.add(AccountFirstRunFragment::new);
-            }
+            mPages.add(SigninFirstRunFragment::new);
             mFreProgressStates.add(FRE_PROGRESS_SIGNIN_SHOWN);
             notifyAdapter = true;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
index 2f98a44..daa6762 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
@@ -11,10 +11,8 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.sync.SyncAndServicesPreferences;
-import org.chromium.chrome.browser.signin.AccountManagementFragment;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.signin.SigninManager.SignInCallback;
 import org.chromium.chrome.browser.signin.UnifiedConsentServiceBridge;
@@ -76,9 +74,7 @@
         signinManager.signIn(accountName, activity, new SignInCallback() {
             @Override
             public void onSignInComplete() {
-                if (ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT)) {
-                    UnifiedConsentServiceBridge.setUrlKeyedAnonymizedDataCollectionEnabled(true);
-                }
+                UnifiedConsentServiceBridge.setUrlKeyedAnonymizedDataCollectionEnabled(true);
                 // Show sync settings if user pressed the "Settings" button.
                 if (setUp) {
                     openSignInSettings(activity);
@@ -101,13 +97,8 @@
     private static void openSignInSettings(Activity activity) {
         final Class<? extends Fragment> fragment;
         final Bundle arguments;
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT)) {
-            fragment = SyncAndServicesPreferences.class;
-            arguments = SyncAndServicesPreferences.createArguments(true);
-        } else {
-            fragment = AccountManagementFragment.class;
-            arguments = null;
-        }
+        fragment = SyncAndServicesPreferences.class;
+        arguments = SyncAndServicesPreferences.createArguments(true);
         PreferencesLauncher.launchSettingsPage(activity, fragment, arguments);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index 82e4eeda..ba5b98a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -12,7 +12,6 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.support.annotation.VisibleForTesting;
-import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.OnScrollListener;
@@ -136,8 +135,6 @@
 
         // 5. Initialize empty view.
         mEmptyView = mSelectableListLayout.initializeEmptyView(
-                VectorDrawableCompat.create(
-                        mActivity.getResources(), R.drawable.history_big, mActivity.getTheme()),
                 R.string.history_manager_empty, R.string.history_manager_no_results);
 
         // 6. Create large icon bridge.
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 613a600..2f4e2ac 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
@@ -18,7 +18,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.LocaleUtils;
 import org.chromium.base.Log;
-import org.chromium.base.PathUtils;
 import org.chromium.base.SysUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.TraceEvent;
@@ -72,9 +71,6 @@
     private boolean mNativeInitializationComplete;
     private boolean mNetworkChangeNotifierInitializationComplete;
 
-    // Public to allow use in ChromeBackupAgent
-    public static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
-
     /**
      * A callback to be executed when there is a new version available in Play Store.
      */
@@ -224,7 +220,6 @@
     private void preInflationStartup() {
         ThreadUtils.assertOnUiThread();
         if (mPreInflationStartupComplete) return;
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
 
         // Ensure critical files are available, so they aren't blocked on the file-system
         // behind long-running accesses in next phase.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index 5dcb8419..1eaf251 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -96,7 +96,7 @@
         @Override
         public void onAction(Object actionData) {
             Context context = ContextUtils.getApplicationContext();
-            PreferencesLauncher.launchSettingsPage(context, SearchEnginePreference.class);
+            PreferencesLauncher.launchSettingsPageCompat(context, SearchEnginePreference.class);
         }
     };
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
index 3a7bd0c..51383f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
@@ -65,7 +65,8 @@
         mLocaleManager = localeManager;
         mSpan = new NoUnderlineClickableSpan(activity.getResources(), (widget) -> {
             mChoice = UserChoice.SETTINGS;
-            PreferencesLauncher.launchSettingsPage(getContext(), SearchEnginePreference.class);
+            PreferencesLauncher.launchSettingsPageCompat(
+                    getContext(), SearchEnginePreference.class);
             dismiss();
         });
         setOnDismissListener(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java
index e8427faa..4bbe427 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java
@@ -12,6 +12,7 @@
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.offlinepages.DownloadUiActionFlags;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.offlinepages.RequestCoordinatorBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.InterceptNavigationDelegateImpl;
 import org.chromium.chrome.browser.tab.Tab;
@@ -110,12 +111,12 @@
     }
 
     private void saveUrlForOffline(String url) {
-        OfflinePageBridge offlinePageBridge = OfflinePageBridge.getForProfile(mProfile);
         if (mHost.getActiveTab() != null) {
-            offlinePageBridge.scheduleDownload(mHost.getActiveTab().getWebContents(),
+            OfflinePageBridge.getForProfile(mProfile).scheduleDownload(
+                    mHost.getActiveTab().getWebContents(),
                     OfflinePageBridge.NTP_SUGGESTIONS_NAMESPACE, url, DownloadUiActionFlags.ALL);
         } else {
-            offlinePageBridge.savePageLater(
+            RequestCoordinatorBridge.getForProfile(mProfile).savePageLater(
                     url, OfflinePageBridge.NTP_SUGGESTIONS_NAMESPACE, true /* userRequested */);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java
index cc4cd8da..32fdd43 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java
@@ -72,7 +72,7 @@
      */
     @MainThread
     @CalledByNative
-    public static void schedule(
+    private static void schedule(
             int /*@SchedulerTaskTime*/ schedulerTaskTime, long windowStartMs, long windowEndMs) {
         BackgroundTaskScheduler scheduler = BackgroundTaskSchedulerFactory.getScheduler();
         Bundle bundle = new Bundle();
@@ -87,6 +87,16 @@
         scheduler.schedule(ContextUtils.getApplicationContext(), taskInfo);
     }
 
+    /**
+     * Cancels the background task for notification scheduler.
+     */
+    @MainThread
+    @CalledByNative
+    private static void cancel() {
+        BackgroundTaskSchedulerFactory.getScheduler().cancel(
+                ContextUtils.getApplicationContext(), TaskIds.NOTIFICATION_SCHEDULER_JOB_ID);
+    }
+
     private native void nativeOnStartTask(Profile profile, Callback<Boolean> callback);
     private native boolean nativeOnStopTask(Profile profile);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index 06600f3..5bc237d6e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -16,6 +16,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileKey;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.offline_items_collection.LaunchLocation;
 import org.chromium.components.offlinepages.DeletePageResult;
@@ -47,16 +48,27 @@
 
     /**
      * Retrieves the OfflinePageBridge for the given profile, creating it the first time
-     * getForProfile is called for a given profile.  Must be called on the UI thread.
+     * getForProfile is called for the profile.  Must be called on the UI thread.
      *
      * @param profile The profile associated with the OfflinePageBridge to get.
      */
     public static OfflinePageBridge getForProfile(Profile profile) {
         ThreadUtils.assertOnUiThread();
 
-        return nativeGetOfflinePageBridgeForProfile(profile);
+        if (profile == null)
+            return null;
+
+        OfflinePageBridge bridge = nativeGetOfflinePageBridgeForProfileKey(profile.getProfileKey());
+
+        // TODO(crbug.com/978871): Storing Profile is a temporary hack during the transition
+        // of some members to RequestCoordinatorBridge.
+        if (bridge != null) {
+            bridge.mProfile = profile;
+        }
+        return bridge;
     }
 
+    private Profile mProfile;
     private long mNativeOfflinePageBridge;
     private boolean mIsNativeOfflinePageModelLoaded;
     private final ObserverList<OfflinePageModelObserver> mObservers =
@@ -211,35 +223,20 @@
      * Gets all the URLs in the request queue.
      *
      * @return A list of {@link SavePageRequest} representing all the queued requests.
+     * @deprecated Use {@link
+     *         RequestCoordinatorBridge#getRequestsInQueue(Callback<SavePageRequest[]>)} instead.
      */
+    @Deprecated
     @VisibleForTesting
     public void getRequestsInQueue(Callback<SavePageRequest[]> callback) {
-        nativeGetRequestsInQueue(mNativeOfflinePageBridge, callback);
-    }
-
-    private static class RequestsRemovedCallback {
-        private Callback<List<RequestRemovedResult>> mCallback;
-
-        public RequestsRemovedCallback(Callback<List<RequestRemovedResult>> callback) {
-            mCallback = callback;
-        }
-
-        @CalledByNative("RequestsRemovedCallback")
-        public void onResult(long[] resultIds, int[] resultCodes) {
-            assert resultIds.length == resultCodes.length;
-
-            List<RequestRemovedResult> results = new ArrayList<>();
-            for (int i = 0; i < resultIds.length; i++) {
-                results.add(new RequestRemovedResult(resultIds[i], resultCodes[i]));
-            }
-
-            mCallback.onResult(results);
-        }
+        RequestCoordinatorBridge.getForProfile(mProfile).getRequestsInQueue(callback);
     }
 
     /**
      * Contains a result for a remove page request.
+     * @deprecated Use {@link RequestCoordinatorBridge.RequestRemovedResult} instead.
      */
+    @Deprecated
     public static class RequestRemovedResult {
         private long mRequestId;
         private int mUpdateRequestResult;
@@ -266,18 +263,24 @@
      * The callback will be called with |null| in the case that the queue is unavailable.  This can
      * happen in incognito, for example.
      *
-     * @param requestIds The IDs of the requests to remove.
+     * @param requestIdList The IDs of the requests to remove.
      * @param callback Called when the removal is done, with the SavePageRequest objects that were
      *     actually removed.
+     * @deprecated Use {@link RequestCoordinatorBridge#removeRequestsFromQueue(List<Long>,
+     *         Callback<List<RequestCoordinatorBridge.RequestRemovedResult>>)} instead.
      */
+    @Deprecated
     public void removeRequestsFromQueue(
             List<Long> requestIdList, Callback<List<RequestRemovedResult>> callback) {
-        long[] requestIds = new long[requestIdList.size()];
-        for (int i = 0; i < requestIdList.size(); i++) {
-            requestIds[i] = requestIdList.get(i).longValue();
-        }
-        nativeRemoveRequestsFromQueue(
-                mNativeOfflinePageBridge, requestIds, new RequestsRemovedCallback(callback));
+        RequestCoordinatorBridge.getForProfile(mProfile).removeRequestsFromQueue(
+                requestIdList, (results) -> {
+                    List<RequestRemovedResult> transformedResults = new ArrayList<>(results.size());
+                    for (RequestCoordinatorBridge.RequestRemovedResult result : results) {
+                        transformedResults.add(new RequestRemovedResult(
+                                result.getRequestId(), result.getUpdateRequestResult()));
+                    }
+                    callback.onResult(transformedResults);
+                });
     }
 
     /**
@@ -355,7 +358,9 @@
      *
      * @param url The given URL to save for later.
      * @param clientId The client ID for the offline page to be saved later.
+     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, ClientId)} instead.
      */
+    @Deprecated
     @VisibleForTesting
     public void savePageLater(String url, ClientId clientId) {
         savePageLater(url, clientId, true);
@@ -369,7 +374,10 @@
      * @param clientId The client ID for the offline page to be saved later.
      * @param userRequested Whether this request should be prioritized because the user explicitly
      *     requested it.
+     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, ClientId, boolean)}
+     *         instead.
      */
+    @Deprecated
     public void savePageLater(final String url, final ClientId clientId, boolean userRequested) {
         savePageLater(url, clientId, userRequested, new OfflinePageOrigin());
     }
@@ -383,7 +391,10 @@
      * @param userRequested Whether this request should be prioritized because the user explicitly
      *                      requested it.
      * @param origin The app that initiated the request.
+     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, ClientId, boolean,
+     *         OfflinePageOrigin)} instead.
      */
+    @Deprecated
     public void savePageLater(final String url, final ClientId clientId, boolean userRequested,
             OfflinePageOrigin origin) {
         savePageLater(url, clientId, userRequested, origin, null);
@@ -401,7 +412,10 @@
      * @param callback Callback for whether the URL is successfully added to queue. Non-zero number
      *                 represents a failure reason (See offline_pages::AddRequestResult enum). 0 is
      * success.
+     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, ClientId, boolean,
+     *         OfflinePageOrigin, Callback<Integer>)} instead.
      */
+    @Deprecated
     public void savePageLater(final String url, final ClientId clientId, boolean userRequested,
             OfflinePageOrigin origin, Callback<Integer> callback) {
         Callback<Integer> wrapper = new Callback<Integer>() {
@@ -412,8 +426,8 @@
                 }
             }
         };
-        nativeSavePageLater(mNativeOfflinePageBridge, wrapper, url, clientId.getNamespace(),
-                clientId.getId(), origin.encodeAsJsonString(), userRequested);
+        RequestCoordinatorBridge.getForProfile(mProfile).savePageLater(
+                url, clientId, userRequested, origin, callback);
     }
 
     /**
@@ -424,7 +438,10 @@
      * @param namespace The namespace for the offline page to be saved later.
      * @param userRequested Whether this request should be prioritized because the user explicitly
      *                      requested it.
+     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, String, boolean)}
+     *         instead.
      */
+    @Deprecated
     public void savePageLater(final String url, final String namespace, boolean userRequested) {
         savePageLater(url, namespace, userRequested, new OfflinePageOrigin());
     }
@@ -438,7 +455,10 @@
      * @param userRequested Whether this request should be prioritized because the user explicitly
      *                      requested it.
      * @param origin The app that initiated the request.
+     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, String, boolean,
+     *         OfflinePageOrigin)} instead.
      */
+    @Deprecated
     public void savePageLater(final String url, final String namespace, boolean userRequested,
             OfflinePageOrigin origin) {
         savePageLater(url, namespace, userRequested, origin, null);
@@ -456,8 +476,11 @@
      * @param origin The app that initiated the request.
      * @param callback Callback to call whether the URL is successfully added to the queue. Non-zero
      *                 number represents failure reason (see offline_pages::AddRequestResult enum).
+     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, String, boolean,
+     *         OfflinePageOrigin, Callback<Integer>)} instead.
      * 0 is success.
      */
+    @Deprecated
     public void savePageLater(final String url, final String namespace, boolean userRequested,
             OfflinePageOrigin origin, Callback<Integer> callback) {
         ClientId clientId = ClientId.createGuidClientIdForNamespace(namespace);
@@ -805,19 +828,14 @@
     }
 
     private static native boolean nativeCanSavePage(String url);
-    private static native OfflinePageBridge nativeGetOfflinePageBridgeForProfile(Profile profile);
+    private static native OfflinePageBridge nativeGetOfflinePageBridgeForProfileKey(
+            ProfileKey profileKey);
     @VisibleForTesting
     native void nativeGetAllPages(long nativeOfflinePageBridge, List<OfflinePageItem> offlinePages,
             final Callback<List<OfflinePageItem>> callback);
     private native void nativeWillCloseTab(long nativeOfflinePageBridge, WebContents webContents);
 
     @VisibleForTesting
-    native void nativeGetRequestsInQueue(
-            long nativeOfflinePageBridge, Callback<SavePageRequest[]> callback);
-    @VisibleForTesting
-    native void nativeRemoveRequestsFromQueue(
-            long nativeOfflinePageBridge, long[] requestIds, RequestsRemovedCallback callback);
-    @VisibleForTesting
     native void nativeGetPageByOfflineId(
             long nativeOfflinePageBridge, long offlineId, Callback<OfflinePageItem> callback);
     @VisibleForTesting
@@ -848,9 +866,6 @@
             Callback<OfflinePageItem> callback);
     private native void nativeSavePage(long nativeOfflinePageBridge, SavePageCallback callback,
             WebContents webContents, String clientNamespace, String clientId, String origin);
-    private native void nativeSavePageLater(long nativeOfflinePageBridge,
-            Callback<Integer> callback, String url, String clientNamespace, String clientId,
-            String origin, boolean userRequested);
     private native String nativeGetOfflinePageHeaderForReload(
             long nativeOfflinePageBridge, WebContents webContents);
     private native boolean nativeIsShowingOfflinePreview(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java
new file mode 100644
index 0000000..7f88318
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java
@@ -0,0 +1,244 @@
+// Copyright 2019 The Chromium 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.offlinepages;
+
+import android.support.annotation.Nullable;
+
+import org.chromium.base.Callback;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.chrome.browser.profiles.Profile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Java access to the C++ offline_pages::RequestCoordinator.
+ */
+@JNINamespace("offline_pages::android")
+public class RequestCoordinatorBridge {
+    private final Profile mProfile;
+
+    /**
+     * Retrieves the RequestCoordinatorBridge for the given profile, creating it the first time
+     * getForProfile is called for a given profile.  Must be called on the UI thread.
+     *
+     * @param profile The profile associated with the OfflinePageBridge to get.
+     */
+    public static @Nullable RequestCoordinatorBridge getForProfile(Profile profile) {
+        ThreadUtils.assertOnUiThread();
+
+        if (profile.isOffTheRecord()) return null;
+
+        return new RequestCoordinatorBridge(profile);
+    }
+
+    /**
+     * Creates a request coordinator bridge for a given profile.
+     */
+    @VisibleForTesting
+    RequestCoordinatorBridge(Profile profile) {
+        mProfile = profile;
+    }
+
+    /**
+     * Gets all the URLs in the request queue.
+     *
+     * @return A list of {@link SavePageRequest} representing all the queued requests.
+     */
+    @VisibleForTesting
+    public void getRequestsInQueue(Callback<SavePageRequest[]> callback) {
+        nativeGetRequestsInQueue(mProfile, callback);
+    }
+
+    private static class RequestsRemovedCallback {
+        private final Callback<List<RequestRemovedResult>> mCallback;
+
+        public RequestsRemovedCallback(Callback<List<RequestRemovedResult>> callback) {
+            mCallback = callback;
+        }
+
+        @CalledByNative("RequestsRemovedCallback")
+        public void onResult(long[] resultIds, int[] resultCodes) {
+            assert resultIds.length == resultCodes.length;
+
+            List<RequestRemovedResult> results = new ArrayList<>();
+            for (int i = 0; i < resultIds.length; i++) {
+                results.add(new RequestRemovedResult(resultIds[i], resultCodes[i]));
+            }
+
+            mCallback.onResult(results);
+        }
+    }
+
+    /**
+     * Contains a result for a remove page request.
+     */
+    public static class RequestRemovedResult {
+        private final long mRequestId;
+        private final int mUpdateRequestResult;
+
+        public RequestRemovedResult(long requestId, int requestResult) {
+            mRequestId = requestId;
+            mUpdateRequestResult = requestResult;
+        }
+
+        /** Request ID as found in the SavePageRequest. */
+        public long getRequestId() {
+            return mRequestId;
+        }
+
+        /** {@see org.chromium.components.offlinepages.background.UpdateRequestResult} enum. */
+        public int getUpdateRequestResult() {
+            return mUpdateRequestResult;
+        }
+    }
+
+    /**
+     * Removes SavePageRequests from the request queue.
+     *
+     * The callback will be called with |null| in the case that the queue is unavailable.  This can
+     * happen in incognito, for example.
+     *
+     * @param requestIdList The IDs of the requests to remove.
+     * @param callback Called when the removal is done, with the SavePageRequest objects that were
+     *     actually removed.
+     */
+    public void removeRequestsFromQueue(
+            List<Long> requestIdList, Callback<List<RequestRemovedResult>> callback) {
+        long[] requestIds = new long[requestIdList.size()];
+        for (int i = 0; i < requestIdList.size(); i++) {
+            requestIds[i] = requestIdList.get(i).longValue();
+        }
+        nativeRemoveRequestsFromQueue(mProfile, requestIds, new RequestsRemovedCallback(callback));
+    }
+
+    /**
+     * Save the given URL as an offline page when the network becomes available.
+     *
+     * The page is marked as not having been saved by the user.  Use the 3-argument form to specify
+     * a user request.
+     *
+     * @param url The given URL to save for later.
+     * @param clientId The client ID for the offline page to be saved later.
+     */
+    @VisibleForTesting
+    public void savePageLater(String url, ClientId clientId) {
+        savePageLater(url, clientId, true);
+    }
+
+    /**
+     * Save the given URL as an offline page when the network becomes available. Origin is
+     * assumed to be Chrome.
+     *
+     * @param url The given URL to save for later.
+     * @param clientId The client ID for the offline page to be saved later.
+     * @param userRequested Whether this request should be prioritized because the user explicitly
+     *     requested it.
+     */
+    public void savePageLater(final String url, final ClientId clientId, boolean userRequested) {
+        savePageLater(url, clientId, userRequested, new OfflinePageOrigin());
+    }
+
+    /**
+     * Save the given URL as an offline page when the network becomes available with the given
+     * origin.
+     *
+     * @param url The given URL to save for later
+     * @param clientId The clientId for the offline page to be saved later.
+     * @param userRequested Whether this request should be prioritized because the user explicitly
+     *                      requested it.
+     * @param origin The app that initiated the request.
+     */
+    public void savePageLater(final String url, final ClientId clientId, boolean userRequested,
+            OfflinePageOrigin origin) {
+        savePageLater(url, clientId, userRequested, origin, null);
+    }
+
+    /**
+     * Save the given URL as an offline page when the network becomes available with the given
+     * origin. Callback with status when done.
+     *
+     * @param url The given URL to save for later.
+     * @param clientId the clientId for the offline page to be saved later.
+     * @param userRequested Whether this request should be prioritized because the user explicitly
+     *                      requested it.
+     * @param origin The app that initiated the request.
+     * @param callback Callback for whether the URL is successfully added to queue. Non-zero number
+     *                 represents a failure reason (See offline_pages::AddRequestResult enum). 0 is
+     * success.
+     */
+    public void savePageLater(final String url, final ClientId clientId, boolean userRequested,
+            OfflinePageOrigin origin, Callback<Integer> callback) {
+        Callback<Integer> wrapper = new Callback<Integer>() {
+            @Override
+            public void onResult(Integer i) {
+                if (callback != null) {
+                    callback.onResult(i);
+                }
+            }
+        };
+        nativeSavePageLater(mProfile, wrapper, url, clientId.getNamespace(), clientId.getId(),
+                origin.encodeAsJsonString(), userRequested);
+    }
+
+    /**
+     * Save the given URL as an offline page when the network becomes available with a randomly
+     * generated clientId in the given namespace. Origin is defaulted to Chrome.
+     *
+     * @param url The given URL to save for later.
+     * @param namespace The namespace for the offline page to be saved later.
+     * @param userRequested Whether this request should be prioritized because the user explicitly
+     *                      requested it.
+     */
+    public void savePageLater(final String url, final String namespace, boolean userRequested) {
+        savePageLater(url, namespace, userRequested, new OfflinePageOrigin());
+    }
+
+    /**
+     * Save the given URL as an offline page when the network becomes available with a randomly
+     * generated clientId in the given namespace and the given origin.
+     *
+     * @param url The given URL to save for later
+     * @param namespace The namespace for the offline page to be saved later.
+     * @param userRequested Whether this request should be prioritized because the user explicitly
+     *                      requested it.
+     * @param origin The app that initiated the request.
+     */
+    public void savePageLater(final String url, final String namespace, boolean userRequested,
+            OfflinePageOrigin origin) {
+        savePageLater(url, namespace, userRequested, origin, null);
+    }
+
+    /**
+     * Save the given URL as an offline page when the network becomes available with a randomly
+     * generated clientId in the given namespace and the given origin. Calls back with whether
+     * the URL has been successfully added to queue.
+     *
+     * @param url The given URL to save for later
+     * @param namespace The namespace for the offline page to be saved later.
+     * @param userRequested Whether this request should be prioritized because the user explicitly
+     *                      requested it.
+     * @param origin The app that initiated the request.
+     * @param callback Callback to call whether the URL is successfully added to the queue. Non-zero
+     *                 number represents failure reason (see offline_pages::AddRequestResult enum).
+     * 0 is success.
+     */
+    public void savePageLater(final String url, final String namespace, boolean userRequested,
+            OfflinePageOrigin origin, Callback<Integer> callback) {
+        ClientId clientId = ClientId.createGuidClientIdForNamespace(namespace);
+        savePageLater(url, clientId, userRequested, origin, callback);
+    }
+
+    private static native void nativeGetRequestsInQueue(
+            Profile profile, Callback<SavePageRequest[]> callback);
+    private static native void nativeRemoveRequestsFromQueue(
+            Profile profile, long[] requestIds, RequestsRemovedCallback callback);
+    private static native void nativeSavePageLater(Profile profile, Callback<Integer> callback,
+            String url, String clientNamespace, String clientId, String origin,
+            boolean userRequested);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/ConnectivityDetector.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/ConnectivityDetector.java
index c4ca8224..c58ddfb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/ConnectivityDetector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/ConnectivityDetector.java
@@ -18,7 +18,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.content.ContentUtils;
@@ -153,10 +152,7 @@
                                 Context.CONNECTIVITY_SERVICE);
             }
 
-            boolean canGetConnectionStateFromSystem = connectivityManager != null;
-            RecordHistogram.recordBooleanHistogram(
-                    "ConnectivityDetector.FromSystem", canGetConnectionStateFromSystem);
-            if (!canGetConnectionStateFromSystem) return ConnectionState.NONE;
+            if (connectivityManager == null) return ConnectionState.NONE;
 
             boolean isCapitivePortal = false;
             Network[] networks = connectivityManager.getAllNetworks();
@@ -329,7 +325,6 @@
     private void processConnectivityCheckResult() {
         // If the connection is validated, we're done.
         if (mConnectionState == ConnectionState.VALIDATED) {
-            recordHttpProbeValidatedStats();
             stopConnectivityCheck();
             return;
         }
@@ -386,11 +381,6 @@
                                     + "ms ret=" + responseCode
                                     + " headers=" + urlConnection.getHeaderFields());
 
-                    RecordHistogram.recordSparseHistogram(useDefaultUrl
-                                    ? "ConnectivityDetector.Probe.HttpResponseCode.Default"
-                                    : "ConnectivityDetector.Probe.HttpResponseCode.Fallback",
-                            responseCode);
-
                     if (responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
                         return ProbeResult.VALIDATED_WITH_NO_CONTENT;
                     } else if (responseCode >= 400) {
@@ -426,10 +416,6 @@
 
             @Override
             protected void onPostExecute(Integer result) {
-                RecordHistogram.recordEnumeratedHistogram(useDefaultUrl
-                                ? "ConnectivityDetector.Probe.ProbeResult.Default"
-                                : "ConnectivityDetector.Probe.ProbeResult.Fallback",
-                        result, ProbeResult.RESULT_COUNT);
                 callback.onResult(result);
             }
         }
@@ -486,20 +472,6 @@
         setConnectionState(newConnectionState);
     }
 
-    private void recordHttpProbeValidatedStats() {
-        if (mConnectivityCheckingState != ConnectivityCheckingState.PROBE_DEFAULT_URL
-                && mConnectivityCheckingState != ConnectivityCheckingState.PROBE_FALLBACK_URL) {
-            return;
-        }
-        RecordHistogram.recordEnumeratedHistogram("ConnectivityDetector.Probe.ValidationUrl",
-                (mConnectivityCheckingState != ConnectivityCheckingState.PROBE_DEFAULT_URL)
-                        ? PROBE_WITH_DEFAULT_URL
-                        : PROBE_WITH_FALLBACK_URL,
-                PROBE_WITH_URL_COUNT);
-        RecordHistogram.recordLongTimesHistogram("ConnectivityDetector.Probe.ValidationTime",
-                SystemClock.elapsedRealtime() - mConnectivityCheckStartTimeMs);
-    }
-
     @VisibleForTesting
     void setConnectionState(@ConnectionState int connectionState) {
         if (mConnectionState == connectionState) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
index 4876c9b0..4c67eb0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
@@ -15,14 +15,22 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.init.BrowserParts;
+import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.init.EmptyBrowserParts;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.notifications.ChromeNotification;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
@@ -38,12 +46,16 @@
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateStatus;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Class supports to build and to send update notification every three weeks if new Chrome version
  * is available. It listens to {@link UpdateStatusProvider}, and handle the intent to start update
  * flow.
  * */
 public class UpdateNotificationController implements Destroyable {
+    private static final String TAG = "UpdateNotif";
     private static final String INLINE_UPDATE_NOTIFICATION_RECEIVED_EXTRA =
             "org.chromium.chrome.browser.omaha.inline_update_notification_received_extra";
     private static final String UPDATE_NOTIFICATION_STATE_EXTRA =
@@ -163,9 +175,52 @@
      * A receiver that try to build the intent to launch Chrome activity.
      */
     public static final class UpdateNotificationReceiver extends BroadcastReceiver {
+        /**
+         * Tracks various launch events when the user interacts with an update notification.
+         * Used in UMA, append values only and map to GoogleUpdateNotificationLaunchEvent in
+         * enums.xml.
+         */
+        @IntDef({LaunchEvent.START, LaunchEvent.START_ACTIVITY_FAILED})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface LaunchEvent {
+            int START = 0;
+            int START_ACTIVITY_FAILED = 1;
+            int NUM_ENTRIES = 2;
+        }
+
         // BroadcastReceiver implementation.
         @Override
         public void onReceive(Context context, Intent intent) {
+            final BrowserParts parts = new EmptyBrowserParts() {
+                @Override
+                public void finishNativeInitialization() {
+                    RecordHistogram.recordEnumeratedHistogram(
+                            "GoogleUpdate.Notification.LaunchEvent", LaunchEvent.START,
+                            LaunchEvent.NUM_ENTRIES);
+                    try {
+                        handleUpdateIntent(context, intent);
+                    } catch (IllegalArgumentException e) {
+                        // If it takes too long to load native library, we may fail to start
+                        // activity.
+                        Log.e(TAG, "Failed to start activity in background.", e);
+                        RecordHistogram.recordEnumeratedHistogram(
+                                "GoogleUpdate.Notification.LaunchEvent",
+                                LaunchEvent.START_ACTIVITY_FAILED, LaunchEvent.NUM_ENTRIES);
+                    }
+                }
+            };
+
+            // Try to load native.
+            try {
+                ChromeBrowserInitializer.getInstance().handlePreNativeStartup(parts);
+                ChromeBrowserInitializer.getInstance().handlePostNativeStartup(true, parts);
+            } catch (ProcessInitException e) {
+                Log.e(TAG, "Unable to load native library after clicking update notification.", e);
+                ChromeApplication.reportStartupErrorAndExit(e);
+            }
+        }
+
+        private void handleUpdateIntent(Context context, Intent intent) {
             @UpdateState
             int state = intent.getIntExtra(UPDATE_NOTIFICATION_STATE_EXTRA, UpdateState.NONE);
             switch (state) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
index 05c3ea0..2bd21b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -28,9 +28,6 @@
 public class AutocompleteController {
     private static final String TAG = "cr_Autocomplete";
 
-    // Maximum number of search/history suggestions to show.
-    private static final int MAX_DEFAULT_SUGGESTION_COUNT = 5;
-
     // Maximum number of voice suggestions to show.
     private static final int MAX_VOICE_SUGGESTION_COUNT = 3;
 
@@ -220,10 +217,6 @@
     @CalledByNative
     protected void onSuggestionsReceived(List<OmniboxSuggestion> suggestions,
             String inlineAutocompleteText, long currentNativeAutocompleteResult) {
-        if (suggestions.size() > MAX_DEFAULT_SUGGESTION_COUNT) {
-            // Trim to the default amount of normal suggestions we can have.
-            suggestions.subList(MAX_DEFAULT_SUGGESTION_COUNT, suggestions.size()).clear();
-        }
 
         // Run through new providers to get an updated list of suggestions.
         suggestions = mVoiceSuggestionProvider.addVoiceSuggestions(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
index ab971c8..81599fd0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
@@ -25,6 +25,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.components.payments.ErrorStrings;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentCurrencyAmount;
@@ -42,6 +43,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -289,7 +291,7 @@
 
         ChromeActivity activity = ChromeActivity.fromWebContents(mWebContents);
         if (activity == null) {
-            notifyErrorInvokingPaymentApp();
+            notifyErrorInvokingPaymentApp(ErrorStrings.ACTIVITY_NOT_FOUND);
             return;
         }
 
@@ -302,8 +304,10 @@
                                         schemelessIframeOrigin, certificateChain, methodDataMap,
                                         total, displayItems, modifiers))
                 .setNegativeButton(R.string.cancel,
-                        (OnClickListener) (dialog, which) -> notifyErrorInvokingPaymentApp())
-                .setOnCancelListener(dialog -> notifyErrorInvokingPaymentApp())
+                        (OnClickListener) (dialog, which)
+                                -> notifyErrorInvokingPaymentApp(ErrorStrings.USER_CANCELLED))
+                .setOnCancelListener(
+                        dialog -> notifyErrorInvokingPaymentApp(ErrorStrings.USER_CANCELLED))
                 .show();
     }
 
@@ -319,13 +323,13 @@
         assert mInstrumentDetailsCallback != null;
 
         if (mWebContents.isDestroyed()) {
-            notifyErrorInvokingPaymentApp();
+            notifyErrorInvokingPaymentApp(ErrorStrings.PAYMENT_APP_LAUNCH_FAIL);
             return;
         }
 
         WindowAndroid window = mWebContents.getTopLevelNativeWindow();
         if (window == null) {
-            notifyErrorInvokingPaymentApp();
+            notifyErrorInvokingPaymentApp(ErrorStrings.PAYMENT_APP_LAUNCH_FAIL);
             return;
         }
 
@@ -333,11 +337,11 @@
                 methodDataMap, total, displayItems, modifiers));
         try {
             if (!window.showIntent(mPayIntent, this, R.string.payments_android_app_error)) {
-                notifyErrorInvokingPaymentApp();
+                notifyErrorInvokingPaymentApp(ErrorStrings.PAYMENT_APP_LAUNCH_FAIL);
             }
         } catch (SecurityException e) {
             // Payment app does not have android:exported="true" on the PAY activity.
-            notifyErrorInvokingPaymentApp();
+            notifyErrorInvokingPaymentApp(ErrorStrings.PAYMENT_APP_PRIVATE_ACTIVITY);
         }
     }
 
@@ -426,8 +430,8 @@
         return result;
     }
 
-    private void notifyErrorInvokingPaymentApp() {
-        mHandler.post(() -> mInstrumentDetailsCallback.onInstrumentDetailsError());
+    private void notifyErrorInvokingPaymentApp(String errorMessage) {
+        mHandler.post(() -> mInstrumentDetailsCallback.onInstrumentDetailsError(errorMessage));
     }
 
     private static String deprecatedSerializeDetails(
@@ -547,8 +551,15 @@
     public void onIntentCompleted(WindowAndroid window, int resultCode, Intent data) {
         ThreadUtils.assertOnUiThread();
         window.removeIntentCallback(this);
-        if (data == null || data.getExtras() == null || resultCode != Activity.RESULT_OK) {
-            mInstrumentDetailsCallback.onInstrumentDetailsError();
+        if (data == null) {
+            mInstrumentDetailsCallback.onInstrumentDetailsError(ErrorStrings.MISSING_INTENT_DATA);
+        } else if (data.getExtras() == null) {
+            mInstrumentDetailsCallback.onInstrumentDetailsError(ErrorStrings.MISSING_INTENT_EXTRAS);
+        } else if (resultCode == Activity.RESULT_CANCELED) {
+            mInstrumentDetailsCallback.onInstrumentDetailsError(ErrorStrings.RESULT_CANCELED);
+        } else if (resultCode != Activity.RESULT_OK) {
+            mInstrumentDetailsCallback.onInstrumentDetailsError(String.format(
+                    Locale.US, ErrorStrings.UNRECOGNIZED_ACTIVITY_RESULT, resultCode));
         } else {
             String details = data.getExtras().getString(EXTRA_RESPONSE_DETAILS);
             if (details == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java
index e4a4a39..e358904 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java
@@ -18,6 +18,7 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.FullCardRequestDelegate;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.NormalizedAddressRequestDelegate;
+import org.chromium.components.payments.ErrorStrings;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentDetailsModifier;
 import org.chromium.payments.mojom.PaymentItem;
@@ -268,7 +269,10 @@
 
     @Override
     public void onFullCardError() {
-        mCallback.onInstrumentDetailsError();
+        // There's no need to disambiguate between user cancelling the CVC unmask and other types of
+        // failures, because a failure to unmask an Autofill card will show the Payment Request UI
+        // again and prompt the user to attempt to complete a transaction using a different card.
+        mCallback.onInstrumentDetailsError(ErrorStrings.USER_CANCELLED);
         mCallback = null;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentInstrument.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentInstrument.java
index 8067993..3ea7906 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentInstrument.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentInstrument.java
@@ -44,8 +44,10 @@
 
         /**
          * Called if unable to retrieve instrument details.
+         * @param errorMessage Developer-facing error message to be used when rejecting the promise
+         *                     returned from PaymentRequest.show().
          */
-        void onInstrumentDetailsError();
+        void onInstrumentDetailsError(String errorMessage);
     }
 
     /** The interface for the requester to abort payment. */
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 d36b7263..fe2ee13 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
@@ -221,7 +221,8 @@
     private final TabModelSelectorObserver mSelectorObserver = new EmptyTabModelSelectorObserver() {
         @Override
         public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
-            onDismiss();
+            mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+            disconnectFromClientWithDebugMessage(ErrorStrings.TAB_SWITCH);
         }
     };
 
@@ -229,7 +230,10 @@
     private final TabModelObserver mTabModelObserver = new EmptyTabModelObserver() {
         @Override
         public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
-            if (tab == null || tab.getId() != lastId) onDismiss();
+            if (tab == null || tab.getId() != lastId) {
+                mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+                disconnectFromClientWithDebugMessage(ErrorStrings.TAB_SWITCH);
+            }
         }
     };
 
@@ -237,7 +241,8 @@
     private final OverviewModeObserver mOverviewModeObserver = new EmptyOverviewModeObserver() {
         @Override
         public void onOverviewModeStartedShowing(boolean showToolbar) {
-            onDismiss();
+            mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+            disconnectFromClientWithDebugMessage(ErrorStrings.TAB_OVERVIEW_MODE);
         }
     };
 
@@ -2119,13 +2124,15 @@
      * Called if unable to retrieve instrument details.
      */
     @Override
-    public void onInstrumentDetailsError() {
+    public void onInstrumentDetailsError(String errorMessage) {
         if (mClient == null) return;
         mInvokedPaymentInstrument = null;
-        // When skipping UI, any errors/cancel from fetching instrument details should be
-        // equivalent to a cancel.
+        // When skipping UI, any errors/cancel from fetching instrument details should abort
+        // payment.
         if (mShouldSkipShowingPaymentRequestUi) {
-            onDismiss();
+            assert !TextUtils.isEmpty(errorMessage);
+            mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+            disconnectFromClientWithDebugMessage(errorMessage);
         } else {
             mUI.onPayButtonProcessingCancelled();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
index 15c7065a..3307133 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
@@ -422,12 +422,16 @@
 
     @CalledByNative
     private static void onPaymentAppInvoked(PaymentInstrument.InstrumentDetailsCallback callback,
-            String methodName, String stringifiedDetails) {
+            String methodName, String stringifiedDetails, String errorMessage) {
         ThreadUtils.assertOnUiThread();
 
-        if (TextUtils.isEmpty(methodName) || TextUtils.isEmpty(stringifiedDetails)) {
-            callback.onInstrumentDetailsError();
+        if (!TextUtils.isEmpty(errorMessage)) {
+            assert TextUtils.isEmpty(methodName);
+            assert TextUtils.isEmpty(stringifiedDetails);
+            callback.onInstrumentDetailsError(errorMessage);
         } else {
+            assert !TextUtils.isEmpty(methodName);
+            assert !TextUtils.isEmpty(stringifiedDetails);
             callback.onInstrumentDetailsReady(methodName, stringifiedDetails);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java
index 9ba66a7..2181a256 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java
@@ -21,7 +21,7 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.base.task.PostTask;
-import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.io.FileDescriptor;
@@ -58,7 +58,7 @@
             // The decoder service relies on PathUtils.
             PostTask.runSynchronously(UiThreadTaskTraits.DEFAULT, () -> {
                 PathUtils.setPrivateDataDirectorySuffix(
-                        ChromeBrowserInitializer.PRIVATE_DATA_DIRECTORY_SUFFIX);
+                        ChromeApplication.PRIVATE_DATA_DIRECTORY_SUFFIX);
             });
 
             LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_CHILD);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 80072b7f..27099d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -386,6 +386,12 @@
             "network_service_warm_up_enabled";
 
     /**
+     * Contains a trial group that was used to determine whether the reached code profiler should be
+     * enabled.
+     */
+    public static final String REACHED_CODE_PROFILER_GROUP_KEY = "reached_code_profiler_group";
+
+    /**
      * Key to cache whether immersive ui mode is enabled.
      */
     public static final String IMMERSIVE_UI_MODE_ENABLED = "immersive_ui_mode_enabled";
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 e31b1e2..19d6fc3e 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
@@ -117,12 +117,7 @@
         PreferenceUtils.addPreferencesFromResource(this, R.xml.main_preferences);
         cachePreferences();
 
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT)) {
-            mSignInPreference.setOnStateChangedCallback(this::onSignInPreferenceStateChanged);
-        } else {
-            getPreferenceScreen().removePreference(findPreference(PREF_ACCOUNT_SECTION));
-            getPreferenceScreen().removePreference(findPreference(PREF_SYNC_AND_SERVICES));
-        }
+        mSignInPreference.setOnStateChangedCallback(this::onSignInPreferenceStateChanged);
 
         updatePasswordsPreference();
 
@@ -238,8 +233,6 @@
     }
 
     private void updateSyncAndServicesPreference() {
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT)) return;
-
         ChromeBasePreference syncAndServices =
                 (ChromeBasePreference) findPreference(PREF_SYNC_AND_SERVICES);
         syncAndServices.setIcon(SyncPreferenceUtils.getSyncStatusIcon(getActivity()));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java
index 1c9205f5..7edf850d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java
@@ -5,7 +5,8 @@
 package org.chromium.chrome.browser.preferences;
 
 import android.os.Bundle;
-import android.preference.PreferenceFragment;
+import android.support.v4.app.ListFragment;
+import android.view.View;
 import android.widget.ListView;
 
 import org.chromium.base.VisibleForTesting;
@@ -14,9 +15,7 @@
 /**
 * A preference fragment for selecting a default search engine.
 */
-public class SearchEnginePreference extends PreferenceFragment {
-    private ListView mListView;
-
+public class SearchEnginePreference extends ListFragment {
     private SearchEngineAdapter mSearchEngineAdapter;
 
     @VisibleForTesting
@@ -39,15 +38,15 @@
         super.onCreate(savedInstanceState);
         getActivity().setTitle(R.string.prefs_search_engine);
         mSearchEngineAdapter = new SearchEngineAdapter(getActivity());
+        setListAdapter(mSearchEngineAdapter);
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        mListView = (ListView) getView().findViewById(android.R.id.list);
-        mListView.setAdapter(mSearchEngineAdapter);
-        mListView.setDivider(null);
-        mListView.setItemsCanFocus(true);
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        ListView listView = getListView();
+        listView.setDivider(null);
+        listView.setItemsCanFocus(true);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
index 6200ffe..13730a88 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
@@ -15,9 +15,7 @@
 
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
-import org.chromium.chrome.browser.preferences.sync.SyncPreferenceUtils;
 import org.chromium.chrome.browser.signin.AccountManagementFragment;
 import org.chromium.chrome.browser.signin.AccountSigninActivity;
 import org.chromium.chrome.browser.signin.DisplayableProfileData;
@@ -224,9 +222,7 @@
         setLayoutResource(R.layout.account_management_account_row);
         setTitle(R.string.sign_in_to_chrome);
 
-        boolean unifiedConsent = ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT);
-        setSummary(
-                unifiedConsent ? R.string.signin_pref_summary : R.string.sign_in_to_chrome_summary);
+        setSummary(R.string.signin_pref_summary);
 
         setFragment(null);
         setIcon(AppCompatResources.getDrawable(getContext(), R.drawable.logo_avatar_anonymous));
@@ -248,14 +244,10 @@
 
         setLayoutResource(R.layout.account_management_account_row);
         setTitle(profileData.getFullNameOrEmail());
-        boolean unifiedConsent = ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT);
-        setSummary(unifiedConsent ? accountName
-                                  : SyncPreferenceUtils.getSyncStatusSummary(getContext()));
+        setSummary(accountName);
         setFragment(AccountManagementFragment.class.getName());
         setIcon(profileData.getImage());
-        boolean showSyncError =
-                !unifiedConsent && SyncPreferenceUtils.showSyncErrorIcon(getContext());
-        setWidgetLayoutResource(showSyncError ? R.layout.sync_error_widget : 0);
+        setWidgetLayoutResource(0);
         setViewEnabled(true);
 
         mSigninPromoController = null;
@@ -290,12 +282,6 @@
                             true);
                     update();
                 });
-
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT)) {
-            View divider = view.findViewById(R.id.divider);
-            assert divider != null;
-            divider.setVisibility(View.GONE);
-        }
     }
 
     // ProfileSyncServiceListener implementation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/ExportFlow.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/ExportFlow.java
index ecca575b..c4e9ae0a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/ExportFlow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/ExportFlow.java
@@ -158,12 +158,7 @@
 
     // Takes care of displaying and hiding the progress bar for exporting, while avoiding
     // flickering.
-    private final DialogManager mProgressBarManager =
-            new DialogManager((@DialogManager.HideActions int action) -> {
-                RecordHistogram.recordEnumeratedHistogram(
-                        "PasswordManager.Android.ExportPasswordsProgressBarUsage",
-                        actionToHistogramValue(action), PROGRESS_COUNT);
-            });
+    private final DialogManager mProgressBarManager = new DialogManager(null);
 
     /**
      * If an error dialog should be shown, this contains the arguments for it, such as the error
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java
index abdba9d..24dd01f0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java
@@ -25,7 +25,6 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference;
 import org.chromium.chrome.browser.preferences.ChromeBasePreference;
@@ -141,23 +140,8 @@
         mSearchItem = menu.findItem(R.id.menu_id_search);
         mSearchItem.setVisible(true);
         mHelpItem = menu.findItem(R.id.menu_id_general_help);
-        SearchUtils.initializeSearchView(mSearchItem, mSearchQuery, getActivity(), (query) -> {
-            maybeRecordTriggeredPasswordSearch(true);
-            filterPasswords(query);
-        });
-    }
-
-    /**
-     * Record the search only, if the feature is enabled and it hasn't been recorded for this
-     * instance of the view.
-     * @param searchTriggered Whether to log a triggered search or no triggered search.
-     */
-    private void maybeRecordTriggeredPasswordSearch(boolean searchTriggered) {
-        if (!mSearchRecorded) {
-            mSearchRecorded = true;
-            RecordHistogram.recordBooleanHistogram(
-                    "PasswordManager.Android.PasswordSearchTriggered", searchTriggered);
-        }
+        SearchUtils.initializeSearchView(
+                mSearchItem, mSearchQuery, getActivity(), this::filterPasswords);
     }
 
     @Override
@@ -364,7 +348,6 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        maybeRecordTriggeredPasswordSearch(false);
         PasswordManagerHandlerProvider.getInstance().removeObserver(this);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java
index f9ce4f2..540ad1c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java
@@ -15,6 +15,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.StrictMode;
+import android.os.SystemClock;
 import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.text.TextUtils;
@@ -303,12 +304,14 @@
         // We keep track of the number of sites waiting to be cleared, and when it reaches 0 we can
         // set our testing variable.
         private int mNumSitesClearing;
+        private long mClearStartTime;
 
         /**
          * We fetch all the websites and clear all the non-important data. This happens
          * asynchronously, and at the end we update the UI with the new storage numbers.
          */
         public void clearData() {
+            mClearStartTime = SystemClock.elapsedRealtime();
             WebsitePermissionsFetcher fetcher = new WebsitePermissionsFetcher(true);
             fetcher.fetchPreferencesForCategory(
                     SiteSettingsCategory.createFromType(SiteSettingsCategory.Type.USE_STORAGE),
@@ -318,7 +321,11 @@
         @Override
         public void onStoredDataCleared() {
             mNumSitesClearing--;
-            if (mNumSitesClearing <= 0) clearUnimportantDataDone();
+            if (mNumSitesClearing <= 0) {
+                RecordHistogram.recordTimesHistogram("Android.ManageSpace.ClearUnimportantTime",
+                        SystemClock.elapsedRealtime() - mClearStartTime);
+                clearUnimportantDataDone();
+            }
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
index 04bc137a..08c556dc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
@@ -88,6 +88,8 @@
      */
     public static final String EXTRA_SELECTED_DOMAINS = "selected_domains";
 
+    // The list that contains preferences.
+    private ListView mListView;
     // The view to show when the list is empty.
     private TextView mEmptyView;
     // The item for searching the list of items.
@@ -325,10 +327,10 @@
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         PreferenceUtils.addPreferencesFromResource(this, R.xml.website_preferences);
-        ListView listView = (ListView) getView().findViewById(android.R.id.list);
+        mListView = (ListView) getView().findViewById(android.R.id.list);
         mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
-        listView.setEmptyView(mEmptyView);
-        listView.setDivider(null);
+        mListView.setEmptyView(mEmptyView);
+        mListView.setDivider(null);
 
         mClearButton = (Button) getView().findViewById(R.id.clear_button);
         if (mClearButton != null) mClearButton.setOnClickListener(this);
@@ -882,6 +884,11 @@
         // Only show the link that explains protected content settings when needed.
         if (!mCategory.showSites(SiteSettingsCategory.Type.PROTECTED_MEDIA)) {
             screen.removePreference(explainProtectedMediaKey);
+            mListView.setFocusable(true);
+        } else {
+            // On small screens with no touch input, nested focusable items inside a LinearLayout in
+            // ListView cause focus problems when using a keyboard (crbug.com/974413).
+            mListView.setFocusable(false);
         }
 
         // When this menu opens, make sure the Blocked list is collapsed.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotification.java b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotification.java
index 024ddb99..ee4e2d12 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotification.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotification.java
@@ -79,7 +79,7 @@
 
         @Override
         public void onAction(Object actionData) {
-            PreferencesLauncher.launchSettingsPage(mContext, SearchEnginePreference.class);
+            PreferencesLauncher.launchSettingsPageCompat(mContext, SearchEnginePreference.class);
             recordEvent(Events.PROMPT_FOLLOWED);
             recordSearchEngineTypeBeforeChoicePresented();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegate.java
index 74959711..59bfb44 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegate.java
@@ -32,7 +32,7 @@
     ChromeSigninManagerDelegate(AndroidSyncSettings androidSyncSettings) {
         assert androidSyncSettings != null;
         mAndroidSyncSettings = androidSyncSettings;
-        mNativeChromeSigninManagerDelegate = ChromeSigninManagerDelegateJni.get().init(this);
+        mNativeChromeSigninManagerDelegate = ChromeSigninManagerDelegateJni.get().init();
     }
 
     @Override
@@ -89,23 +89,22 @@
     }
 
     @Override
-    public void disableSyncAndWipeData(@JCaller SigninManager signinManager,
-            long nativeSigninManagerAndroid, boolean isManagedOrForceWipe,
-            final Runnable wipeDataCallback) {
+    public void disableSyncAndWipeData(
+            boolean isManagedOrForceWipe, final Runnable wipeDataCallback) {
         mAndroidSyncSettings.updateAccount(null);
         if (isManagedOrForceWipe) {
-            SigninManagerJni.get().wipeProfileData(
-                    signinManager, nativeSigninManagerAndroid, wipeDataCallback);
+            ChromeSigninManagerDelegateJni.get().wipeProfileData(
+                    this, mNativeChromeSigninManagerDelegate, wipeDataCallback);
         } else {
-            SigninManagerJni.get().wipeGoogleServiceWorkerCaches(
-                    signinManager, nativeSigninManagerAndroid, wipeDataCallback);
+            ChromeSigninManagerDelegateJni.get().wipeGoogleServiceWorkerCaches(
+                    this, mNativeChromeSigninManagerDelegate, wipeDataCallback);
         }
     }
 
     // Native methods.
     @NativeMethods
     interface Natives {
-        long init(@JCaller ChromeSigninManagerDelegate self);
+        long init();
 
         void destroy(
                 @JCaller ChromeSigninManagerDelegate self, long nativeChromeSigninManagerDelegate);
@@ -122,5 +121,11 @@
 
         String getManagementDomain(
                 @JCaller ChromeSigninManagerDelegate self, long nativeChromeSigninManagerDelegate);
+
+        void wipeProfileData(@JCaller ChromeSigninManagerDelegate self,
+                long nativeChromeSigninManagerDelegate, Runnable callback);
+
+        void wipeGoogleServiceWorkerCaches(@JCaller ChromeSigninManagerDelegate self,
+                long nativeChromeSigninManagerDelegate, Runnable callback);
     }
 }
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 8982e353..fef46f32 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
@@ -104,6 +104,7 @@
     private boolean mDestroyed;
     private boolean mIsSigninInProgress;
     private boolean mHasGmsError;
+    private boolean mRecordUndoSignin;
 
     private UserRecoverableErrorHandler.ModalDialog mGooglePlayServicesUpdateErrorHandler;
     private AlertDialog mGmsIsUpdatingDialog;
@@ -247,6 +248,9 @@
         }
         mProfileDataCache = new ProfileDataCache(getActivity(),
                 getResources().getDimensionPixelSize(R.dimen.user_picture_size), badgeConfig);
+        // By default this is set to true so that when system back button is pressed user action
+        // is recorded in onDestroy().
+        mRecordUndoSignin = true;
     }
 
     @Override
@@ -258,6 +262,7 @@
             mConfirmSyncDataStateMachine.cancel(/* isBeingDestroyed = */ true);
             mConfirmSyncDataStateMachine = null;
         }
+        if (mRecordUndoSignin) RecordUserAction.record("Signin_Undo_Signin");
         mDestroyed = true;
     }
 
@@ -378,12 +383,15 @@
     }
 
     private void onRefuseButtonClicked(View button) {
+        RecordUserAction.record("Signin_Undo_Signin");
+        mRecordUndoSignin = false;
         onSigninRefused();
     }
 
     private void onAcceptButtonClicked(View button) {
         if (!areControlsEnabled()) return;
         mIsSigninInProgress = true;
+        mRecordUndoSignin = false;
         RecordUserAction.record("Signin_Signin_WithDefaultSyncSettings");
 
         // Record the fact that the user consented to the consent text by clicking on a button
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index 8daf52d..53c0bdf4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -645,7 +645,7 @@
         // http://crbug.com/589028
         ChromeSigninController.get().setSignedInAccountName(null);
         if (mSignOutState.mWipeDataHooks != null) mSignOutState.mWipeDataHooks.preWipeData();
-        mDelegate.disableSyncAndWipeData(this, mNativeSigninManagerAndroid,
+        mDelegate.disableSyncAndWipeData(
                 mSignOutState.mManagementDomain != null || mSignOutState.mForceWipeUserData,
                 this::onProfileDataWiped);
         mAccountTrackerService.invalidateAccountSeedStatus(true);
@@ -725,12 +725,6 @@
         void signOut(@JCaller SigninManager self, long nativeSigninManagerAndroid,
                 @SignoutReason int reason);
 
-        void wipeProfileData(
-                @JCaller SigninManager self, long nativeSigninManagerAndroid, Runnable callback);
-
-        void wipeGoogleServiceWorkerCaches(
-                @JCaller SigninManager self, long nativeSigninManagerAndroid, Runnable callback);
-
         void clearLastSignedInUser(@JCaller SigninManager self, long nativeSigninManagerAndroid);
 
         void logInSignedInUser(@JCaller SigninManager self, long nativeSigninManagerAndroid);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerDelegate.java
index fdd09e14..3ad643d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerDelegate.java
@@ -9,7 +9,6 @@
 import android.content.Context;
 
 import org.chromium.base.Callback;
-import org.chromium.base.annotations.JCaller;
 
 /**
  * Interface providing SigninManager access to dependencies that are not part of the SignIn
@@ -70,14 +69,9 @@
     /**
      * Called AFTER native sign-out is complete, this method clears various
      * account and profile data associated with the previous signin and aborts sync.
-     * @param signinManager a reference on SigninManager used for the native calls
-     * @param nativeSigninManagerAndroid a reference on the native SigninManager used for native
-     *                                   calls
      * @param isManagedOrForceWipe if the account is managed or force wipe enabled, which triggers a
      *                            different cleanup flow
      * @param wipeDataCallback to be called once profile data cleanup is complete
      */
-    public void disableSyncAndWipeData(@JCaller SigninManager signinManager,
-            long nativeSigninManagerAndroid, boolean isManagedOrForceWipe,
-            Runnable wipeDataCallback);
+    public void disableSyncAndWipeData(boolean isManagedOrForceWipe, Runnable wipeDataCallback);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
index b7818977..bbf8854b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
@@ -83,6 +83,7 @@
         } else {
             attachView();
         }
+        mTab.updateAccessibilityVisibility();
 
         TabContentManager tabContentManager = mTab.getActivity().getTabContentManager();
         if (tabContentManager != null) {
@@ -103,6 +104,7 @@
         if (webContents != null) {
             webContents.onShow();
         }
+        mTab.updateAccessibilityVisibility();
 
         mView = null;
         mFqdn = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index a1fd6ac..843ed77 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -19,6 +19,7 @@
 import org.chromium.base.BuildInfo;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.FieldTrialList;
 import org.chromium.base.SysUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
@@ -93,6 +94,8 @@
 
     private static Boolean sDownloadAutoResumptionEnabledInNative;
 
+    private static String sReachedCodeProfilerTrialGroup;
+
     private static final String NTP_BUTTON_TRIAL_NAME = "NewTabPage";
     private static final String NTP_BUTTON_VARIANT_PARAM_NAME = "variation";
 
@@ -192,6 +195,7 @@
         cacheImmersiveUiModeEnabled();
         cacheSwapPixelFormatToFixConvertFromTranslucentEnabled();
         cacheTabPersistentStoreTaskRunnerVariant();
+        cacheReachedCodeProfilerTrialGroup();
 
         if (isHighEndPhone()) cacheGridTabSwitcherEnabled();
         if (isHighEndPhone()) cacheTabGroupsAndroidEnabled();
@@ -800,6 +804,33 @@
                         ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT));
     }
 
+    /**
+     * Caches the trial group of the reached code profiler feature to be using on next startup.
+     */
+    private static void cacheReachedCodeProfilerTrialGroup() {
+        // Make sure that the existing value is saved in a static variable before overwriting it.
+        if (sReachedCodeProfilerTrialGroup == null) {
+            getReachedCodeProfilerTrialGroup();
+        }
+
+        ChromePreferenceManager.getInstance().writeString(
+                ChromePreferenceManager.REACHED_CODE_PROFILER_GROUP_KEY,
+                FieldTrialList.findFullName(ChromeFeatureList.REACHED_CODE_PROFILER));
+    }
+
+    /**
+     * @return The trial group of the reached code profiler.
+     */
+    @CalledByNative
+    public static String getReachedCodeProfilerTrialGroup() {
+        if (sReachedCodeProfilerTrialGroup == null) {
+            sReachedCodeProfilerTrialGroup = ChromePreferenceManager.getInstance().readString(
+                    ChromePreferenceManager.REACHED_CODE_PROFILER_GROUP_KEY, "");
+        }
+
+        return sReachedCodeProfilerTrialGroup;
+    }
+
     private static native void nativeSetCustomTabVisible(boolean visible);
     private static native void nativeSetIsInMultiWindowMode(boolean isInMultiWindowMode);
     private static native boolean nativeIsNetworkServiceWarmUpEnabled();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
index 43fb9323..ee0b5c5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
@@ -32,6 +32,7 @@
 
     private static final String GMSCORE_PACKAGE_NAME = "com.google.android.gms";
     private static final int GMSCORE_MIN_VERSION = 12800000;
+    private static final int GMSCORE_MIN_VERSION_ISUVPAA = 16200000;
 
     /** Ensures only one request is processed at a time. */
     private boolean mIsOperationPending;
@@ -100,11 +101,6 @@
         // ChromeActivity could be null.
         if (context == null) {
             callback.call(false);
-        }
-
-        if (PackageUtils.getPackageVersion(context, GMSCORE_PACKAGE_NAME) < GMSCORE_MIN_VERSION
-                || Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
-            callback.call(false);
             return;
         }
 
@@ -113,9 +109,21 @@
             return;
         }
 
-        FingerprintManager fingerprintManager =
-                (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
-        callback.call(fingerprintManager != null && fingerprintManager.hasEnrolledFingerprints());
+        if (PackageUtils.getPackageVersion(context, GMSCORE_PACKAGE_NAME)
+                >= GMSCORE_MIN_VERSION_ISUVPAA) {
+            mIsUserVerifyingPlatformAuthenticatorAvailableCallback = callback;
+            Fido2ApiHandler.getInstance().isUserVerifyingPlatformAuthenticatorAvailable(
+                    mRenderFrameHost, this);
+        } else if (PackageUtils.getPackageVersion(context, GMSCORE_PACKAGE_NAME)
+                        >= GMSCORE_MIN_VERSION
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            FingerprintManager fingerprintManager =
+                    (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
+            callback.call(
+                    fingerprintManager != null && fingerprintManager.hasEnrolledFingerprints());
+        } else {
+            callback.call(false);
+        }
     }
 
     @Override
@@ -145,12 +153,12 @@
     public void onIsUserVerifyingPlatformAuthenticatorAvailableResponse(boolean isUVPAA) {
         assert mIsUserVerifyingPlatformAuthenticatorAvailableCallback != null;
         mIsUserVerifyingPlatformAuthenticatorAvailableCallback.call(isUVPAA);
-        close();
+        mIsUserVerifyingPlatformAuthenticatorAvailableCallback = null;
     }
 
     @Override
     public void onError(Integer status) {
-        assert((mMakeCredentialCallback != null && mGetAssertionCallback == null)
+        assert ((mMakeCredentialCallback != null && mGetAssertionCallback == null)
                 || (mMakeCredentialCallback == null && mGetAssertionCallback != null));
         if (mMakeCredentialCallback != null) {
             mMakeCredentialCallback.call(status, null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
index 631acd7..cccc7a2f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -7,7 +7,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.view.ViewCompat;
@@ -226,18 +225,15 @@
     /**
      * Initializes the view shown when the selectable list is empty.
      *
-     * @param emptyDrawable The Drawable to show when the selectable list is empty.
      * @param emptyStringResId The string to show when the selectable list is empty.
      * @param searchEmptyStringResId The string to show when the selectable list is empty during
      *                               a search.
      * @return The {@link TextView} displayed when the list is empty.
      */
-    public TextView initializeEmptyView(
-            Drawable emptyDrawable, int emptyStringResId, int searchEmptyStringResId) {
+    public TextView initializeEmptyView(int emptyStringResId, int searchEmptyStringResId) {
         mEmptyStringResId = emptyStringResId;
         mSearchEmptyStringResId = searchEmptyStringResId;
 
-        mEmptyView.setCompoundDrawablesWithIntrinsicBounds(null, emptyDrawable, null, null);
         mEmptyView.setText(mEmptyStringResId);
 
         // Dummy listener to have the touch events dispatched to this view tree for navigation UI.
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 50deb89..acd95cc 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -339,9 +339,6 @@
       <message name="IDS_PREFS_SYNC_AND_SERVICES_CONTENT_DESCRIPTION" desc="The accessibility text to read when the 'Sync and Google services' Settings page is opened from the sign-in page. This text is attached to the 'Navigate Up' button shown at the top of the screen. The first two sentences describe the screen that is currently shown to the user, while 'Navigate up' is a description for the button this text is attached to. 'Navigate up' should match TC ID 6794660482873516081.">
         You are currently customizing your Sync and Google service settings. To finish turning on sync, tap the Confirm button near the bottom of the screen. Navigate up
       </message>
-      <message name="IDS_SIGN_IN_TO_CHROME_SUMMARY" desc="Summary for the button to sign in to Chrome, explaining benefits of signing in.">
-        Sign in to get your bookmarks, history, passwords, and other settings on all your devices
-      </message>
       <message name="IDS_SIGNIN_PREF_SUMMARY" desc="Summary for the entry in Settings to sign in to Chrome, explaining benefits of signing in.">
         Sync and personalize across devices
       </message>
@@ -369,9 +366,6 @@
       <message name="IDS_SIGN_IN_GOOGLE_ACTIVITY_CONTROLS_SUMMARY_CHILD_ACCOUNT" desc="Message of Google activity controls preference in signed in accounts settings screen">
         Control how your browsing history is used to personalize Search and more
       </message>
-      <message name="IDS_SIGN_IN_SYNC_ERROR_WIDGET" desc="Accessibility description of the sync error widget in settings screen">
-        Sync error occurred, tap to get details.
-      </message>
       <!-- Unified consent preferences -->
       <message name="IDS_SIGN_OUT_AND_TURN_OFF_SYNC" desc="The text for a preferences row that for signs out the user and turns off sync.">
         Sign out and turn off sync
@@ -2578,9 +2572,6 @@
       <message name="IDS_DOWNLOAD_PAGE" desc="Download this page.">
         Download page
       </message>
-      <message name="IDS_DOWNLOAD_MANAGER_UI_EMPTY" desc="Indicates that there are no downloaded items.">
-        No downloads here
-      </message>
       <message name="IDS_DOWNLOAD_MANAGER_NO_DOWNLOADS" desc="Text appearing on an empty tab that indicates that downloaded files appear on this tab.">
         Files that you download appear here
       </message>
@@ -2632,7 +2623,7 @@
 
       <!-- Browsing History UI -->
       <message name="IDS_HISTORY_MANAGER_EMPTY" desc="Indicates that there are no browsing history items.">
-        No history here
+        Pages that you visit appear here
       </message>
       <message name="IDS_HISTORY_MANAGER_NO_RESULTS" desc="Text explaining that no browsing history items match a search query.">
         No history found
@@ -3063,7 +3054,7 @@
         Bookmarks
       </message>
       <message name="IDS_BOOKMARKS_FOLDER_EMPTY" desc="Text explaining that the currently selected bookmarks folder is empty.">
-        No bookmarks here
+        Pages that you bookmark appear here
       </message>
       <message name="IDS_NO_BOOKMARKS" desc="Text describing that there is no bookmarks in a bookmark folder.">
         No bookmarks
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_BOOKMARKS_FOLDER_EMPTY.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_BOOKMARKS_FOLDER_EMPTY.png.sha1
new file mode 100644
index 0000000..b65f642
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_BOOKMARKS_FOLDER_EMPTY.png.sha1
@@ -0,0 +1 @@
+be7e9b605aedceb66b50c5a526fed1e3f2902699
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_HISTORY_MANAGER_EMPTY.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_HISTORY_MANAGER_EMPTY.png.sha1
new file mode 100644
index 0000000..2bfd2629
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_HISTORY_MANAGER_EMPTY.png.sha1
@@ -0,0 +1 @@
+c47f7ee3ded5925c96e3119a5ee319c82867c7b1
\ No newline at end of file
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_ar.xtb b/chrome/android/java/strings/translations/android_chrome_strings_ar.xtb
index dc31673..6688b61 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_ar.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_ar.xtb
@@ -101,7 +101,7 @@
 <translation id="1692118695553449118">المزامنة ممكنة</translation>
 <translation id="169515064810179024">حظر مواقع الويب من الوصول إلى مستشعرات الحركة</translation>
 <translation id="1697480839793160072">الهواتف</translation>
-<translation id="1698058317516161971">الرسائل الإلكترونية</translation>
+<translation id="1698058317516161971">عناوين البريد الإلكتروني</translation>
 <translation id="1717218214683051432">مستشعرات الحركة</translation>
 <translation id="1718835860248848330">آخر ساعة</translation>
 <translation id="1733116627827457509"><ph name="FILE_SIZE" /> - تم تحديثه منذ <ph name="TIME_SINCE_UPDATE" /> دقائق</translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_et.xtb b/chrome/android/java/strings/translations/android_chrome_strings_et.xtb
index 2a43d76..5c8e545f 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_et.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_et.xtb
@@ -101,7 +101,7 @@
 <translation id="1692118695553449118">Sünkroonimine on sisse lülitatud</translation>
 <translation id="169515064810179024">Blokeeri saitide juurdepääs liikumisanduritele</translation>
 <translation id="1697480839793160072">Telefonid</translation>
-<translation id="1698058317516161971">Meilid</translation>
+<translation id="1698058317516161971">E-posti aadressid</translation>
 <translation id="1717218214683051432">Liikumisandurid</translation>
 <translation id="1718835860248848330">Viimase tunni jooksul</translation>
 <translation id="1733116627827457509"><ph name="FILE_SIZE" /> – värskendati <ph name="TIME_SINCE_UPDATE" /></translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_id.xtb b/chrome/android/java/strings/translations/android_chrome_strings_id.xtb
index e15b2d05..808b696 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_id.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_id.xtb
@@ -557,7 +557,7 @@
 <translation id="5048398596102334565">Mengizinkan situs mengakses sensor gerakan (disarankan)</translation>
 <translation id="5063480226653192405">Penggunaan</translation>
 <translation id="5073204694187207510">Sembunyikan petak layar penuh</translation>
-<translation id="5082793167783849073">Bandingkan halaman secara cepat dengan membuat grup. Untuk memulai, tap lama link.</translation>
+<translation id="5082793167783849073">Bandingkan halaman secara cepat dengan membuat grup. Untuk memulai, sentuh lama link.</translation>
 <translation id="5087580092889165836">Tambahkan kartu</translation>
 <translation id="5098406615395468004">Tambahkan ke aplikasi saya</translation>
 <translation id="5100237604440890931">Diciutkan - klik untuk memperluas.</translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_nl.xtb b/chrome/android/java/strings/translations/android_chrome_strings_nl.xtb
index 95e23d0..b584940 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_nl.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_nl.xtb
@@ -928,7 +928,7 @@
 <translation id="7638584964844754484">Onjuiste wachtwoordzin</translation>
 <translation id="7641339528570811325">Browsegegevens wissen</translation>
 <translation id="7648422057306047504">Wachtwoorden versleutelen met Google-inloggegevens</translation>
-<translation id="7649070708921625228">Help</translation>
+<translation id="7649070708921625228">Hulp</translation>
 <translation id="7658239707568436148">Annuleren</translation>
 <translation id="7665369617277396874">Account toevoegen</translation>
 <translation id="766587987807204883">Hier worden artikelen weergegeven die je zelfs kunt lezen wanneer je offline bent</translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_ta.xtb b/chrome/android/java/strings/translations/android_chrome_strings_ta.xtb
index 6260ba1..9889fcb9 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_ta.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_ta.xtb
@@ -359,7 +359,7 @@
 <translation id="363596933471559332">சேமிக்கப்பட்டுள்ள நற்சான்றிதழ்களைப் பயன்படுத்தி, இணையதளங்களில் தானாகவே உள்நுழையவும். இந்த அம்சம் முடக்கப்பட்டிருக்கும் போது, ஒவ்வொரு முறையும் இணையதளத்தில் உள்நுழைவதற்கு முன்பும் சரிபார்க்கும்படி கேட்கப்படுவீர்கள்.</translation>
 <translation id="3658159451045945436">மீட்டமைத்தால், பார்வையிட்ட தளங்களின் பட்டியல் உட்பட டேட்டா சேமிப்புகளின் வரலாறு அழிக்கப்படும்.</translation>
 <translation id="3662546969139119822">இங்கே வரலாறு எதுவுமில்லை</translation>
-<translation id="3663367437272849150"><ph name="NUM_SELECTED" /> தாவல்களை குழுவாக்கும்.</translation>
+<translation id="3663367437272849150"><ph name="NUM_SELECTED" /> தாவல்களைக் குழுவாக்கும்.</translation>
 <translation id="3672452749423051839">வழிநடத்தல் பிழை பரிந்துரைகள்</translation>
 <translation id="3692944402865947621">சேமிப்பிடம் இல்லாததால், <ph name="FILE_NAME" />ஐப் பதிவிறக்க முடியவில்லை.</translation>
 <translation id="3714981814255182093">கண்டறி பட்டியைத் திறக்கும்</translation>
diff --git a/chrome/android/java/strings/translations/android_chrome_strings_uk.xtb b/chrome/android/java/strings/translations/android_chrome_strings_uk.xtb
index 6730aca..f61a6242 100644
--- a/chrome/android/java/strings/translations/android_chrome_strings_uk.xtb
+++ b/chrome/android/java/strings/translations/android_chrome_strings_uk.xtb
@@ -100,7 +100,7 @@
 <translation id="1679068421605151609">Інструменти для розробників</translation>
 <translation id="1692118695553449118">Синхронізацію ввімкнено</translation>
 <translation id="169515064810179024">Заборонити сайтам доступ до датчиків руху</translation>
-<translation id="1697480839793160072">Номери телефону</translation>
+<translation id="1697480839793160072">Номери телефонів</translation>
 <translation id="1698058317516161971">Електронні адреси</translation>
 <translation id="1717218214683051432">Датчики руху</translation>
 <translation id="1718835860248848330">Остання година</translation>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java
index 17b8273..aebbbad0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java
@@ -33,6 +33,8 @@
     // Shared preferences key for the reached code profiler.
     private static final String REACHED_CODE_PROFILER_ENABLED_KEY = "reached_code_profiler_enabled";
 
+    private static final String FAKE_GROUP_NAME = "FakeGroup";
+
     /**
      * Tests that passing a command line switch enables the reached code profiler no matter what.
      */
@@ -91,6 +93,21 @@
     }
 
     /**
+     * Tests that the reached code profiler field trial group is saved in shared preferences for
+     * being used on the next startup.
+     */
+    @Test
+    @SmallTest
+    @CommandLineFlags.
+    Add("force-fieldtrials=" + ChromeFeatureList.REACHED_CODE_PROFILER + "/" + FAKE_GROUP_NAME)
+    public void testSharedPreferenceTrialGroupIsCached() throws Exception {
+        mActivityTestRule.startMainActivityFromLauncher();
+        Assert.assertEquals(FAKE_GROUP_NAME,
+                ChromePreferenceManager.getInstance().readString(
+                        ChromePreferenceManager.REACHED_CODE_PROFILER_GROUP_KEY, null));
+    }
+
+    /**
      * The reached code profiler is always disabled in some configurations. This helper allows to
      * check if the profiler is enabled in supported configurations.
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java
index 0a8ef87..3a0bd05 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java
@@ -7,12 +7,10 @@
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.PathUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.ProcessInitException;
@@ -31,15 +29,6 @@
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
-    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
-
-    @Before
-    public void setUp() throws Exception {
-        // TODO(aberent): Find the correct place to put this.  Calling ensureInitialized() on the
-        //                current pathway fails to set this variable with the modern linker.
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
-    }
-
     /**
      * Test that the fundamental method for writing the histogram
      * {@link ChromeBackupAgent#recordRestoreHistogram()} works correctly
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index c26559b..ec2bf52f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -65,7 +65,6 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.ObserverList.RewindableIterator;
-import org.chromium.base.PathUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.metrics.RecordHistogram;
@@ -164,7 +163,6 @@
     private static final String TARGET_BLANK_TEST_PAGE =
             "/chrome/test/data/android/cct_target_blank.html";
     private static final String TEST_MENU_TITLE = "testMenuTitle";
-    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
     private static final String WEBLITE_PREFIX = "http://googleweblight.com/i?u=";
     private static final String JS_MESSAGE = "from_js";
     private static final String TITLE_FROM_POSTMESSAGE_TO_CHANNEL =
@@ -225,7 +223,6 @@
         mTestServer = EmbeddedTestServer.createAndStartServer(appContext);
         mTestPage = mTestServer.getURL(TEST_PAGE);
         mTestPage2 = mTestServer.getURL(TEST_PAGE_2);
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
         LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
         mWebServer = TestWebServer.start();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
index cb68399..e8eebe12 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
@@ -29,7 +29,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.PathUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.test.util.CallbackHelper;
@@ -59,11 +58,9 @@
     private static final String URL = "http://www.google.com";
     private static final String URL2 = "https://www.android.com";
     private static final String INVALID_SCHEME_URL = "intent://www.google.com";
-    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
 
     @Before
     public void setUp() throws Exception {
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
         LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
         mCustomTabsConnection = CustomTabsTestUtils.setUpConnection();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
index 2e65072..0945878 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
@@ -25,7 +25,6 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.PathUtils;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.MetricsUtils.HistogramDelta;
@@ -66,14 +65,12 @@
     private Context mContext;
     private EmbeddedTestServer mServer;
 
-    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
     private static final Uri ORIGIN = Uri.parse("http://cats.google.com");
     private static final int NET_OK = 0;
 
     @Before
     public void setUp() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(true));
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
         mConnection = CustomTabsTestUtils.setUpConnection();
         mContext = InstrumentationRegistry.getInstrumentation()
                            .getTargetContext()
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
index 0bcae52..a031eaa5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import static org.chromium.chrome.browser.init.ChromeBrowserInitializer.PRIVATE_DATA_DIRECTORY_SUFFIX;
-
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.Context;
@@ -30,7 +28,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.CommandLine;
-import org.chromium.base.PathUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.task.PostTask;
@@ -115,7 +112,6 @@
     public void setUp() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(true));
 
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
         LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
         mWebServer = TestWebServer.start();
         if (mOverrideTrustedCdn.isEnabled()) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
index f17cc49..5e19a3e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
@@ -774,7 +774,7 @@
         // Click Other.
         onView(withId(org.chromium.chrome.download.R.id.spinner)).perform(click());
         onView(withText("Other")).perform(click());
-        onView(withText("No downloads here")).check(matches(isDisplayed()));
+        onView(withText("Files that you download appear here")).check(matches(isDisplayed()));
     }
 
     private DownloadActivity startDownloadActivity() throws Exception {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ServicificationDownloadTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ServicificationDownloadTest.java
index 21548fed..d59408d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ServicificationDownloadTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ServicificationDownloadTest.java
@@ -99,6 +99,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             DownloadManagerService downloadManagerService =
                     DownloadManagerService.getDownloadManagerService();
+            downloadManagerService.disableAddCompletedDownloadToDownloadManager();
             ((SystemDownloadNotifier) downloadManagerService.getDownloadNotifier())
                     .setDownloadNotificationService(mNotificationService);
             downloadManagerService.createInterruptedDownloadForTest(url, DOWNLOAD_GUID, tempFile);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
index 935a9f4..d79f553 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
@@ -7,7 +7,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
+import android.support.test.filters.MediumTest;
 import android.util.Base64;
 
 import org.junit.After;
@@ -67,7 +67,6 @@
 
     private static final String TEST_PAGE = "/chrome/test/data/android/about.html";
     private static final int TIMEOUT_MS = 5000;
-    private static final long POLLING_INTERVAL = 100;
     private static final ClientId TEST_CLIENT_ID =
             new ClientId(OfflinePageBridge.DOWNLOAD_NAMESPACE, "1234");
 
@@ -98,6 +97,7 @@
             });
         });
         Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        if (!incognitoProfile) Assert.assertNotNull(mOfflinePageBridge);
     }
 
     @Before
@@ -124,7 +124,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
     public void testLoadOfflinePagesWhenEmpty() throws Exception {
         List<OfflinePageItem> offlinePages = OfflineTestUtil.getAllPages();
@@ -132,10 +132,10 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
-    @DisableIf.Build(
-            message = "https://crbug.com/853255", sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP)
+    @DisableIf.
+    Build(message = "https://crbug.com/853255", sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP)
     public void testAddOfflinePageAndLoad() throws Exception {
         mActivityTestRule.loadUrl(mTestPage);
         savePage(SavePageResult.SUCCESS, mTestPage);
@@ -146,7 +146,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
     public void testGetPageByBookmarkId() throws Exception {
         mActivityTestRule.loadUrl(mTestPage);
@@ -159,10 +159,10 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
-    @DisableIf.Build(
-            message = "https://crbug.com/853255", sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP)
+    @DisableIf.
+    Build(message = "https://crbug.com/853255", sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP)
     public void testDeleteOfflinePage() throws Exception {
         deletePage(TEST_CLIENT_ID, DeletePageResult.SUCCESS);
         mActivityTestRule.loadUrl(mTestPage);
@@ -175,7 +175,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
     public void testGetRequestsInQueue() throws Exception {
         String url = "https://www.google.com/";
@@ -209,7 +209,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
     public void testOfflinePageBridgeDisabledInIncognito() throws Exception {
         initializeBridgeForProfile(true);
@@ -217,7 +217,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
     public void testRemoveRequestsFromQueue() throws Exception {
         String url = "https://www.google.com/";
@@ -246,7 +246,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testDeletePagesByOfflineIds() throws Exception {
         // Save 3 pages and record their offline IDs to delete later.
         Set<String> pageUrls = new HashSet<>();
@@ -285,7 +285,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testGetPagesByNamespace() throws Exception {
         // Save 3 pages and record their offline IDs to delete later.
         Set<Long> offlineIdsToFetch = new HashSet<>();
@@ -322,7 +322,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
     @DisabledTest(message = "crbug.com/954205")
     public void testDownloadPage() throws Exception {
@@ -356,7 +356,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testSavePageWithRequestOrigin() throws Exception {
         final OfflinePageOrigin origin =
                 new OfflinePageOrigin("abc.xyz", new String[] {"deadbeef"});
@@ -386,7 +386,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @DisabledTest(message = "crbug.com/842801")
     public void testSavePageNoOrigin() throws Exception {
         mActivityTestRule.loadUrl(mTestPage);
@@ -396,10 +396,10 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     @RetryOnFailure
-    @DisableIf.Build(
-            message = "https://crbug.com/853255", sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP)
+    @DisableIf.
+    Build(message = "https://crbug.com/853255", sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP)
     public void testGetLoadUrlParamsForOpeningMhtmlFileUrl() throws Exception {
         mActivityTestRule.loadUrl(mTestPage);
         savePage(SavePageResult.SUCCESS, mTestPage);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.java
new file mode 100644
index 0000000..d9b95b4
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.java
@@ -0,0 +1,184 @@
+// Copyright 2019 The Chromium 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.offlinepages;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.Callback;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.RetryOnFailure;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.offlinepages.background.UpdateRequestResult;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.net.NetworkChangeNotifier;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/** Unit tests for {@link RequestCoordinatorBridge}. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class RequestCoordinatorBridgeTest {
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    private static final int TIMEOUT_MS = 5000;
+
+    private RequestCoordinatorBridge mRequestCoordinatorBridge;
+
+    private void initializeBridgeForProfile(final boolean incognitoProfile)
+            throws InterruptedException {
+        final Semaphore semaphore = new Semaphore(0);
+        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
+            Profile profile = Profile.getLastUsedProfile();
+            if (incognitoProfile) {
+                profile = profile.getOffTheRecordProfile();
+            }
+            mRequestCoordinatorBridge = RequestCoordinatorBridge.getForProfile(profile);
+            semaphore.release();
+        });
+        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mActivityTestRule.startMainActivityOnBlankPage();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            // Ensure we start in an offline state.
+            NetworkChangeNotifier.forceConnectivityState(false);
+            if (!NetworkChangeNotifier.isInitialized()) {
+                NetworkChangeNotifier.init();
+            }
+        });
+
+        initializeBridgeForProfile(false);
+    }
+
+    @Test
+    @MediumTest
+    @RetryOnFailure
+    public void testGetRequestsInQueue() throws Exception {
+        String url = "https://www.google.com/";
+        String namespace = "custom_tabs";
+        savePageLater(url, namespace);
+        SavePageRequest[] requests = OfflineTestUtil.getRequestsInQueue();
+        Assert.assertEquals(1, requests.length);
+        Assert.assertEquals(namespace, requests[0].getClientId().getNamespace());
+        Assert.assertEquals(url, requests[0].getUrl());
+
+        String url2 = "https://mail.google.com/";
+        String namespace2 = "last_n";
+        savePageLater(url2, namespace2);
+        requests = OfflineTestUtil.getRequestsInQueue();
+        Assert.assertEquals(2, requests.length);
+
+        HashSet<String> expectedUrls = new HashSet<>();
+        expectedUrls.add(url);
+        expectedUrls.add(url2);
+
+        HashSet<String> expectedNamespaces = new HashSet<>();
+        expectedNamespaces.add(namespace);
+        expectedNamespaces.add(namespace2);
+
+        for (SavePageRequest request : requests) {
+            Assert.assertTrue(expectedNamespaces.contains(request.getClientId().getNamespace()));
+            expectedNamespaces.remove(request.getClientId().getNamespace());
+            Assert.assertTrue(expectedUrls.contains(request.getUrl()));
+            expectedUrls.remove(request.getUrl());
+        }
+    }
+
+    @Test
+    @MediumTest
+    @RetryOnFailure
+    public void testRequestCoordinatorBridgeDisabledInIncognito() throws Exception {
+        initializeBridgeForProfile(true);
+        Assert.assertEquals(null, mRequestCoordinatorBridge);
+    }
+
+    @Test
+    @MediumTest
+    @RetryOnFailure
+    public void testRemoveRequestsFromQueue() throws Exception {
+        String url = "https://www.google.com/";
+        String namespace = "custom_tabs";
+        savePageLater(url, namespace);
+
+        String url2 = "https://mail.google.com/";
+        String namespace2 = "last_n";
+        savePageLater(url2, namespace2);
+
+        SavePageRequest[] requests = OfflineTestUtil.getRequestsInQueue();
+        Assert.assertEquals(2, requests.length);
+
+        List<Long> requestsToRemove = new ArrayList<>();
+        requestsToRemove.add(Long.valueOf(requests[1].getRequestId()));
+
+        List<RequestCoordinatorBridge.RequestRemovedResult> removed =
+                removeRequestsFromQueue(requestsToRemove);
+        Assert.assertEquals(requests[1].getRequestId(), removed.get(0).getRequestId());
+        Assert.assertEquals(UpdateRequestResult.SUCCESS, removed.get(0).getUpdateRequestResult());
+        SavePageRequest[] remaining = OfflineTestUtil.getRequestsInQueue();
+        Assert.assertEquals(1, remaining.length);
+
+        Assert.assertEquals(requests[0].getRequestId(), remaining[0].getRequestId());
+        Assert.assertEquals(requests[0].getUrl(), remaining[0].getUrl());
+    }
+
+    private void savePageLater(final String url, final String namespace)
+            throws InterruptedException {
+        final Semaphore semaphore = new Semaphore(0);
+        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
+            mRequestCoordinatorBridge.savePageLater(url, namespace, true /* userRequested */,
+                    new OfflinePageOrigin(), new Callback<Integer>() {
+                        @Override
+                        public void onResult(Integer i) {
+                            Assert.assertEquals("SavePageLater did not succeed", Integer.valueOf(0),
+                                    i); // 0 is SUCCESS
+                            semaphore.release();
+                        }
+                    });
+        });
+        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    private List<RequestCoordinatorBridge.RequestRemovedResult> removeRequestsFromQueue(
+            final List<Long> requestsToRemove) throws InterruptedException {
+        final AtomicReference<List<RequestCoordinatorBridge.RequestRemovedResult>> ref =
+                new AtomicReference<>();
+        final Semaphore semaphore = new Semaphore(0);
+        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
+            mRequestCoordinatorBridge.removeRequestsFromQueue(requestsToRemove,
+                    new Callback<List<RequestCoordinatorBridge.RequestRemovedResult>>() {
+                        @Override
+                        public void onResult(List<RequestCoordinatorBridge.RequestRemovedResult>
+                                        removedRequests) {
+                            ref.set(removedRequests);
+                            semaphore.release();
+                        }
+                    });
+        });
+        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        return ref.get();
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java
index 8c9b9e45..f232d12 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java
@@ -7,6 +7,7 @@
 import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.HAVE_INSTRUMENTS;
 import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.IMMEDIATE_RESPONSE;
 
+import android.os.Build;
 import android.support.test.filters.MediumTest;
 
 import org.junit.Assert;
@@ -16,6 +17,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -167,6 +169,8 @@
      */
     @Test
     @MediumTest
+    @DisableIf.
+    Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "https://crbug.com/979159")
     @Feature({"Payments", "RenderTest"})
     public void testRetryWithPayerErrors() throws Throwable {
         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java
index f5cb129..8fec810 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java
@@ -118,7 +118,8 @@
 
         // Set the second search engine as the default using TemplateUrlService.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            SearchEnginePreference pref = (SearchEnginePreference) prefActivity.getMainFragment();
+            SearchEnginePreference pref =
+                    (SearchEnginePreference) prefActivity.getMainFragmentCompat();
             pref.setValueForTesting("1");
 
             // Ensure that the second search engine in the list is selected.
@@ -240,13 +241,15 @@
 
         // Set the first search engine as the default using TemplateUrlService.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            SearchEnginePreference pref = (SearchEnginePreference) prefActivity.getMainFragment();
+            SearchEnginePreference pref =
+                    (SearchEnginePreference) prefActivity.getMainFragmentCompat();
             pref.setValueForTesting("0");
         });
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             // Ensure that the first search engine in the list is selected.
-            SearchEnginePreference pref = (SearchEnginePreference) prefActivity.getMainFragment();
+            SearchEnginePreference pref =
+                    (SearchEnginePreference) prefActivity.getMainFragmentCompat();
             Assert.assertNotNull(pref);
             Assert.assertEquals("0", pref.getValueForTesting());
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java
index 2bd28d01..16fb570 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java
@@ -829,10 +829,6 @@
         MetricsUtils.HistogramDelta countDelta = new MetricsUtils.HistogramDelta(
                 "PasswordManager.ExportedPasswordsPerUserInCSV", 123);
 
-        MetricsUtils.HistogramDelta progressBarDelta = new MetricsUtils.HistogramDelta(
-                "PasswordManager.Android.ExportPasswordsProgressBarUsage",
-                ExportFlow.PROGRESS_NOT_SHOWN);
-
         // Confirm the export warning to fire the sharing intent.
         Espresso.onView(withText(R.string.save_password_preferences_export_action_title))
                 .perform(click());
@@ -847,7 +843,6 @@
 
         Assert.assertEquals(1, successDelta.getDelta());
         Assert.assertEquals(1, countDelta.getDelta());
-        Assert.assertEquals(1, progressBarDelta.getDelta());
     }
 
     /**
@@ -1080,10 +1075,6 @@
         Espresso.onView(withText(R.string.settings_passwords_preparing_export))
                 .check(matches(isDisplayed()));
 
-        MetricsUtils.HistogramDelta progressBarDelta = new MetricsUtils.HistogramDelta(
-                "PasswordManager.Android.ExportPasswordsProgressBarUsage",
-                ExportFlow.PROGRESS_HIDDEN_DELAYED);
-
         File tempFile = createFakeExportedPasswordsFile();
         // Now pretend that passwords have been serialized.
         mHandler.getExportSuccessCallback().onResult(12, tempFile.getPath());
@@ -1105,8 +1096,6 @@
         Intents.release();
 
         tempFile.delete();
-
-        Assert.assertEquals(1, progressBarDelta.getDelta());
     }
 
     /**
@@ -1145,10 +1134,6 @@
         Espresso.onView(withText(R.string.settings_passwords_preparing_export))
                 .check(matches(isDisplayed()));
 
-        MetricsUtils.HistogramDelta progressBarDelta = new MetricsUtils.HistogramDelta(
-                "PasswordManager.Android.ExportPasswordsProgressBarUsage",
-                ExportFlow.PROGRESS_HIDDEN_DIRECTLY);
-
         File tempFile = createFakeExportedPasswordsFile();
 
         // Now pretend that passwords have been serialized.
@@ -1167,8 +1152,6 @@
         Intents.release();
 
         tempFile.delete();
-
-        Assert.assertEquals(1, progressBarDelta.getDelta());
     }
 
     /**
@@ -1877,52 +1860,4 @@
                                 allOf(withId(R.id.search_src_text), withText("Zeu"))));
         Espresso.onView(withId(R.id.search_src_text)).check(matches(withText("Zeu")));
     }
-
-    /**
-     * Check that triggering searches and inspected search results are recorded in histograms.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Preferences"})
-    public void testSearchIsRecordedInHistograms() throws Exception {
-        MetricsUtils.HistogramDelta triggered_delta = new MetricsUtils.HistogramDelta(
-                "PasswordManager.Android.PasswordSearchTriggered", 1);
-        MetricsUtils.HistogramDelta untriggered_delta = new MetricsUtils.HistogramDelta(
-                "PasswordManager.Android.PasswordSearchTriggered", 0);
-        MetricsUtils.HistogramDelta viewed_after_search_delta = new MetricsUtils.HistogramDelta(
-                "PasswordManager.Android.PasswordCredentialEntry", 3);
-        setPasswordSourceWithMultipleEntries(GREEK_GODS);
-        Preferences preferences =
-                PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(),
-                        SavePasswordsPreferences.class.getName());
-
-        // Open the search and filter all but "Zeus".
-        Espresso.onView(withSearchMenuIdOrText()).perform(click());
-        Espresso.onView(withId(R.id.search_src_text))
-                .perform(click(), typeText("Zeu"), closeSoftKeyboard());
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
-        Instrumentation.ActivityMonitor monitor =
-                InstrumentationRegistry.getInstrumentation().addMonitor(
-                        new IntentFilter(Intent.ACTION_VIEW), null, false);
-
-        Espresso.onView(withText(ZEUS_ON_EARTH.getUserName()))
-                .check(matches(isDisplayed()))
-                .perform(click());
-        monitor.waitForActivityWithTimeout(UI_UPDATING_TIMEOUT_MS);
-        Assert.assertEquals("Monitor for has not been called", 1, monitor.getHits());
-        InstrumentationRegistry.getInstrumentation().removeMonitor(monitor);
-        Espresso.onView(withContentDescription(R.string.abc_action_bar_up_description))
-                .perform(click()); // Go back to the search list.
-        Espresso.onView(isRoot()).check(
-                (root, e) -> waitForView((ViewGroup) root, withId(R.id.search_src_text)));
-
-        Assert.assertEquals(1, viewed_after_search_delta.getDelta());
-
-        preferences.finish();
-        waitToFinish(preferences, UI_UPDATING_TIMEOUT_MS);
-
-        Assert.assertEquals(0, untriggered_delta.getDelta());
-        Assert.assertEquals(1, triggered_delta.getDelta());
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java
index e2af5d5..77e7979 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java
@@ -15,6 +15,7 @@
 import android.print.PrintDocumentInfo;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -379,6 +380,25 @@
         }
     }
 
+    /**
+     * Regresstion test for crbug.com/974581. In some cases, native printing code will fail without
+     * starting a printing task in Java side. pdfWritingDone() will be called with |pageCount| = 0
+     * in this case. We don't need to do anything for this in Java side for now.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Printing"})
+    public void testPdfWritingDoneCalledWithoutInitailizePrintingTask() throws Throwable {
+        if (!ApiCompatibilityUtils.isPrintingSupported()) return;
+
+        mActivityTestRule.startMainActivityWithURL(URL);
+        final PrintingControllerImpl controller = createControllerOnUiThread();
+
+        // Calling pdfWritingDone() with |pageCount| = 0 before onWrite() was called. It shouldn't
+        // crash.
+        TestThreadUtils.runOnUiThreadBlocking(() -> controller.pdfWritingDone(0));
+    }
+
     private PrintingControllerImpl createControllerOnUiThread() {
         return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
             return (PrintingControllerImpl) PrintingControllerImpl.create(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/AuthenticatorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/AuthenticatorTest.java
index 944e1fae..214954f9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/AuthenticatorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/AuthenticatorTest.java
@@ -61,6 +61,12 @@
                 RenderFrameHost frameHost, HandlerResponseCallback callback) {
             callback.onError(AuthenticatorStatus.NOT_IMPLEMENTED);
         }
+
+        @Override
+        protected void isUserVerifyingPlatformAuthenticatorAvailable(
+                RenderFrameHost frameHost, HandlerResponseCallback callback) {
+            callback.onIsUserVerifyingPlatformAuthenticatorAvailableResponse(false);
+        }
     }
 
     /** Waits until the JavaScript code supplies a result. */
@@ -149,6 +155,7 @@
     @Feature({"WebAuth"})
     public void testIsUserVerifyingPlatformAuthenticatorAvailable() throws Exception {
         mActivityTestRule.loadUrl(mUrl);
+        Fido2ApiHandler.overrideInstanceForTesting(mMockHandler);
         mActivityTestRule.runJavaScriptCodeInCurrentTab(
                 "doIsUserVerifyingPlatformAuthenticatorAvailable()");
         Assert.assertEquals("Success", mUpdateWaiter.waitForUpdate());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtilsTest.java
index cb6462a..c445fe3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtilsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtilsTest.java
@@ -62,7 +62,6 @@
         // methods, which subclasses are expected to be able to override.
         InOrder inOrder = inOrder(mExternalAuthUtils);
         inOrder.verify(mExternalAuthUtils).checkGooglePlayServicesAvailable(mContext);
-        inOrder.verify(mExternalAuthUtils).recordConnectionResult(ConnectionResult.SUCCESS);
         inOrder.verify(mExternalAuthUtils, never()).isUserRecoverableError(anyInt());
         inOrder.verify(mExternalAuthUtils, never()).describeError(anyInt());
     }
@@ -82,7 +81,6 @@
         // methods, which subclasses are expected to be able to override.
         InOrder inOrder = inOrder(mExternalAuthUtils);
         inOrder.verify(mExternalAuthUtils).checkGooglePlayServicesAvailable(mContext);
-        inOrder.verify(mExternalAuthUtils).recordConnectionResult(ERR);
         inOrder.verify(mExternalAuthUtils).isUserRecoverableError(ERR);
     }
 
@@ -102,7 +100,6 @@
         // methods, which subclasses are expected to be able to override.
         InOrder inOrder = inOrder(mExternalAuthUtils, mUserRecoverableErrorHandler);
         inOrder.verify(mExternalAuthUtils).checkGooglePlayServicesAvailable(mContext);
-        inOrder.verify(mExternalAuthUtils).recordConnectionResult(ERR);
         inOrder.verify(mExternalAuthUtils).isUserRecoverableError(ERR);
         inOrder.verify(mUserRecoverableErrorHandler).handleError(mContext, ERR);
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegateTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegateTest.java
index 6ec64840..c3085e5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegateTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegateTest.java
@@ -34,9 +34,6 @@
     @Mock
     ChromeSigninManagerDelegate.Natives mNativeMock;
 
-    @Mock
-    SigninManager.Natives mSigninManagerNativeMock;
-
     private ChromeSigninManagerDelegate mDelegate;
 
     @Before
@@ -44,7 +41,6 @@
         initMocks(this);
 
         mocker.mock(ChromeSigninManagerDelegateJni.TEST_HOOKS, mNativeMock);
-        mocker.mock(SigninManagerJni.TEST_HOOKS, mSigninManagerNativeMock);
 
         // SigninManager interacts with AndroidSyncSettings, but its not the focus
         // of this test. Using MockSyncContentResolver reduces burden of test setup.
@@ -56,34 +52,28 @@
     public void signOutWithManagedDomain() {
         // Stub out various native calls. Some of these are verified as never called
         // and those stubs simply allow that verification to catch any issues.
-        doNothing().when(mSigninManagerNativeMock).wipeProfileData(any(), anyLong(), any());
-        doNothing()
-                .when(mSigninManagerNativeMock)
-                .wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
+        doNothing().when(mNativeMock).wipeProfileData(any(), anyLong(), any());
+        doNothing().when(mNativeMock).wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
 
         // Simulate call from SigninManager
-        mDelegate.disableSyncAndWipeData(null, 0L, true, null);
+        mDelegate.disableSyncAndWipeData(true, null);
 
         // Sign-out should only clear the profile when the user is managed.
-        verify(mSigninManagerNativeMock, times(1)).wipeProfileData(any(), anyLong(), any());
-        verify(mSigninManagerNativeMock, never())
-                .wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
+        verify(mNativeMock, times(1)).wipeProfileData(any(), anyLong(), any());
+        verify(mNativeMock, never()).wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
     }
     @Test
     public void signOutWithNullDomain() {
         // Stub out various native calls. Some of these are verified as never called
         // and those stubs simply allow that verification to catch any issues.
-        doNothing().when(mSigninManagerNativeMock).wipeProfileData(any(), anyLong(), any());
-        doNothing()
-                .when(mSigninManagerNativeMock)
-                .wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
+        doNothing().when(mNativeMock).wipeProfileData(any(), anyLong(), any());
+        doNothing().when(mNativeMock).wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
 
         // Simulate call from SigninManager
-        mDelegate.disableSyncAndWipeData(null, 0L, false, null);
+        mDelegate.disableSyncAndWipeData(false, null);
 
         // Sign-out should only clear the service worker cache when the user is not managed.
-        verify(mSigninManagerNativeMock, never()).wipeProfileData(any(), anyLong(), any());
-        verify(mSigninManagerNativeMock, times(1))
-                .wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
+        verify(mNativeMock, never()).wipeProfileData(any(), anyLong(), any());
+        verify(mNativeMock, times(1)).wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
index a259b05..ae2c0f3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
@@ -73,7 +73,7 @@
         // Stub out various native calls. Some of these are verified as never called
         // and those stubs simply allow that verification to catch any issues.
         doNothing().when(mNativeMock).signOut(any(), anyLong(), anyInt());
-        doNothing().when(mDelegateMock).disableSyncAndWipeData(any(), anyLong(), eq(true), any());
+        doNothing().when(mDelegateMock).disableSyncAndWipeData(eq(true), any());
         // See verification of nativeWipeProfileData below.
         doReturn("TestDomain").when(mDelegateMock).getManagementDomain();
 
@@ -83,12 +83,12 @@
         // nativeSignOut should be called *before* clearing any account data.
         // http://crbug.com/589028
         verify(mNativeMock, times(1)).signOut(any(), anyLong(), eq(SignoutReason.SIGNOUT_TEST));
-        verify(mDelegateMock, never()).disableSyncAndWipeData(any(), anyLong(), eq(true), any());
+        verify(mDelegateMock, never()).disableSyncAndWipeData(eq(true), any());
 
         // Simulate native callback to trigger clearing of account data.
         mSigninManager.onNativeSignOut();
 
-        verify(mDelegateMock, times(1)).disableSyncAndWipeData(any(), anyLong(), eq(true), any());
+        verify(mDelegateMock, times(1)).disableSyncAndWipeData(eq(true), any());
     }
 
     @Test
@@ -96,7 +96,7 @@
         // Stub out various native calls. Some of these are verified as never called
         // and those stubs simply allow that verification to catch any issues.
         doNothing().when(mNativeMock).signOut(any(), anyLong(), anyInt());
-        doNothing().when(mDelegateMock).disableSyncAndWipeData(any(), anyLong(), eq(false), any());
+        doNothing().when(mDelegateMock).disableSyncAndWipeData(eq(false), any());
         // See verification of nativeWipeGoogleServiceWorkerCaches below.
         doReturn(null).when(mDelegateMock).getManagementDomain();
 
@@ -106,12 +106,12 @@
         // nativeSignOut should be called *before* clearing any account data.
         // http://crbug.com/589028
         verify(mNativeMock, times(1)).signOut(any(), anyLong(), eq(SignoutReason.SIGNOUT_TEST));
-        verify(mDelegateMock, never()).disableSyncAndWipeData(any(), anyLong(), eq(false), any());
+        verify(mDelegateMock, never()).disableSyncAndWipeData(eq(false), any());
 
         // Simulate native callback to trigger clearing of account data.
         mSigninManager.onNativeSignOut();
 
-        verify(mDelegateMock, times(1)).disableSyncAndWipeData(any(), anyLong(), eq(false), any());
+        verify(mDelegateMock, times(1)).disableSyncAndWipeData(eq(false), any());
     }
 
     @Test
@@ -119,7 +119,7 @@
         // Stub out various native calls. Some of these are verified as never called
         // and those stubs simply allow that verification to catch any issues.
         doNothing().when(mNativeMock).signOut(any(), anyLong(), anyInt());
-        doNothing().when(mDelegateMock).disableSyncAndWipeData(any(), anyLong(), eq(false), any());
+        doNothing().when(mDelegateMock).disableSyncAndWipeData(eq(false), any());
         // See verification of nativeWipeGoogleServiceWorkerCaches below.
         doReturn(null).when(mDelegateMock).getManagementDomain();
 
@@ -129,12 +129,12 @@
         // nativeSignOut should be called *before* clearing any account data.
         // http://crbug.com/589028
         verify(mNativeMock, times(1)).signOut(any(), anyLong(), eq(SignoutReason.SIGNOUT_TEST));
-        verify(mDelegateMock, never()).disableSyncAndWipeData(any(), anyLong(), eq(false), any());
+        verify(mDelegateMock, never()).disableSyncAndWipeData(eq(false), any());
 
         // Simulate native callback to trigger clearing of account data.
         mSigninManager.onNativeSignOut();
 
-        verify(mDelegateMock, times(1)).disableSyncAndWipeData(any(), anyLong(), eq(true), any());
+        verify(mDelegateMock, times(1)).disableSyncAndWipeData(eq(true), any());
     }
 
     @Test
@@ -142,7 +142,7 @@
         // Stub out various native calls. Some of these are verified as never called
         // and those stubs simply allow that verification to catch any issues.
         doNothing().when(mNativeMock).signOut(any(), anyLong(), anyInt());
-        doNothing().when(mDelegateMock).disableSyncAndWipeData(any(), anyLong(), eq(true), any());
+        doNothing().when(mDelegateMock).disableSyncAndWipeData(eq(true), any());
         // See verification of nativeWipeProfileData below.
         doReturn("TestDomain").when(mDelegateMock).getManagementDomain();
 
@@ -153,7 +153,7 @@
         // from the native side.
         verify(mNativeMock, never()).signOut(any(), anyLong(), anyInt());
 
-        verify(mDelegateMock, times(1)).disableSyncAndWipeData(any(), anyLong(), eq(true), any());
+        verify(mDelegateMock, times(1)).disableSyncAndWipeData(eq(true), any());
     }
 
     @Test
@@ -161,7 +161,7 @@
         // Stub out various native calls. Some of these are verified as never called
         // and those stubs simply allow that verification to catch any issues.
         doNothing().when(mNativeMock).signOut(any(), anyLong(), anyInt());
-        doNothing().when(mDelegateMock).disableSyncAndWipeData(any(), anyLong(), eq(false), any());
+        doNothing().when(mDelegateMock).disableSyncAndWipeData(eq(false), any());
         // See verification of nativeWipeGoogleServiceWorkerCaches below.
         doReturn(null).when(mDelegateMock).getManagementDomain();
 
@@ -173,7 +173,7 @@
         // from the native side.
         verify(mNativeMock, never()).signOut(any(), anyLong(), anyInt());
 
-        verify(mDelegateMock, times(1)).disableSyncAndWipeData(any(), anyLong(), eq(false), any());
+        verify(mDelegateMock, times(1)).disableSyncAndWipeData(eq(false), any());
     }
 
     @Test
@@ -193,7 +193,6 @@
         })
                 .when(mNativeMock)
                 .signOut(any(), anyLong(), anyInt());
-        doNothing().when(mNativeMock).wipeGoogleServiceWorkerCaches(any(), anyLong(), any());
 
         mSigninManager.signOut(SignoutReason.SIGNOUT_TEST);
         assertTrue(mSigninManager.isOperationInProgress());
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index e95f0960..bd362a3 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-77.0.3834.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-77.0.3836.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/android/touchless/java/res/layout/touchless_action_card.xml b/chrome/android/touchless/java/res/layout/touchless_action_card.xml
new file mode 100644
index 0000000..6ae4b6c
--- /dev/null
+++ b/chrome/android/touchless/java/res/layout/touchless_action_card.xml
@@ -0,0 +1,20 @@
+<?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. -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:focusable="false">
+
+    <!-- android:id should match what ActionItem#ViewHolder expects. -->
+    <include android:id="@+id/action_button" layout="@layout/dialog_list_item"/>
+
+    <org.chromium.chrome.browser.ntp.cards.ProgressIndicatorView
+        android:id="@+id/progress_indicator"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"/>
+
+</FrameLayout>
diff --git a/chrome/android/touchless/java/res/layout/touchless_suggestions_tile_view.xml b/chrome/android/touchless/java/res/layout/touchless_suggestions_tile_view.xml
index ae5d82d..5287523 100644
--- a/chrome/android/touchless/java/res/layout/touchless_suggestions_tile_view.xml
+++ b/chrome/android/touchless/java/res/layout/touchless_suggestions_tile_view.xml
@@ -26,8 +26,7 @@
         android:layout_gravity="center"
         app:smallSize="@dimen/most_likely_quantized_icon_size_small"
         app:largeSize="@dimen/most_likely_quantized_icon_size_large"
-        android:layout_margin="@dimen/most_likely_quantized_icon_margin"
-        android:importantForAccessibility="no" />
+        android:layout_margin="@dimen/most_likely_quantized_icon_margin" />
 
     <!-- Focus highlight. -->
     <View
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsAdapter.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsAdapter.java
index eb2f5e04..e0d95152 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsAdapter.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsAdapter.java
@@ -182,6 +182,7 @@
                                     WindowOpenDisposition.CURRENT_TAB, UrlConstants.EXPLORE_URL));
             ContextMenuManager.registerViewForTouchlessContextMenu(
                     tile, new ContextMenuManager.EmptyDelegate());
+            tile.setContentDescription(tile.getResources().getString(R.string.ntp_all_apps));
         } else if (holder.getItemViewType() == ViewType.SUGGESTION_TYPE) {
             // If site suggestion, attach context menu handler; clicks navigate to site url.
             int itemCount = mModel.get(ITEM_COUNT_KEY);
@@ -204,6 +205,7 @@
 
                 tile.setOnCreateContextMenuListener(interactionDelegate);
                 ContextMenuManager.registerViewForTouchlessContextMenu(tile, interactionDelegate);
+                tile.setContentDescription(item.get(SiteSuggestionModel.TITLE_KEY));
             }
         }
     }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsCoordinator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsCoordinator.java
index f8d835cd..5961b2d 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsCoordinator.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsCoordinator.java
@@ -52,7 +52,6 @@
             SuggestionsNavigationDelegate navigationDelegate, ContextMenuManager contextMenuManager,
             ImageFetcher imageFetcher, TouchlessLayoutManager touchlessLayoutManager) {
         Context context = parentView.getContext();
-        SiteSuggestionsLayoutManager layoutManager = new SiteSuggestionsLayoutManager(context);
         PropertyModel model =
                 new PropertyModel
                         .Builder(CURRENT_INDEX_KEY, INITIAL_INDEX_KEY, SUGGESTIONS_KEY,
@@ -66,6 +65,9 @@
         View suggestionsView =
                 ((ViewStub) parentView.findViewById(R.id.most_likely_stub)).inflate();
 
+        SiteSuggestionsLayoutManager layoutManager =
+                new SiteSuggestionsLayoutManager(context, model);
+
         RoundedIconGenerator iconGenerator = ViewUtils.createDefaultRoundedIconGenerator(
                 context.getResources(), /* circularIcon = */ true);
         int iconSize =
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsLayoutManager.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsLayoutManager.java
index a859ea4..f09ea6a 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsLayoutManager.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsLayoutManager.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.touchless;
 
+import static org.chromium.chrome.browser.touchless.SiteSuggestionsCoordinator.ITEM_COUNT_KEY;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.support.v7.widget.RecyclerView;
@@ -12,6 +14,7 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.touchless.R;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Custom layout manager for site suggestions carousel. Handles custom effects such as
@@ -37,14 +40,16 @@
     private boolean mNeedRebind;
     private Context mContext;
     private RecyclerView mRecyclerView;
+    private PropertyModel mModel;
 
-    SiteSuggestionsLayoutManager(Context context) {
+    SiteSuggestionsLayoutManager(Context context, PropertyModel model) {
         setAutoMeasureEnabled(true);
         mContext = context;
         mFocusPosition = 0;
         mNeedRebind = false;
         mFocusView = null;
         mShouldFocusAfterLayout = false;
+        mModel = model;
     }
 
     @Override
@@ -245,6 +250,18 @@
         mRecyclerView = null;
     }
 
+    @Override
+    public int getRowCountForAccessibility(
+            RecyclerView.Recycler recycler, RecyclerView.State state) {
+        return 1;
+    }
+
+    @Override
+    public int getColumnCountForAccessibility(
+            RecyclerView.Recycler recycler, RecyclerView.State state) {
+        return mModel.get(ITEM_COUNT_KEY);
+    }
+
     /**
      * Requests layout such that it forces rebind of all visible items with
      * updated data from the adapter.
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolder.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolder.java
index edbd857..16dad97 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolder.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolder.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.touchless;
 
 import android.view.View;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -28,7 +28,8 @@
     TouchlessActionItemViewHolder(SuggestionsRecyclerView recyclerView,
             ContextMenuManager contextMenuManager, final SuggestionsUiDelegate uiDelegate,
             UiConfig uiConfig) {
-        super(R.layout.dialog_list_item, recyclerView, contextMenuManager, uiDelegate, uiConfig);
+        super(R.layout.touchless_action_card, recyclerView, contextMenuManager, uiDelegate,
+                uiConfig);
         mTextView = itemView.findViewById(R.id.dialog_item_text);
         mImageView = itemView.findViewById(R.id.dialog_item_icon);
     }
@@ -37,12 +38,12 @@
     public void onBindViewHolder(ActionItem item) {
         super.onBindViewHolder(item);
 
-        LinearLayout.LayoutParams params =
-                new LinearLayout.LayoutParams(itemView.getLayoutParams());
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(itemView.getLayoutParams());
         params.bottomMargin = itemView.getResources().getDimensionPixelSize(
                 R.dimen.touchless_new_tab_recycler_view_over_scroll);
         itemView.setLayoutParams(params);
-        itemView.setBackground(ApiCompatibilityUtils.getDrawable(
+
+        mButton.setBackground(ApiCompatibilityUtils.getDrawable(
                 itemView.getResources(), R.drawable.hairline_border_card_background));
         mTextView.setText(itemView.getResources().getString(R.string.more_articles));
         mImageView.setImageDrawable(
@@ -64,23 +65,6 @@
     }
 
     @Override
-    protected void setState(@ActionItem.State int state) {
-        assert state != ActionItem.State.HIDDEN;
-
-        // Similar to the method in ActionItem.ViewHolder, but removing the animted progress
-        // indicator that's not supported for touchless devices.
-        if (state == ActionItem.State.BUTTON) {
-            mButton.setVisibility(View.VISIBLE);
-        } else if (state == ActionItem.State.INITIAL_LOADING
-                || state == ActionItem.State.MORE_BUTTON_LOADING) {
-            mButton.setVisibility(View.INVISIBLE);
-        } else {
-            // Not even HIDDEN is supported as the item should not be able to receive updates.
-            assert false : "ActionViewHolder got notified of an unsupported state: " + state;
-        }
-    }
-
-    @Override
     protected void showFetchFailureMessage() {
         Toast.makeText(itemView.getContext(), R.string.ntp_suggestions_fetch_failed,
                      Toast.LENGTH_SHORT)
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java
index 5ed0af8c..2780b7d 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java
@@ -135,6 +135,8 @@
             PropertyModel model, Pair<ViewGroup, ModelListAdapter> view, PropertyKey propertyKey) {
         ViewGroup dialogView = view.first;
         ModelListAdapter optionsAdapter = view.second;
+        assert !model.get(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY);
+        assert propertyKey != ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY;
         // TODO(mdjones): If the default buttons are used assert no list items and convert the
         //                buttons to list items.
         if (TouchlessDialogProperties.IS_FULLSCREEN == propertyKey) {
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml
index 0061613..89bbdc4 100644
--- a/chrome/android/webapk/shell_apk/AndroidManifest.xml
+++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -24,6 +24,10 @@
     <uses-permission android:name="android.permission.VIBRATE"></uses-permission>
     {{{raw_manifest_tags}}}
 
+    <uses-sdk
+        android:minSdkVersion="16"
+        android:targetSdkVersion="28" />
+
     <application
         android:icon="@mipmap/ic_launcher"
         android:label="@string/short_name"
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index c3a202956..0e870a3 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 97
+current_shell_apk_version = 98
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 3099bceb..ecdd269 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -451,7 +451,6 @@
 
   if (is_chromeos) {
     deps += [
-      "//chrome/browser/chromeos/kiosk_next_home/mojom",
       "//chrome/browser/chromeos/supervision/mojom",
       "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/machine_learning:mojo_bindings",
diff --git a/chrome/app/app_management_strings.grdp b/chrome/app/app_management_strings.grdp
index 3c20cf7d..c352a3b 100644
--- a/chrome/app/app_management_strings.grdp
+++ b/chrome/app/app_management_strings.grdp
@@ -79,4 +79,10 @@
   <message name="IDS_APP_MANAGEMENT_PIN_ENFORCED_BY_POLICY" desc="Short text for the pin/unpin context menu to tell user the setting is enforced by policy of an app.">
     Pinned by administrator
   </message>
+  <message name="IDS_APP_MANAGEMENT_CONTACTS" desc="Label for the Android contacts (address book) permission toggle.">
+    Contacts
+  </message>
+  <message name="IDS_APP_MANAGEMENT_STORAGE" desc="Label for the Android storage permission toggle.">
+    Storage
+  </message>
 </grit-part>
diff --git a/chrome/app/chrome_content_browser_overlay_manifest.cc b/chrome/app/chrome_content_browser_overlay_manifest.cc
index 5d342de..dacf006 100644
--- a/chrome/app/chrome_content_browser_overlay_manifest.cc
+++ b/chrome/app/chrome_content_browser_overlay_manifest.cc
@@ -49,7 +49,6 @@
 #include "third_party/blink/public/mojom/webshare/webshare.mojom.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/kiosk_next_home/mojom/kiosk_next_home_interface_broker.mojom.h"  // nogncheck
 #include "chrome/browser/chromeos/supervision/mojom/onboarding_controller.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/machine_learning/machine_learning_internals_page_handler.mojom.h"
@@ -207,7 +206,6 @@
                 chrome::mojom::PrerenderCanceler,
 #if defined(OS_CHROMEOS)
                 chromeos::ime::mojom::InputEngineManager,
-                chromeos::kiosk_next_home::mojom::KioskNextHomeInterfaceBroker,
                 chromeos::machine_learning::mojom::PageHandler,
                 chromeos::media_perception::mojom::MediaPerception,
                 chromeos::supervision::mojom::OnboardingController,
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index f7eda2fa..9f6a07fe 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1241,7 +1241,19 @@
     Dock has an issue
   </message>
   <message name="IDS_WILCO_NOTIFICATION_DOCK_ERROR_MESSAGE" desc="The message of the notification shown to inform the user that attached dock presents hardware failures.">
-    Try disconnecting and reconnecting your docking station cable. If that doesn't help, have your dock serviced.
+    Your dock needs to be serviced. Without a working fan your dock will shut down.
+  </message>
+  <message name="IDS_WILCO_NOTIFICATION_DOCK_DISPLAY_TITLE" desc="The title of the notification shown to inform the user that HDMI and USB Type-C cannot be used for displays at the same time with the attached dock.">
+    Display connected to the dock has an issue
+  </message>
+  <message name="IDS_WILCO_NOTIFICATION_DOCK_DISPLAY_MESSAGE" desc="The message of the notification shown to inform the user that HDMI and USB Type-C cannot be used for displays at the same time with the attached dock.">
+    Your dock's HDMI port can't be used when the USB Type-C port is used for video output. Please use a different port for one of the displays.
+  </message>
+  <message name="IDS_WILCO_NOTIFICATION_DOCK_THUNDERBOLT_TITLE" desc="The title of the notification shown to inform the user that attached dock will operate in USB Type-C compatible mode.">
+    Thunderbolt is not supported
+  </message>
+  <message name="IDS_WILCO_NOTIFICATION_DOCK_THUNDERBOLT_MESSAGE" desc="The message of the notification shown to inform the user that attached dock will operate in USB Type-C compatible mode.">
+    Your docking station will operate in USB Type-C compatible mode.
   </message>
   <message name="IDS_WILCO_NOTIFICATION_LEARN_MORE" desc="Label on button in various Wilco notification.">
     Learn More
@@ -1753,6 +1765,9 @@
   <message name="IDS_NETWORK_TYPE_WIFI" desc="Label for WiFi networks.">
     Wi-Fi
   </message>
+  <message name="IDS_NETWORK_TYPE_METERED_WIFI" desc="Label for metered Wi-Fi networks (i.e., Wi-Fi networks which have resticted data usage such as a monthly limit).">
+    metered Wi-Fi
+  </message>
   <message name="IDS_NETWORK_TYPE_WIMAX" desc="Label for WiMAX networks.">
     WiMAX
   </message>
@@ -2202,8 +2217,8 @@
   <message name="IDS_UPGRADE_OFFLINE" desc="Status label: Currently offline">
     You are currently offline.
   </message>
-  <message name="IDS_UPGRADE_DISALLOWED" desc="Status label: Upgrade disallowed for the current network">
-    You are currently connected to the <ph name="NETWORK_TYPE">$1<ex>mobile</ex></ph> network.
+  <message name="IDS_UPGRADE_DISALLOWED" desc="Status label: Shown when an upgrade is disallowed for the current network to alert the user to what the current network type is.">
+    You're connected to a <ph name="NETWORK_TYPE">$1<ex>mobile</ex></ph> network.
   </message>
   <message name="IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED" desc="Status label: for updates you need to connect to an Ethernet, Wi-Fi or mobile data">
     To check for updates, please use Ethernet, Wi-Fi or mobile data.
@@ -2211,8 +2226,8 @@
   <message name="IDS_UPGRADE_NETWORK_LIST_CELLULAR_DISALLOWED" desc="Status label: for updates you need to connect to an Ethernet or Wi-Fi">
     To check for updates, please use Ethernet or Wi-Fi.
   </message>
-  <message name="IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED_NOT_AUTOMATIC" desc="Status label: for automatic updates you need to connect to an Ethernet or Wi-Fi">
-    Automatic updates only happen over Ethernet or Wi-Fi.
+  <message name="IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED_NOT_AUTOMATIC" desc="Status label: Shown when the user is connected to a network type that does not support automatic updates (e.g., could be a mobile network with limited data per month). In this case, users can still attempt an update manually.">
+    Automatic updates don't download on this network type, but you can check for updates manually.
   </message>
 
   <!-- EOL Notification Strings -->
@@ -3305,29 +3320,6 @@
     Please wait...
   </message>
 
-  <!-- Supervision Onboarding OOBE screen. -->
-  <message name="IDS_SUPERVISION_ONBOARDING_NEXT_BUTTON" desc="Label for button that shows the next page in the Supervision Onboarding flow.">
-    Next
-  </message>
-  <message name="IDS_SUPERVISION_ONBOARDING_SKIP_BUTTON" desc="Label for button that skips the whole Supervision Onboarding flow.">
-    Skip
-  </message>
-  <message name="IDS_SUPERVISION_ONBOARDING_BACK_BUTTON" desc="Label for button that shows the previous page in the Supervision Onboarding flow.">
-    Back
-  </message>
-  <message name="IDS_SUPERVISION_ONBOARDING_RETRY_BUTTON" desc="Label for button that tries to load again a page in the Supervision Onboarding flow.">
-    Try again
-  </message>
-  <message name="IDS_SUPERVISION_ONBOARDING_RETRY_DIALOG_TITLE" desc="Label for title of a dialog shown when there's an error loading a Supervision Onboarding page.">
-    Something went wrong
-  </message>
-  <message name="IDS_SUPERVISION_ONBOARDING_RETRY_DIALOG_MESSAGE" desc="Message for a dialog shown when there's an error loading a Supervision Onboarding page.">
-    Try again later
-  </message>
-  <message name="IDS_SUPERVISION_ONBOARDING_WAIT_MESSAGE" desc="Message shown while we load the Supervision Onboarding flow.">
-    Please wait...
-  </message>
-
   <!-- Supervision transition OOBE screen. -->
   <message name="IDS_REMOVING_SUPERVISION_TITLE" desc="Title of the supervision transition OOBE screen when stopping supervision.">
     Stopping supervision...
@@ -4097,6 +4089,30 @@
   <message name="IDS_ADD_SUPERVISION_PAGE_TITLE" desc="Title for the Chrome OS Add Supervision screen.">
     Add Supervision
   </message>
+  <message name="IDS_ADD_SUPERVISION_NETWORK_DOWN_HEADING" desc="Heading string shown in the Add Supervision page when the network is not available.">
+    Network not available
+  </message>
+  <message name="IDS_ADD_SUPERVISION_NETWORK_DOWN_DESCRIPTION" desc="Description shown in the Add Supervision page when the network is not available.">
+    Please check your network connection and retry.
+  </message>
+  <message name="IDS_ADD_SUPERVISION_NETWORK_DOWN_BUTTON_LABEL" desc="Label of the button shown on the Add Supervision page when the network is not available.">
+    Close
+  </message>
+  <message name="IDS_ADD_SUPERVISION_EXIT_DIALOG_TITLE" desc="Title for the dialog shown when attempting to exit the Add Supervision screen.">
+    Are you sure you want to exit?
+  </message>
+  <!-- TODO(danan):  Add final string and remove untranslateable -->
+  <message translateable="false" name="IDS_ADD_SUPERVISION_EXIT_DIALOG_BODY" desc="Body for the dialog shown when attempting to exit the Add Supervision screen.">
+    Supervision is already set up for <ph name="USER_FIRST_NAME">$1<ex>John</ex></ph>'s Google Account.
+You can always stop supervision from the Famly Link app. We've sent instructions to you in an email that explains how to install Family Link.
+  </message>
+  <message name="IDS_ADD_SUPERVISION_EXIT_DIALOG_CANCEL_BUTTON_LABEL" desc="Label for the cancel button on the dialog shown when attempting to exit the Add Supervision screen.">
+    Cancel
+  </message>
+  <message name="IDS_ADD_SUPERVISION_EXIT_DIALOG_SIGNOUT_BUTTON_LABEL" desc="Label for the signout button on the dialog shown when attempting to exit the Add Supervision screen.">
+    Sign out
+  </message>
+
     <!-- Strings for gnubby U2FD authentication UI -->
   <message name="IDS_GNUBBY_NOTIFICATION_TITLE" desc="Title for notification informing user to press power button for gnubby U2FD.">
     A security key is being requested
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 9e2e10d..ddaaecc 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9024,6 +9024,12 @@
       <message name="IDS_DEVICE_LOG_REFRESH" desc="Button to refresh device log">
         Refresh
       </message>
+      <message name="IDS_DEVICE_LOG_CLEAR" desc="Button to clear device log">
+        Clear
+      </message>
+      <message name="IDS_DEVICE_LOG_NO_ENTRIES" desc="Text shown when device log is empty">
+        No log entries
+      </message>
       <message name="IDS_DEVICE_LOG_LEVEL_SHOW" desc="'Show' label before logging level checkboxes">
         Show:
       </message>
@@ -9300,6 +9306,17 @@
       Ads blocked on this site
     </message>
 
+    <!-- Heavy Ad Intervention strings. -->
+    <message name="IDS_HEAVY_AD_INTERVENTION_BUTTON_DETAILS" desc="Label for the button that expands the details on the heavy ad intervention page">
+      Details
+    </message>
+    <message name="IDS_HEAVY_AD_INTERVENTION_HEADING" desc="Heading of the heavy ad intervention page">
+      Ad removed.
+    </message>
+    <message name="IDS_HEAVY_AD_INTERVENTION_SUMMARY" desc="Summary in heavy ad intervention page when ad was removed for using too many system resources.">
+      This ad uses too many resources for your device, so Chrome removed it.
+    </message>
+
     <if expr="is_win">
       <message name="IDS_UTILITY_PROCESS_UTILITY_WIN_NAME" desc="The name of the utility process used to handle Windows utility operations.">
         Windows Utilities
diff --git a/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_BUTTON_DETAILS.png.sha1 b/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_BUTTON_DETAILS.png.sha1
new file mode 100644
index 0000000..bb169a8
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_BUTTON_DETAILS.png.sha1
@@ -0,0 +1 @@
+6226847da5ed02848a785481490aaa0bf6ab4568
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_HEADING.png.sha1 b/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_HEADING.png.sha1
new file mode 100644
index 0000000..bb169a8
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_HEADING.png.sha1
@@ -0,0 +1 @@
+6226847da5ed02848a785481490aaa0bf6ab4568
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_SUMMARY.png.sha1 b/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_SUMMARY.png.sha1
new file mode 100644
index 0000000..9161c82a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_HEAVY_AD_INTERVENTION_SUMMARY.png.sha1
@@ -0,0 +1 @@
+10c16e52050ac9feb24231b3da5c3c626e662a65
\ No newline at end of file
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp
index e4ad56e..1e8d849 100644
--- a/chrome/app/profiles_strings.grdp
+++ b/chrome/app/profiles_strings.grdp
@@ -475,6 +475,9 @@
     <message name="IDS_PROFILES_CREATE_TITLE" desc="Title of the create profile dialog">
       Add person
     </message>
+    <message name="IDS_PROFILES_CREATE_NAME_PLACEHOLDER" desc="Name placeholder in the input field of the create profile dialog.">
+      Name
+    </message>
     <message name="IDS_PROFILES_CREATE_LOCAL_ERROR" desc="Message shown when a local error (for example, a disk error) occurs during profile creation.">
       The new user couldn't be created. Please check your hard drive space and permissions and try again.
     </message>
diff --git a/chrome/app/resources/chromium_strings_es.xtb b/chrome/app/resources/chromium_strings_es.xtb
index 8860024..eaa371d 100644
--- a/chrome/app/resources/chromium_strings_es.xtb
+++ b/chrome/app/resources/chromium_strings_es.xtb
@@ -153,7 +153,7 @@
 <translation id="6120345080069858279">Chromium guardará esta contraseña en tu cuenta de Google para que no tengas que recordarla.</translation>
 <translation id="6129621093834146363"><ph name="FILE_NAME" /> es peligroso, por lo que Chromium lo ha bloqueado.</translation>
 <translation id="6212496753309875659">Este ordenador ya cuenta con una versión más reciente de Chromium. Si el software no funciona, desinstala Chromium y vuelve a intentarlo.</translation>
-<translation id="6219195342503754812">{0,plural, =0{Chromium se reiniciará ahora}=1{Chromium se reiniciará en el transcurso de 1 segundo}other{Chromium se reiniciará en el transcurso de # segundos}}</translation>
+<translation id="6219195342503754812">{0,plural, =0{Chromium se reiniciará ahora}=1{Chromium se reiniciará dentro de 1 segundo}other{Chromium se reiniciará dentro de # segundos}}</translation>
 <translation id="6248213926982192922">Establecer Chromium como navegador predeterminado</translation>
 <translation id="6268381023930128611">¿Cerrar sesión en Chromium?</translation>
 <translation id="6295779123002464101">Es posible que <ph name="FILE_NAME" /> sea peligroso, por lo que Chromium lo ha bloqueado.</translation>
@@ -210,7 +210,7 @@
 <translation id="7449453770951226939"><ph name="PAGE_TITLE" /> ‑ Chromium Dev</translation>
 <translation id="7451052299415159299">Chromium necesita permiso para acceder a la cámara en este sitio web</translation>
 <translation id="7483335560992089831">No se puede instalar la misma versión de Chromium que se está ejecutando actualmente. Cierra Chromium y vuelve a intentarlo.</translation>
-<translation id="753534427205733210">{0,plural, =1{Chromium se reiniciará en el transcurso de 1 minuto}other{Chromium se reiniciará en el transcurso de # minutos}}</translation>
+<translation id="753534427205733210">{0,plural, =1{Chromium se reiniciará dentro de 1 minuto}other{Chromium se reiniciará dentro de # minutos}}</translation>
 <translation id="7549178288319965365">Información de Chromium OS</translation>
 <translation id="7561906087460245826">Borrar también los datos de Chromium (<ph name="URL" />)</translation>
 <translation id="761356813943268536">Chromium está utilizando la cámara y el micrófono.</translation>
@@ -265,7 +265,7 @@
 <translation id="9025992965467895364">Esta página utiliza demasiada memoria y Chromium la ha pausado.</translation>
 <translation id="9036189287518468038">Menú de aplicaciones de Chromium</translation>
 <translation id="9089354809943900324">Chromium no está actualizado</translation>
-<translation id="9093206154853821181">{0,plural, =1{Chromium se reiniciará en el transcurso de 1 hora}other{Chromium se reiniciará en el transcurso de # horas}}</translation>
+<translation id="9093206154853821181">{0,plural, =1{Chromium se reiniciará dentro de 1 hora}other{Chromium se reiniciará dentro de # horas}}</translation>
 <translation id="91086099826398415">Abrir enlace en una pes&amp;taña nueva de Chromium</translation>
 <translation id="911206726377975832">¿Quieres borrar también los datos de navegación?</translation>
 <translation id="9158494823179993217">El administrador del sistema ha configurado Chromium para que abra otro navegador al acceder a <ph name="TARGET_URL_HOSTNAME" />.</translation>
diff --git a/chrome/app/resources/generated_resources_am.xtb b/chrome/app/resources/generated_resources_am.xtb
index e4e8cc2..7f74efc 100644
--- a/chrome/app/resources/generated_resources_am.xtb
+++ b/chrome/app/resources/generated_resources_am.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">አትባ</translation>
 <translation id="236141728043665931">ሁልጊዜ የማይክሮፎን መዳረሻ አግድ</translation>
 <translation id="2365507699358342471">ይህ ጣቢያ ወደ ቅንጥብ ሰሌዳው የተቀዱ ጽሑፍን እና ምስሎችን መመልከት ይችላል።</translation>
-<translation id="2367199180085172140">ፋይል አጋራን ያክሉ</translation>
 <translation id="2367972762794486313">መተግበሪያዎችን አሳይ</translation>
 <translation id="2369105924912929484">የተጣሩ የገጽ ይዘቶችን ይቀያይሩ</translation>
 <translation id="2371076942591664043">&amp;ሲጠናቀቅ ክፈት</translation>
@@ -3296,7 +3295,6 @@
 <translation id="6026047032548434446">መተግበሪያ ይጫን?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - ዩኤስቢ መሣሪያ ተገናኝቷል</translation>
 <translation id="6029027682598229313">የLinux ጭነት ተጠናቋል።</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />]ን ሰርዝ፣ አዝራር</translation>
 <translation id="6029587122245504742">በጣም ቀርፋፋው</translation>
 <translation id="6032912588568283682">የፋይል ስርዓት</translation>
 <translation id="6038929619733116134">ረባሽ ወይም አሳሳች ማስታወቂያዎችን ጣቢያው የሚያሳይ ከሆነ አግድ</translation>
@@ -4597,7 +4595,6 @@
 <translation id="7973962044839454485">PPP ማረጋገጫ በተሳሳተ የተጠቃሚ ስም ወይም የይለፍ ቃል ምክንያት አልተሳካም</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" />ን መጠቀም እንደገና ይሞክሩ</translation>
 <translation id="7974936243149753750">ትርፍ ቅኝት</translation>
-<translation id="79766959863778284">XR የተነጠለ የመሣሪያ አገልግሎት</translation>
 <translation id="7978412674231730200">የግል ቁልፍ</translation>
 <translation id="7978450511781612192">ይህ ከእርስዎ የGoogle መለያዎች ዘግተው እንዲወጡ ያደርግዎታል። የእርስዎ እልባቶች፣ ታሪክ፣ የይለፍ ቃላት እና ተጨማሪ ነገሮች ከእንግዲህ አይሰምሩም።</translation>
 <translation id="7979036127916589816">የማመሳሰል ስህተት</translation>
@@ -5370,7 +5367,6 @@
 <translation id="9138978632494473300">ለሚከተሉት ቦታዎች አቋራጮችን ያክሉ፦</translation>
 <translation id="9140067245205650184">የማይደገፍ የባህሪ ጥቆማን እየተጠቀሙ ነው፦ <ph name="BAD_FLAG" />። እርጋታ እና ደህንነት ችግር ይደርስባቸዋል።</translation>
 <translation id="9143298529634201539">የአስተያየት ጥቆማ ይወገድ?</translation>
-<translation id="9147304170847707004">የማረጋገጫ ዘዴ</translation>
 <translation id="9147392381910171771">&amp;አማራጮች</translation>
 <translation id="9148058034647219655">ውጣ</translation>
 <translation id="9148126808321036104">እንደገና ይግቡ</translation>
diff --git a/chrome/app/resources/generated_resources_ar.xtb b/chrome/app/resources/generated_resources_ar.xtb
index b4b1f2d..dabdd5cd 100644
--- a/chrome/app/resources/generated_resources_ar.xtb
+++ b/chrome/app/resources/generated_resources_ar.xtb
@@ -301,7 +301,7 @@
 <translation id="144283815522798837">عدد العناصر المحدَّدة: <ph name="NUMBER_OF_ITEMS_SELECTED" /></translation>
 <translation id="1444628761356461360">يتولى إدارة هذا الإعداد مالك الجهاز، <ph name="OWNER_EMAIL" />.</translation>
 <translation id="144518587530125858">تعذر تحميل '<ph name="IMAGE_PATH" />' للمظهر.</translation>
-<translation id="1445693676523799095">قد يستغرق هذا بعض الوقت</translation>
+<translation id="1445693676523799095">قد يستغرق هذا بعض الوقت.</translation>
 <translation id="1451375123200651445">صفحة ويب، ملف واحد</translation>
 <translation id="1451917004835509682">إضافة شخص خاضع للإشراف</translation>
 <translation id="1454223536435069390">الت&amp;قاط لقطة شاشة</translation>
@@ -609,7 +609,7 @@
 <translation id="1895252664692693738">يتبقى <ph name="TIME_LEFT" /></translation>
 <translation id="1895658205118569222">إيقاف التشغيل</translation>
 <translation id="1895934970388272448">يجب عليك تأكيد التسجيل في الطابعة لإنهاء هذه العملية - يُرجى التحقق منها الآن.</translation>
-<translation id="1899826437968063457">‏يحتاج الجهاز الافتراضي (VM) للمكوّن الإضافى إلى إذن للتشغيل</translation>
+<translation id="1899826437968063457">‏يحتاج Plugin VM إلى إذن للتشغيل</translation>
 <translation id="1901303067676059328">تح&amp;ديد الكلّ</translation>
 <translation id="1902576642799138955">فترة الصلاحية</translation>
 <translation id="1904394285866191268">{NUM_TABS,plural, =1{كتم صوت علامة تبويب واحدة}zero{كتم صوت علامات التبويب}two{كتم صوت علامتيِّ التبويب}few{كتم صوت علامات التبويب}many{كتم صوت علامات التبويب}other{كتم صوت علامات التبويب}}</translation>
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">تحسين</translation>
 <translation id="236141728043665931">حظر الدخول إلى الميكروفون دومًا</translation>
 <translation id="2365507699358342471">يمكن لهذا الموقع الاطلاع على النصوص والصور التي تم نسخها إلى الحافظة.</translation>
-<translation id="2367199180085172140">إضافة "مشاركة ملف"</translation>
 <translation id="2367972762794486313">إظهار التطبيقات</translation>
 <translation id="2369105924912929484">تبديل أنواع محتوى الصفحات المستخلصة</translation>
 <translation id="2371076942591664043">فتح الملفّ عند &amp;انتهاء التحميل</translation>
@@ -1992,7 +1991,7 @@
 <translation id="3966072572894326936">اختيار مجلد آخر...</translation>
 <translation id="3967822245660637423">اكتمل التنزيل</translation>
 <translation id="3967919079500697218">تم إيقاف إمكانية التقاط لقطات شاشة من قِبل المشرف..</translation>
-<translation id="3969092967100188979">تفعيل، تجوال</translation>
+<translation id="3969092967100188979">مفعَّل، تجوال</translation>
 <translation id="3970114302595058915">رقم التعريف</translation>
 <translation id="397105322502079400">جارٍ الحساب...</translation>
 <translation id="3975565978598857337">تعذّر الاتصال بخادم النطاق</translation>
@@ -2242,7 +2241,7 @@
 <translation id="4378551569595875038">جارٍ الاتصال...</translation>
 <translation id="4378556263712303865">طلب الجهاز</translation>
 <translation id="4380648069038809855">الدخول إلى وضع ملء الشاشة</translation>
-<translation id="4382131447572146376">{COUNT,plural, =0{<ph name="EMAIL" />}=1{<ph name="EMAIL" /> وحساب واحد آخر}two{<ph name="EMAIL" />و حسابان آخران (<ph name="EXTRA_ACCOUNTS" />)}few{<ph name="EMAIL" />و <ph name="EXTRA_ACCOUNTS" /> حسابات أخرى}many{<ph name="EMAIL" />و <ph name="EXTRA_ACCOUNTS" /> حسابًا آخر}other{<ph name="EMAIL" />و <ph name="EXTRA_ACCOUNTS" /> حساب آخر}}</translation>
+<translation id="4382131447572146376">{COUNT,plural, =0{<ph name="EMAIL" />}=1{<ph name="EMAIL" /> وحساب واحد آخر}two{<ph name="EMAIL" /> وحسابان آخران (<ph name="EXTRA_ACCOUNTS" />)}few{<ph name="EMAIL" /> و<ph name="EXTRA_ACCOUNTS" /> حسابات أخرى}many{<ph name="EMAIL" /> و<ph name="EXTRA_ACCOUNTS" /> حسابًا آخر}other{<ph name="EMAIL" /> و<ph name="EXTRA_ACCOUNTS" /> حساب آخر}}</translation>
 <translation id="4384312707950789900">إضافة إلى الشبكات المُفضلة</translation>
 <translation id="4384652540891215547">تفعيل الإضافة</translation>
 <translation id="438503109373656455">ساراتوجا</translation>
@@ -2373,7 +2372,7 @@
 <translation id="4596295440756783523">لديك شهادات في الملف تحدد هذه الخوادم</translation>
 <translation id="4598556348158889687">إدارة مساحة التخزين</translation>
 <translation id="4598776695426288251">‏اتصال Wi-Fi متاح عبر عدة أجهزة</translation>
-<translation id="4599134080475764833">‏الجهاز الافتراضي (VM) للمكوّن الإضافي جاهز للاستخدام</translation>
+<translation id="4599134080475764833">‏Plugin VM جاهز للاستخدام</translation>
 <translation id="4602466770786743961">إتاحة دخول <ph name="HOST" /> دومًا إلى الكاميرا والميكروفون التابعين لك</translation>
 <translation id="4608500690299898628">&amp;بحث...</translation>
 <translation id="4608520674724523647">صورة توضيحية للتسجيل بنجاح</translation>
@@ -2918,7 +2917,7 @@
 <translation id="5457113250005438886">غير صالحة</translation>
 <translation id="5457459357461771897">التعرّف على الصور، والموسيقى، والوسائط الأخرى من الكمبيوتر وحذفها</translation>
 <translation id="5457599981699367932">تصفَّح كزائر</translation>
-<translation id="5457991019809708398">تفعيل، بلا تجوال</translation>
+<translation id="5457991019809708398">مفعَّل، بلا تجوال</translation>
 <translation id="5458998536542739734">ملاحظات شاشة التأمين</translation>
 <translation id="5463275305984126951">فهرس المجلد <ph name="LOCATION" /></translation>
 <translation id="5463856536939868464">قائمة تحتوي على إشارات مخفية</translation>
@@ -3294,7 +3293,6 @@
 <translation id="6026047032548434446">هل تريد تثبيت التطبيق؟</translation>
 <translation id="6026819612896463875">‏<ph name="WINDOW_TITLE" /> - تم توصيل جهاز USB</translation>
 <translation id="6029027682598229313">‏تم تثبيت نظام التشغيل Linux بنجاح.</translation>
-<translation id="6029292188939175871">الزر حذف [<ph name="FINGERPRINT_NAME" />]</translation>
 <translation id="6029587122245504742">الأبطأ</translation>
 <translation id="6032912588568283682">نظام الملفات</translation>
 <translation id="6038929619733116134">الحظر في حال كان موقع الويب يعرض إعلانات مضلِّلة أو غير مرغوب فيها</translation>
@@ -3493,7 +3491,7 @@
 <translation id="6317318380444133405">الطابعة ليست مدعومة بعد الآن.</translation>
 <translation id="6317369057005134371">جارٍ انتظار نافذة التطبيق...</translation>
 <translation id="6318407754858604988">بدأ التنزيل</translation>
-<translation id="6318944945640833942">تعذّر العثور على طابعة. يرجى إدخال عنوان الطابعة مرة أخرى.</translation>
+<translation id="6318944945640833942">تعذّر العثور على طابعة. يُرجى إدخال عنوان الطابعة مرة أخرى.</translation>
 <translation id="6322653941595359182">‏إرسال رسائل نصيّة واستلامها من جهاز Chromebook</translation>
 <translation id="6324916366299863871">تعديل الاختصار</translation>
 <translation id="6325191661371220117">إيقاف التشغيل التلقائي</translation>
@@ -3750,7 +3748,7 @@
 <translation id="6725073593266469338">خدمة واجهة المستخدم</translation>
 <translation id="6725206449694821596">‏بروتوكول الطباعة على الإنترنت (IPP)</translation>
 <translation id="67269783048918309">‏يمكنك إرسال بيانات الاستخدام والتشخيص. يرسل هذا الجهاز حاليًا بيانات استخدام التطبيق والجهاز والتشخيص تلقائيًا إلى Google. لن يتم استخدام ذلك لتعريف حسابك الفرعي وسيساعد في استقرار عمل النظام والتطبيقات، بالإضافة إلى التحسينات الأخرى. كما ستساعد بعض البيانات المجمّعة تطبيقات Google وشركائها، مثل مطوّري برامج نظام التشغيل Android. ويفرض المالك هذا <ph name="BEGIN_LINK1" />الإعداد<ph name="END_LINK1" />. في حال تفعيل إعداد "النشاط الإضافي على الويب وفي التطبيقات" لحسابك الفرعي، قد يتم حفظ هذه البيانات في حسابك على Google. <ph name="BEGIN_LINK2" />مزيد من المعلومات<ph name="END_LINK2" /></translation>
-<translation id="6727969043791803658">الجهاز متصل، ومستوى شحن البطارية <ph name="BATTERY_PERCENTAGE" /> %</translation>
+<translation id="6727969043791803658">البلوتوث متصل، ومستوى شحن البطارية: <ph name="BATTERY_PERCENTAGE" /> %</translation>
 <translation id="6732801395666424405">لم يتم تحميل الشهادات</translation>
 <translation id="6732900235521116609">تتعذَّر إزالة الاختصار</translation>
 <translation id="6735304988756581115">عرض ملفات تعريف الارتباط وبيانات الموقع الأخرى...</translation>
@@ -3843,7 +3841,7 @@
 <translation id="6876155724392614295">دراجة</translation>
 <translation id="6877460900831874810">‏تفعيل تشغيل وسائط Chrome في شاشة القفل</translation>
 <translation id="6878422606530379992">استخدام أجهزة الاستشعار مسموح به</translation>
-<translation id="6878665006737889642">يُرجى إعادة محاولة التثبيت</translation>
+<translation id="6878665006737889642">يُرجى إعادة محاولة التثبيت.</translation>
 <translation id="6880587130513028875">تم منع عرض الصور في هذه الصفحة.</translation>
 <translation id="6883319974225028188">عفوًا! تعذّر النظام في حفظ تهيئة الجهاز.</translation>
 <translation id="6885771755599377173">معاينة معلومات النظام</translation>
@@ -4596,7 +4594,6 @@
 <translation id="7973962044839454485">‏تعذّرت مصادقة PPP نظرًا لوجود خطأ في اسم المستخدم أو كلمة المرور</translation>
 <translation id="7974566588408714340">إعادة المحاولة باستخدام <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">الخروج عن إطار الشاشة</translation>
-<translation id="79766959863778284">‏خدمة جهاز معزولة بواسطة XR</translation>
 <translation id="7978412674231730200">مفتاح خاص</translation>
 <translation id="7978450511781612192">‏سيؤدي ذلك إلى تسجيل خروجك من حساباتك على Google. ولن تتم بعد ذلك مزامنة الإشارات المرجعية والسجلّ وكلمات المرور والمزيد.</translation>
 <translation id="7979036127916589816">حدث خطأ في المزامنة</translation>
@@ -5184,7 +5181,7 @@
 <translation id="8862003515646449717">التبديل إلى متصفح سريع</translation>
 <translation id="8863753581171631212">فتح الرابط في نافذة جديدة في <ph name="APP" /></translation>
 <translation id="8864055848767439877">جارٍ مشاركة <ph name="TAB_NAME" /> مع <ph name="APP_NAME" />.</translation>
-<translation id="8864458770072227512">تمت إزالة <ph name="EMAIL" /> من هذا الجهاز</translation>
+<translation id="8864458770072227512">تمت إزالة <ph name="EMAIL" /> من هذا الجهاز.</translation>
 <translation id="8868626022555786497">قيد الاستخدام</translation>
 <translation id="8870318296973696995">الصفحة الرئيسية</translation>
 <translation id="8870413625673593573">العناصر المغلقة مؤخرًا</translation>
@@ -5307,7 +5304,7 @@
 <translation id="9044646465488564462">تعذّر الاتصال بالشبكة: <ph name="DETAILS" /></translation>
 <translation id="9045430190527754450">‏يتم إرسال عنوان ويب الصفحة التي تحاول الوصول إليها إلى Google.</translation>
 <translation id="9046895021617826162">تعذّر الاتصال</translation>
-<translation id="9047391224416514812">يتوفر حساب بنفس اسم المستخدم هذا من قبل</translation>
+<translation id="9047391224416514812">يتوفّر حساب باسم المستخدم نفسه حاليًا.</translation>
 <translation id="9050666287014529139">عبارة المرور</translation>
 <translation id="9052208328806230490">لقد سجّلت طابعاتك في <ph name="CLOUD_PRINT_NAME" /> باستخدام الحساب <ph name="EMAIL" /></translation>
 <translation id="9052404922357793350">متابعة الحظر</translation>
@@ -5365,7 +5362,6 @@
 <translation id="9138978632494473300">إضافة اختصارات إلى الأماكن التالية:</translation>
 <translation id="9140067245205650184">أنت تستخدم علامة ميزة غير مدعومة: <ph name="BAD_FLAG" />. سيؤثر ذلك سلبًا على وظائف الميزة وأمانها.</translation>
 <translation id="9143298529634201539">هل تريد إزالة الاقتراح؟</translation>
-<translation id="9147304170847707004">طريقة المصادقة</translation>
 <translation id="9147392381910171771">&amp;الخيارات</translation>
 <translation id="9148058034647219655">إنهاء</translation>
 <translation id="9148126808321036104">تسجيل الدخول مرة أخرى</translation>
@@ -5424,7 +5420,7 @@
 <translation id="939598580284253335">إدخال عبارة المرور</translation>
 <translation id="939736085109172342">مجلد جديد</translation>
 <translation id="942532530371314860">‏يشارك <ph name="APP_NAME" /> علامة تبويب متصفح Chrome والصوت.</translation>
-<translation id="945166830402967374">التواصل مع مشرف جهاز مؤسستك</translation>
+<translation id="945166830402967374">تواصل مع مشرف أجهزة مؤسستك.</translation>
 <translation id="945522503751344254">إرسال تعليقات</translation>
 <translation id="947329552760389097">وعناصر الفحص</translation>
 <translation id="952992212772159698">غير نشطة</translation>
diff --git a/chrome/app/resources/generated_resources_bg.xtb b/chrome/app/resources/generated_resources_bg.xtb
index 3e1b495..4a3aa098 100644
--- a/chrome/app/resources/generated_resources_bg.xtb
+++ b/chrome/app/resources/generated_resources_bg.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">Оптимизиране</translation>
 <translation id="236141728043665931">Достъпът до микрофона да се блокира винаги</translation>
 <translation id="2365507699358342471">Този сайт може да преглежда текста и изображенията, копирани в буферната памет.</translation>
-<translation id="2367199180085172140">Добавяне на споделяне на файл</translation>
 <translation id="2367972762794486313">Показване на приложенията</translation>
 <translation id="2369105924912929484">Превключване на функцията за извеждане на основното съдържание на страниците</translation>
 <translation id="2371076942591664043">Отваряне &amp;след изтегляне</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Да се инсталира ли приложението?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – установена е връзка с USB устройство</translation>
 <translation id="6029027682598229313">Инсталирането на Linux завърши.</translation>
-<translation id="6029292188939175871">изтриване на [<ph name="FINGERPRINT_NAME" />], бутон</translation>
 <translation id="6029587122245504742">Най-бавна</translation>
 <translation id="6032912588568283682">Файлова система</translation>
 <translation id="6038929619733116134">Блокиране, ако на сайта се показват натрапчиви или подвеждащи реклами</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">Удостоверяването през PPP не бе успешно поради неправилно потребителско име или парола</translation>
 <translation id="7974566588408714340">Нов опит посредством <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Допълнителна област</translation>
-<translation id="79766959863778284">Услуга за изолирани устройства за XR</translation>
 <translation id="7978412674231730200">Личен ключ</translation>
 <translation id="7978450511781612192">Така ще излезете от профилите си в Google. Вашите отметки, история, пароли и др. повече няма да се синхронизират.</translation>
 <translation id="7979036127916589816">Грешка при синхронизирането</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">Добавяне на преки пътища към следните места:</translation>
 <translation id="9140067245205650184">Използвате неподдържан флаг за функция: <ph name="BAD_FLAG" />. Стабилността и сигурността ще пострадат.</translation>
 <translation id="9143298529634201539">Да се премахне ли предложението?</translation>
-<translation id="9147304170847707004">Метод за удостоверяване</translation>
 <translation id="9147392381910171771">&amp;Опции</translation>
 <translation id="9148058034647219655">Изход</translation>
 <translation id="9148126808321036104">Влезте отново</translation>
diff --git a/chrome/app/resources/generated_resources_bn.xtb b/chrome/app/resources/generated_resources_bn.xtb
index bed6645..7bc5adc 100644
--- a/chrome/app/resources/generated_resources_bn.xtb
+++ b/chrome/app/resources/generated_resources_bn.xtb
@@ -914,7 +914,6 @@
 <translation id="236117173274098341">অপ্টিমাইজ করুন</translation>
 <translation id="236141728043665931">মাইক্রোফোনের অ্যাক্সেস সর্বদা অবরুদ্ধ রাখুন</translation>
 <translation id="2365507699358342471">এই সাইটটি ক্লিপবোর্ডে কপি করা টেক্সট এবং ছবি দেখতে পায়।</translation>
-<translation id="2367199180085172140">ফাইল শেয়ার যোগ করুন</translation>
 <translation id="2367972762794486313">অ্যাপ্লিকেশানগুলি দেখান</translation>
 <translation id="2369105924912929484">ডিস্টিল পৃষ্ঠার কন্টেন্ট টগল করুন</translation>
 <translation id="2371076942591664043">&amp;সম্পন্ন হলে খুলুন</translation>
@@ -949,7 +948,7 @@
 <translation id="2433452467737464329">এই পৃষ্ঠাটিকে অটো-রিফ্রেশ করতে URL-এ একটি কোয়েরি param যোগ করুন: chrome://network/?refresh=&lt;sec&gt;</translation>
 <translation id="2433507940547922241">উপস্থিতি</translation>
 <translation id="2433836460518180625">শুধুমাত্র ডিভাইস আনলক করুন</translation>
-<translation id="2434449159125086437">প্রিন্টার সেট-আপ করা যাচ্ছে না। কনফিগারেশন পরীক্ষা করে আবার চেষ্টা করুন।</translation>
+<translation id="2434449159125086437">প্রিন্টার সেট-আপ করা যাচ্ছে না। কনফিগারেশন চেক করে আবার চেষ্টা করুন।</translation>
 <translation id="2435248616906486374">নেটওয়ার্ক সংযোগ বিচ্ছিন্ন হয়েছে</translation>
 <translation id="2435457462613246316">পাসওয়ার্ড দেখান</translation>
 <translation id="2436186046335138073">সমস্ত <ph name="PROTOCOL" /> লিঙ্কগুলিকে খুলতে <ph name="HANDLER_HOSTNAME" />কে সক্ষম করবেন?</translation>
@@ -2411,7 +2410,7 @@
 <translation id="4662788913887017617">এই বুকমার্কটি আপনার iPhone এর সাথে শেয়ার করুন</translation>
 <translation id="4663373278480897665">ক্যামেরা ব্যবহারের অনুমতি দেওয়া হয়েছে</translation>
 <translation id="4664482161435122549">PKCS #12 রপ্তানি  ত্রুটি</translation>
-<translation id="4664736447097490764">Kerberos কনফিগারেশনের ফাইল এখান থেকে এডিট করুন।</translation>
+<translation id="4664736447097490764">Kerberos কনফিগারেশন ফাইল এখান থেকে এডিট করুন।</translation>
 <translation id="4665014895760275686">প্রস্তুতকর্তা</translation>
 <translation id="4665446389743427678"><ph name="SITE" /> সাইটে স্টোর করা সমস্ত ডেটা মুছে ফেলা হবে।</translation>
 <translation id="4668721319092543482"><ph name="PLUGIN_NAME" /> সক্ষম করার জন্য ক্লিক করুন</translation>
@@ -3295,7 +3294,6 @@
 <translation id="6026047032548434446">অ্যাপ ইনস্টল করবেন?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB ডিভাইস সংযুক্ত হয়েছে</translation>
 <translation id="6029027682598229313">Linux ইনস্টল সম্পূর্ণ হয়েছে।</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] মুছে ফেলুন, বোতাম</translation>
 <translation id="6029587122245504742">সবচেয়ে ধীরে</translation>
 <translation id="6032912588568283682">ফাইল সিস্টেম</translation>
 <translation id="6038929619733116134">সাইটে থাকা ব্যাঘাত সৃষ্টিকারী বা বিভ্রান্তিকর বিজ্ঞাপন ব্লক করুন</translation>
@@ -3494,7 +3492,7 @@
 <translation id="6317318380444133405">আর সমর্থিত নয়৷</translation>
 <translation id="6317369057005134371">অ্যাপ্লিকেশন উইন্ডোর জন্য অপেক্ষা করা হচ্ছে...</translation>
 <translation id="6318407754858604988">ডাউনলোড শুরু হয়েছে</translation>
-<translation id="6318944945640833942">কোনও প্রিন্টার শনাক্ত করা যায়নি। প্রিন্টারের ঠিকানা আবার লিখুন।</translation>
+<translation id="6318944945640833942">কোনও প্রিন্টার শনাক্ত করা যায়নি। প্রিন্টার অ্যাড্রেস আবার লিখুন।</translation>
 <translation id="6322653941595359182">Chromebook থেকে টেক্সট মেসেজ পাঠান ও পান</translation>
 <translation id="6324916366299863871">শর্টকার্ট এডিট করুন</translation>
 <translation id="6325191661371220117">স্বয়ংক্রিয় লঞ্চ অক্ষম করুন</translation>
@@ -4596,7 +4594,6 @@
 <translation id="7973962044839454485">একটি ভুল ইউজারনেম বা পাসওয়ার্ডের কারণে PPP যাচাই করা যায়নি৷</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" /> ব্যবহার করার জন্য আবার চেষ্টা করুন</translation>
 <translation id="7974936243149753750">ওভারস্ক্যান</translation>
-<translation id="79766959863778284">XR আইসোলেটেড ডিভাইস পরিষেবা</translation>
 <translation id="7978412674231730200">ব্যক্তিগত কী</translation>
 <translation id="7978450511781612192">এটি আপনাকে আপনার Google অ্যাকাউন্ট থেকে সাইন-আউট করিয়ে দেবে। আপনার বুকমার্ক, ইতিহাস, পাসওয়ার্ড এবং আরও অনেক কিছু আর সিঙ্ক করা হবে না।</translation>
 <translation id="7979036127916589816">সিঙ্ক ত্রুটি</translation>
@@ -5365,7 +5362,6 @@
 <translation id="9138978632494473300">নিম্নল্লিখিত স্থানগুলিতে শর্টকাটগুলি যোগ করুন:</translation>
 <translation id="9140067245205650184">আপনি যে ফিচার ফ্ল্যাগটি ব্যবহার করছেন সেটি সমর্থিত নয়: <ph name="BAD_FLAG" />। স্থিতিশীলতা এবং নিরাপত্তা ব্যাহত হতে পারে।</translation>
 <translation id="9143298529634201539">সাজেশন সরিয়ে দিতে চান?</translation>
-<translation id="9147304170847707004">যাচাইকরণের পদ্ধতি</translation>
 <translation id="9147392381910171771">&amp;বিকল্প</translation>
 <translation id="9148058034647219655">প্রস্থান করুন</translation>
 <translation id="9148126808321036104">আবার সাইন-ইন করুন</translation>
diff --git a/chrome/app/resources/generated_resources_ca.xtb b/chrome/app/resources/generated_resources_ca.xtb
index 3cdaa61..48ccdfd97 100644
--- a/chrome/app/resources/generated_resources_ca.xtb
+++ b/chrome/app/resources/generated_resources_ca.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimitza</translation>
 <translation id="236141728043665931">Bloqueja sempre l'accés al micròfon</translation>
 <translation id="2365507699358342471">Aquest lloc web pot veure el text i les imatges copiats al porta-retalls.</translation>
-<translation id="2367199180085172140">Afegeix un nou ús compartit de fitxers</translation>
 <translation id="2367972762794486313">Mostra les aplicacions</translation>
 <translation id="2369105924912929484">Commuta el contingut sintetitzat de la pàgina</translation>
 <translation id="2371076942591664043">Obre quan &amp;acabi</translation>
@@ -3296,7 +3295,6 @@
 <translation id="6026047032548434446">Vols instal·lar l'aplicació?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" />: hi ha un dispositiu USB connectat</translation>
 <translation id="6029027682598229313">La instal·lació de Linux ha finalitzat.</translation>
-<translation id="6029292188939175871">suprimeix [<ph name="FINGERPRINT_NAME" />], botó</translation>
 <translation id="6029587122245504742">Mínima</translation>
 <translation id="6032912588568283682">Sistema de fitxers</translation>
 <translation id="6038929619733116134">Bloqueja els anuncis si el lloc web mostra publicitat intrusiva o enganyosa</translation>
@@ -4595,7 +4593,6 @@
 <translation id="7973962044839454485">L'autenticació de PPA ha fallat perquè el nom d'usuari o la contrasenya no eren correctes</translation>
 <translation id="7974566588408714340">Torna-ho a provar amb <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Ajusta la mida</translation>
-<translation id="79766959863778284">Servei de dispositius aïllats XR</translation>
 <translation id="7978412674231730200">Clau privada</translation>
 <translation id="7978450511781612192">Aquesta acció tancarà la sessió dels Comptes de Google. Les adreces d'interès, l'historial, les contrasenyes i altres opcions es deixaran de sincronitzar.</translation>
 <translation id="7979036127916589816">Error de sincronització</translation>
@@ -5363,7 +5360,6 @@
 <translation id="9138978632494473300">Afegeix dreceres als llocs següents:</translation>
 <translation id="9140067245205650184">Utilitzes una marca de funció que no és compatible: <ph name="BAD_FLAG" />. Se'n ressentiran l'estabilitat i la seguretat.</translation>
 <translation id="9143298529634201539">Vols suprimir el suggeriment?</translation>
-<translation id="9147304170847707004">Mètode d'autenticació</translation>
 <translation id="9147392381910171771">&amp;Opcions</translation>
 <translation id="9148058034647219655">Surt</translation>
 <translation id="9148126808321036104">Torna a iniciar la sessió</translation>
diff --git a/chrome/app/resources/generated_resources_cs.xtb b/chrome/app/resources/generated_resources_cs.xtb
index b394341..412a3c9 100644
--- a/chrome/app/resources/generated_resources_cs.xtb
+++ b/chrome/app/resources/generated_resources_cs.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimalizovat</translation>
 <translation id="236141728043665931">Vždy blokovat přístup k mikrofonu</translation>
 <translation id="2365507699358342471">Tato stránka má přístup k textu a obrázkům zkopírovaným do schránky.</translation>
-<translation id="2367199180085172140">Přidat sdílenou složku</translation>
 <translation id="2367972762794486313">Zobrazit aplikace</translation>
 <translation id="2369105924912929484">Přepnout zhuštěný obsah stránky</translation>
 <translation id="2371076942591664043">Po dokončení otevřít</translation>
@@ -3295,7 +3294,6 @@
 <translation id="6026047032548434446">Nainstalovat aplikaci?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – připojeno k zařízení USB</translation>
 <translation id="6029027682598229313">Instalace systému Linux je dokončena.</translation>
-<translation id="6029292188939175871">smazat [<ph name="FINGERPRINT_NAME" />], tlačítko</translation>
 <translation id="6029587122245504742">Nejpomalejší</translation>
 <translation id="6032912588568283682">Systém souborů</translation>
 <translation id="6038929619733116134">Blokovat, pokud web zobrazuje rušivé nebo zavádějící reklamy</translation>
@@ -4594,7 +4592,6 @@
 <translation id="7973962044839454485">Ověření PPP se nezdařilo kvůli nesprávnému uživatelskému jménu nebo heslu</translation>
 <translation id="7974566588408714340">Zkusit znovu pomocí rozšíření <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Přesah obrazu</translation>
-<translation id="79766959863778284">Služba izolovaného zařízení XR</translation>
 <translation id="7978412674231730200">Soukromý klíč</translation>
 <translation id="7978450511781612192">Tímto se odhlásíte ze svých účtů Google. Vaše záložky, historie, hesla a další údaje již nebudou synchronizovány.</translation>
 <translation id="7979036127916589816">Chyba synchronizace</translation>
@@ -5362,7 +5359,6 @@
 <translation id="9138978632494473300">Přidat zkratky na následující místa:</translation>
 <translation id="9140067245205650184">Používáte nepodporovaný příznak funkce: <ph name="BAD_FLAG" />. Bude to mít negativní vliv na stabilitu a zabezpečení.</translation>
 <translation id="9143298529634201539">Odstranit návrh?</translation>
-<translation id="9147304170847707004">Metoda ověření</translation>
 <translation id="9147392381910171771">&amp;Možnosti</translation>
 <translation id="9148058034647219655">Ukončit</translation>
 <translation id="9148126808321036104">Znovu přihlásit</translation>
diff --git a/chrome/app/resources/generated_resources_da.xtb b/chrome/app/resources/generated_resources_da.xtb
index 3296cfe..b971cc53 100644
--- a/chrome/app/resources/generated_resources_da.xtb
+++ b/chrome/app/resources/generated_resources_da.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">Optimer</translation>
 <translation id="236141728043665931">Bloker altid mikrofonadgang</translation>
 <translation id="2365507699358342471">Dette website kan se tekst og billeder, der er kopieret til udklipsholderen.</translation>
-<translation id="2367199180085172140">Tilføj fildeling</translation>
 <translation id="2367972762794486313">Vis apps</translation>
 <translation id="2369105924912929484">Slå destilleret sideindhold til eller fra</translation>
 <translation id="2371076942591664043">Åbn når &amp;færdigt</translation>
@@ -3297,7 +3296,6 @@
 <translation id="6026047032548434446">Vil du installere appen?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – en USB-enhed er forbundet</translation>
 <translation id="6029027682598229313">Installationen af Linux er fuldført.</translation>
-<translation id="6029292188939175871">slet [<ph name="FINGERPRINT_NAME" />], knap</translation>
 <translation id="6029587122245504742">Langsomst</translation>
 <translation id="6032912588568283682">Filsystem</translation>
 <translation id="6038929619733116134">Bloker, hvis websitet viser påtrængende eller vildledende annoncer</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">PPP-godkendelsen mislykkedes på grund af forkert brugernavn eller adgangskode</translation>
 <translation id="7974566588408714340">Prøv igen ved hjælp af <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Overscan</translation>
-<translation id="79766959863778284">XR Isolated Device Service</translation>
 <translation id="7978412674231730200">Privat nøgle</translation>
 <translation id="7978450511781612192">Dermed logges du ud af dine Google-konti. Dine bogmærker, din historik, dine adgangskoder m.m. synkroniseres ikke længere.</translation>
 <translation id="7979036127916589816">Synkroniseringsfejl</translation>
@@ -5368,7 +5365,6 @@
 <translation id="9138978632494473300">Føj genveje til følgende steder:</translation>
 <translation id="9140067245205650184">Du benytter en ikke-understøttet funktionsmarkering: <ph name="BAD_FLAG" />. Det går ud over stabiliteten og sikkerheden.</translation>
 <translation id="9143298529634201539">Vil du fjerne forslaget?</translation>
-<translation id="9147304170847707004">Godkendelsesmetode</translation>
 <translation id="9147392381910171771">&amp;Valgmuligheder</translation>
 <translation id="9148058034647219655">Afslut</translation>
 <translation id="9148126808321036104">Log ind igen</translation>
diff --git a/chrome/app/resources/generated_resources_de.xtb b/chrome/app/resources/generated_resources_de.xtb
index 9e87063..3c4ff39 100644
--- a/chrome/app/resources/generated_resources_de.xtb
+++ b/chrome/app/resources/generated_resources_de.xtb
@@ -606,7 +606,7 @@
 <translation id="1895252664692693738">Noch <ph name="TIME_LEFT" /></translation>
 <translation id="1895658205118569222">Herunterfahren</translation>
 <translation id="1895934970388272448">Bestätigen Sie nun zum Abschluss des Vorgangs die Registrierung auf Ihrem Drucker.</translation>
-<translation id="1899826437968063457">Die Plug-in-VM benötigt die Berechtigung zur Ausführung</translation>
+<translation id="1899826437968063457">Plugin VM benötigt die Berechtigung zur Ausführung</translation>
 <translation id="1901303067676059328">&amp;Alles auswählen</translation>
 <translation id="1902576642799138955">Gültigkeitsdauer</translation>
 <translation id="1904394285866191268">{NUM_TABS,plural, =1{Tab stummschalten}other{Tabs stummschalten}}</translation>
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">Optimieren</translation>
 <translation id="236141728043665931">Zugriff auf das Mikrofon immer blockieren</translation>
 <translation id="2365507699358342471">Diese Website kann Texte und Bilder aus der Zwischenablage abrufen.</translation>
-<translation id="2367199180085172140">Netzwerkfreigabe hinzufügen</translation>
 <translation id="2367972762794486313">Apps anzeigen</translation>
 <translation id="2369105924912929484">Zusammengefasste Seiteninhalte ein- oder ausblenden</translation>
 <translation id="2371076942591664043">Nach &amp;Download öffnen</translation>
@@ -1186,7 +1185,7 @@
 <translation id="2771268254788431918">Mobile Daten aktiviert</translation>
 <translation id="2771816809568414714">Käse</translation>
 <translation id="2772936498786524345">Ninja</translation>
-<translation id="2773288106548584039">Unterstützung für ältere Browser</translation>
+<translation id="2773288106548584039">Unterstützung älterer Browser</translation>
 <translation id="2773802008104670137">Diese Art von Datei kann Schaden auf Ihrem Computer anrichten.</translation>
 <translation id="2775104091073479743">Fingerabdrücke bearbeiten</translation>
 <translation id="2776441542064982094">Im Netzwerk wurden keine Geräte zum Registrieren gefunden. Wenn Ihr Gerät eingeschaltet und mit dem Internet verbunden ist, registrieren Sie es entsprechend der Anleitung des zugehörigen Gerätehandbuchs.</translation>
@@ -1308,7 +1307,7 @@
 <translation id="29488703364906173">Der schnelle, einfache und sichere Browser, entwickelt für das moderne Web</translation>
 <translation id="2949289451367477459">Standort verwenden: Apps und Dienste mit Berechtigung zur Standortermittlung dürfen den Standort dieses Geräts nutzen. Google kann in regelmäßigen Abständen anonymisierte Standortdaten erfassen und diese zur Verbesserung der Standortgenauigkeit und standortbezogener Dienste verwenden. <ph name="BEGIN_LINK1" />Weitere Informationen<ph name="END_LINK1" /></translation>
 <translation id="2958721676848865875">Erweiterung packen: Warnung</translation>
-<translation id="2959842337402130152">Die Wiederherstellung ist nicht möglich, da nicht genügend Speicherplatz verfügbar ist. Bitte geben Sie <ph name="SPACE_REQUIRED" /> auf dem Gerät frei und versuche es dann noch einmal.</translation>
+<translation id="2959842337402130152">Die Wiederherstellung ist nicht möglich, da nicht genügend Speicherplatz verfügbar ist. Bitte geben Sie <ph name="SPACE_REQUIRED" /> auf dem Gerät frei und versuchen Sie es dann noch einmal.</translation>
 <translation id="296026337010986570">Fertig! Die schädliche Software wurde entfernt. Sie können Ihre Erweiterungen jetzt wieder aktivieren. Rufen Sie dazu einfach &lt;a href="chrome://extensions"&gt;Erweiterungen&lt;/a&gt; auf.</translation>
 <translation id="2961090598421146107"><ph name="CERTIFICATE_NAME" /> (Verlängerung gewährt)</translation>
 <translation id="2961695502793809356">Klicken, um weiterzugehen. Gedrückt halten, um den Verlauf aufzurufen</translation>
@@ -1992,7 +1991,7 @@
 <translation id="3966072572894326936">Anderen Ordner wählen...</translation>
 <translation id="3967822245660637423">Download abgeschlossen</translation>
 <translation id="3967919079500697218">Die Funktion zum Erfassen von Screenshots wurde vom Administrator deaktiviert.</translation>
-<translation id="3969092967100188979">An, Roaming</translation>
+<translation id="3969092967100188979">An, Roaming aktiv</translation>
 <translation id="3970114302595058915">ID</translation>
 <translation id="397105322502079400">Wird berechnet...</translation>
 <translation id="3975565978598857337">Fehler bei Verbindungsaufbau mit Server für diesen Bereich</translation>
@@ -2242,7 +2241,7 @@
 <translation id="4378551569595875038">Verbindung wird hergestellt...</translation>
 <translation id="4378556263712303865">Geräteanforderung</translation>
 <translation id="4380648069038809855">Vollbildmodus aktiviert</translation>
-<translation id="4382131447572146376">{COUNT,plural, =0{<ph name="EMAIL" />}=1{<ph name="EMAIL" /> +1 weiteres Konto}other{<ph name="EMAIL" />, +<ph name="EXTRA_ACCOUNTS" /> weitere Konten}}</translation>
+<translation id="4382131447572146376">{COUNT,plural, =0{<ph name="EMAIL" />}=1{<ph name="EMAIL" /> + 1 weiteres Konto}other{<ph name="EMAIL" /> + <ph name="EXTRA_ACCOUNTS" /> weitere Konten}}</translation>
 <translation id="4384312707950789900">Zu bevorzugten Netzwerken hinzufügen</translation>
 <translation id="4384652540891215547">Erweiterung aktivieren</translation>
 <translation id="438503109373656455">Brauner</translation>
@@ -2373,7 +2372,7 @@
 <translation id="4596295440756783523">In Ihren gespeicherten Zertifikaten sind folgende Server angegeben</translation>
 <translation id="4598556348158889687">Speicherplatzverwaltung</translation>
 <translation id="4598776695426288251">WLAN über mehrere Geräte verfügbar</translation>
-<translation id="4599134080475764833">Die Plug-in-VM kann jetzt verwendet werden</translation>
+<translation id="4599134080475764833">Plugin VM kann jetzt verwendet werden</translation>
 <translation id="4602466770786743961"><ph name="HOST" /> immer Zugriff auf Kamera und Mikrofon gestatten</translation>
 <translation id="4608500690299898628">&amp;Suchen...</translation>
 <translation id="4608520674724523647">Illustration zur Anmeldung</translation>
@@ -3294,7 +3293,6 @@
 <translation id="6026047032548434446">App installieren?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – USB-Gerät verbunden</translation>
 <translation id="6029027682598229313">Die Linux-Installation ist abgeschlossen.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] löschen, Schaltfläche</translation>
 <translation id="6029587122245504742">Langsamste</translation>
 <translation id="6032912588568283682">Dateisystem</translation>
 <translation id="6038929619733116134">Blockieren, wenn Website aufdringliche oder irreführende Werbung anzeigt</translation>
@@ -3492,7 +3490,7 @@
 <translation id="6317318380444133405">Wird nicht mehr unterstützt.</translation>
 <translation id="6317369057005134371">Warten auf Anwendungsfenster...</translation>
 <translation id="6318407754858604988">Download gestartet</translation>
-<translation id="6318944945640833942">Es wurde kein Drucker erkannt. Geben Sie die Druckeradresse noch einmal ein.</translation>
+<translation id="6318944945640833942">Es wurde kein Drucker erkannt. Bitte geben Sie die Druckeradresse noch einmal ein.</translation>
 <translation id="6322653941595359182">Textnachrichten auf Ihrem Chromebook senden und empfangen</translation>
 <translation id="6324916366299863871">Verknüpfung bearbeiten</translation>
 <translation id="6325191661371220117">Automatisches Starten deaktivieren</translation>
@@ -4595,7 +4593,6 @@
 <translation id="7973962044839454485">Fehler bei PPP-Authentifizierung aufgrund eines falschen Nutzernamens oder Passworts</translation>
 <translation id="7974566588408714340">Über <ph name="EXTENSIONNAME" /> erneut versuchen</translation>
 <translation id="7974936243149753750">Overscan</translation>
-<translation id="79766959863778284">Isolierter Gerätedienst für XR</translation>
 <translation id="7978412674231730200">Privater Schlüssel</translation>
 <translation id="7978450511781612192">Dadurch werden Sie aus Ihren Google-Konten abgemeldet. Ihre Lesezeichen, Ihr Verlauf, Ihre Passwörter usw. werden nicht mehr synchronisiert.</translation>
 <translation id="7979036127916589816">Synchronisierungsfehler</translation>
@@ -5365,7 +5362,6 @@
 <translation id="9138978632494473300">Fügen Sie zu den folgenden Orten Verknüpfungen hinzu:</translation>
 <translation id="9140067245205650184">Sie verwenden eine nicht unterstützte Funktionsmarkierung: <ph name="BAD_FLAG" />. Dadurch werden Stabilität und Sicherheit beeinträchtigt.</translation>
 <translation id="9143298529634201539">Vorschlag entfernen?</translation>
-<translation id="9147304170847707004">Authentifizierungsmethode</translation>
 <translation id="9147392381910171771">&amp;Optionen</translation>
 <translation id="9148058034647219655">Beenden</translation>
 <translation id="9148126808321036104">Erneut anmelden</translation>
diff --git a/chrome/app/resources/generated_resources_el.xtb b/chrome/app/resources/generated_resources_el.xtb
index a359106..95ca1f4 100644
--- a/chrome/app/resources/generated_resources_el.xtb
+++ b/chrome/app/resources/generated_resources_el.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Βελτιστοποίηση</translation>
 <translation id="236141728043665931">Να μην επιτρέπται ποτέ η πρόσβαση στο μικρόφωνο</translation>
 <translation id="2365507699358342471">Αυτός ο ιστότοπος μπορεί να δει το κείμενο και τις εικόνες που έχουν αντιγραφεί στο πρόχειρο.</translation>
-<translation id="2367199180085172140">Προσθήκη κοινόχρηστου αρχείου</translation>
 <translation id="2367972762794486313">Εμφάνιση εφαρμογών</translation>
 <translation id="2369105924912929484">Εναλλαγή φιλτραρισμένου περιεχομένου σελίδας</translation>
 <translation id="2371076942591664043">Άνοιγμα κατά την &amp;ολοκλήρωση</translation>
@@ -3300,7 +3299,6 @@
 <translation id="6026047032548434446">Εγκατάσταση εφαρμογής;</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - Συνδέθηκε συσκευή USB</translation>
 <translation id="6029027682598229313">Η εγκατάσταση του Linux ολοκληρώθηκε.</translation>
-<translation id="6029292188939175871">διαγραφή [<ph name="FINGERPRINT_NAME" />], κουμπί</translation>
 <translation id="6029587122245504742">Πιο αργή</translation>
 <translation id="6032912588568283682">Σύστημα αρχείων</translation>
 <translation id="6038929619733116134">Αποκλεισμός εάν ο ιστότοπος εμφανίζει παρεμβατικές ή παραπλανητικές διαφημίσεις</translation>
@@ -4601,7 +4599,6 @@
 <translation id="7973962044839454485">Αποτυχία ελέγχου ταυτότητας PPP λόγω εσφαλμένου ονόματος χρήστη ή κωδικού πρόσβασης</translation>
 <translation id="7974566588408714340">Επανάληψη προσπάθειας με χρήση <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Υπερσάρωση</translation>
-<translation id="79766959863778284">Υπηρεσία απομονωμένης συσκευής XR</translation>
 <translation id="7978412674231730200">Ιδιωτικό κλειδί</translation>
 <translation id="7978450511781612192">Με αυτήν την ενέργεια, θα αποσυνδεθείτε από τους Λογαριασμούς σας Google. Οι σελιδοδείκτες, το ιστορικό, οι κωδικοί πρόσβασης και άλλες ρυθμίσεις δεν θα συγχρονίζονται πλέον.</translation>
 <translation id="7979036127916589816">Σφάλμα συγχρονισμού</translation>
@@ -5368,7 +5365,6 @@
 <translation id="9138978632494473300">Προσθήκη συντομεύσεων στα παρακάτω σημεία:</translation>
 <translation id="9140067245205650184">Χρησιμοποιείτε μια επισήμανση λειτουργίας που δεν υποστηρίζεται: <ph name="BAD_FLAG" />. Θα παρουσιαστούν προβλήματα σταθερότητας και ασφάλειας.</translation>
 <translation id="9143298529634201539">Να καταργηθεί η πρόταση;</translation>
-<translation id="9147304170847707004">Μέθοδος ελέγχου ταυτότητας</translation>
 <translation id="9147392381910171771">&amp;Επιλογές</translation>
 <translation id="9148058034647219655">Έξοδος</translation>
 <translation id="9148126808321036104">Συνδεθείτε ξανά</translation>
diff --git a/chrome/app/resources/generated_resources_en-GB.xtb b/chrome/app/resources/generated_resources_en-GB.xtb
index 9e241c3..5d3386b 100644
--- a/chrome/app/resources/generated_resources_en-GB.xtb
+++ b/chrome/app/resources/generated_resources_en-GB.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimise</translation>
 <translation id="236141728043665931">Always block microphone access</translation>
 <translation id="2365507699358342471">This site can see text and images copied to the clipboard.</translation>
-<translation id="2367199180085172140">Add File Share</translation>
 <translation id="2367972762794486313">Show apps</translation>
 <translation id="2369105924912929484">Toggle distilled page contents</translation>
 <translation id="2371076942591664043">Open when &amp;done</translation>
@@ -3298,7 +3297,6 @@
 <translation id="6026047032548434446">Install App?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – USB device connected</translation>
 <translation id="6029027682598229313">Linux installation is complete.</translation>
-<translation id="6029292188939175871">delete [<ph name="FINGERPRINT_NAME" />], button</translation>
 <translation id="6029587122245504742">Slowest</translation>
 <translation id="6032912588568283682">File system</translation>
 <translation id="6038929619733116134">Block if site shows intrusive or misleading ads</translation>
@@ -4599,7 +4597,6 @@
 <translation id="7973962044839454485">PPP authentication failed due to an incorrect username or password</translation>
 <translation id="7974566588408714340">Retry using <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Overscan</translation>
-<translation id="79766959863778284">XR Isolated Device Service</translation>
 <translation id="7978412674231730200">Private key</translation>
 <translation id="7978450511781612192">This will sign you out of your Google accounts. Your bookmarks, history, passwords and more will no longer be synced.</translation>
 <translation id="7979036127916589816">Sync Error</translation>
@@ -5368,7 +5365,6 @@
 <translation id="9138978632494473300">Add shortcuts to the following places:</translation>
 <translation id="9140067245205650184">You are using an unsupported feature flag: <ph name="BAD_FLAG" />. Stability and security will suffer.</translation>
 <translation id="9143298529634201539">Remove suggestion?</translation>
-<translation id="9147304170847707004">Authentication Method</translation>
 <translation id="9147392381910171771">&amp;Options</translation>
 <translation id="9148058034647219655">Exit</translation>
 <translation id="9148126808321036104">Sign in again</translation>
diff --git a/chrome/app/resources/generated_resources_es-419.xtb b/chrome/app/resources/generated_resources_es-419.xtb
index ce3f650..4c275dd 100644
--- a/chrome/app/resources/generated_resources_es-419.xtb
+++ b/chrome/app/resources/generated_resources_es-419.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">Optimizar</translation>
 <translation id="236141728043665931">Bloquear siempre el acceso al micrófono</translation>
 <translation id="2365507699358342471">Este sitio puede ver el texto y las imágenes que se copiaron en el portapapeles.</translation>
-<translation id="2367199180085172140">Agregar archivos compartidos</translation>
 <translation id="2367972762794486313">Mostrar aplicaciones</translation>
 <translation id="2369105924912929484">Activar o desactivar el contenido simplificado de la página</translation>
 <translation id="2371076942591664043">Abrir al &amp;finalizar</translation>
@@ -3296,7 +3295,6 @@
 <translation id="6026047032548434446">¿Deseas instalar la app?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" />: Se conectó el dispositivo USB</translation>
 <translation id="6029027682598229313">Se completó la instalación de Linux.</translation>
-<translation id="6029292188939175871">borrar [<ph name="FINGERPRINT_NAME" />], botón</translation>
 <translation id="6029587122245504742">Más lento</translation>
 <translation id="6032912588568283682">Sistema de archivos</translation>
 <translation id="6038929619733116134">Bloquear si el sitio muestra anuncios intrusivos o engañosos</translation>
@@ -4597,7 +4595,6 @@
 <translation id="7973962044839454485">Falló la autenticación de PPP debido a un nombre de usuario o una contraseña incorrectos.</translation>
 <translation id="7974566588408714340">Volver a intentarlo con <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Desajuste de dimensiones</translation>
-<translation id="79766959863778284">Servicio de dispositivos aislados XR</translation>
 <translation id="7978412674231730200">Clave privada</translation>
 <translation id="7978450511781612192">Con esta acción, saldrás de tus cuentas de Google. Ya no se sincronizarán tus favoritos, historial ni contraseñas, entre otros datos.</translation>
 <translation id="7979036127916589816">Error de sincronización</translation>
@@ -5367,7 +5364,6 @@
 <translation id="9138978632494473300">Agregar accesos directos a los siguientes lugares:</translation>
 <translation id="9140067245205650184">Estás usando una marca de función no compatible: <ph name="BAD_FLAG" />. Esto afectará la estabilidad y la seguridad.</translation>
 <translation id="9143298529634201539">¿Quieres quitar la sugerencia?</translation>
-<translation id="9147304170847707004">Método de autenticación</translation>
 <translation id="9147392381910171771">&amp;Opciones</translation>
 <translation id="9148058034647219655">Salir</translation>
 <translation id="9148126808321036104">Accede nuevamente</translation>
diff --git a/chrome/app/resources/generated_resources_es.xtb b/chrome/app/resources/generated_resources_es.xtb
index 977ae95..99b68d4 100644
--- a/chrome/app/resources/generated_resources_es.xtb
+++ b/chrome/app/resources/generated_resources_es.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimizar</translation>
 <translation id="236141728043665931">Bloquear siempre el acceso al micrófono</translation>
 <translation id="2365507699358342471">Este sitio web puede ver el texto y las imágenes que se hayan copiado en el portapapeles.</translation>
-<translation id="2367199180085172140">Añadir sistema de archivos compartidos</translation>
 <translation id="2367972762794486313">Mostrar aplicaciones</translation>
 <translation id="2369105924912929484">Alternar contenido de la página convertida</translation>
 <translation id="2371076942591664043">Abrir al &amp;finalizar</translation>
@@ -3296,7 +3295,6 @@
 <translation id="6026047032548434446">¿Quieres instalar la aplicación?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" />: dispositivo USB conectado</translation>
 <translation id="6029027682598229313">Se ha completado la instalación de Linux.</translation>
-<translation id="6029292188939175871">eliminar [<ph name="FINGERPRINT_NAME" />], botón</translation>
 <translation id="6029587122245504742">Velocidad mínima</translation>
 <translation id="6032912588568283682">Sistema de archivos</translation>
 <translation id="6038929619733116134">Bloquear si el sitio web muestra anuncios invasivos o engañosos</translation>
@@ -4597,7 +4595,6 @@
 <translation id="7973962044839454485">Se ha producido un error en la autenticación de PPP porque la contraseña o el nombre de usuario son incorrectos</translation>
 <translation id="7974566588408714340">Intentar de nuevo con <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Reajuste</translation>
-<translation id="79766959863778284">Servicio de dispositivo aislado XR</translation>
 <translation id="7978412674231730200">Clave privada</translation>
 <translation id="7978450511781612192">Se cerrará la sesión en tus cuentas de Google. Tus marcadores, historial, contraseñas y otros elementos dejarán de sincronizarse.</translation>
 <translation id="7979036127916589816">Error de sincronización</translation>
@@ -5366,7 +5363,6 @@
 <translation id="9138978632494473300">Añadir accesos directos a las siguientes ubicaciones:</translation>
 <translation id="9140067245205650184">Estás usando una marca de función no admitida (<ph name="BAD_FLAG" />), lo que afectará a la estabilidad y la seguridad.</translation>
 <translation id="9143298529634201539">¿Quieres eliminar la sugerencia?</translation>
-<translation id="9147304170847707004">Método de autenticación</translation>
 <translation id="9147392381910171771">&amp;Opciones</translation>
 <translation id="9148058034647219655">Salir</translation>
 <translation id="9148126808321036104">Volver a iniciar sesión</translation>
diff --git a/chrome/app/resources/generated_resources_et.xtb b/chrome/app/resources/generated_resources_et.xtb
index b6f0622..b9ab9c4 100644
--- a/chrome/app/resources/generated_resources_et.xtb
+++ b/chrome/app/resources/generated_resources_et.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimeeri</translation>
 <translation id="236141728043665931">Blokeeri alati juurdepääs mikrofonile</translation>
 <translation id="2365507699358342471">See sait näeb lõikelauale kopeeritud teksti ja kujutisi.</translation>
-<translation id="2367199180085172140">Lisa failide võrguhoidla</translation>
 <translation id="2367972762794486313">Rakenduste kuvamine</translation>
 <translation id="2369105924912929484">Lülita destilleeritud lehe sisu sisse/välja</translation>
 <translation id="2371076942591664043">Ava, kui on &amp;valmis</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Kas soovite rakenduse installida?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – USB-seade on ühendatud</translation>
 <translation id="6029027682598229313">Linuxi installimine lõpetati.</translation>
-<translation id="6029292188939175871">kustuta [<ph name="FINGERPRINT_NAME" />], nupp</translation>
 <translation id="6029587122245504742">Kõige aeglasem</translation>
 <translation id="6032912588568283682">Failisüsteem</translation>
 <translation id="6038929619733116134">Blokeeri, kui sait kuvab sekkuvaid või eksitavaid reklaame</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">PPP autentimine ebaõnnestus vale kasutajanime või parooli tõttu</translation>
 <translation id="7974566588408714340">Proovi uuesti, kasutades laiendust <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Üleskannimine</translation>
-<translation id="79766959863778284">XR-i isoleeritud seadmeteenus</translation>
 <translation id="7978412674231730200">Privaatvõti</translation>
 <translation id="7978450511781612192">See logib teid teie Google'i kontodelt välja. Teie järjehoidjaid, ajalugu, paroole ja muud ei sünkroonita enam.</translation>
 <translation id="7979036127916589816">Sünkroonimise viga</translation>
@@ -5370,7 +5367,6 @@
 <translation id="9138978632494473300">Lisa otseteed järgmistele kohtadele:</translation>
 <translation id="9140067245205650184">Kasutate toeta funktsioonimärgistust: <ph name="BAD_FLAG" />. See vähendab stabiilsust ja turvalisust.</translation>
 <translation id="9143298529634201539">Kas eemaldada soovitus?</translation>
-<translation id="9147304170847707004">Autentimismeetod</translation>
 <translation id="9147392381910171771">&amp;Valikud</translation>
 <translation id="9148058034647219655">Välju</translation>
 <translation id="9148126808321036104">Logige uuesti sisse</translation>
diff --git a/chrome/app/resources/generated_resources_fa.xtb b/chrome/app/resources/generated_resources_fa.xtb
index e53127a..47d1dda9 100644
--- a/chrome/app/resources/generated_resources_fa.xtb
+++ b/chrome/app/resources/generated_resources_fa.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">بهینه‌سازی</translation>
 <translation id="236141728043665931">دسترسی به میکروفون همیشه مسدود شود</translation>
 <translation id="2365507699358342471">این سایت می‌تواند به نوشتار و تصاویر کپی‌شده در بریده‌دان دسترسی پیدا کند.</translation>
-<translation id="2367199180085172140">افزودن فایل هم‌رسانی</translation>
 <translation id="2367972762794486313">نمایش برنامه‌ها</translation>
 <translation id="2369105924912929484">نمایش دادن/پنهان کردن محتوای صفحه پالایش‌شده</translation>
 <translation id="2371076942591664043">پس از &amp;تکمیل باز شود</translation>
@@ -3295,7 +3294,6 @@
 <translation id="6026047032548434446">برنامه نصب شود؟</translation>
 <translation id="6026819612896463875">‏<ph name="WINDOW_TITLE" /> - دستگاه USB متصل است</translation>
 <translation id="6029027682598229313">‏نصب Linux کامل شد.</translation>
-<translation id="6029292188939175871">حذف [<ph name="FINGERPRINT_NAME" />]، دکمه</translation>
 <translation id="6029587122245504742">آهسته‌ترین</translation>
 <translation id="6032912588568283682">سیستم فایل</translation>
 <translation id="6038929619733116134">اگر سایتْ آگهی‌های مزاحم یا گمراه‌کننده نشان می‌دهد مسدود شود</translation>
@@ -4596,7 +4594,6 @@
 <translation id="7973962044839454485">‏احراز هویت PPP به دلیل نام کاربری یا گذرواژه نادرست انجام نشد</translation>
 <translation id="7974566588408714340">تلاش مجدد با <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">تصویر بزرگ‌تر از صفحه</translation>
-<translation id="79766959863778284">‏سرویس دستگاه جداشده با XR</translation>
 <translation id="7978412674231730200">کلید خصوصی</translation>
 <translation id="7978450511781612192">‏با این کار از سیستم حساب Google خود خارج می‌شوید. نشانک‌ها، سابقه، گذرواژه‌ها و سایر موارد دیگر همگام‌سازی نمی‌شوند.</translation>
 <translation id="7979036127916589816">خطای همگام‌سازی</translation>
@@ -5365,7 +5362,6 @@
 <translation id="9138978632494473300">افزودن میان‌بر به مکان‌های زیر:</translation>
 <translation id="9140067245205650184">از پرچم‌گذاری پیشتیبانی‌نشده ویژگی <ph name="BAD_FLAG" /> استفاده می‌کنید. ثبات و امنیت صدمه می‌بینند.</translation>
 <translation id="9143298529634201539">پیشنهاد حذف شود؟</translation>
-<translation id="9147304170847707004">روش احراز هویت</translation>
 <translation id="9147392381910171771">&amp;گزینه‌ها</translation>
 <translation id="9148058034647219655">خروج</translation>
 <translation id="9148126808321036104">ورود مجدد به سیستم</translation>
diff --git a/chrome/app/resources/generated_resources_fi.xtb b/chrome/app/resources/generated_resources_fi.xtb
index 7707081..4e21437 100644
--- a/chrome/app/resources/generated_resources_fi.xtb
+++ b/chrome/app/resources/generated_resources_fi.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimoi</translation>
 <translation id="236141728043665931">Estä aina mikrofonin käyttö</translation>
 <translation id="2365507699358342471">Tämä sivu näkee leikepöydälle kopioidun tekstin ja kuvat.</translation>
-<translation id="2367199180085172140">Lisää jaettu tiedosto</translation>
 <translation id="2367972762794486313">Näytä sovellukset</translation>
 <translation id="2369105924912929484">Tiivistetty sivun sisältö päälle/pois</translation>
 <translation id="2371076942591664043">Avaa, kun val&amp;mis</translation>
@@ -2537,7 +2536,7 @@
 <translation id="4863769717153320198">Näyttää olevan <ph name="WIDTH" /> x <ph name="HEIGHT" /> (oletus)</translation>
 <translation id="4864369630010738180">Kirjaudutaan...</translation>
 <translation id="486635084936119914">Avaa tietyn tyyppiset tiedostot automaattisesti latauksen jälkeen.</translation>
-<translation id="4869142322204669043">Google voi räätälöidä palveluitaan (esimerkiksi <ph name="IDS_SHORT_PRODUCT_NAME" />, Kääntäjä, Haku ja mainokset) avaamiesi sivustojen sisällön sekä selaus- ja muun toimintasi perusteella. Voit muuttaa tätä koska tahansa asetuksista.</translation>
+<translation id="4869142322204669043">Google voi personoida palveluitaan (esimerkiksi <ph name="IDS_SHORT_PRODUCT_NAME" />, Kääntäjä, Haku ja mainokset) avaamiesi sivustojen sisällön sekä selaus- ja muun toimintasi perusteella. Voit muuttaa tätä koska tahansa asetuksista.</translation>
 <translation id="48704129375571883">Lisää ominaisuuksia</translation>
 <translation id="4870758487381879312">Syötä ylläpitäjältä saatu salasana hakeaksesi kokoonpanotiedot</translation>
 <translation id="4870903493621965035">Ei laitepareja</translation>
@@ -2786,7 +2785,7 @@
 <translation id="5255859108402770436">Kirjaudu uudelleen sisään</translation>
 <translation id="5256861893479663409">Kaikilla sivustoilla</translation>
 <translation id="5258992782919386492">Asenna tälle laitteelle</translation>
-<translation id="5260334392110301220">Älykkäät lainaukset</translation>
+<translation id="5260334392110301220">Älykkäät lainausmerkit</translation>
 <translation id="5260508466980570042">Sähköpostiosoitteesi tai salasanasi vahvistaminen epäonnistui. Yritä uudelleen.</translation>
 <translation id="5261683757250193089">Avaa Web Storessa</translation>
 <translation id="5264148714798105376">Tämä voi kestää hetken.</translation>
@@ -3297,7 +3296,6 @@
 <translation id="6026047032548434446">Asennetaanko sovellus?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – USB-laite yhdistetty</translation>
 <translation id="6029027682598229313">Linuxin asennus on valmis.</translation>
-<translation id="6029292188939175871">poista [<ph name="FINGERPRINT_NAME" />], painike</translation>
 <translation id="6029587122245504742">Hitain</translation>
 <translation id="6032912588568283682">Tiedostojärjestelmä</translation>
 <translation id="6038929619733116134">Estä, jos sivustolla on häiritseviä tai harhaanjohtavia mainoksia</translation>
@@ -3475,7 +3473,7 @@
 <translation id="6285120108426285413"><ph name="FILE_NAME" /> on harvinainen ladattava tiedosto, ja se voi olla vaarallinen.</translation>
 <translation id="6285120908535925801">{NUM_PRINTER,plural, =1{Verkossasi on uusi tulostin}other{Verkossasi on uusia tulostimia}}</translation>
 <translation id="6286708577777130801">Tallennettujen salasanojen tiedot</translation>
-<translation id="6289452883081499048">Play ja muut räätälöidyt Google-palvelut</translation>
+<translation id="6289452883081499048">Play ja muut personoidut Google-palvelut</translation>
 <translation id="6290556621549272952">Tämän ominaisuuden avulla voit katsella Chromiumin sisältöä television ja muiden laitteiden näytöltä.</translation>
 <translation id="6291949900244949761">Kysy aina, kun sivusto pyytää USB-laitteen käyttölupaa (suositus)</translation>
 <translation id="6291953229176937411">Näytä Finderi&amp;ssä</translation>
@@ -4599,7 +4597,6 @@
 <translation id="7973962044839454485">PPP-todennus epäonnistui virheellisen käyttäjänimen tai salasanan vuoksi</translation>
 <translation id="7974566588408714340">Yritä uudelleen laajennuksella <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Yliskannaus</translation>
-<translation id="79766959863778284">XR-eristetty laitepalvelu</translation>
 <translation id="7978412674231730200">Yksityinen avain</translation>
 <translation id="7978450511781612192">Tämä kirjaa sinut ulos Google-tileiltäsi. Kirjanmerkkejä, historiaa, salasanoja tai muita ei enää synkronoida.</translation>
 <translation id="7979036127916589816">Synkronointivirhe</translation>
@@ -5368,7 +5365,6 @@
 <translation id="9138978632494473300">Lisää pikanäppäimiä seuraaviin kohtiin:</translation>
 <translation id="9140067245205650184">Käytät ominaisuusmerkintää, jota ei tueta: <ph name="BAD_FLAG" />. Vakaus ja suojaus kärsivät.</translation>
 <translation id="9143298529634201539">Poistetaanko ehdotus?</translation>
-<translation id="9147304170847707004">Todennustapa</translation>
 <translation id="9147392381910171771">&amp;Asetukset</translation>
 <translation id="9148058034647219655">Poistu</translation>
 <translation id="9148126808321036104">Kirjaudu sisään uudelleen</translation>
diff --git a/chrome/app/resources/generated_resources_fil.xtb b/chrome/app/resources/generated_resources_fil.xtb
index 1da60f7..8a3158d 100644
--- a/chrome/app/resources/generated_resources_fil.xtb
+++ b/chrome/app/resources/generated_resources_fil.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">I-optimize</translation>
 <translation id="236141728043665931">Palaging i-block ang pag-access sa mikropono</translation>
 <translation id="2365507699358342471">Makikita ng site na ito ang text at mga larawang kinopya sa clipboard.</translation>
-<translation id="2367199180085172140">Magdagdag ng Pagbabahagi ng File</translation>
 <translation id="2367972762794486313">Ipakita ang apps</translation>
 <translation id="2369105924912929484">I-toggle ang mga na-distill na content ng page</translation>
 <translation id="2371076942591664043">Buksan kapag &amp;tapos na</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">I-install ang App?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - May nakakonektang USB device</translation>
 <translation id="6029027682598229313">Tapos nang i-install ang Linux.</translation>
-<translation id="6029292188939175871">i-delete ang [<ph name="FINGERPRINT_NAME" />], button</translation>
 <translation id="6029587122245504742">Pinakamabagal</translation>
 <translation id="6032912588568283682">System ng file</translation>
 <translation id="6038929619733116134">I-block kung nagpapakita ang site ng mga nakakasagabal o nakakapanlinlang na ad</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">Nabigo ang pagpapatotoo sa PPP dahil sa hindi tamang username o password</translation>
 <translation id="7974566588408714340">Subukang muli gamit ang <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Overscan</translation>
-<translation id="79766959863778284">XR Isolated Device Service</translation>
 <translation id="7978412674231730200">Private na key</translation>
 <translation id="7978450511781612192">Dahil dito, masa-sign out ka sa iyong mga Google Account. Hindi na masi-sync ang iyong mga bookmark, history, mga password, at higit pa.</translation>
 <translation id="7979036127916589816">Error sa Pag-sync</translation>
@@ -5371,7 +5368,6 @@
 <translation id="9138978632494473300">Magdagdag ng mga shortcut sa mga sumusunod na lugar:</translation>
 <translation id="9140067245205650184">Gumagamit ka ng hindi sinusuportahang flag ng feature: <ph name="BAD_FLAG" />. Mababawasan ang stability at seguridad.</translation>
 <translation id="9143298529634201539">Alisin ang suhestyon?</translation>
-<translation id="9147304170847707004">Paraan ng Pag-authenticate</translation>
 <translation id="9147392381910171771">&amp;Mga pagpipilian</translation>
 <translation id="9148058034647219655">Lumabas</translation>
 <translation id="9148126808321036104">Muling mag-sign in</translation>
diff --git a/chrome/app/resources/generated_resources_fr.xtb b/chrome/app/resources/generated_resources_fr.xtb
index fcd6948..54af6d9f 100644
--- a/chrome/app/resources/generated_resources_fr.xtb
+++ b/chrome/app/resources/generated_resources_fr.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimiser</translation>
 <translation id="236141728043665931">Toujours bloquer l'accès au micro</translation>
 <translation id="2365507699358342471">Ce site peut voir le texte et les images copiés dans le presse-papiers.</translation>
-<translation id="2367199180085172140">Ajouter un partage de fichiers</translation>
 <translation id="2367972762794486313">Afficher les applications</translation>
 <translation id="2369105924912929484">Activer/Désactiver les contenus de page simplifiés</translation>
 <translation id="2371076942591664043">Ouvrir une fois le téléchargement &amp;terminé</translation>
@@ -3300,7 +3299,6 @@
 <translation id="6026047032548434446">Installer l'application ?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> –  Appareil USB connecté</translation>
 <translation id="6029027682598229313">L'installation de Linux est terminée.</translation>
-<translation id="6029292188939175871">supprimer [<ph name="FINGERPRINT_NAME" />], bouton</translation>
 <translation id="6029587122245504742">La plus lente</translation>
 <translation id="6032912588568283682">Système de fichiers</translation>
 <translation id="6038929619733116134">Bloquer si le site affiche des annonces intrusives ou trompeuses</translation>
@@ -3498,7 +3496,7 @@
 <translation id="6317318380444133405">L'imprimante est désormais incompatible.</translation>
 <translation id="6317369057005134371">Création de la fenêtre de l'application en cours…</translation>
 <translation id="6318407754858604988">Le téléchargement a commencé</translation>
-<translation id="6318944945640833942">Imprimante non détectée. Veuillez saisir à nouveau son adresse.</translation>
+<translation id="6318944945640833942">Impossible de détecter l'imprimante. Veuillez saisir à nouveau son adresse.</translation>
 <translation id="6322653941595359182">Envoyer et recevoir des SMS depuis votre Chromebook</translation>
 <translation id="6324916366299863871">Modifier le raccourci</translation>
 <translation id="6325191661371220117">Désactiver le lancement automatique</translation>
@@ -4601,7 +4599,6 @@
 <translation id="7973962044839454485">Échec de l'authentification PPP en raison de la saisie d'un nom d'utilisateur ou d'un mot de passe erronés.</translation>
 <translation id="7974566588408714340">Réessayer avec "<ph name="EXTENSIONNAME" />"</translation>
 <translation id="7974936243149753750">Surbalayage</translation>
-<translation id="79766959863778284">Service Appareils isolés XR</translation>
 <translation id="7978412674231730200">Clé privée</translation>
 <translation id="7978450511781612192">Vous serez déconnecté de vos comptes Google. Vos favoris, votre historique et vos mots de passe ne seront plus synchronisés.</translation>
 <translation id="7979036127916589816">Erreur de synchronisation</translation>
@@ -5312,7 +5309,7 @@
 <translation id="9044646465488564462">Échec de la connexion au réseau : <ph name="DETAILS" />.</translation>
 <translation id="9045430190527754450">Envoie l'adresse Web de la page que vous essayez d'ouvrir à Google</translation>
 <translation id="9046895021617826162">Échec de la connexion</translation>
-<translation id="9047391224416514812">Un compte associé à ce nom d'utilisateur existe déjà</translation>
+<translation id="9047391224416514812">Ce nom d'utilisateur est déjà associé à un autre compte</translation>
 <translation id="9050666287014529139">Phrase secrète</translation>
 <translation id="9052208328806230490">Vous avez enregistré vos imprimantes sur <ph name="CLOUD_PRINT_NAME" /> via le compte <ph name="EMAIL" />.</translation>
 <translation id="9052404922357793350">Continuer de bloquer</translation>
@@ -5370,7 +5367,6 @@
 <translation id="9138978632494473300">Ajouter des raccourcis vers les emplacements suivants :</translation>
 <translation id="9140067245205650184">Vous utilisez un indicateur de fonctionnalité non pris en charge : <ph name="BAD_FLAG" />. La stabilité et la sécurité en seront affectées.</translation>
 <translation id="9143298529634201539">Supprimer la suggestion ?</translation>
-<translation id="9147304170847707004">Méthode d'authentification</translation>
 <translation id="9147392381910171771">&amp;Options</translation>
 <translation id="9148058034647219655">Quitter</translation>
 <translation id="9148126808321036104">Nouvelle connexion</translation>
diff --git a/chrome/app/resources/generated_resources_gu.xtb b/chrome/app/resources/generated_resources_gu.xtb
index fb64a0a..5ded246e 100644
--- a/chrome/app/resources/generated_resources_gu.xtb
+++ b/chrome/app/resources/generated_resources_gu.xtb
@@ -911,7 +911,6 @@
 <translation id="236117173274098341">ઑપ્ટિમાઇઝ કરો</translation>
 <translation id="236141728043665931">માઇક્રોફોનની ઍક્સેસને હંમેશા અવરોધિત કરો</translation>
 <translation id="2365507699358342471">આ સાઇટ ક્લિપબોર્ડ પર કૉપિ કરેલ ટેક્સ્ટ અને છબીઓ જોઈ શકે છે.</translation>
-<translation id="2367199180085172140">ફાઇલ શેર ઉમેરો</translation>
 <translation id="2367972762794486313">ઍપ્લિકેશનો બતાવો</translation>
 <translation id="2369105924912929484">શામેલ પેજ કન્ટેન્ટ ટૉગલ કરો</translation>
 <translation id="2371076942591664043">&amp;પૂર્ણ થાય ત્યારે ખોલો</translation>
@@ -3297,7 +3296,6 @@
 <translation id="6026047032548434446">ઍપ ઇન્સ્ટૉલ કરીએ?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB ઉપકરણ કનેક્ટ કર્યું</translation>
 <translation id="6029027682598229313">Linux ઇન્સ્ટૉલેશન પૂર્ણ થયું.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] ડિલીટ કરવાનું બટન</translation>
 <translation id="6029587122245504742">સૌથી ધીમી</translation>
 <translation id="6032912588568283682">ફાઇલ સિસ્ટમ</translation>
 <translation id="6038929619733116134">જો સાઇટ ઘૃણાસ્પદ અથવા ભ્રામક જાહેરાતો બતાવતી હોય, તો બ્લૉક કરો</translation>
@@ -4597,7 +4595,6 @@
 <translation id="7973962044839454485">ખોટા વપરાશકર્તાનામ અથવા પાસવર્ડને કારણે PPP પ્રમાણીકરણ નિષ્ફળ થયું</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" /> નો ઉપયોગ કરીને ફરીથી પ્રયાસ કરો</translation>
 <translation id="7974936243149753750">ઓવરસ્કૅન</translation>
-<translation id="79766959863778284">XR આઇસોલેટેડ ડિવાઇસ સેવા</translation>
 <translation id="7978412674231730200">ખાનગી કી</translation>
 <translation id="7978450511781612192">આનાથી તમે તમારા Google એકાઉન્ટમાંથી સાઇન આઉટ થઈ જશો. ત્યાર પછી તમારાં બુકમાર્ક, ઇતિહાસ, પાસવર્ડ અને ઘણું બધું સિંક કરવામાં નહીં આવે.</translation>
 <translation id="7979036127916589816">સમન્વય ભૂલ</translation>
@@ -5361,7 +5358,6 @@
 <translation id="9138978632494473300">નીચેના સ્થાનોના શૉર્ટકટ્સ ઉમેરો:</translation>
 <translation id="9140067245205650184">તમે અસમર્થિત સુવિધા ફ્લૅગનો ઉપયોગ કરી રહ્યા છો: <ph name="BAD_FLAG" />. સ્થિરતા તથા સુરક્ષા જોખમાશે.</translation>
 <translation id="9143298529634201539">સૂચન કાઢી નાખીએ?</translation>
-<translation id="9147304170847707004">પ્રમાણીકરણ પદ્ધતિ</translation>
 <translation id="9147392381910171771">&amp;વિકલ્પો</translation>
 <translation id="9148058034647219655">બહાર નીકળો</translation>
 <translation id="9148126808321036104">ફરીથી સાઇન ઇન કરો </translation>
diff --git a/chrome/app/resources/generated_resources_hi.xtb b/chrome/app/resources/generated_resources_hi.xtb
index bae265f..009e9896 100644
--- a/chrome/app/resources/generated_resources_hi.xtb
+++ b/chrome/app/resources/generated_resources_hi.xtb
@@ -609,7 +609,7 @@
 <translation id="1895252664692693738"><ph name="TIME_LEFT" /> बाकी है</translation>
 <translation id="1895658205118569222">बंद करें</translation>
 <translation id="1895934970388272448">यह प्रोसेस पूरा करने के लिए आपको अपने प्रिंटर पर रजिस्ट्रेशन की पुष्टि करनी होगी - इसे अभी चुनें.</translation>
-<translation id="1899826437968063457">प्लग इन VM को चालू होने के लिए अनुमति चाहिए</translation>
+<translation id="1899826437968063457">प्लग इन VM को चलाने के लिए अनुमति की ज़रूरत है</translation>
 <translation id="1901303067676059328">&amp;सभी को चुनें</translation>
 <translation id="1902576642799138955">मान्यता अवधि</translation>
 <translation id="1904394285866191268">{NUM_TABS,plural, =1{टैब म्यूट करें}one{टैब म्यूट करें}other{टैब म्यूट करें}}</translation>
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">ऑप्टिमाइज़ करें</translation>
 <translation id="236141728043665931">माइक्रोफोन एक्सेस हमेशा ब्लॉक करें</translation>
 <translation id="2365507699358342471">यह साइट क्लिपबोर्ड पर कॉपी किए गए लेख और इमेज देख सकती है</translation>
-<translation id="2367199180085172140">File Share जोड़ें</translation>
 <translation id="2367972762794486313">ऐप्स  दिखाएं</translation>
 <translation id="2369105924912929484">फ़िल्टर की गई सामग्री वाले पेज का टाॅगल</translation>
 <translation id="2371076942591664043">&amp;पू्र्ण होने पर खोलें</translation>
@@ -1104,7 +1103,7 @@
 <translation id="2649045351178520408">Base64 में एन्कोड किया गया ASCII प्रमाणपत्र चेन</translation>
 <translation id="2653033005692233957">नहीं खोजा जा सका</translation>
 <translation id="2653266418988778031">अगर आप किसी सर्टिफ़िकेशन अधिकारी (CA) प्रमाणपत्र को मिटाते हैं, तो आपका ब्राउज़र ऐसे किसी भी प्रमाणपत्र पर भरोसा नहीं करेगा, जिसे CA ने जारी किया है.</translation>
-<translation id="2653275834716714682">टेक्स्ट में बदलाव</translation>
+<translation id="2653275834716714682">Text Replacement</translation>
 <translation id="2653659639078652383">सबमिट करें</translation>
 <translation id="265390580714150011">फ़ील्ड मान</translation>
 <translation id="2654166010170466751">साइटों को भुगतान हैंडलर इंस्टॉल करने की अनुमति दें</translation>
@@ -1313,7 +1312,7 @@
 <translation id="29488703364906173">Chrome, आधुनिक वेब के लिए बनाया गया एक तेज़, आसान, और सुरक्षि‍त वेब ब्राउज़र है.</translation>
 <translation id="2949289451367477459">जगह की जानकारी का इस्तेमाल करें. जगह की जानकारी देखने की मंज़ूरी वाले ऐप्लिकेशन और सेवाओं को इस डिवाइस की जगह की जानकारी का इस्तेमाल करने दें. Google, जगह की जानकारी को ज़्यादा बेहतर बनाने और उससे जुड़ी सेवाओं में सुधार करने के लिए समय-समय पर जगह की जानकारी का डेटा इकट्ठा कर सकता है. साथ ही, वह आपकी पहचान ज़ाहिर किए बिना इस डेटा का इस्तेमाल भी कर सकता है. <ph name="BEGIN_LINK1" />ज़्यादा जानें<ph name="END_LINK1" /></translation>
 <translation id="2958721676848865875">पैक एक्‍सटेंशन चेतावनी</translation>
-<translation id="2959842337402130152">स्टोरेज के लिए कम जगह बची है, इसलिए कंटेनर बहाल नहीं किया जा सका. डिवाइस से <ph name="SPACE_REQUIRED" /> खाली करें और फिर से कोशिश करें.</translation>
+<translation id="2959842337402130152">स्टोरेज के लिए कम जगह बची है, इसलिए कंटेनर बहाल नहीं किया जा सका. डिवाइस से <ph name="SPACE_REQUIRED" /> खाली करके फिर से कोशिश करें.</translation>
 <translation id="296026337010986570">हो गया! नुकसान पहुंचाने वाला सॉफ़्टवेयर हटा दिया गया है. एक्सटेंशन वापस चालू करने के लिए &lt;a href="chrome://extensions"&gt;एक्सटेंशन&lt;/a&gt; पर जाएं.</translation>
 <translation id="2961090598421146107"><ph name="CERTIFICATE_NAME" /> (एक्सटेंशन प्रदान किया गया)</translation>
 <translation id="2961695502793809356">आगे जाने के लिए क्लिक करें, इतिहास देखने के लिए दबाकर रखें</translation>
@@ -2787,7 +2786,7 @@
 <translation id="5255859108402770436">फिर से साइन इन करें</translation>
 <translation id="5256861893479663409">सभी साइट पर</translation>
 <translation id="5258992782919386492">इस डिवाइस पर इंस्टॉल करें</translation>
-<translation id="5260334392110301220">स्मार्ट कोट</translation>
+<translation id="5260334392110301220">Smart Quotes</translation>
 <translation id="5260508466980570042">क्षमा करें, आपका ईमेल या पासवर्ड सत्‍यापित नहीं हो सका. कृपया फिर से प्रयास करें.</translation>
 <translation id="5261683757250193089">वेब स्टोर में खोलें</translation>
 <translation id="5264148714798105376">इसमें एक मिनट या कुछ ज़्यादा समय लग सकता है.</translation>
@@ -2822,7 +2821,7 @@
 <translation id="5301751748813680278">अतिथि के रूप में प्रवेश कर रहा है.</translation>
 <translation id="5301954838959518834">ठीक है, समझ लिया</translation>
 <translation id="5302048478445481009">भाषा</translation>
-<translation id="5302932258331363306">विकल्प दिखाएं</translation>
+<translation id="5302932258331363306">Show Substitutions</translation>
 <translation id="5305688511332277257">कुछ भी इंस्टॉल नहीं है</translation>
 <translation id="5307030433605830021">स्रोत काम नहीं करता है</translation>
 <translation id="5308380583665731573">कनेक्ट करें</translation>
@@ -2922,7 +2921,7 @@
 <translation id="5457113250005438886">अमान्य</translation>
 <translation id="5457459357461771897">अपने कंप्यूटर से फ़ोटो, संगीत और दूसरे मीडिया पढ़ें और मिटाएं</translation>
 <translation id="5457599981699367932">मेहमान के रूप में ब्राउज़ करें</translation>
-<translation id="5457991019809708398">चालू है , रोमिंग में नहीं है</translation>
+<translation id="5457991019809708398">चालू है, रोमिंग में नहीं है</translation>
 <translation id="5458998536542739734">लॉक स्क्रीन नोट</translation>
 <translation id="5463275305984126951"><ph name="LOCATION" /> का इंडेक्स</translation>
 <translation id="5463856536939868464">मेन्यू में बुकमार्क छुपे हुए हैं</translation>
@@ -3298,7 +3297,6 @@
 <translation id="6026047032548434446">ऐप्लिकेशन इंस्टॉल करें?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB डिवाइस कनेक्ट है</translation>
 <translation id="6029027682598229313">Linux इंस्टॉल हो गया है.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] मिटाएं बटन</translation>
 <translation id="6029587122245504742">सबसे धीमा</translation>
 <translation id="6032912588568283682">फ़ाइल सिस्टम</translation>
 <translation id="6038929619733116134">अगर साइट में तंग करने वाले या गुमराह करने वाले विज्ञापन दिखाई दें तो उन्हें ब्लॉक करें</translation>
@@ -4015,7 +4013,7 @@
 <translation id="7120865473764644444">सिंक सर्वर से कनेक्ट नहीं किया जा सका. पुनः प्रयास किया जा रहा है...</translation>
 <translation id="7121362699166175603">पता बार मेंअपने-आपपूर्णता और इतिहास को हटाता है. आपके Google खाते में <ph name="BEGIN_LINK" />myactivity.google.com<ph name="END_LINK" /> पर अन्य प्रकार के ब्राउज़िंग इतिहास हो सकतेे हैं.</translation>
 <translation id="7121389946694989825">इस्तेमाल और निदान से जुड़ा डेटा भेजें. यह डिवाइस फ़िलहाल Google को निदान, डिवाइस, और ऐप्लिकेशन के इस्तेमाल से जुड़ा डेटा अपने आप भेज रहा है. इसका इस्तेमाल आपके बच्चे की पहचान करने के लिए नहीं किया जाएगा. यह सिस्टम और ऐप्लिकेशन की स्थिरता और दूसरे सुधारों में मदद करेगा. कुछ एग्रीगेट डेटा Google ऐप्लिकेशन और पार्टनर, जैसे कि Android डेवलपरों की भी मदद करेगा. अगर आपके बच्चे के लिए दूसरी 'वेब और ऐप्लिकेशन गतिविधि' सेटिंग चालू है, तो यह डेटा उसके Google खाते में सेव किया जा सकता है. <ph name="BEGIN_LINK1" />ज़्यादा जानें<ph name="END_LINK1" /></translation>
-<translation id="7121728544325372695">स्मार्ट डैश</translation>
+<translation id="7121728544325372695">Smart Dashes</translation>
 <translation id="7123360114020465152">अब काम नहीं करता है</translation>
 <translation id="7124929488592184705"><ph name="DOCUMENT_NAME" /> को प्रिंट करने में गड़बड़ी</translation>
 <translation id="7127980134843952133">डाउनलोड इतिहास</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">किसी गलत उपयोगकर्ता नाम या पासवर्ड के कारण PPP प्रमाणीकरण विफल रहा</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" /> का उपयोग करके फिर से प्रयास करें</translation>
 <translation id="7974936243149753750">ओवरस्कैन</translation>
-<translation id="79766959863778284">XR Isolated Device Service</translation>
 <translation id="7978412674231730200">निजी कुंजी</translation>
 <translation id="7978450511781612192">इससे आप अपने Google खातों से साइन आउट हो जाएंगे. आपके बुकमार्क, इतिहास, पासवर्ड और कई चीज़ें अब सिंक नहीं की जाएंगी.</translation>
 <translation id="7979036127916589816">समन्वयन गड़बड़ी</translation>
@@ -5140,7 +5137,7 @@
 <translation id="8795916974678578410">नई विंडो</translation>
 <translation id="8797459392481275117">इस साइट का कभी भी अनुवाद ना करें</translation>
 <translation id="8798099450830957504">सामान्य</translation>
-<translation id="8798441408945964110">प्रदाता का नाम</translation>
+<translation id="8798441408945964110">कंपनी का नाम</translation>
 <translation id="8800004011501252845">इसके लिए गंतव्य दिखा रहा है:</translation>
 <translation id="8803953437405899238">एक क्लिक में नया टैब खोलें</translation>
 <translation id="8804398419035066391">सहयोगी वेबसाइटों से डॉयलॉग करें</translation>
@@ -5367,7 +5364,6 @@
 <translation id="9138978632494473300">निम्न स्थानों के लिए शॉर्टकट जोड़ें:</translation>
 <translation id="9140067245205650184">आप एक काम नहीं करने वाले फ़ीचर फ़्लैग का उपयोग कर रहे हैं: <ph name="BAD_FLAG" />. स्थिरता और सुरक्षा पर इससे फ़र्क पड़ेगा.</translation>
 <translation id="9143298529634201539">सुझाव हटाएं?</translation>
-<translation id="9147304170847707004">पुष्टि करने का तरीका</translation>
 <translation id="9147392381910171771">&amp;विकल्प</translation>
 <translation id="9148058034647219655">बाहर निकलें</translation>
 <translation id="9148126808321036104">फिर से साइन इन करें</translation>
diff --git a/chrome/app/resources/generated_resources_hr.xtb b/chrome/app/resources/generated_resources_hr.xtb
index 01903f7..6c01b940 100644
--- a/chrome/app/resources/generated_resources_hr.xtb
+++ b/chrome/app/resources/generated_resources_hr.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimiziraj</translation>
 <translation id="236141728043665931">Uvijek blokiraj pristup mikrofonu</translation>
 <translation id="2365507699358342471">Ova web-lokacija može vidjeti tekst i slike kopirane u međuspremnik.</translation>
-<translation id="2367199180085172140">Dodaj dijeljenje datoteka</translation>
 <translation id="2367972762794486313">Prikaz aplikacija</translation>
 <translation id="2369105924912929484">Isključi/uključi sažeti sadržaj web-stranice</translation>
 <translation id="2371076942591664043">Otvori nakon &amp;dovršetka</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Želite li instalirati aplikaciju?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – USB uređaj povezan</translation>
 <translation id="6029027682598229313">Dovršena je instalacija Linuxa.</translation>
-<translation id="6029292188939175871">izbriši [<ph name="FINGERPRINT_NAME" />], gumb</translation>
 <translation id="6029587122245504742">Najsporije</translation>
 <translation id="6032912588568283682">Datotečni sustav</translation>
 <translation id="6038929619733116134">Blokiraj ako web-lokacija prikazuje ometajuće ili obmanjujuće oglase</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">PPP autentifikacija nije uspjela zbog netočnog korisničkog imena ili zaporke</translation>
 <translation id="7974566588408714340">Pokušaj ponovo pomoću proširenja <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Rubno područje zaslona</translation>
-<translation id="79766959863778284">XR Isolated Device Service</translation>
 <translation id="7978412674231730200">Osobni ključ</translation>
 <translation id="7978450511781612192">Time ćete se odjaviti s Google računa. Vaše se oznake, povijest, zaporke i druge postavke više neće sinkronizirati.</translation>
 <translation id="7979036127916589816">Sinkronizacijska pogreška</translation>
@@ -5370,7 +5367,6 @@
 <translation id="9138978632494473300">Dodajte prečace na sljedećim mjestima:</translation>
 <translation id="9140067245205650184">Upotrebljavate nepodržanu oznaku značajke: <ph name="BAD_FLAG" />. Ugrožena je stabilnost i sigurnost.</translation>
 <translation id="9143298529634201539">Želite li ukloniti prijedlog?</translation>
-<translation id="9147304170847707004">Način autentifikacije</translation>
 <translation id="9147392381910171771">&amp;Opcije</translation>
 <translation id="9148058034647219655">Izlaz</translation>
 <translation id="9148126808321036104">Prijavite se ponovno</translation>
diff --git a/chrome/app/resources/generated_resources_hu.xtb b/chrome/app/resources/generated_resources_hu.xtb
index eef7f85..a407edda 100644
--- a/chrome/app/resources/generated_resources_hu.xtb
+++ b/chrome/app/resources/generated_resources_hu.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimalizálás</translation>
 <translation id="236141728043665931">Mikrofon elérésének állandó tiltása</translation>
 <translation id="2365507699358342471">Ez a webhely megtekintheti a vágólapra másolt szövegeket és képeket.</translation>
-<translation id="2367199180085172140">Fájlmegosztás hozzáadása</translation>
 <translation id="2367972762794486313">Alkalmazások megjelenítése</translation>
 <translation id="2369105924912929484">Szűrt oldaltartalmak be- vagy kikapcsolása</translation>
 <translation id="2371076942591664043">Megnyitás, amikor &amp;kész</translation>
@@ -3301,7 +3300,6 @@
 <translation id="6026047032548434446">Telepíti az alkalmazást?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – USB-eszköz csatlakoztatva</translation>
 <translation id="6029027682598229313">Sikeresen befejeződött a Linux telepítése.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] törlése, gomb</translation>
 <translation id="6029587122245504742">Leglassabb</translation>
 <translation id="6032912588568283682">Fájlrendszer</translation>
 <translation id="6038929619733116134">Letiltás, ha a webhely tolakodó vagy félrevezető hirdetéseket jelenít meg</translation>
@@ -4602,7 +4600,6 @@
 <translation id="7973962044839454485">Nem sikerült a PPP-hitelesítés helytelen felhasználónév vagy jelszó miatt</translation>
 <translation id="7974566588408714340">A(z) <ph name="EXTENSIONNAME" /> használatának újrapróbálása</translation>
 <translation id="7974936243149753750">Túlpásztázás</translation>
-<translation id="79766959863778284">XR eszközizolációs szolgáltatás</translation>
 <translation id="7978412674231730200">Privát kulcs</translation>
 <translation id="7978450511781612192">Ezzel kijelentkezik Google-fiókjaiból. Könyvjelzőinek, előzményeinek, jelszavainak és egyéb adatainak szinkronizálása megszűnik.</translation>
 <translation id="7979036127916589816">Szinkronizálási hiba</translation>
@@ -5371,7 +5368,6 @@
 <translation id="9138978632494473300">Parancsikonok hozzáadása a következő helyekhez:</translation>
 <translation id="9140067245205650184">Nem támogatott funkciókapcsolót használ: <ph name="BAD_FLAG" />. Ennek a stabilitás és a biztonság látja kárát.</translation>
 <translation id="9143298529634201539">Eltávolítja a javaslatot?</translation>
-<translation id="9147304170847707004">Hitelesítési mód</translation>
 <translation id="9147392381910171771">&amp;Beállítások</translation>
 <translation id="9148058034647219655">Kilépés</translation>
 <translation id="9148126808321036104">Jelentkezzen be újra</translation>
diff --git a/chrome/app/resources/generated_resources_id.xtb b/chrome/app/resources/generated_resources_id.xtb
index f65a0a0..d0f81cd6 100644
--- a/chrome/app/resources/generated_resources_id.xtb
+++ b/chrome/app/resources/generated_resources_id.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimalkan</translation>
 <translation id="236141728043665931">Selalu blokir akses mikrofon</translation>
 <translation id="2365507699358342471">Situs ini dapat melihat teks dan gambar yang disalin ke papan klip.</translation>
-<translation id="2367199180085172140">Tambahkan Berbagi File</translation>
 <translation id="2367972762794486313">Tampilkan aplikasi</translation>
 <translation id="2369105924912929484">Aktifkan/nonaktifkan konten halaman yang disingkat</translation>
 <translation id="2371076942591664043">Buka setelah &amp;selesai</translation>
@@ -951,7 +950,7 @@
 <translation id="2433452467737464329">Tambahkan parameter kueri di URL untuk menyegarkan halaman secara otomatis: chrome://network/?refresh=&lt;sec&gt;</translation>
 <translation id="2433507940547922241">Tampilan</translation>
 <translation id="2433836460518180625">Hanya buka kunci perangkat</translation>
-<translation id="2434449159125086437">Tidak bisa menyiapkan printer. Harap periksa konfigurasi dan coba lagi.</translation>
+<translation id="2434449159125086437">Tidak dapat menyiapkan printer. Harap periksa konfigurasi dan coba lagi.</translation>
 <translation id="2435248616906486374">Jaringan terputus</translation>
 <translation id="2435457462613246316">Tampilkan sandi</translation>
 <translation id="2436186046335138073">Izinkan <ph name="HANDLER_HOSTNAME" /> membuka semua link <ph name="PROTOCOL" />?</translation>
@@ -1313,7 +1312,7 @@
 <translation id="29488703364906173">Browser web yang cepat, sederhana, dan aman, yang diciptakan untuk web modern.</translation>
 <translation id="2949289451367477459">Gunakan lokasi. Izinkan aplikasi dan layanan yang memiliki izin akses lokasi untuk menggunakan lokasi perangkat ini. Google dapat mengumpulkan data lokasi secara berkala dan menggunakan data ini secara anonim untuk meningkatkan akurasi lokasi dan layanan berdasarkan lokasi. <ph name="BEGIN_LINK1" />Pelajari Lebih Lanjut<ph name="END_LINK1" /></translation>
 <translation id="2958721676848865875">Peringatan ekstensi pack</translation>
-<translation id="2959842337402130152">Tidak bisa memulihkan karena ruang penyimpanan tidak cukup. Kosongkan <ph name="SPACE_REQUIRED" /> dari perangkat dan coba lagi.</translation>
+<translation id="2959842337402130152">Tidak dapat memulihkan karena ruang penyimpanan tidak cukup. Kosongkan <ph name="SPACE_REQUIRED" /> dari perangkat dan coba lagi.</translation>
 <translation id="296026337010986570">Selesai! Software berbahaya telah dihapus. Untuk mengaktifkan kembali ekstensi, buka &lt;a href="chrome://extensions"&gt;Ekstensi&lt;/a&gt;.</translation>
 <translation id="2961090598421146107"><ph name="CERTIFICATE_NAME" /> (ekstensi tersedia)</translation>
 <translation id="2961695502793809356">Klik untuk maju, tahan untuk melihat histori</translation>
@@ -3297,7 +3296,6 @@
 <translation id="6026047032548434446">Instal aplikasi?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - Perangkat USB tersambung</translation>
 <translation id="6029027682598229313">Penginstalan Linux selesai.</translation>
-<translation id="6029292188939175871">tombol hapus [<ph name="FINGERPRINT_NAME" />]</translation>
 <translation id="6029587122245504742">Paling lambat</translation>
 <translation id="6032912588568283682">Sistem file</translation>
 <translation id="6038929619733116134">Blokir jika situs menampilkan iklan yang mengganggu atau menyesatkan</translation>
@@ -3495,7 +3493,7 @@
 <translation id="6317318380444133405">Tidak didukung lagi.</translation>
 <translation id="6317369057005134371">Menunggu jendela aplikasi...</translation>
 <translation id="6318407754858604988">Download dimulai</translation>
-<translation id="6318944945640833942">Tidak bisa mendeteksi printer. Harap masukkan alamat printer lagi.</translation>
+<translation id="6318944945640833942">Tidak dapat mendeteksi printer. Harap masukkan alamat printer lagi.</translation>
 <translation id="6322653941595359182">Mengirim dan menerima SMS dari Chromebook</translation>
 <translation id="6324916366299863871">Edit pintasan</translation>
 <translation id="6325191661371220117">Nonaktifkan luncurkan otomatis</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">Autentifikasi PDB gagal karena nama pengguna dan sandi salah</translation>
 <translation id="7974566588408714340">Coba lagi menggunakan <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Pemindaian berlebih</translation>
-<translation id="79766959863778284">Layanan Perangkat Terisolasi XR</translation>
 <translation id="7978412674231730200">Kunci pribadi</translation>
 <translation id="7978450511781612192">Tindakan ini akan membuat Anda logout dari Akun Google. Bookmark, histori, sandi, dan lainnya tidak akan disinkronkan lagi.</translation>
 <translation id="7979036127916589816">Kesalahan Sinkronisasi</translation>
@@ -4775,7 +4772,7 @@
 <translation id="8227119283605456246">Lampirkan file</translation>
 <translation id="8230134520748321204">Simpan sandi untuk <ph name="ORIGIN" />?</translation>
 <translation id="8234795456569844941">Bantu engineer kami memperbaiki masalah ini. Beri tahu kami yang terjadi tepat sebelum Anda mendapatkan pesan error profil:</translation>
-<translation id="8236917170563564587">Bagikan tap ini saja</translation>
+<translation id="8236917170563564587">Bagikan tab ini saja</translation>
 <translation id="8241040075392580210">Mendung</translation>
 <translation id="8241806945692107836">Menentukan konfigurasi perangkat...</translation>
 <translation id="8241868517363889229">Membaca dan mengubah bookmark Anda</translation>
@@ -5357,7 +5354,7 @@
 <translation id="9121814364785106365">Buka sebagai tab terpasang</translation>
 <translation id="9124003689441359348">Sandi yang disimpan akan muncul di sini</translation>
 <translation id="9125466540846359910"><ph name="LICENSE_TYPE" /> (<ph name="LICENSE_COUNT" /> tersisa)</translation>
-<translation id="9128317794749765148">Tidak bisa menyelesaikan penyiapan</translation>
+<translation id="9128317794749765148">Tidak dapat menyelesaikan penyiapan</translation>
 <translation id="9128870381267983090">Sambungkan ke jaringan</translation>
 <translation id="9130015405878219958">Mode yang dimasukkan tidak valid.</translation>
 <translation id="9131487537093447019">Kirim pesan ke dan terima pesan dari perangkat Bluetooth.</translation>
@@ -5367,7 +5364,6 @@
 <translation id="9138978632494473300">Tambahkan pintasan ke tempat berikut:</translation>
 <translation id="9140067245205650184">Anda menggunakan tanda fitur yang tidak didukung: <ph name="BAD_FLAG" />. Stabilitas dan keamanan akan terganggu.</translation>
 <translation id="9143298529634201539">Hapus saran?</translation>
-<translation id="9147304170847707004">Metode Autentikasi</translation>
 <translation id="9147392381910171771">&amp;Pilihan</translation>
 <translation id="9148058034647219655">Keluar</translation>
 <translation id="9148126808321036104">Login sekali lagi</translation>
diff --git a/chrome/app/resources/generated_resources_it.xtb b/chrome/app/resources/generated_resources_it.xtb
index 63e62821..afa3bfa 100644
--- a/chrome/app/resources/generated_resources_it.xtb
+++ b/chrome/app/resources/generated_resources_it.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Ottimizza</translation>
 <translation id="236141728043665931">Impedisci sempre l'accesso al microfono</translation>
 <translation id="2365507699358342471">Questo sito può leggere testo e immagini copiati negli appunti</translation>
-<translation id="2367199180085172140">Aggiungi Condivisione file</translation>
 <translation id="2367972762794486313">Mostra app</translation>
 <translation id="2369105924912929484">Attiva i contenuti della pagina distill</translation>
 <translation id="2371076942591664043">Apri al &amp;termine</translation>
@@ -2243,7 +2242,7 @@
 <translation id="4378551569595875038">Connessione...</translation>
 <translation id="4378556263712303865">Richiesta dispositivo</translation>
 <translation id="4380648069038809855">Modalità a schermo intero attivata</translation>
-<translation id="4382131447572146376">{COUNT,plural, =0{<ph name="EMAIL" />}=1{<ph name="EMAIL" /> e 1 altro account}other{<ph name="EMAIL" />, e altri <ph name="EXTRA_ACCOUNTS" /> account}}</translation>
+<translation id="4382131447572146376">{COUNT,plural, =0{<ph name="EMAIL" />}=1{<ph name="EMAIL" /> e 1 altro account}other{<ph name="EMAIL" /> e altri <ph name="EXTRA_ACCOUNTS" /> account}}</translation>
 <translation id="4384312707950789900">Aggiungi ai preferiti</translation>
 <translation id="4384652540891215547">Attiva l'estensione</translation>
 <translation id="438503109373656455">Furia</translation>
@@ -3295,7 +3294,6 @@
 <translation id="6026047032548434446">Vuoi installare l'app?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - Dispositivo USB collegato</translation>
 <translation id="6029027682598229313">Installazione di Linux completata.</translation>
-<translation id="6029292188939175871">elimina [<ph name="FINGERPRINT_NAME" />], pulsante</translation>
 <translation id="6029587122245504742">Minima</translation>
 <translation id="6032912588568283682">File system</translation>
 <translation id="6038929619733116134">Blocca se il sito mostra annunci invasivi o fuorvianti</translation>
@@ -4594,7 +4592,6 @@
 <translation id="7973962044839454485">Autenticazione PPP non riuscita a causa di un nome utente o una password sbagliati</translation>
 <translation id="7974566588408714340">Riprova utilizzando <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Overscan</translation>
-<translation id="79766959863778284">Servizio per dispositivi XR isolati</translation>
 <translation id="7978412674231730200">Chiave privata</translation>
 <translation id="7978450511781612192">I tuoi Account Google verranno scollegati e verrà interrotta la sincronizzazione di preferiti, cronologia, password e altro.</translation>
 <translation id="7979036127916589816">Errore di sincronizzazione</translation>
@@ -5362,7 +5359,6 @@
 <translation id="9138978632494473300">Aggiungi scorciatoie per le seguenti posizioni:</translation>
 <translation id="9140067245205650184">Stai utilizzando un flag di funzione non supportato: <ph name="BAD_FLAG" />. Stabilità e sicurezza ne risentiranno.</translation>
 <translation id="9143298529634201539">Rimuovere il suggerimento?</translation>
-<translation id="9147304170847707004">Metodo di autenticazione</translation>
 <translation id="9147392381910171771">&amp;Opzioni</translation>
 <translation id="9148058034647219655">Esci</translation>
 <translation id="9148126808321036104">Esegui di nuovo l'accesso</translation>
diff --git a/chrome/app/resources/generated_resources_iw.xtb b/chrome/app/resources/generated_resources_iw.xtb
index e921f8e6..ecb7e396 100644
--- a/chrome/app/resources/generated_resources_iw.xtb
+++ b/chrome/app/resources/generated_resources_iw.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">אופטימיזציה</translation>
 <translation id="236141728043665931">חסום תמיד גישה למיקרופון</translation>
 <translation id="2365507699358342471">לאתר הזה יש גישה אל טקסט ותמונות שהועתקו אל לוח העריכה.</translation>
-<translation id="2367199180085172140">הוספת שיתוף קובץ</translation>
 <translation id="2367972762794486313">הצג יישומים</translation>
 <translation id="2369105924912929484">הפעלה/השבתה של זיקוק תוכן הדף</translation>
 <translation id="2371076942591664043">פתח &amp;בסיום</translation>
@@ -1310,7 +1309,7 @@
 <translation id="29488703364906173">דפדפן אינטרנט מהיר, פשוט ובטוח, המותאם לאינטרנט המודרני.</translation>
 <translation id="2949289451367477459">‏שימוש במיקום. מתן רשות לשירותים עם הרשאות מיקום להשתמש במיקום המכשיר הזה. Google עשויה לאסוף מדי פעם נתוני מיקום ולהשתמש בהם באופן אנונימי כדי לשפר את דיוק המיקום ושירותים מבוססי-מיקום. <ph name="BEGIN_LINK1" />מידע נוסף<ph name="END_LINK1" /></translation>
 <translation id="2958721676848865875">אזהרת 'ארוז תוסף'</translation>
-<translation id="2959842337402130152">לא ניתן לשחזר מפני שאין מספיק מקום אחסון. צריך לפנות <ph name="SPACE_REQUIRED" /> במכשיר ולנסות שוב.</translation>
+<translation id="2959842337402130152">לא ניתן לשחזר מפני שאין מספיק שטח אחסון. צריך לפנות <ph name="SPACE_REQUIRED" /> במכשיר ולנסות שוב.</translation>
 <translation id="296026337010986570">‏סיימנו! התוכנות המזיקות הוסרו. כדי להפעיל מחדש את התוספים, צריך לעבור אל &lt;a href="chrome://extensions"&gt;תוספים&lt;/a&gt;.</translation>
 <translation id="2961090598421146107"><ph name="CERTIFICATE_NAME" /> (ניתנה הארכה)</translation>
 <translation id="2961695502793809356">לחץ כדי לעבור קדימה, לחץ והחזק כדי לראות את ההיסטוריה</translation>
@@ -3296,7 +3295,6 @@
 <translation id="6026047032548434446">להתקין את האפליקציה?</translation>
 <translation id="6026819612896463875">‏<ph name="WINDOW_TITLE" /> - USB מחובר</translation>
 <translation id="6029027682598229313">‏התקנת Linux הושלמה.</translation>
-<translation id="6029292188939175871">מחיקת [<ph name="FINGERPRINT_NAME" />], לחצן</translation>
 <translation id="6029587122245504742">הכי איטי</translation>
 <translation id="6032912588568283682">מערכת קבצים</translation>
 <translation id="6038929619733116134">חסימה אם באתר מוצגות מודעות מפריעות או מטעות</translation>
@@ -4594,7 +4592,6 @@
 <translation id="7973962044839454485">‏אימות PPP נכשל בשל שם משתמש או סיסמה שגויים</translation>
 <translation id="7974566588408714340">נסה שוב באמצעות <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">סריקת יתר</translation>
-<translation id="79766959863778284">‏שירות XR למכשיר מבודד</translation>
 <translation id="7978412674231730200">מפתח פרטי</translation>
 <translation id="7978450511781612192">‏הפעולה הזאת תוציא אותך מחשבונות Google שלך. הסימניות, ההיסטוריה, הסיסמאות ופרטים אחרים יפסיקו לעבור סנכרון.</translation>
 <translation id="7979036127916589816">שגיאת סנכרון</translation>
@@ -4773,7 +4770,7 @@
 <translation id="8227119283605456246">צירוף קובץ</translation>
 <translation id="8230134520748321204">האם לשמור את הסיסמה של <ph name="ORIGIN" />?</translation>
 <translation id="8234795456569844941">עזור למהנדסים שלנו לפתור את הבעיה הזו. ספר לנו מה קרה לפני שהוצגה לך הודעת השגיאה לגבי הפרופיל:</translation>
-<translation id="8236917170563564587">שיתוף הכרטיסייה הזו במקומה</translation>
+<translation id="8236917170563564587">שיתוף הכרטיסייה הזו</translation>
 <translation id="8241040075392580210">מוצל</translation>
 <translation id="8241806945692107836">קובע את תצורת המכשיר...</translation>
 <translation id="8241868517363889229">לקרוא ולשנות את הסימניות שלך</translation>
@@ -5365,7 +5362,6 @@
 <translation id="9138978632494473300">הוסף קיצורי דרך במקומות הבאים:</translation>
 <translation id="9140067245205650184">נעשה שימוש בתכונה ניסיונית שאינה נתמכת: <ph name="BAD_FLAG" />. היציבות והאבטחה ייפגעו.</translation>
 <translation id="9143298529634201539">האם להסיר את ההצעה?</translation>
-<translation id="9147304170847707004">שיטת אימות</translation>
 <translation id="9147392381910171771">&amp;אפשרויות</translation>
 <translation id="9148058034647219655">יציאה</translation>
 <translation id="9148126808321036104">היכנס שוב</translation>
diff --git a/chrome/app/resources/generated_resources_ja.xtb b/chrome/app/resources/generated_resources_ja.xtb
index 73db456..3e893953 100644
--- a/chrome/app/resources/generated_resources_ja.xtb
+++ b/chrome/app/resources/generated_resources_ja.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">最適化</translation>
 <translation id="236141728043665931">マイクのアクセスを常にブロックする</translation>
 <translation id="2365507699358342471">このサイトでは、クリップボードにコピーされているテキストや画像へのアクセスが許可されています。</translation>
-<translation id="2367199180085172140">ファイル共有を追加</translation>
 <translation id="2367972762794486313">アプリを表示</translation>
 <translation id="2369105924912929484">抽出されたページのコンテンツを切り替える</translation>
 <translation id="2371076942591664043">ダウンロードしたら開く(&amp;D)</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">アプリをインストールしますか?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB デバイスに接続されています</translation>
 <translation id="6029027682598229313">Linux のインストールが完了しました。</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] を削除するボタン</translation>
 <translation id="6029587122245504742">最も遅い</translation>
 <translation id="6032912588568283682">ファイル システム</translation>
 <translation id="6038929619733116134">煩わしい広告や誤解を招く広告が表示されるサイトの場合にブロックします</translation>
@@ -4601,7 +4599,6 @@
 <translation id="7973962044839454485">ユーザー名またはパスワードが正しくないため、PPP 認証に失敗しました</translation>
 <translation id="7974566588408714340">「<ph name="EXTENSIONNAME" />」で再試行</translation>
 <translation id="7974936243149753750">オーバースキャン</translation>
-<translation id="79766959863778284">XR 分離デバイスサービス</translation>
 <translation id="7978412674231730200">秘密鍵</translation>
 <translation id="7978450511781612192">この操作を行うと Google アカウントからログアウトし、ブックマーク、履歴、パスワードなどの設定は同期されなくなります。</translation>
 <translation id="7979036127916589816">同期エラー</translation>
@@ -5370,7 +5367,6 @@
 <translation id="9138978632494473300">ショートカットを次の場所に追加:</translation>
 <translation id="9140067245205650184">サポートされていない機能フラグ <ph name="BAD_FLAG" /> が使用されています。これにより、安全性とセキュリティが損なわれます。</translation>
 <translation id="9143298529634201539">候補を削除しますか?</translation>
-<translation id="9147304170847707004">認証方法</translation>
 <translation id="9147392381910171771">オプション(&amp;O)</translation>
 <translation id="9148058034647219655">終了</translation>
 <translation id="9148126808321036104">もう一度ログインする</translation>
diff --git a/chrome/app/resources/generated_resources_kn.xtb b/chrome/app/resources/generated_resources_kn.xtb
index f390fd6f..0bdc160b 100644
--- a/chrome/app/resources/generated_resources_kn.xtb
+++ b/chrome/app/resources/generated_resources_kn.xtb
@@ -916,7 +916,6 @@
 <translation id="236117173274098341">ಆಪ್ಟಿಮೈಸ್ ಮಾಡಿ</translation>
 <translation id="236141728043665931">ಯಾವಾಗಲೂ ಮೈಕ್ರೋಫೋನ್ ಪ್ರವೇಶವನ್ನು ನಿರ್ಬಂಧಿಸಿ</translation>
 <translation id="2365507699358342471">ಕ್ಲಿಪ್‌ಬೋರ್ಡ್‌ಗೆ ನಕಲಿಸಿರುವ ಪಠ್ಯ ಮತ್ತು ಚಿತ್ರಗಳನ್ನು ಈ ಸೈಟ್ ವೀಕ್ಷಿಸಬಹುದು.</translation>
-<translation id="2367199180085172140">ಫೈಲ್‌ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಸೇರಿಸಿ</translation>
 <translation id="2367972762794486313">ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ತೋರಿಸು</translation>
 <translation id="2369105924912929484">ಡಿಸ್ಟಿಲ್ ಮಾಡಿರುವ ಪುಟದ ಕಂಟೆಂಟ್‌ಗಳನ್ನು ಟಾಗಲ್ ಮಾಡಿ</translation>
 <translation id="2371076942591664043">&amp;ಮುಗಿಸಿದಾಗ ತೆರೆಯಿರಿ</translation>
@@ -3301,7 +3300,6 @@
 <translation id="6026047032548434446">ಆ್ಯಪ್ ಇನ್‌ಸ್ಟಾಲ್‌ ಮಾಡಬೇಕೆ?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB ಸಾಧನ ಸಂಪರ್ಕಗೊಂಡಿದೆ</translation>
 <translation id="6029027682598229313">Linux ಇನ್‌ಸ್ಟಾಲ್‌ ಮಾಡುವಿಕೆ ಪೂರ್ಣಗೊಂಡಿದೆ.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] ಅಳಿಸು, ಬಟನ್</translation>
 <translation id="6029587122245504742">ಅತಿ ನಿಧಾನ</translation>
 <translation id="6032912588568283682">ಫೈಲ್ ಸಿಸ್ಟಂ</translation>
 <translation id="6038929619733116134">ಅತಿಕ್ರಮಣಕಾರಿಯಾಗಿರುವ ಅಥವಾ ತಪ್ಪುದಾರಿಗೆಳೆಯುವ ಜಾಹೀರಾತುಗಳನ್ನು ಸೈಟ್ ತೋರಿಸಿದಲ್ಲಿ ಅದನ್ನು ನಿರ್ಬಂಧಿಸಿ</translation>
@@ -3850,7 +3848,7 @@
 <translation id="6876155724392614295">ಬೈಕ್</translation>
 <translation id="6877460900831874810">ಸ್ಕ್ರೀನ್ ಲಾಕ್‌ನಲ್ಲಿದ್ದಾಗ Chrome ಮಾಧ್ಯಮ ಪ್ಲೇಬ್ಯಾಕ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ</translation>
 <translation id="6878422606530379992">ಸೆನ್ಸರ್‌ಗಳನ್ನು ಅನುಮತಿಸಲಾಗಿದೆ</translation>
-<translation id="6878665006737889642">ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡುವುದನ್ನು ಪುನಃ ಪ್ರಯತ್ನಿಸಿ</translation>
+<translation id="6878665006737889642">ಪುನಃ ಇನ್‍‍ಸ್ಟಾಲ್ ಮಾಡಿ</translation>
 <translation id="6880587130513028875">ಈ ಪುಟದಲ್ಲಿ ಚಿತ್ರಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ.</translation>
 <translation id="6883319974225028188">ಓಹ್‌‌! ಸಾಧನದ ಕಾನ್ಫಿಗರೇಶನ್ ಉಳಿಸಲು ಸಿಸ್ಟಂ ವಿಫಲವಾಗಿದೆ.</translation>
 <translation id="6885771755599377173">ಸಿಸ್ಟಂ ಮಾಹಿತಿ ಪೂರ್ವವೀಕ್ಷಣೆ</translation>
@@ -4603,7 +4601,6 @@
 <translation id="7973962044839454485">ತಪ್ಪಾದ ಬಳಕೆದಾರ ಹೆಸರು ಮತ್ತು ಪಾಸ್‌ವರ್ಡ್‌ನಿಂದಾಗಿ PPP ದೃಢೀಕರಣ ವಿಫಲವಾಗಿದೆ</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" /> ಬಳಸುವ ಮೂಲಕ ಮರುಪ್ರಯತ್ನಿಸು</translation>
 <translation id="7974936243149753750">ಓವರ್‌ಸ್ಕ್ಯಾನ್</translation>
-<translation id="79766959863778284">XR ಪ್ರತ್ಯೇಕ ಸಾಧನ ಸೇವೆ</translation>
 <translation id="7978412674231730200">ಖಾಸಗಿ ಕೀಲಿ</translation>
 <translation id="7978450511781612192">ಇದು ನಿಮ್ಮ Google ಖಾತೆಗಳಿಂದ ನಿಮ್ಮನ್ನು ಸೈನ್ ಔಟ್ ಮಾಡುತ್ತದೆ. ನಿಮ್ಮ ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳು, ಇತಿಹಾಸ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಹಾಗೂ ಹೆಚ್ಚಿನವುಗಳನ್ನು ಇನ್ನು ಮುಂದೆ ಸಿಂಕ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ.</translation>
 <translation id="7979036127916589816">ಸಿಂಕ್ ದೋಷ</translation>
@@ -5373,7 +5370,6 @@
 <translation id="9138978632494473300">ಕೆಳಗೆ ಸ್ಥಳಗಳಿಗೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಿ:</translation>
 <translation id="9140067245205650184">ನೀವು ಬೆಂಬಲಿತವಲ್ಲದ ವೈಶಿಷ್ಟ್ಯ ಫ್ಲ್ಯಾಗ್ ಅನ್ನು ಬಳಸುತ್ತಿದ್ದೀರಿ: <ph name="BAD_FLAG" />. ಸ್ಥಿರತೆ ಮತ್ತು ಸುರಕ್ಷತೆಯು ಹಾನಿಯಾಗುತ್ತದೆ.</translation>
 <translation id="9143298529634201539">ಸಲಹೆಯನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?</translation>
-<translation id="9147304170847707004">ದೃಢೀಕರಣ ವಿಧಾನ</translation>
 <translation id="9147392381910171771">&amp;ಆಯ್ಕೆಗಳು</translation>
 <translation id="9148058034647219655">ನಿರ್ಗಮಿಸಿ</translation>
 <translation id="9148126808321036104">ಪುನಃ ಸೈನ್ ಇನ್  ಆಗಿ</translation>
diff --git a/chrome/app/resources/generated_resources_ko.xtb b/chrome/app/resources/generated_resources_ko.xtb
index b0c07664..2e9e51a 100644
--- a/chrome/app/resources/generated_resources_ko.xtb
+++ b/chrome/app/resources/generated_resources_ko.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">최적화</translation>
 <translation id="236141728043665931">마이크 액세스 항상 차단</translation>
 <translation id="2365507699358342471">이 사이트는 클립보드에 복사된 텍스트와 이미지에 액세스할 수 있습니다.</translation>
-<translation id="2367199180085172140">파일 공유 추가</translation>
 <translation id="2367972762794486313">앱 표시</translation>
 <translation id="2369105924912929484">추출된 페이지 콘텐츠 전환</translation>
 <translation id="2371076942591664043">완료되면 열기(&amp;D)</translation>
@@ -2786,7 +2785,7 @@
 <translation id="5255859108402770436">다시 로그인</translation>
 <translation id="5256861893479663409">모든 사이트에서</translation>
 <translation id="5258992782919386492">이 기기에 설치</translation>
-<translation id="5260334392110301220">스마트 인용문</translation>
+<translation id="5260334392110301220">스마트 인용</translation>
 <translation id="5260508466980570042">이메일 또는 비밀번호를 확인할 수 없습니다. 다시 시도해 주세요.</translation>
 <translation id="5261683757250193089">웹 스토어에서 열기</translation>
 <translation id="5264148714798105376">1분 정도 소요될 수 있습니다.</translation>
@@ -2821,7 +2820,7 @@
 <translation id="5301751748813680278">게스트로 로그인 중</translation>
 <translation id="5301954838959518834">확인</translation>
 <translation id="5302048478445481009">언어</translation>
-<translation id="5302932258331363306">대체 항목 표시</translation>
+<translation id="5302932258331363306">텍스트 대치</translation>
 <translation id="5305688511332277257">설치된 인증서 없음</translation>
 <translation id="5307030433605830021">지원되지 않는 소스</translation>
 <translation id="5308380583665731573">연결</translation>
@@ -3297,7 +3296,6 @@
 <translation id="6026047032548434446">앱을 설치하시겠습니까?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB 기기 연결됨</translation>
 <translation id="6029027682598229313">Linux 설치가 완료되었습니다.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] 삭제, 버튼</translation>
 <translation id="6029587122245504742">가장 느리게</translation>
 <translation id="6032912588568283682">파일 시스템</translation>
 <translation id="6038929619733116134">사이트에서 방해가 되거나 사용자를 현혹하는 광고를 표시하는 경우 광고를 차단합니다.</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">사용자 이름 또는 비밀번호가 잘못되어 PPP 인증에 실패했습니다.</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" />을(를) 사용하여 다시 시도</translation>
 <translation id="7974936243149753750">오버스캔</translation>
-<translation id="79766959863778284">XR 격리된 기기 서비스</translation>
 <translation id="7978412674231730200">비공개 키</translation>
 <translation id="7978450511781612192">Google 계정에서 로그아웃됩니다. 북마크, 방문 기록, 비밀번호 등이 더 이상 동기화되지 않습니다.</translation>
 <translation id="7979036127916589816">동기화 오류</translation>
@@ -5367,7 +5364,6 @@
 <translation id="9138978632494473300">다음 위치에 바로가기 추가:</translation>
 <translation id="9140067245205650184">지원되지 않는 기능 플래그(<ph name="BAD_FLAG" />)를 사용 중이므로 안정성과 보안에 문제가 발생합니다.</translation>
 <translation id="9143298529634201539">추천을 삭제하시겠습니까?</translation>
-<translation id="9147304170847707004">인증 방법</translation>
 <translation id="9147392381910171771">옵션(&amp;O)</translation>
 <translation id="9148058034647219655">나가기</translation>
 <translation id="9148126808321036104">다시 로그인</translation>
diff --git a/chrome/app/resources/generated_resources_lt.xtb b/chrome/app/resources/generated_resources_lt.xtb
index ec365b5..3bbdb1f1 100644
--- a/chrome/app/resources/generated_resources_lt.xtb
+++ b/chrome/app/resources/generated_resources_lt.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimizuoti</translation>
 <translation id="236141728043665931">Visada blokuoti prieigą prie mikrofono</translation>
 <translation id="2365507699358342471">Ši svetainė gali peržiūrėti tekstą ir vaizdus, nukopijuotus į iškarpinę.</translation>
-<translation id="2367199180085172140">Nustatyti failo bendrinimą</translation>
 <translation id="2367972762794486313">Rodyti programas</translation>
 <translation id="2369105924912929484">Perjungti skaityti pritaikyto puslapio turinį</translation>
 <translation id="2371076942591664043">Baigus &amp;atidaryti</translation>
@@ -3300,7 +3299,6 @@
 <translation id="6026047032548434446">Įdiegti programą?</translation>
 <translation id="6026819612896463875">„<ph name="WINDOW_TITLE" />“ – USB įrenginys prijungtas</translation>
 <translation id="6029027682598229313">„Linux“ įdiegta.</translation>
-<translation id="6029292188939175871">ištrinti [<ph name="FINGERPRINT_NAME" />], mygtukas</translation>
 <translation id="6029587122245504742">Lėčiausias</translation>
 <translation id="6032912588568283682">Failų sistema</translation>
 <translation id="6038929619733116134">Blokuoti, jei svetainėje rodomi nepageidaujami arba klaidinantys skelbimai</translation>
@@ -4601,7 +4599,6 @@
 <translation id="7973962044839454485">Nepavyko autentifikuoti PGP dėl netinkamo naudotojo vardo ar slaptažodžio</translation>
 <translation id="7974566588408714340">Dar kartą bandyti naudoti „<ph name="EXTENSIONNAME" />“</translation>
 <translation id="7974936243149753750">Sritis aplink vaizdo kraštus</translation>
-<translation id="79766959863778284">XR Isolated Device Service</translation>
 <translation id="7978412674231730200">Asmeninis raktas</translation>
 <translation id="7978450511781612192">Atlikę šį veiksmą būsite atjungti nuo „Google“ paskyrų. Žymės, istorija, slaptažodžiai ir kt. nebebus sinchronizuojama.</translation>
 <translation id="7979036127916589816">Sinchronizavimo klaida</translation>
@@ -5370,7 +5367,6 @@
 <translation id="9138978632494473300">Pridėti sparčiųjų klavišų nurodytose vietose:</translation>
 <translation id="9140067245205650184">Naudojate nepalaikomą funkcijos žymą: „<ph name="BAD_FLAG" />“. Tai neigiamai paveiks stabilumą ir saugą.</translation>
 <translation id="9143298529634201539">Pašalinti pasiūlymą?</translation>
-<translation id="9147304170847707004">Autentifikavimo metodas</translation>
 <translation id="9147392381910171771">&amp;Parinktys</translation>
 <translation id="9148058034647219655">Išeiti</translation>
 <translation id="9148126808321036104">Prisijungti dar kartą</translation>
diff --git a/chrome/app/resources/generated_resources_lv.xtb b/chrome/app/resources/generated_resources_lv.xtb
index 678e9e1..135559b 100644
--- a/chrome/app/resources/generated_resources_lv.xtb
+++ b/chrome/app/resources/generated_resources_lv.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimizēt</translation>
 <translation id="236141728043665931">Vienmēr bloķēt piekļuvi mikrofonam</translation>
 <translation id="2365507699358342471">Šī vietne var skatīt starpliktuvē kopēto tekstu un attēlus.</translation>
-<translation id="2367199180085172140">Pievienot failu kopīgošanu</translation>
 <translation id="2367972762794486313">Rādīt lietotnes</translation>
 <translation id="2369105924912929484">Pārslēgt filtrētu lapas saturu</translation>
 <translation id="2371076942591664043">Atvērt, kad esat beidzis</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Vai instalēt lietotni?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> — pievienota USB ierīce</translation>
 <translation id="6029027682598229313">Linux instalēšana ir pabeigta.</translation>
-<translation id="6029292188939175871">dzēst [<ph name="FINGERPRINT_NAME" />], poga</translation>
 <translation id="6029587122245504742">Lēnākais</translation>
 <translation id="6032912588568283682">Failu sistēma</translation>
 <translation id="6038929619733116134">Bloķēt, ja vietnē tiek rādītas traucējošas vai maldinošas reklāmas</translation>
@@ -4599,7 +4597,6 @@
 <translation id="7973962044839454485">PSP autentifikācija neizdevās nepareiza lietotājvārda un paroles dēļ.</translation>
 <translation id="7974566588408714340">Mēģināt vēlreiz, izmantojot paplašinājumu <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Attēla izvērse</translation>
-<translation id="79766959863778284">XR izolētas ierīces pakalpojums</translation>
 <translation id="7978412674231730200">Privātā atslēga</translation>
 <translation id="7978450511781612192">Tādējādi tiksiet izrakstīsiet no sava Google konta. Jūsu grāmatzīmes, vēsture, paroles, kā arī cita informācija vairs netiks sinhronizēta.</translation>
 <translation id="7979036127916589816">Sinhronizācijas kļūda</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">Pievienot saīsnes šādās vietās:</translation>
 <translation id="9140067245205650184">Jūs izmantojat neatbalstītu funkcijas karodziņu: <ph name="BAD_FLAG" />. Tas negatīvi ietekmē darbības stabilitāti un drošību.</translation>
 <translation id="9143298529634201539">Vai noņemt ieteikumu?</translation>
-<translation id="9147304170847707004">Autentifikācijas metode</translation>
 <translation id="9147392381910171771">&amp;Iespējas</translation>
 <translation id="9148058034647219655">Iziet</translation>
 <translation id="9148126808321036104">Pierakstieties vēlreiz</translation>
diff --git a/chrome/app/resources/generated_resources_ml.xtb b/chrome/app/resources/generated_resources_ml.xtb
index 9632e89..9aca52b 100644
--- a/chrome/app/resources/generated_resources_ml.xtb
+++ b/chrome/app/resources/generated_resources_ml.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">ഓപ്‌റ്റിമൈസ് ചെയ്യുക</translation>
 <translation id="236141728043665931">എപ്പോഴും മൈക്രോഫോൺ ആക്‌സസ്സ് തടയുക</translation>
 <translation id="2365507699358342471">ക്ലിപ്പ്‌ബോർഡിലേക്ക് പകർത്തിയിട്ടുള്ള ടെക്‌സ്‌റ്റും ചിത്രങ്ങളും ഈ സൈറ്റിന് കാണാനാകും.</translation>
-<translation id="2367199180085172140">ഫയൽ പങ്കിടൽ ചേർക്കുക</translation>
 <translation id="2367972762794486313">അപ്ലിക്കേഷനുകൾ കാണിക്കുക</translation>
 <translation id="2369105924912929484">ഡിസ്‌റ്റിൽ ചെയ്‌ത പേജ് ഉള്ളടക്കങ്ങൾ മാറ്റുക</translation>
 <translation id="2371076942591664043">ചെയ്തുകഴിയുമ്പോള്‍ &amp;തുറക്കുക</translation>
@@ -1310,7 +1309,7 @@
 <translation id="29488703364906173">ആധുനിക വെബിനായി സൃഷ്‌ടിച്ചിരിക്കുന്ന വേഗതയാർന്നതും ലളിതവും സുരക്ഷിതവുമായ വെബ് ബ്രൗസർ.</translation>
 <translation id="2949289451367477459">ലൊക്കേഷൻ ഉപയോഗിക്കുക. ലൊക്കേഷൻ അനുമതിയുള്ള ആപ്പുകളെയും സേവനങ്ങളെയും ഈ ഉപകരണത്തിന്‍റെ ലൊക്കേഷൻ ഉപയോഗിക്കാൻ അനുവദിക്കുക. Google, ഇടയ്ക്കിടെ ലൊക്കേഷൻ ഡാറ്റ ശേഖരിക്കുകയും, ലൊക്കേഷൻ കൃത്യതയും ലൊക്കേഷൻ അധിഷ്ഠിത സേവനങ്ങളും മെച്ചപ്പെടുത്താൻ, രഹസ്യസ്വഭാവത്തോടെ ഈ ഡാറ്റ ഉപയോഗിക്കുകയും ചെയ്തേക്കാം. <ph name="BEGIN_LINK1" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="2958721676848865875">പായ്‌ക്ക് വിപുലീകരണ മുന്നറിയിപ്പ്</translation>
-<translation id="2959842337402130152">സ്‌റ്റോറേജ് സ്‌പെയ്‌സ് ഇല്ലാത്തതിനാൽ പുനഃസ്ഥാപിക്കാപിക്കാനായില്ല. ഉപകരണത്തിൽ <ph name="SPACE_REQUIRED" /> ഇടമുണ്ടാക്കി വീണ്ടും ശ്രമിക്കുക.</translation>
+<translation id="2959842337402130152">സ്‌റ്റോറേജ് സ്‌പെയ്‌സ് ഇല്ലാത്തതിനാൽ പുനഃസ്ഥാപിക്കാനായില്ല. ഉപകരണത്തിൽ <ph name="SPACE_REQUIRED" /> ഇടമുണ്ടാക്കി വീണ്ടും ശ്രമിക്കുക.</translation>
 <translation id="296026337010986570">പൂര്‍ത്തിയായി! ദോഷകരമായ സോഫ്‌റ്റ്‌വെയര്‍ നീക്കം ചെയ്‌തു. വിപുലീകരണങ്ങള്‍ വീണ്ടും ഓണാക്കാന്‍, &lt;a href="chrome://extensions"&gt;വിപുലീകരണങ്ങള്‍&lt;/a&gt; സന്ദര്‍ശിക്കുക.</translation>
 <translation id="2961090598421146107"><ph name="CERTIFICATE_NAME" /> (വിപുലീകരണം നൽകി)</translation>
 <translation id="2961695502793809356">മുന്നോട്ട് പോകാൻ ക്ലിക്ക് ചെയ്യുക, ചരിത്രം കാണാന്‍ ഹോള്‍ഡ് ചെയ്യുക</translation>
@@ -3296,7 +3295,6 @@
 <translation id="6026047032548434446">ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യണോ?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB ഉപകരണം കണക്‌റ്റ് ചെയ്‌തു</translation>
 <translation id="6029027682598229313">Linux ഇൻസ്റ്റലേഷൻ പൂർത്തിയായി.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] ഇല്ലാതാക്കുക, ബട്ടണ്‍</translation>
 <translation id="6029587122245504742">ഏറ്റവും വേഗത ‌കുറഞ്ഞത്</translation>
 <translation id="6032912588568283682">ഫയല്‍ സിസ്റ്റം</translation>
 <translation id="6038929619733116134">സൈറ്റ്, അനാവശ്യമോ തെറ്റിദ്ധരിപ്പിക്കുന്നതോ ആയ പരസ്യങ്ങൾ കാണിക്കുന്നുണ്ടെങ്കിൽ ബ്ലോക്ക് ചെയ്യുക</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">തെറ്റായ ഉപയോക്തൃനാമമോ പാസ്‌വേഡോ കാരണം PPP പരിശോധിച്ചുറപ്പിക്കൽ പരാജയപ്പെട്ടു</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" /> ഉപയോഗിച്ച് വീണ്ടും ശ്രമിക്കുക</translation>
 <translation id="7974936243149753750">ഓവർസ്‌കാൻ</translation>
-<translation id="79766959863778284">XR മാറ്റി നിർത്തിയ ഉപകരണ സേവനം</translation>
 <translation id="7978412674231730200">സ്വകാര്യ കീ</translation>
 <translation id="7978450511781612192">ഇത് നിങ്ങളെ Google അക്കൗണ്ടുകളിൽ നിന്ന് സൈൻ ഔട്ട് ചെയ്യിക്കും. നിങ്ങളുടെ ബുക്ക്‌മാർക്കുകൾ, ചരിത്രം, പാ‌സ്‌വേഡുകൾ എന്നിവയും മറ്റും ഇനിയങ്ങോട്ട് സമന്വയിക്കില്ല.</translation>
 <translation id="7979036127916589816">സമന്വയ പിശക്</translation>
@@ -5366,7 +5363,6 @@
 <translation id="9138978632494473300">ഇനിപ്പറയുന്ന സ്ഥലങ്ങളിലേക്ക് കുറുക്കുവഴികൾ ചേർക്കുക:</translation>
 <translation id="9140067245205650184">നിങ്ങള്‍ ഒരു പിന്തുണയ്ക്കാത്ത ഫീച്ചർ ഫ്ലാഗ് ഉപയോഗിക്കുന്നു: <ph name="BAD_FLAG" />. സ്ഥിരതയെയും സുരക്ഷയെയും ബാധിക്കും.</translation>
 <translation id="9143298529634201539">നിർദ്ദേശം നീക്കം ചെയ്യണോ?</translation>
-<translation id="9147304170847707004">പരിശോധിച്ചുറപ്പിക്കൽ രീതി</translation>
 <translation id="9147392381910171771">&amp;ഓപ്ഷനുകള്‍</translation>
 <translation id="9148058034647219655">പുറത്തുകടക്കുക</translation>
 <translation id="9148126808321036104">വീണ്ടും പ്രവേശിക്കുക</translation>
diff --git a/chrome/app/resources/generated_resources_mr.xtb b/chrome/app/resources/generated_resources_mr.xtb
index b8ebc06..2d82747 100644
--- a/chrome/app/resources/generated_resources_mr.xtb
+++ b/chrome/app/resources/generated_resources_mr.xtb
@@ -914,7 +914,6 @@
 <translation id="236117173274098341">ऑप्टिमाइझ करा</translation>
 <translation id="236141728043665931">मायक्रोफोन अॅक्सेस नेहमी ब्लॉक करा</translation>
 <translation id="2365507699358342471">या पेजवर क्लिपबोर्डवर कॉपी केलेला मजकूर आणि इमेज दिसू शकतात.</translation>
-<translation id="2367199180085172140">फाइल शेअर जोडा</translation>
 <translation id="2367972762794486313">अ‍ॅप्स दर्शवा</translation>
 <translation id="2369105924912929484">डिस्टिल केलेले पेज आशय टॉगल करा</translation>
 <translation id="2371076942591664043">&amp;पूर्ण झाल्यानंतर उघडा</translation>
@@ -2415,7 +2414,7 @@
 <translation id="4662788913887017617">हा बुकमार्क आपल्या iPhone सह शेअर करा</translation>
 <translation id="4663373278480897665">कॅमेऱ्याला अनुमती आहे</translation>
 <translation id="4664482161435122549">PKCS #12 निर्यात एरर</translation>
-<translation id="4664736447097490764">करब्रोस कॉन्फिगरेशन फाइल येथे संपादित करा.</translation>
+<translation id="4664736447097490764">Kerberos कॉन्फिगरेशन फाइल येथे संपादित करा.</translation>
 <translation id="4665014895760275686">निर्माता</translation>
 <translation id="4665446389743427678"><ph name="SITE" /> द्वारे स्टोअर केलेला डेटा हटवला जाईल.</translation>
 <translation id="4668721319092543482"><ph name="PLUGIN_NAME" /> सुरू करण्यासाठीवर क्लिक करा</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">अ‍ॅप इंस्टॉल करायचे?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB डिव्हाइस कनेक्ट केले</translation>
 <translation id="6029027682598229313">Linux इंस्टॉलेशन पूर्ण झाले आहे.</translation>
-<translation id="6029292188939175871">हटवा [<ph name="FINGERPRINT_NAME" />], बटण</translation>
 <translation id="6029587122245504742">सर्वात धीमे</translation>
 <translation id="6032912588568283682">फाइल सिस्‍टिम</translation>
 <translation id="6038929619733116134">साइट अनाहूत किंवा दिशाभूल करणाऱ्या जाहिराती दाखवत असल्यास ब्लॉक करा</translation>
@@ -4165,7 +4163,7 @@
 <translation id="7340431621085453413"><ph name="FULLSCREEN_ORIGIN" /> आता फुल स्क्रीन आहे.</translation>
 <translation id="7340650977506865820">साइट तुमची स्क्रीन शेअर करत आहे</translation>
 <translation id="7341834142292923918">या साइटचा ॲक्सेस हवा आहे</translation>
-<translation id="7344488796804562294">प्रगत करब्रोस कॉन्फिगरेशन</translation>
+<translation id="7344488796804562294">प्रगत Kerberos कॉन्फिगरेशन</translation>
 <translation id="7345706641791090287">तुमचा पासवर्ड कन्फर्म करा</translation>
 <translation id="7346909386216857016">ठीक आहे, समजले</translation>
 <translation id="7347751611463936647">हे एक्स्टेंशन वापरण्यासाठी, "<ph name="EXTENSION_KEYWORD" />", नंतर TAB, नंतर तुमची आज्ञा किंवा शोध टाइप करा.</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">अयोग्य वापरकर्तानावामुळे किंवा पासवर्डमुळे PPP ऑथेंटिकेशन अयशस्वी झाले</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" /> वापरून पुन्हा प्रयत्न करून पहा</translation>
 <translation id="7974936243149753750">ओव्हरस्कॅन</translation>
-<translation id="79766959863778284">XR डिव्हाइस सेवा वेगळी करते</translation>
 <translation id="7978412674231730200">खाजगी की</translation>
 <translation id="7978450511781612192">हे तुम्हाला तुमच्या Google खात्यामधून साइन आउट करेल. तुमचे बुकमार्क, इतिहास, पासवर्ड आणि बरेच काही आता सिंक होणार नाही.</translation>
 <translation id="7979036127916589816">सिंक एरर</translation>
@@ -5368,7 +5365,6 @@
 <translation id="9138978632494473300">खालील ठिकाणी शॉर्टकट जोडा:</translation>
 <translation id="9140067245205650184">तुम्ही एक सपोर्ट नसलेला वैशिष्ट्य फ्लॅग वापरत आहात: <ph name="BAD_FLAG" />. स्थिरता आणि सुरक्षा प्रभावित होईल.</translation>
 <translation id="9143298529634201539">सूचना काढून टाकायची का?</translation>
-<translation id="9147304170847707004">ऑथेंटिकेशन पद्धत</translation>
 <translation id="9147392381910171771">&amp;पर्याय</translation>
 <translation id="9148058034647219655">निर्गमन</translation>
 <translation id="9148126808321036104">पुन्हा साइन इन करा </translation>
diff --git a/chrome/app/resources/generated_resources_ms.xtb b/chrome/app/resources/generated_resources_ms.xtb
index 50f06488..9fce39f 100644
--- a/chrome/app/resources/generated_resources_ms.xtb
+++ b/chrome/app/resources/generated_resources_ms.xtb
@@ -301,7 +301,7 @@
 <translation id="144283815522798837"><ph name="NUMBER_OF_ITEMS_SELECTED" /> dipilih</translation>
 <translation id="1444628761356461360">Tetapan ini diuruskan oleh pemilik peranti, <ph name="OWNER_EMAIL" />.</translation>
 <translation id="144518587530125858">Tidak dapat memuatkan '<ph name="IMAGE_PATH" />' untuk tema.</translation>
-<translation id="1445693676523799095">Ini mungkin mengambil masa yang agak lama</translation>
+<translation id="1445693676523799095">Persediaan ini mungkin mengambil masa yang agak lama</translation>
 <translation id="1451375123200651445">Laman Web, Fail Tunggal</translation>
 <translation id="1451917004835509682">Tambahkan Orang di Bawah Seliaan</translation>
 <translation id="1454223536435069390">A&amp;mbil tangkapan skrin</translation>
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimumkan</translation>
 <translation id="236141728043665931">Sentiasa sekat akses mikrofon</translation>
 <translation id="2365507699358342471">Tapak ini dapat melihat teks dan imej yang disalin ke papan keratan.</translation>
-<translation id="2367199180085172140">Tambah Perkongsian Fail</translation>
 <translation id="2367972762794486313">Paparkan apl</translation>
 <translation id="2369105924912929484">Togol kandungan halaman disuling</translation>
 <translation id="2371076942591664043">Buka apabila &amp;selesai</translation>
@@ -3300,7 +3299,6 @@
 <translation id="6026047032548434446">Pasang Apl?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - Peranti USB disambungkan</translation>
 <translation id="6029027682598229313">Pemasangan Linux selesai.</translation>
-<translation id="6029292188939175871">padamkan [<ph name="FINGERPRINT_NAME" />], butang</translation>
 <translation id="6029587122245504742">Paling perlahan</translation>
 <translation id="6032912588568283682">Sistem fail</translation>
 <translation id="6038929619733116134">Sekat jika tapak menyiarkan iklan yang mengganggu atau mengelirukan</translation>
@@ -4601,7 +4599,6 @@
 <translation id="7973962044839454485">Pengesahan PPP gagal disebabkan oleh nama pengguna atau kata laluan yang salah</translation>
 <translation id="7974566588408714340">Cuba semula menggunakan <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Terlebih imbas</translation>
-<translation id="79766959863778284">Perkhidmatan Peranti Terpencil XR</translation>
 <translation id="7978412674231730200">Kunci persendirian</translation>
 <translation id="7978450511781612192">Tindakan ini akan menyebabkan anda log keluar daripada Akaun Google anda. Penanda halaman, sejarah, kata laluan dan pelbagai lagi tidak akan disegerakkan lagi.</translation>
 <translation id="7979036127916589816">Ralat Segerak</translation>
@@ -5190,7 +5187,7 @@
 <translation id="8862003515646449717">Tukar kepada penyemak imbas yang pantas</translation>
 <translation id="8863753581171631212">Buka pautan dalam <ph name="APP" /> baharu</translation>
 <translation id="8864055848767439877">Berkongsi <ph name="TAB_NAME" /> ke <ph name="APP_NAME" /></translation>
-<translation id="8864458770072227512"><ph name="EMAIL" /> telah dialih keluar akaun daripada peranti ini</translation>
+<translation id="8864458770072227512"><ph name="EMAIL" /> telah dialih keluar daripada peranti ini</translation>
 <translation id="8868626022555786497">Sedang digunakan</translation>
 <translation id="8870318296973696995">Halaman utama</translation>
 <translation id="8870413625673593573">Ditutup Baru-baru Ini</translation>
@@ -5371,7 +5368,6 @@
 <translation id="9138978632494473300">Tambahkan pintasan ke tempat berikut:</translation>
 <translation id="9140067245205650184">Anda menggunakan bendera ciri yang tidak disokong: <ph name="BAD_FLAG" />. Kestabilan dan keselamatan akan terjejas.</translation>
 <translation id="9143298529634201539">Alih keluar cadangan?</translation>
-<translation id="9147304170847707004">Kaedah Pengesahan</translation>
 <translation id="9147392381910171771">&amp;Pilihan</translation>
 <translation id="9148058034647219655">Keluar</translation>
 <translation id="9148126808321036104">Log masuk semula</translation>
diff --git a/chrome/app/resources/generated_resources_nl.xtb b/chrome/app/resources/generated_resources_nl.xtb
index 056fc1b..0ec656d5 100644
--- a/chrome/app/resources/generated_resources_nl.xtb
+++ b/chrome/app/resources/generated_resources_nl.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimaliseren</translation>
 <translation id="236141728043665931">Microfoontoegang altijd blokkeren</translation>
 <translation id="2365507699358342471">Deze site kan tekst en afbeeldingen bekijken die naar het klembord zijn gekopieerd.</translation>
-<translation id="2367199180085172140">File Share toevoegen</translation>
 <translation id="2367972762794486313">Apps weergeven</translation>
 <translation id="2369105924912929484">Afgeleide paginacontent in-/uitschakelen</translation>
 <translation id="2371076942591664043">Openen wanneer geree&amp;d</translation>
@@ -2416,7 +2415,7 @@
 <translation id="4662788913887017617">Deze bladwijzer delen met je iPhone</translation>
 <translation id="4663373278480897665">Camera toegestaan</translation>
 <translation id="4664482161435122549">Fout bij exporteren van PKCS #12</translation>
-<translation id="4664736447097490764">Bewerk het Kerberos-configuratiebestand hier.</translation>
+<translation id="4664736447097490764">Bewerk hier het Kerberos-configuratiebestand.</translation>
 <translation id="4665014895760275686">Fabrikant</translation>
 <translation id="4665446389743427678">Alle gegevens die zijn opgeslagen door <ph name="SITE" />, worden verwijderd.</translation>
 <translation id="4668721319092543482">Klik om <ph name="PLUGIN_NAME" /> in te schakelen</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">App installeren?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" />: USB-apparaat verbonden</translation>
 <translation id="6029027682598229313">Linux-installatie is voltooid.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] verwijderen, knop</translation>
 <translation id="6029587122245504742">Langzaamst</translation>
 <translation id="6032912588568283682">Bestandssysteem</translation>
 <translation id="6038929619733116134">Blokkeren als site opdringerige of misleidende advertenties weergeeft</translation>
@@ -4354,7 +4352,7 @@
 <translation id="7647403192093989392">Geen recente activiteiten</translation>
 <translation id="7648142322539582331">Maak verbinding met internet om ouderlijk toezicht in te stellen</translation>
 <translation id="7648992873808071793">Bestanden op dit apparaat bewaren</translation>
-<translation id="7649070708921625228">Help</translation>
+<translation id="7649070708921625228">Hulp</translation>
 <translation id="7650511557061837441">'<ph name="TRIGGERING_EXTENSION_NAME" />' wil '<ph name="EXTENSION_NAME" />' verwijderen.</translation>
 <translation id="7652808307838961528">Persoon bewerken: <ph name="PROFILE_NAME" /></translation>
 <translation id="765293928828334535">Apps, extensies en gebruikersscripts kunnen niet worden toegevoegd vanaf deze website</translation>
@@ -4599,7 +4597,6 @@
 <translation id="7973962044839454485">PPP-verificatie mislukt wegens een onjuiste gebruikersnaam of wachtwoord</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" /> opnieuw proberen te gebruiken</translation>
 <translation id="7974936243149753750">Overscan</translation>
-<translation id="79766959863778284">Geïsoleerde XR-apparaatservice</translation>
 <translation id="7978412674231730200">Privésleutel</translation>
 <translation id="7978450511781612192">Hiermee word je uitgelogd van je Google-accounts. Onder meer je bladwijzers, geschiedenis en wachtwoorden worden niet meer gesynchroniseerd.</translation>
 <translation id="7979036127916589816">Synchronisatiefout</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">Snelle links toevoegen op de volgende locaties:</translation>
 <translation id="9140067245205650184">Je gebruikt een niet-ondersteunde functiemarkering: <ph name="BAD_FLAG" />. De stabiliteit en beveiliging zullen hieronder lijden.</translation>
 <translation id="9143298529634201539">Suggestie verwijderen?</translation>
-<translation id="9147304170847707004">Verificatiemethode</translation>
 <translation id="9147392381910171771">&amp;Opties</translation>
 <translation id="9148058034647219655">Sluiten</translation>
 <translation id="9148126808321036104">Opnieuw inloggen</translation>
diff --git a/chrome/app/resources/generated_resources_no.xtb b/chrome/app/resources/generated_resources_no.xtb
index f622a2bd..7c9ba2cf 100644
--- a/chrome/app/resources/generated_resources_no.xtb
+++ b/chrome/app/resources/generated_resources_no.xtb
@@ -606,7 +606,7 @@
 <translation id="1895252664692693738"><ph name="TIME_LEFT" /> igjen</translation>
 <translation id="1895658205118569222">Avslutning</translation>
 <translation id="1895934970388272448">Du må bekrefte registreringen på skriveren for å fullføre denne prosessen – sjekk det nå.</translation>
-<translation id="1899826437968063457">VM for programtillegg trenger tillatelse til å kjøre</translation>
+<translation id="1899826437968063457">Plugin VM trenger tillatelse til å kjøre</translation>
 <translation id="1901303067676059328">Marker &amp;alt</translation>
 <translation id="1902576642799138955">Gyldighetsperiode</translation>
 <translation id="1904394285866191268">{NUM_TABS,plural, =1{Kutt lyden for fanen}other{Kutt lyden for fanene}}</translation>
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">Optimaliser</translation>
 <translation id="236141728043665931">Blokkér alltid bruk av mikrofonen</translation>
 <translation id="2365507699358342471">Dette nettstedet kan se tekst og bilder som er kopiert til utklippstavlen.</translation>
-<translation id="2367199180085172140">Legg til fildeling</translation>
 <translation id="2367972762794486313">Vis apper</translation>
 <translation id="2369105924912929484">Slå av/på destillert sideinnhold</translation>
 <translation id="2371076942591664043">Åpne når ne&amp;dlastingen er ferdig</translation>
@@ -1309,7 +1308,7 @@
 <translation id="29488703364906173">En rask, enkel og sikker nettleser, utviklet for et moderne Internett.</translation>
 <translation id="2949289451367477459">Bruk posisjon. Tillat at apper og tjenester med posisjonstillatelse bruker posisjonen til enheten. Google kan samle inn posisjonsdata med jevne mellomrom og bruke disse dataene til å forbedre posisjonsnøyaktigheten og posisjonsbaserte tjenester på en anonym måte. <ph name="BEGIN_LINK1" />Finn ut mer<ph name="END_LINK1" /></translation>
 <translation id="2958721676848865875">Advarsel om pakkeutvidelse</translation>
-<translation id="2959842337402130152">Det er ikke nok lagringplass til å gjenopprette. Frigjør <ph name="SPACE_REQUIRED" /> på enheten, og prøv på nytt.</translation>
+<translation id="2959842337402130152">Det er ikke nok lagringsplass til å gjenopprette. Frigjør <ph name="SPACE_REQUIRED" /> på enheten, og prøv på nytt.</translation>
 <translation id="296026337010986570">Ferdig! Skadelig programvare er fjernet. For å slå på utvidelser igjen, gå til &lt;a href="chrome://extensions"&gt;Utvidelser&lt;/a&gt;.</translation>
 <translation id="2961090598421146107"><ph name="CERTIFICATE_NAME" /> (fra utvidelse)</translation>
 <translation id="2961695502793809356">Klikk for å gå fremover – hold for å se logg</translation>
@@ -2371,7 +2370,7 @@
 <translation id="4596295440756783523">Du har registrerte sertifikater som identifiserer disse tjenerne</translation>
 <translation id="4598556348158889687">Lagringsbehandling</translation>
 <translation id="4598776695426288251">Wi-Fi er tilgjengelig via flere enheter</translation>
-<translation id="4599134080475764833">VM for programtillegg er klar til bruk</translation>
+<translation id="4599134080475764833">Plugin VM er klar til bruk</translation>
 <translation id="4602466770786743961">Tillat alltid at <ph name="HOST" /> bruker kameraet og mikrofonen</translation>
 <translation id="4608500690299898628">&amp;Finn</translation>
 <translation id="4608520674724523647">Illustrasjon av vellykket registrering</translation>
@@ -2781,7 +2780,7 @@
 <translation id="5255859108402770436">Logg på igjen</translation>
 <translation id="5256861893479663409">På alle nettsteder</translation>
 <translation id="5258992782919386492">Installer på denne enheten</translation>
-<translation id="5260334392110301220">Smarte hermetegn</translation>
+<translation id="5260334392110301220">Smarte anførselstegn</translation>
 <translation id="5260508466980570042">Beklager, e-postadressen eller passordet kan ikke verifiseres. Prøv på nytt.</translation>
 <translation id="5261683757250193089">Åpne i Nettmarked</translation>
 <translation id="5264148714798105376">Dette kan ta omtrent ett minutt.</translation>
@@ -3292,7 +3291,6 @@
 <translation id="6026047032548434446">Vil du installere appen?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – en USB-enhet er koblet til</translation>
 <translation id="6029027682598229313">Linux er ferdig installert.</translation>
-<translation id="6029292188939175871">slett [<ph name="FINGERPRINT_NAME" />], knapp</translation>
 <translation id="6029587122245504742">Langsomst</translation>
 <translation id="6032912588568283682">Filsystem</translation>
 <translation id="6038929619733116134">Blokkér hvis nettstedet viser forstyrrende eller villedende annonser</translation>
@@ -4591,7 +4589,6 @@
 <translation id="7973962044839454485">PPP-autentiseringen mislyktes på grunn av feil brukernavn eller passord</translation>
 <translation id="7974566588408714340">Prøv på nytt med <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Overskanning</translation>
-<translation id="79766959863778284">XR-isolert enhetstjeneste</translation>
 <translation id="7978412674231730200">Privatnøkkel</translation>
 <translation id="7978450511781612192">Dette logger deg av Google-kontoene dine. Bokmerkene dine, loggen din, passordene dine med mer blir ikke lenger synkronisert.</translation>
 <translation id="7979036127916589816">Feil ved synkronisering</translation>
@@ -5360,7 +5357,6 @@
 <translation id="9138978632494473300">Legg til hurtigtaster til disse stedene:</translation>
 <translation id="9140067245205650184">Du bruker et funksjonsflagg som ikke støttes: <ph name="BAD_FLAG" />. Dette reduserer stabiliteten og sikkerheten.</translation>
 <translation id="9143298529634201539">Vil du fjerne forslaget?</translation>
-<translation id="9147304170847707004">Autentiseringsmetode</translation>
 <translation id="9147392381910171771">&amp;Alternativer</translation>
 <translation id="9148058034647219655">Avslutt</translation>
 <translation id="9148126808321036104">Logg på igjen</translation>
diff --git a/chrome/app/resources/generated_resources_pl.xtb b/chrome/app/resources/generated_resources_pl.xtb
index 70d479b..6f71173 100644
--- a/chrome/app/resources/generated_resources_pl.xtb
+++ b/chrome/app/resources/generated_resources_pl.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optymalizuj</translation>
 <translation id="236141728043665931">Zawsze blokuj dostęp do mikrofonu</translation>
 <translation id="2365507699358342471">Ta witryna ma dostęp do tekstu i obrazów skopiowanych do schowka.</translation>
-<translation id="2367199180085172140">Dodaj udział plików</translation>
 <translation id="2367972762794486313">Pokaż aplikacje</translation>
 <translation id="2369105924912929484">Przełącz wydestylowaną zawartość strony</translation>
 <translation id="2371076942591664043">Otwórz po &amp;zakończeniu</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Zainstalować aplikację?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – połączono z urządzeniem USB</translation>
 <translation id="6029027682598229313">Instalacja Linuksa została ukończona.</translation>
-<translation id="6029292188939175871">usuń odcisk palca [<ph name="FINGERPRINT_NAME" />], przycisk</translation>
 <translation id="6029587122245504742">Najwolniej</translation>
 <translation id="6032912588568283682">System plików</translation>
 <translation id="6038929619733116134">Blokuj, jeśli na stronie wyświetlają się uciążliwe lub wprowadzające w błąd reklamy</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">Uwierzytelnianie PPP nie powiodło się z powodu nieprawidłowej nazwy użytkownika lub hasła</translation>
 <translation id="7974566588408714340">Ponów za pomocą rozszerzenia <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Nadmiarowość obrazu</translation>
-<translation id="79766959863778284">Usługa izolowanego urządzenia XR</translation>
 <translation id="7978412674231730200">Klucz prywatny</translation>
 <translation id="7978450511781612192">Spowoduje to wylogowanie Cię z Twoich kont Google. Twoje zakładki, historia, hasła i inne dane przestaną być synchronizowane.</translation>
 <translation id="7979036127916589816">Błąd synchronizacji</translation>
@@ -5370,7 +5367,6 @@
 <translation id="9138978632494473300">Dodaj skróty do tych miejsc:</translation>
 <translation id="9140067245205650184">Korzystasz z nieobsługiwanej flagi funkcji: <ph name="BAD_FLAG" />. Ma to negatywny wpływ na stabilność i zabezpieczenia.</translation>
 <translation id="9143298529634201539">Usunąć sugestię?</translation>
-<translation id="9147304170847707004">Metoda uwierzytelniania</translation>
 <translation id="9147392381910171771">&amp;Opcje</translation>
 <translation id="9148058034647219655">Zakończ</translation>
 <translation id="9148126808321036104">Zaloguj się ponownie</translation>
diff --git a/chrome/app/resources/generated_resources_pt-BR.xtb b/chrome/app/resources/generated_resources_pt-BR.xtb
index 1984e3e..8b7b6e6 100644
--- a/chrome/app/resources/generated_resources_pt-BR.xtb
+++ b/chrome/app/resources/generated_resources_pt-BR.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Otimizar</translation>
 <translation id="236141728043665931">Sempre bloquear o acesso ao microfone</translation>
 <translation id="2365507699358342471">Este site pode ver textos e imagens copiados para a área de transferência.</translation>
-<translation id="2367199180085172140">Adicionar compartilhamento de arquivos</translation>
 <translation id="2367972762794486313">Mostrar aplicativos</translation>
 <translation id="2369105924912929484">Alternar conteúdo da página extraído</translation>
 <translation id="2371076942591664043">Abrir quando estiver &amp;concluído</translation>
@@ -3300,7 +3299,6 @@
 <translation id="6026047032548434446">Instalar app?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - Dispositivo USB conectado</translation>
 <translation id="6029027682598229313">A instalação do Linux foi concluída.</translation>
-<translation id="6029292188939175871">botão excluir [<ph name="FINGERPRINT_NAME" />]</translation>
 <translation id="6029587122245504742">Mais lenta</translation>
 <translation id="6032912588568283682">Sistema de arquivos</translation>
 <translation id="6038929619733116134">Bloquear se o site mostrar anúncios invasivos ou enganosos</translation>
@@ -4602,7 +4600,6 @@
 <translation id="7973962044839454485">Falha da autenticação PPC devido a senha ou nome de usuário incorreto</translation>
 <translation id="7974566588408714340">Tentar novamente usando <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Overscan</translation>
-<translation id="79766959863778284">Serviço de dispositivo isolado XR</translation>
 <translation id="7978412674231730200">Chave privada</translation>
 <translation id="7978450511781612192">Isso desconectará você das suas Contas do Google. Seus favoritos, histórico, senhas e outras configurações não serão mais sincronizados.</translation>
 <translation id="7979036127916589816">Erro de sincronização</translation>
@@ -5372,7 +5369,6 @@
 <translation id="9138978632494473300">Adiciona atalhos aos seguintes lugares:</translation>
 <translation id="9140067245205650184">Sinalizador de recurso incompatível usado: <ph name="BAD_FLAG" />. Estabilidade e segurança afetadas.</translation>
 <translation id="9143298529634201539">Remover sugestão?</translation>
-<translation id="9147304170847707004">Método de autenticação</translation>
 <translation id="9147392381910171771">&amp;Opções</translation>
 <translation id="9148058034647219655">Sair</translation>
 <translation id="9148126808321036104">Faça login novamente</translation>
@@ -5431,7 +5427,7 @@
 <translation id="939598580284253335">Inserir senha</translation>
 <translation id="939736085109172342">Nova pasta</translation>
 <translation id="942532530371314860">O app <ph name="APP_NAME" /> está compartilhando áudio e uma guia do Chrome.</translation>
-<translation id="945166830402967374">Entre em contato com o administrador do dispositivo da sua organização</translation>
+<translation id="945166830402967374">Entre em contato com o administrador de dispositivos da sua organização</translation>
 <translation id="945522503751344254">Enviar comentários</translation>
 <translation id="947329552760389097">&amp;Inspecionar elementos</translation>
 <translation id="952992212772159698">Desativado</translation>
diff --git a/chrome/app/resources/generated_resources_pt-PT.xtb b/chrome/app/resources/generated_resources_pt-PT.xtb
index 5fe0a85..6201ca0a 100644
--- a/chrome/app/resources/generated_resources_pt-PT.xtb
+++ b/chrome/app/resources/generated_resources_pt-PT.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Otimizar</translation>
 <translation id="236141728043665931">Bloquear sempre o acesso ao microfone</translation>
 <translation id="2365507699358342471">Este site pode ver o texto e as imagens copiados para a área de transferência.</translation>
-<translation id="2367199180085172140">Adicionar partilha de ficheiros</translation>
 <translation id="2367972762794486313">Mostrar aplicações</translation>
 <translation id="2369105924912929484">Ativar/desativar conteúdos da página destilados</translation>
 <translation id="2371076942591664043">Abrir quando estiver concluí&amp;do</translation>
@@ -3300,7 +3299,6 @@
 <translation id="6026047032548434446">Pretende instalar a aplicação?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – dispositivo USB ligado</translation>
 <translation id="6029027682598229313">A instalação do Linux está concluída.</translation>
-<translation id="6029292188939175871">eliminar [<ph name="FINGERPRINT_NAME" />], botão</translation>
 <translation id="6029587122245504742">A mais lenta</translation>
 <translation id="6032912588568283682">Sistema de ficheiros</translation>
 <translation id="6038929619733116134">Bloquear se o site apresentar anúncios intrusivos ou enganadores</translation>
@@ -4601,7 +4599,6 @@
 <translation id="7973962044839454485">Falha na autenticação PPP devido a um nome de utilizador ou palavra-passe incorretos</translation>
 <translation id="7974566588408714340">Tente novamente com <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Overscan</translation>
-<translation id="79766959863778284">Serviço de dispositivos isolados XR</translation>
 <translation id="7978412674231730200">Chave privada</translation>
 <translation id="7978450511781612192">Deste modo, termina sessão nas suas Contas Google. Os marcadores, o histórico, as palavras-passe e muito mais deixam de ser sincronizados.</translation>
 <translation id="7979036127916589816">Erro de sincronização</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">Adicionar atalhos aos seguintes locais:</translation>
 <translation id="9140067245205650184">Está a utilizar um sinalizador de funcionalidade não suportado: <ph name="BAD_FLAG" />. A estabilidade e a segurança serão afetadas.</translation>
 <translation id="9143298529634201539">Pretende remover a sugestão?</translation>
-<translation id="9147304170847707004">Método de autenticação</translation>
 <translation id="9147392381910171771">&amp;Opções</translation>
 <translation id="9148058034647219655">Sair</translation>
 <translation id="9148126808321036104">Iniciar sessão novamente</translation>
diff --git a/chrome/app/resources/generated_resources_ro.xtb b/chrome/app/resources/generated_resources_ro.xtb
index f2649cbb4..d0ee245 100644
--- a/chrome/app/resources/generated_resources_ro.xtb
+++ b/chrome/app/resources/generated_resources_ro.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimizează</translation>
 <translation id="236141728043665931">Blocați întotdeauna accesul la microfon</translation>
 <translation id="2365507699358342471">Acest site poate să vadă textul și imaginile copiate în clipboard.</translation>
-<translation id="2367199180085172140">Adaugă un dispozitiv de stocare în rețea</translation>
 <translation id="2367972762794486313">Afișați aplicații</translation>
 <translation id="2369105924912929484">Comută conținutul paginii convertite</translation>
 <translation id="2371076942591664043">Deschide când s-a &amp;descărcat</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Instalezi aplicația?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – dispozitivul USB a fost conectat</translation>
 <translation id="6029027682598229313">Instalarea Linux s-a finalizat.</translation>
-<translation id="6029292188939175871">șterge [<ph name="FINGERPRINT_NAME" />], buton</translation>
 <translation id="6029587122245504742">Cea mai lentă</translation>
 <translation id="6032912588568283682">Sistem de fișiere</translation>
 <translation id="6038929619733116134">Blochează dacă site-ul afișează anunțuri deranjante sau înșelătoare</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">Autentificarea PPP nu a reușit, din cauza unui nume de utilizator sau a unei parole greșite</translation>
 <translation id="7974566588408714340">Reîncearcă folosind <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Suprascanare</translation>
-<translation id="79766959863778284">Serviciul de dispozitive izolate XR</translation>
 <translation id="7978412674231730200">Cheie privată</translation>
 <translation id="7978450511781612192">Astfel, te vei deconecta de la Conturile Google. Marcajele, istoricul, parolele și alte setări nu vor mai fi sincronizate.</translation>
 <translation id="7979036127916589816">Eroare de sincronizare</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">Adaugă comenzi rapide către următoarele locații:</translation>
 <translation id="9140067245205650184">Folosești un semnalizator de funcție neacceptat: <ph name="BAD_FLAG" />. Stabilitatea și securitatea vor fi afectate.</translation>
 <translation id="9143298529634201539">Elimini sugestia?</translation>
-<translation id="9147304170847707004">Metodă de autentificare</translation>
 <translation id="9147392381910171771">&amp;Opțiuni</translation>
 <translation id="9148058034647219655">Ieși</translation>
 <translation id="9148126808321036104">Conectează-te din nou</translation>
diff --git a/chrome/app/resources/generated_resources_ru.xtb b/chrome/app/resources/generated_resources_ru.xtb
index 66acf1069..4aa4940 100644
--- a/chrome/app/resources/generated_resources_ru.xtb
+++ b/chrome/app/resources/generated_resources_ru.xtb
@@ -914,7 +914,6 @@
 <translation id="236117173274098341">Оптимизировать</translation>
 <translation id="236141728043665931">Всегда блокировать доступ к микрофону</translation>
 <translation id="2365507699358342471">У этого сайта есть доступ к тексту и изображениям, скопированным в буфер обмена</translation>
-<translation id="2367199180085172140">Добавить файл в общий доступ.</translation>
 <translation id="2367972762794486313">Показать сервисы</translation>
 <translation id="2369105924912929484">Переключиться на сжатый вид страницы</translation>
 <translation id="2371076942591664043">Открыть по &amp;завершении</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Установить приложение?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" />: подключено USB-устройство</translation>
 <translation id="6029027682598229313">Установка Linux завершена.</translation>
-<translation id="6029292188939175871">Кнопка удалить [<ph name="FINGERPRINT_NAME" />]</translation>
 <translation id="6029587122245504742">Минимальная</translation>
 <translation id="6032912588568283682">Файловая система</translation>
 <translation id="6038929619733116134">Блокировать, если сайт показывает навязчивую или вводящую в заблуждение рекламу.</translation>
@@ -3918,7 +3916,7 @@
 <translation id="6985235333261347343">Агент восстановления ключей Microsoft</translation>
 <translation id="698524779381350301">Автоматически разрешать доступ к следующим сайтам</translation>
 <translation id="6985607387932385770">Принтеры</translation>
-<translation id="6990778048354947307">Темная тема</translation>
+<translation id="6990778048354947307">Тёмная тема</translation>
 <translation id="6991665348624301627">Выбор места назначения</translation>
 <translation id="6992554835374084304">Включите расширенную проверку правописания</translation>
 <translation id="6997642619627518301"><ph name="NAME_PH" /> – Журнал активности</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">Не удалось выполнить аутентификацию PPP: неверное имя пользователя или пароль</translation>
 <translation id="7974566588408714340">Подключиться через <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Каемка экрана</translation>
-<translation id="79766959863778284">XR Isolated Device Service</translation>
 <translation id="7978412674231730200">Закрытый ключ</translation>
 <translation id="7978450511781612192">Вы выйдете из аккаунта Google. Закладки, история, пароли и другие параметры перестанут синхронизироваться с ним.</translation>
 <translation id="7979036127916589816">Ошибка синхронизации</translation>
@@ -5368,7 +5365,6 @@
 <translation id="9138978632494473300">Куда добавить ярлыки:</translation>
 <translation id="9140067245205650184">Вы используете неподдерживаемый флаг функции: <ph name="BAD_FLAG" />. Стабильность и безопасность будут нарушены.</translation>
 <translation id="9143298529634201539">Удалить подсказку?</translation>
-<translation id="9147304170847707004">Метод аутентификации</translation>
 <translation id="9147392381910171771">&amp;Параметры</translation>
 <translation id="9148058034647219655">Выйти</translation>
 <translation id="9148126808321036104">Повторите вход</translation>
diff --git a/chrome/app/resources/generated_resources_sk.xtb b/chrome/app/resources/generated_resources_sk.xtb
index 188dd0f3..3ad85836 100644
--- a/chrome/app/resources/generated_resources_sk.xtb
+++ b/chrome/app/resources/generated_resources_sk.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimalizovať</translation>
 <translation id="236141728043665931">Vždy blokovať prístup k mikrofónu</translation>
 <translation id="2365507699358342471">Tento web má prístup k textu a obrázkom skopírovaným do schránky.</translation>
-<translation id="2367199180085172140">Pridať zdieľanie súboru</translation>
 <translation id="2367972762794486313">Zobraziť aplikácie</translation>
 <translation id="2369105924912929484">Prepnúť obsah vyčistených stránok</translation>
 <translation id="2371076942591664043">Po stiahnutí otvoriť</translation>
@@ -3297,7 +3296,6 @@
 <translation id="6026047032548434446">Inštalovať aplikáciu?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – Bolo pripojené zariadenie USB</translation>
 <translation id="6029027682598229313">Inštalácia systému Linux bola dokončená.</translation>
-<translation id="6029292188939175871">odstrániť [<ph name="FINGERPRINT_NAME" />], tlačidlo</translation>
 <translation id="6029587122245504742">Najpomalšia</translation>
 <translation id="6032912588568283682">Systém súborov</translation>
 <translation id="6038929619733116134">Blokovať, ak web zobrazuje obťažujúce alebo zavádzajúce reklamy</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">Overenie PKS zlyhalo z dôvodu nesprávneho používateľského mena alebo hesla</translation>
 <translation id="7974566588408714340">Skúsiť znova pomocou rozšírenia <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Presah obsahu</translation>
-<translation id="79766959863778284">Služba oddelených zariadení XR</translation>
 <translation id="7978412674231730200">Súkromný kľúč</translation>
 <translation id="7978450511781612192">Táto možnosť vás odhlási z účtov Google. Záložky, história, heslá a ďalší obsah sa prestanú synchronizovať.</translation>
 <translation id="7979036127916589816">Chyba synchronizácie</translation>
@@ -5366,7 +5363,6 @@
 <translation id="9138978632494473300">Pridať odkazy na nasledujúce miesta:</translation>
 <translation id="9140067245205650184">Používate nepodporovaný príznak funkcie: <ph name="BAD_FLAG" />. Prejaví sa to na nižšej stabilite a horšom zabezpečení</translation>
 <translation id="9143298529634201539">Chcete odstrániť návrh?</translation>
-<translation id="9147304170847707004">Spôsob overenia</translation>
 <translation id="9147392381910171771">&amp;Možnosti</translation>
 <translation id="9148058034647219655">Ukončiť</translation>
 <translation id="9148126808321036104">Znova sa prihlásiť</translation>
diff --git a/chrome/app/resources/generated_resources_sl.xtb b/chrome/app/resources/generated_resources_sl.xtb
index 4697382..c6591da1 100644
--- a/chrome/app/resources/generated_resources_sl.xtb
+++ b/chrome/app/resources/generated_resources_sl.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimizacija</translation>
 <translation id="236141728043665931">Vedno prepreči dostop do mikrofona</translation>
 <translation id="2365507699358342471">To spletno mesto si ne more ogledati besedila in slik, kopiranih v odložišče.</translation>
-<translation id="2367199180085172140">Dodajanje naprave za skupno rabo datotek</translation>
 <translation id="2367972762794486313">Pokaži aplikacije</translation>
 <translation id="2369105924912929484">Preklop strnjene vsebine na strani</translation>
 <translation id="2371076942591664043">Odpri, ko je &amp;dokončano</translation>
@@ -3301,7 +3300,6 @@
 <translation id="6026047032548434446">Želite namestiti aplikacijo?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – naprava USB povezana</translation>
 <translation id="6029027682598229313">Namestitev Linuxa je dokončana.</translation>
-<translation id="6029292188939175871">izbriši [<ph name="FINGERPRINT_NAME" />], gumb</translation>
 <translation id="6029587122245504742">Najpočasnejša</translation>
 <translation id="6032912588568283682">Datotečni sistem</translation>
 <translation id="6038929619733116134">Blokiraj, če spletno mesto prikazuje vsiljive ali zavajajoče oglase</translation>
@@ -4602,7 +4600,6 @@
 <translation id="7973962044839454485">Preverjanje pristnosti PPP ni uspelo zaradi napačnega uporabniškega imena ali gesla</translation>
 <translation id="7974566588408714340">Poskusi znova z razširitvijo <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Upodabljanje čez rob zaslona</translation>
-<translation id="79766959863778284">Storitev osamljene naprave XR</translation>
 <translation id="7978412674231730200">Zasebni ključ</translation>
 <translation id="7978450511781612192">S tem boste odjavljeni iz Google Računov. Zaznamki, zgodovina, gesla in drugi podatki ne bodo več sinhronizirani.</translation>
 <translation id="7979036127916589816">Sinhronizacijska napaka</translation>
@@ -5371,7 +5368,6 @@
 <translation id="9138978632494473300">Dodajte bližnjice tem mestom:</translation>
 <translation id="9140067245205650184">Uporabljate nepodprto zastavico funkcije: <ph name="BAD_FLAG" />. Ogroženi bosta stabilnost in varnost.</translation>
 <translation id="9143298529634201539">Želite odstraniti predlog?</translation>
-<translation id="9147304170847707004">Način preverjanja pristnosti</translation>
 <translation id="9147392381910171771">&amp;Možnosti</translation>
 <translation id="9148058034647219655">Zapri</translation>
 <translation id="9148126808321036104">Prijavite se znova</translation>
diff --git a/chrome/app/resources/generated_resources_sr.xtb b/chrome/app/resources/generated_resources_sr.xtb
index f62c480..81276d5 100644
--- a/chrome/app/resources/generated_resources_sr.xtb
+++ b/chrome/app/resources/generated_resources_sr.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">Оптимизуј</translation>
 <translation id="236141728043665931">Увек блокирај приступ микрофону</translation>
 <translation id="2365507699358342471">Овај сајт може да види текст и слике који су копирани у привремену меморију.</translation>
-<translation id="2367199180085172140">Додајте дељење датотека</translation>
 <translation id="2367972762794486313">Прикажите апликације</translation>
 <translation id="2369105924912929484">Укључи/искључи дестиловани садржај странице</translation>
 <translation id="2371076942591664043">Отвори кад буде &amp;довршено</translation>
@@ -1100,7 +1099,7 @@
 <translation id="2649045351178520408">ASCII датотека шифрована методом „Base64“, ланац сертификата</translation>
 <translation id="2653033005692233957">Претрага није успела</translation>
 <translation id="2653266418988778031">Ако избришете сертификат ауторитета за издавање сертификата, прегледач више неће веровати сертификатима које је тај ауторитет издао.</translation>
-<translation id="2653275834716714682">Text Replacement (Замена текста)</translation>
+<translation id="2653275834716714682">Замена текста</translation>
 <translation id="2653659639078652383">Пошаљи</translation>
 <translation id="265390580714150011">Вредност поља</translation>
 <translation id="2654166010170466751">Дозволи сајтовима да инсталирају обрађиваче плаћања</translation>
@@ -2784,7 +2783,7 @@
 <translation id="5255859108402770436">Пријавите се поново</translation>
 <translation id="5256861893479663409">На свим сајтовима</translation>
 <translation id="5258992782919386492">Инсталирај на овом уређају</translation>
-<translation id="5260334392110301220">Smart Quotes (Паметни наводи)</translation>
+<translation id="5260334392110301220">Паметни наводници</translation>
 <translation id="5260508466980570042">Жао нам је, нисмо успели да верификујемо имејл или лозинку. Пробајте поново.</translation>
 <translation id="5261683757250193089">Отвори у Веб-продавници</translation>
 <translation id="5264148714798105376">Ово ће потрајати минут или два.</translation>
@@ -2819,7 +2818,7 @@
 <translation id="5301751748813680278">Приступате као гост.</translation>
 <translation id="5301954838959518834">Важи</translation>
 <translation id="5302048478445481009">Језик</translation>
-<translation id="5302932258331363306">Show Substitutions (Прикажи замене)</translation>
+<translation id="5302932258331363306">Прикажи замене</translation>
 <translation id="5305688511332277257">Ништа није инсталирано</translation>
 <translation id="5307030433605830021">Извор није подржан</translation>
 <translation id="5308380583665731573">Повезивање</translation>
@@ -3297,7 +3296,6 @@
 <translation id="6026047032548434446">Желите ли да инсталирате апликацију?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – USB уређај је повезан</translation>
 <translation id="6029027682598229313">Инсталација Linux-а је завршена.</translation>
-<translation id="6029292188939175871">избришите [<ph name="FINGERPRINT_NAME" />], дугме</translation>
 <translation id="6029587122245504742">Најспорије</translation>
 <translation id="6032912588568283682">Систем датотека</translation>
 <translation id="6038929619733116134">Блокирај ако сајт приказује огласе који ометају активности или обмањујуће огласе</translation>
@@ -4014,7 +4012,7 @@
 <translation id="7120865473764644444">Успостављање везе са сервером за синхронизацију није успело. Покушава се поново…</translation>
 <translation id="7121362699166175603">Брише историју и аутоматска довршавања у траци за адресу. Google налог може да има друге облике историје прегледања на <ph name="BEGIN_LINK" />myactivity.google.com<ph name="END_LINK" />.</translation>
 <translation id="7121389946694989825">Шаљите податке о коришћењу и дијагностичке податке. Овај уређај тренутно аутоматски шаље податке о дијагностици, уређају и коришћењу апликација Google-у. Те информације се неће користити за идентификацију детета и помоћи ће у одржавању стабилности система и апликације и другим подешавањима. Неки обједињени подаци ће такође помоћи Google апликацијама и партнерима, попут Android програмера. Ако за дете укључите подешавање додатне активности на вебу и у апликацијама, ти подаци ће се можда чувати на Google налогу детета. <ph name="BEGIN_LINK1" />Сазнајте више<ph name="END_LINK1" /></translation>
-<translation id="7121728544325372695">Smart Dashes (Паметне црте)</translation>
+<translation id="7121728544325372695">Паметне црте</translation>
 <translation id="7123360114020465152">Није више подржан</translation>
 <translation id="7124929488592184705">Грешка при штампању: <ph name="DOCUMENT_NAME" /></translation>
 <translation id="7127980134843952133">Историја преузимања</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">Провера аутентичности преко протокола PPP није успела због нетачног корисничког имена или лозинке</translation>
 <translation id="7974566588408714340">Пробај поново помоћу <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Ивично подручје екрана</translation>
-<translation id="79766959863778284">XR услуга изолованог уређаја</translation>
 <translation id="7978412674231730200">Приватни кључ</translation>
 <translation id="7978450511781612192">Овим ћете се одјавити са Google налога. Обележивачи, историја, лозинке и додатни садржај се више неће синхронизовати.</translation>
 <translation id="7979036127916589816">Грешка у синхронизацији</translation>
@@ -5165,7 +5162,7 @@
 <translation id="8827752199525959199">Још радњи, лозинка за <ph name="USERNAME" /> на <ph name="DOMAIN" /></translation>
 <translation id="882854468542856424">Не дозвољавај ниједном сајту да открива Bluetooth уређаје у близини</translation>
 <translation id="8828933418460119530">Име DNS-а</translation>
-<translation id="883062543841130884">Substitutions (Замене)</translation>
+<translation id="883062543841130884">Замене</translation>
 <translation id="8830796635868321089">Провера ажурирања није успела помоћу актуелних подешавања проксија. Прилагодите <ph name="PROXY_SETTINGS_LINK_START" />подешавања проксија<ph name="PROXY_SETTINGS_LINK_END" />.</translation>
 <translation id="8831664945713891930">Отвори подешавања додатака</translation>
 <translation id="8834039744648160717">Конфигурацију мреже контролише <ph name="USER_EMAIL" />.</translation>
@@ -5367,7 +5364,6 @@
 <translation id="9138978632494473300">Додајте пречице до следећих места:</translation>
 <translation id="9140067245205650184">Користите неподржану ознаку функције: <ph name="BAD_FLAG" />. Угрозићете стабилност и безбедност.</translation>
 <translation id="9143298529634201539">Желите ли да уклоните предлог?</translation>
-<translation id="9147304170847707004">Метод за потврду идентитета</translation>
 <translation id="9147392381910171771">&amp;Опције</translation>
 <translation id="9148058034647219655">Изађи</translation>
 <translation id="9148126808321036104">Пријави ме поново</translation>
diff --git a/chrome/app/resources/generated_resources_sv.xtb b/chrome/app/resources/generated_resources_sv.xtb
index c06227e..b6280b0 100644
--- a/chrome/app/resources/generated_resources_sv.xtb
+++ b/chrome/app/resources/generated_resources_sv.xtb
@@ -914,7 +914,6 @@
 <translation id="236117173274098341">Optimera</translation>
 <translation id="236141728043665931">Blockera alltid mikrofonåtkomsten</translation>
 <translation id="2365507699358342471">Den här webbplatsen får tillgång till text och bilder som kopierats till Urklipp.</translation>
-<translation id="2367199180085172140">Lägg till File Share</translation>
 <translation id="2367972762794486313">Visa appar</translation>
 <translation id="2369105924912929484">Aktivera/inaktivera destillerat sidinnehåll</translation>
 <translation id="2371076942591664043">Öppna när nedladdning är &amp;klar</translation>
@@ -3298,7 +3297,6 @@
 <translation id="6026047032548434446">Vill du installera appen?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – ansluten till USB-enhet</translation>
 <translation id="6029027682598229313">Linux-installationen är klar.</translation>
-<translation id="6029292188939175871">radera [<ph name="FINGERPRINT_NAME" />], knapp</translation>
 <translation id="6029587122245504742">Långsammast</translation>
 <translation id="6032912588568283682">Filsystem</translation>
 <translation id="6038929619733116134">Blockera om påträngande eller vilseledande annonser visas på webbplatsen</translation>
@@ -4599,7 +4597,6 @@
 <translation id="7973962044839454485">PPP-autentisering misslyckades på grund av felaktigt användarnamn eller lösenord</translation>
 <translation id="7974566588408714340">Försök igen med <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Överskanning</translation>
-<translation id="79766959863778284">XR Isolated Device Service</translation>
 <translation id="7978412674231730200">Privat nyckel</translation>
 <translation id="7978450511781612192">Du loggas ut från dina Google-konton. Bokmärken, historik, lösenord och annan information synkroniseras inte längre.</translation>
 <translation id="7979036127916589816">Synkroniseringsfel</translation>
@@ -5368,7 +5365,6 @@
 <translation id="9138978632494473300">Lägg till genvägar till följande platser:</translation>
 <translation id="9140067245205650184">Du använder en funktionsflagga som inte stöds: <ph name="BAD_FLAG" />. Detta påverkar stabilitet och säkerhet negativt.</translation>
 <translation id="9143298529634201539">Vill du ta bort förslaget?</translation>
-<translation id="9147304170847707004">Autentiseringsmetod</translation>
 <translation id="9147392381910171771">&amp;Alternativ</translation>
 <translation id="9148058034647219655">Avsluta</translation>
 <translation id="9148126808321036104">Logga in igen</translation>
diff --git a/chrome/app/resources/generated_resources_sw.xtb b/chrome/app/resources/generated_resources_sw.xtb
index 70345c8..f643275 100644
--- a/chrome/app/resources/generated_resources_sw.xtb
+++ b/chrome/app/resources/generated_resources_sw.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">Boresha</translation>
 <translation id="236141728043665931">Zuia ufikiaji wa maikrofoni kila wakati</translation>
 <translation id="2365507699358342471">Tovuti hii inaweza kuona maandishi na picha zilizonakiliwa kwenye ubao wa kunakili.</translation>
-<translation id="2367199180085172140">Ongeza Faili Utakayoshiriki</translation>
 <translation id="2367972762794486313">Onyesha programu</translation>
 <translation id="2369105924912929484">Washa maudhui ya kurasa zilizo chujwa</translation>
 <translation id="2371076942591664043">Fungua baada ya &amp;kumaliza</translation>
@@ -2814,7 +2813,7 @@
 <translation id="5301751748813680278">Unaingia kama Mgeni.</translation>
 <translation id="5301954838959518834">Sawa, nimeelewa</translation>
 <translation id="5302048478445481009">Lugha</translation>
-<translation id="5302932258331363306">Onyesha Chaguo Mbadala</translation>
+<translation id="5302932258331363306">Onyesha Vibadala</translation>
 <translation id="5305688511332277257">Hakuna iliyosakinishwa</translation>
 <translation id="5307030433605830021">Chanzo hakitumiki</translation>
 <translation id="5308380583665731573">Unganisha</translation>
@@ -3290,7 +3289,6 @@
 <translation id="6026047032548434446">Ungependa Kusakinisha Programu?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - Kifaa cha USB kimeunganishwa</translation>
 <translation id="6029027682598229313">Imemaliza kusakinisha Linux.</translation>
-<translation id="6029292188939175871">futa [<ph name="FINGERPRINT_NAME" />], kitufe</translation>
 <translation id="6029587122245504742">Kasi ya chini zaidi</translation>
 <translation id="6032912588568283682">Mfumo wa faili</translation>
 <translation id="6038929619733116134">Zuia iwapo tovuti inaonyesha matangazo yanayopotosha au yanayokatiza huduma</translation>
@@ -4593,7 +4591,6 @@
 <translation id="7973962044839454485">Uthibitishaji wa PPP ulishindwa kutokana na jina la mtumiaji na nenosiri lisilo sahihi</translation>
 <translation id="7974566588408714340">Jaribu tena kutumia <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Angalia kwa ujumla</translation>
-<translation id="79766959863778284">Huduma ya Vifaa Vya XR Vilivyotengwa</translation>
 <translation id="7978412674231730200">Ufunguo binafsi</translation>
 <translation id="7978450511781612192">Hatua hii itakuondoa kwenye Akaunti za Google. Alamisho, historia, manenosiri yako na mengineyo hayatasawazishwa tena.</translation>
 <translation id="7979036127916589816">Hitilafu ya Usawazishaji</translation>
@@ -5363,7 +5360,6 @@
 <translation id="9138978632494473300">Ongeza njia za mkato kwenye sehemu zifuatazo:</translation>
 <translation id="9140067245205650184">Unatumia kitia alama cha kipengele kisichoruhusiwa: <ph name="BAD_FLAG" />. Uthabiti na usalama utaathirika.</translation>
 <translation id="9143298529634201539">Ungependa kuondoa pendekezo?</translation>
-<translation id="9147304170847707004">Njia ya Kuthibitisha</translation>
 <translation id="9147392381910171771">&amp;Chaguo</translation>
 <translation id="9148058034647219655">Ondoka</translation>
 <translation id="9148126808321036104">Ingia tena</translation>
diff --git a/chrome/app/resources/generated_resources_ta.xtb b/chrome/app/resources/generated_resources_ta.xtb
index f9060261..6c5bc48 100644
--- a/chrome/app/resources/generated_resources_ta.xtb
+++ b/chrome/app/resources/generated_resources_ta.xtb
@@ -741,7 +741,7 @@
 <translation id="212862741129535676">காலஇடைவெளி நிலையின் பணிசெயல் சதவீதம்</translation>
 <translation id="2129825002735785149">செருகுநிரலைப் புதுப்பி</translation>
 <translation id="2131077480075264">"<ph name="IMPORT_NAME" />" ஆல் அனுமதிக்கப்படாததால், "<ph name="APP_NAME" />"ஐ நிறுவ முடியவில்லை</translation>
-<translation id="21354425047973905">பின்களை மறை</translation>
+<translation id="21354425047973905">பின்களை மறைக்கும்</translation>
 <translation id="2135456203358955318">டாக் செய்யப்பட்ட பெரிதாக்கி</translation>
 <translation id="2135787500304447609">&amp;மீண்டும் தொடங்கு</translation>
 <translation id="2136372518715274136">புதிய கடவுச்சொல்லை உள்ளிடுக</translation>
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">மேம்படுத்து</translation>
 <translation id="236141728043665931">மைக்ரோஃபோன் அணுகலை எப்போதும் தடு</translation>
 <translation id="2365507699358342471">கிளிப்போர்டுக்கு நகலெடுத்த உரையையும் படங்களையும், இந்தத் தளத்தால் பார்க்க முடியும்.</translation>
-<translation id="2367199180085172140">கோப்புப் பகிர்வைச் சேர்</translation>
 <translation id="2367972762794486313">பயன்பாடுகளைக் காட்டு</translation>
 <translation id="2369105924912929484">எளிமையான பக்க உள்ளடக்கத்திற்கு நிலைமாற்று</translation>
 <translation id="2371076942591664043">&amp;முடிந்ததும் திற</translation>
@@ -1314,7 +1313,7 @@
 <translation id="29488703364906173">நவீன இணையத்திற்காக, விரைவான, எளிமையான மற்றும் பாதுகாப்பான இணைய உலாவியாக உருவாக்கப்பட்டது.</translation>
 <translation id="2949289451367477459">இருப்பிடத்தைப் பயன்படுத்தும். இருப்பிட அனுமதி உள்ள ஆப்ஸையும் சேவைகளையும் இந்தச் சாதனத்தின் இருப்பிடத்தைப் பயன்படுத்த அனுமதிக்கும். இருப்பிடத்தின் துல்லியத்தன்மையையும் இருப்பிடம் சார்ந்த சேவைகளையும் மேம்படுத்த, Google அவ்வப்போது இருப்பிடத் தரவைச் சேகரித்து, அதை அடையாளமற்ற வகையில் பயன்படுத்தக்கூடும்.<ph name="BEGIN_LINK1" />மேலும் அறிக<ph name="END_LINK1" /></translation>
 <translation id="2958721676848865875">தொகுப்பு நீட்டிப்பு எச்சரிக்கை</translation>
-<translation id="2959842337402130152">சேமிப்பகம் இல்லாததால் மீட்டெடுக்க இயலவில்லை. சாதனத்திலிருந்து <ph name="SPACE_REQUIRED" />யை காலிசெய்து மீண்டும் முயலவும்.</translation>
+<translation id="2959842337402130152">தேவையான அளவு சேமிப்பிடம் இல்லாததால் மீட்டெடுக்க இயலவில்லை. சாதனத்திலிருந்து <ph name="SPACE_REQUIRED" />யை காலிசெய்து மீண்டும் முயலவும்.</translation>
 <translation id="296026337010986570">முடிந்தது! தீங்கிழைக்கும் மென்பொருள் அகற்றப்பட்டது. நீட்டிப்புகளை மீண்டும் இயக்க, &lt;a href="chrome://extensions"&gt;நீட்டிப்புகள்&lt;/a&gt; பிரிவுக்குச் செல்லவும்.</translation>
 <translation id="2961090598421146107"><ph name="CERTIFICATE_NAME" /> (நீட்டிப்பு வழங்கப்பட்டது)</translation>
 <translation id="2961695502793809356">அடுத்தப் பக்கத்திற்கு செல்ல கிளிக் செய்க, வரலாற்றைக் காண அழுத்திக்கொண்டே இருங்கள்</translation>
@@ -2388,7 +2387,7 @@
 <translation id="4613144866899789710">Linux நிறுவலை ரத்துசெய்கிறது...</translation>
 <translation id="4613271546271159013">புதிய தாவலைத் திறக்கும்போது காண்பிக்கப்படும் பக்கத்தை நீட்டிப்பு மாற்றியுள்ளது.</translation>
 <translation id="4615586811063744755">குக்கீ எதுவும் தேர்ந்தெடுக்கப்படவில்லை</translation>
-<translation id="4617001782309103936">மிகச் சுருக்கமாக உள்ளது</translation>
+<translation id="4617001782309103936">மிகவும் சிறிதாக உள்ளது</translation>
 <translation id="4617270414136722281">நீட்டிப்பு விருப்பங்கள்</translation>
 <translation id="4619615317237390068">பிற சாதனங்களின் தாவல்கள்</translation>
 <translation id="4620809267248568679">இந்த அமைப்பு நீட்டிப்பால் செயல்படுத்தப்படுகிறது.</translation>
@@ -2788,7 +2787,7 @@
 <translation id="5255859108402770436">மீண்டும் உள்நுழைக</translation>
 <translation id="5256861893479663409">எல்லாத் தளங்களிலும்</translation>
 <translation id="5258992782919386492">இந்தச் சாதனத்தில் நிறுவு</translation>
-<translation id="5260334392110301220">Smart Quotes</translation>
+<translation id="5260334392110301220">ஸ்மார்ட் கொட்டேஷன் மார்க்ஸ்</translation>
 <translation id="5260508466980570042">மன்னிக்கவும், உங்கள் மின்னஞ்சலையும் கடவுச்சொல்லையும் சரிபார்க்க முடியவில்லை. தயவுசெய்து மீண்டும் முயற்சி செய்க.</translation>
 <translation id="5261683757250193089">இணைய அங்காடியில் திற</translation>
 <translation id="5264148714798105376">இதற்கு ஒரு நிமிடம் அல்லது அதற்கும் மேல் ஆகலாம்.</translation>
@@ -3300,7 +3299,6 @@
 <translation id="6026047032548434446">ஆப்ஸை நிறுவவா?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB சாதனம் இணைக்கப்பட்டது</translation>
 <translation id="6029027682598229313">Linux நிறுவல் முடிந்தது.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />]ஐ நீக்கு, பட்டன்</translation>
 <translation id="6029587122245504742">குறைந்தபட்ச வேகம்</translation>
 <translation id="6032912588568283682">கோப்பு முறைமை</translation>
 <translation id="6038929619733116134">குறுக்கிடும் அல்லது தவறாக வழிநடத்தும் விளம்பரங்களை தளம் காட்டினால், அவற்றைத் தடுக்கும்</translation>
@@ -4017,7 +4015,7 @@
 <translation id="7120865473764644444">ஒத்திசைவுச் சேவையகத்துடன் இணைக்க முடியவில்லை. மீண்டும் முயற்சிக்கிறது...</translation>
 <translation id="7121362699166175603">வரலாற்றையும் முகவரிப் பட்டியில் தன்னிரப்பிகளையும் அழிக்கும். உங்கள் Google கணக்கு, <ph name="BEGIN_LINK" />myactivity.google.com<ph name="END_LINK" /> என்ற இணைப்பில் உலாவல் வரலாறு தொடர்பான பிற தகவல்களைக் கொண்டிருக்கக்கூடும்.</translation>
 <translation id="7121389946694989825">உபயோகம் &amp; கண்டறிதல் தரவை அனுப்புக. கண்டறிதல் தரவு, சாதனம் மற்றும் ஆப்ஸ் உபயோகத் தரவு போன்றவற்றை இந்தச் சாதனம் தற்போது Googleளுக்குத் தானாக அனுப்புகிறது. இது உங்கள் பிள்ளையை அடையாளம் கண்டறியப் பயன்படுத்தப்படாது, இது சிஸ்டம் மற்றும் ஆப்ஸின் நிலைத்தன்மையை மேம்படுத்தவும் பிற மேம்பாடுகளைச் செய்யவும் உதவும். ஒருங்கிணைக்கப்பட்ட சில தரவுகள், Google ஆப்ஸ் மற்றும் Android டெவெலப்பர்கள் போன்ற கூட்டாளர்களுக்கும் உதவும். உங்கள் பிள்ளையின் கணக்கில் கூடுதல் ’இணையம் &amp; ஆப்ஸ் செயல்பாடு’ அமைப்பு இயக்கப்பட்டிருந்தால், இந்தத் தரவு அவரது Google கணக்கில் சேமிக்கப்படலாம். <ph name="BEGIN_LINK1" />மேலும் அறிக<ph name="END_LINK1" /></translation>
-<translation id="7121728544325372695">Smart Dashes</translation>
+<translation id="7121728544325372695">ஸ்மார்ட் டேஷ்கள்</translation>
 <translation id="7123360114020465152">இனி ஆதரிக்கப்படாது</translation>
 <translation id="7124929488592184705"><ph name="DOCUMENT_NAME" />ஐ அச்சிடுவதில் பிழை</translation>
 <translation id="7127980134843952133">பதிவிறக்க வரலாறு</translation>
@@ -4603,7 +4601,6 @@
 <translation id="7973962044839454485">தவறான பயனர்பெயர் அல்லது கடவுச்சொல் காரணமாக PPP அங்கீகாரம் தோல்வியடைந்தது</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" />ஐப் பயன்படுத்த முயற்சி</translation>
 <translation id="7974936243149753750">ஓவர்ஸ்கேன்</translation>
-<translation id="79766959863778284">XR தனிமைப்படுத்தப்பட்ட சாதனச் சேவை</translation>
 <translation id="7978412674231730200">தனிப்பட்ட விசை</translation>
 <translation id="7978450511781612192">உங்கள் Google கணக்குகளிலிருந்து வெளியேற்றப்படுவீர்கள். இனி உங்கள் புக்மார்க்குகள், வரலாறு, கடவுச்சொற்கள் மற்றும் பிற அமைப்புகள் ஒத்திசைக்கப்படாது.</translation>
 <translation id="7979036127916589816">ஒத்திசைவு பிழை</translation>
@@ -5372,7 +5369,6 @@
 <translation id="9138978632494473300">பின்வரும் இடங்களில் குறுக்குவழிகளைச் சேர்:</translation>
 <translation id="9140067245205650184">ஆதரிக்கப்படாத அம்சக் கொடியைப் பயன்படுத்துகிறீர்கள்: <ph name="BAD_FLAG" />. நிலைப்புத்தன்மையும் பாதுகாப்பும் பாதிக்கப்படும்.</translation>
 <translation id="9143298529634201539">பரிந்துரையை அகற்றவா?</translation>
-<translation id="9147304170847707004">அங்கீகார முறை</translation>
 <translation id="9147392381910171771">&amp;விருப்பத்தேர்வுகள்</translation>
 <translation id="9148058034647219655">வெளியேறு</translation>
 <translation id="9148126808321036104">மீண்டும் உள்நுழைக</translation>
diff --git a/chrome/app/resources/generated_resources_te.xtb b/chrome/app/resources/generated_resources_te.xtb
index 30702018..e7d075c 100644
--- a/chrome/app/resources/generated_resources_te.xtb
+++ b/chrome/app/resources/generated_resources_te.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">ఆప్టిమైజ్ చేయండి</translation>
 <translation id="236141728043665931">ఎల్లప్పుడూ మైక్రోఫోన్ యాక్సెస్‌ను బ్లాక్ చేయి</translation>
 <translation id="2365507699358342471">క్లిప్‌బోర్డ్‌కు కాపీ చేసిన వచనం మరియు చిత్రాలను ఈ సైట్ చూడగలదు.</translation>
-<translation id="2367199180085172140">ఫైల్ షేర్‌ని జోడించండి</translation>
 <translation id="2367972762794486313">అనువర్తనాలను చూపు</translation>
 <translation id="2369105924912929484">వర్గీకృత పేజీ కంటెంట్‌లను టోగుల్ చేయండి</translation>
 <translation id="2371076942591664043">&amp;పూర్తవగానే తెరువు</translation>
@@ -3298,7 +3297,6 @@
 <translation id="6026047032548434446">యాప్‌ను ఇన్‌స్టాల్ చేయాలా?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB పరికరం కనెక్ట్ చేయబడింది</translation>
 <translation id="6029027682598229313">Linuxను ఇన్‌స్టాల్ చేయడం పూర్తయింది.</translation>
-<translation id="6029292188939175871">తొలగించు [<ph name="FINGERPRINT_NAME" />], బటన్</translation>
 <translation id="6029587122245504742">అత్యంత నెమ్మది</translation>
 <translation id="6032912588568283682">ఫైల్ సిస్టమ్</translation>
 <translation id="6038929619733116134">సైట్ అనుచితమైన లేదా తప్పుదారి పట్టించే ప్రకటనలను చూపినట్లయితే బ్లాక్ చేయండి</translation>
@@ -4598,7 +4596,6 @@
 <translation id="7973962044839454485">వినియోగదారు పేరు లేదా పాస్‌వర్డ్ తప్పు అయినందున PPP ప్రామాణీకరణ విఫలమైంది</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" />ని ఉపయోగించి మళ్లీ ప్రయత్నించు</translation>
 <translation id="7974936243149753750">ఓవర్‌స్కాన్ సర్దుబాటు</translation>
-<translation id="79766959863778284">XR విడిగా ఉంచబడిన పరికర సేవ</translation>
 <translation id="7978412674231730200">వ్యక్తిగత కీ</translation>
 <translation id="7978450511781612192">ఇది మిమ్మల్ని మీ Google ఖాతాల నుండి సైన్ అవుట్ చేస్తుంది. మీ బుక్‌మార్క్‌లు, చరిత్ర, పాస్‌వర్డ్‌లు మరియు మరిన్ని ఇకపై సమకాలీకరించబడవు.</translation>
 <translation id="7979036127916589816">సింక్ ఎర్రర్</translation>
@@ -5365,7 +5362,6 @@
 <translation id="9138978632494473300">కింది స్థలాలకు షార్ట్‌కట్‌లను జోడించండి:</translation>
 <translation id="9140067245205650184">మీరు మద్దతు లేని ఫీచర్ ఫ్లాగ్‌ను ఉపయోగిస్తున్నారు: <ph name="BAD_FLAG" />. స్థిరత్వం మరియు భద్రతలలో ఇబ్బందులు ఏర్పడతాయి.</translation>
 <translation id="9143298529634201539">సూచనను తీసివేయాలా?</translation>
-<translation id="9147304170847707004">ప్రమాణీకరణ పద్ధతి</translation>
 <translation id="9147392381910171771">&amp;ఐచ్ఛికాలు</translation>
 <translation id="9148058034647219655">నిష్క్రమించు</translation>
 <translation id="9148126808321036104">మళ్ళీ సైన్ ఇన్ చేయండి</translation>
@@ -5424,7 +5420,7 @@
 <translation id="939598580284253335">రహస్య పదబంధాన్ని నమోదు చేయండి</translation>
 <translation id="939736085109172342">క్రొత్త  ఫోల్డర్</translation>
 <translation id="942532530371314860"><ph name="APP_NAME" /> ఒక Chrome ట్యాబ్‌ను మరియు ఆడియోను భాగస్వామ్యం చేస్తోంది.</translation>
-<translation id="945166830402967374">మీ సంస్థ నిర్వాహకులను సంప్రదించండి</translation>
+<translation id="945166830402967374">మీ సంస్థ యొక్క పరికరం నిర్వాహకులను సంప్రదించండి</translation>
 <translation id="945522503751344254">అభిప్రాయాన్ని పంపండి</translation>
 <translation id="947329552760389097">&amp;మూలకాలను పర్యవేక్షించు</translation>
 <translation id="952992212772159698">సక్రియం చెయ్యబడలేదు</translation>
diff --git a/chrome/app/resources/generated_resources_th.xtb b/chrome/app/resources/generated_resources_th.xtb
index 54d185668..b3eb42c 100644
--- a/chrome/app/resources/generated_resources_th.xtb
+++ b/chrome/app/resources/generated_resources_th.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">เพิ่มประสิทธิภาพ</translation>
 <translation id="236141728043665931">บล็อกการเข้าถึงไมโครโฟนเสมอ</translation>
 <translation id="2365507699358342471">เว็บไซต์นี้จะดูข้อความและรูปภาพที่คัดลอกไปยังคลิปบอร์ดได้</translation>
-<translation id="2367199180085172140">เพิ่มการแชร์ไฟล์</translation>
 <translation id="2367972762794486313">แสดงแอป</translation>
 <translation id="2369105924912929484">สลับเนื้อหาของหน้าที่กลั่นกรองแล้ว</translation>
 <translation id="2371076942591664043">เปิดเมื่อเ&amp;สร็จ</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">ติดตั้งแอปไหม</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - เชื่อมต่ออุปกรณ์ USB อยู่</translation>
 <translation id="6029027682598229313">ติดตั้ง Linux เสร็จสมบูรณ์แล้ว</translation>
-<translation id="6029292188939175871">ลบ [<ph name="FINGERPRINT_NAME" />], ปุ่ม</translation>
 <translation id="6029587122245504742">ช้าที่สุด</translation>
 <translation id="6032912588568283682">ระบบไฟล์</translation>
 <translation id="6038929619733116134">บล็อกหากเว็บไซต์แสดงโฆษณาที่แทรกหรือทำให้เข้าใจผิด</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">การตรวจสอบสิทธิ์ PPP ล้มเหลวเนื่องจากชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง</translation>
 <translation id="7974566588408714340">ลองอีกครั้งโดยใช้ <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">โอเวอร์สแกน</translation>
-<translation id="79766959863778284">บริการอุปกรณ์ที่แยก XR</translation>
 <translation id="7978412674231730200">กุญแจส่วนตัว</translation>
 <translation id="7978450511781612192">การดำเนินการนี้จะนำคุณออกจากบัญชี Google บุ๊กมาร์ก ประวัติการเข้าชม รหัสผ่าน และข้อมูลอื่นๆ จะไม่ซิงค์อีกต่อไป</translation>
 <translation id="7979036127916589816">เกิดข้อผิดพลาดในการทำให้ข้อมูลตรงกัน</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">เพิ่มทางลัดไปยังตำแหน่งต่อไปนี้:</translation>
 <translation id="9140067245205650184">คุณใช้แฟล็กฟีเจอร์ที่ระบบไม่สนับสนุน: <ph name="BAD_FLAG" /> ซึ่งจะส่งผลต่อความเสถียรและความปลอดภัย</translation>
 <translation id="9143298529634201539">นำคำแนะนำออกไหม</translation>
-<translation id="9147304170847707004">วิธีการตรวจสอบสิทธิ์</translation>
 <translation id="9147392381910171771">&amp;ตัวเลือก</translation>
 <translation id="9148058034647219655">ออก</translation>
 <translation id="9148126808321036104">ลงชื่อเข้าใช้อีกครั้ง</translation>
diff --git a/chrome/app/resources/generated_resources_tr.xtb b/chrome/app/resources/generated_resources_tr.xtb
index dcf99f9..530c9f8 100644
--- a/chrome/app/resources/generated_resources_tr.xtb
+++ b/chrome/app/resources/generated_resources_tr.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Optimize et</translation>
 <translation id="236141728043665931">Mikrofon erişimini her zaman engelle</translation>
 <translation id="2365507699358342471">Bu site, panoya kopyalanan metin ve resimleri görebilir.</translation>
-<translation id="2367199180085172140">Dosya Paylaşımı Ekle</translation>
 <translation id="2367972762794486313">Uygulamaları göster</translation>
 <translation id="2369105924912929484">Arıtılmış sayfa içeriğini açma/kapatma</translation>
 <translation id="2371076942591664043">İşlem tamamlandığın&amp;da aç</translation>
@@ -3300,7 +3299,6 @@
 <translation id="6026047032548434446">Uygulama Yüklensin mi?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - USB cihazı bağlandı</translation>
 <translation id="6029027682598229313">Linux yüklemesi tamamlandı.</translation>
-<translation id="6029292188939175871">[<ph name="FINGERPRINT_NAME" />] parmak izini sil, düğme</translation>
 <translation id="6029587122245504742">En yavaş</translation>
 <translation id="6032912588568283682">Dosya sistemi</translation>
 <translation id="6038929619733116134">Site, araya giren veya yanıltıcı reklamlar gösteriyorsa engelleyin</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">Yanlış kullanıcı adı veya şifreden dolayı PPP kimlik doğrulaması başarısız oldu</translation>
 <translation id="7974566588408714340"><ph name="EXTENSIONNAME" /> uzantısını kullanmayı yeniden dene</translation>
 <translation id="7974936243149753750">Fazla tarama</translation>
-<translation id="79766959863778284">XR Yalıtılmış Cihaz Hizmeti</translation>
 <translation id="7978412674231730200">Özel anahtar</translation>
 <translation id="7978450511781612192">Bu işlem Google hesaplarınızın oturumunu kapatacak. Yer işaretleriniz, şifreleriniz ve diğer içerikler artık senkronize edilmeyecek.</translation>
 <translation id="7979036127916589816">Senkronizasyon Hatası</translation>
@@ -5188,7 +5185,7 @@
 <translation id="8862003515646449717">Hızlı bir tarayıcıya geç</translation>
 <translation id="8863753581171631212">Bağlantıyı yeni <ph name="APP" /> uygulamasında aç</translation>
 <translation id="8864055848767439877"><ph name="TAB_NAME" />, <ph name="APP_NAME" /> ile paylaşılıyor</translation>
-<translation id="8864458770072227512"><ph name="EMAIL" />, bu cihazdan kaldırıldı</translation>
+<translation id="8864458770072227512"><ph name="EMAIL" /> bu cihazdan kaldırıldı</translation>
 <translation id="8868626022555786497">Kullanımda</translation>
 <translation id="8870318296973696995">Ana sayfa</translation>
 <translation id="8870413625673593573">Son Kapatılan</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">Aşağıdaki yerlere kısayollar ekleyin:</translation>
 <translation id="9140067245205650184">Desteklenmeyen bir özellik işareti kullanıyorsunuz: <ph name="BAD_FLAG" />. Kararlılık ve güvenlik seviyesi düşecektir.</translation>
 <translation id="9143298529634201539">Öneri kaldırılsın mı?</translation>
-<translation id="9147304170847707004">Kimlik Doğrulama Yöntemi</translation>
 <translation id="9147392381910171771">&amp;Seçenekler</translation>
 <translation id="9148058034647219655">Çıkış</translation>
 <translation id="9148126808321036104">Tekrar oturum açın</translation>
diff --git a/chrome/app/resources/generated_resources_uk.xtb b/chrome/app/resources/generated_resources_uk.xtb
index 3e8d21ef..da4b875 100644
--- a/chrome/app/resources/generated_resources_uk.xtb
+++ b/chrome/app/resources/generated_resources_uk.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Оптимізувати</translation>
 <translation id="236141728043665931">Завжди блокувати доступ до мікрофона</translation>
 <translation id="2365507699358342471">Цей сайт може переглядати тексти й зображення в буфері обміну.</translation>
-<translation id="2367199180085172140">Додати файл для спільного доступу</translation>
 <translation id="2367972762794486313">Показати програми</translation>
 <translation id="2369105924912929484">Перемкнути вміст очищеної сторінки</translation>
 <translation id="2371076942591664043">Відкрити коли &amp;виконано</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Установити додаток?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> – пристрій USB підключено</translation>
 <translation id="6029027682598229313">Встановлення Linux завершено.</translation>
-<translation id="6029292188939175871">видалити [<ph name="FINGERPRINT_NAME" />], кнопка</translation>
 <translation id="6029587122245504742">найповільніше</translation>
 <translation id="6032912588568283682">Файлова система</translation>
 <translation id="6038929619733116134">Блокувати, якщо сайт показує нав’язливі чи оманливі оголошення</translation>
@@ -3754,7 +3752,7 @@
 <translation id="6725073593266469338">Сервіс інтерфейсу</translation>
 <translation id="6725206449694821596">Протокол друку через Інтернет (IPP)</translation>
 <translation id="67269783048918309">Надсилати дані про використання й діагностику. Цей пристрій наразі автоматично надсилає в Google дані про діагностику та використання пристрою і додатків. Ця інформація не використовуватиметься для встановлення особи вашої дитини, а допоможе покращити стабільність системи, додатків тощо. Деякі зведені дані також корисні для додатків і партнерів Google, як-от розробників Android. Це <ph name="BEGIN_LINK1" />налаштування<ph name="END_LINK1" /> застосовує власник. Якщо ввімкнено додаткову Історію додатків і веб-пошуку, ці дані зберігаються в обліковому записі Google дитини. <ph name="BEGIN_LINK2" />Докладніше<ph name="END_LINK2" /></translation>
-<translation id="6727969043791803658">Під'єднано, заряд акумулятора – <ph name="BATTERY_PERCENTAGE" />%</translation>
+<translation id="6727969043791803658">Під'єднано, акумулятор заряджено на <ph name="BATTERY_PERCENTAGE" />%</translation>
 <translation id="6732801395666424405">Сертифікати не завантажено</translation>
 <translation id="6732900235521116609">Неможливо видалити ярлик</translation>
 <translation id="6735304988756581115">Показати файли cookie та інші дані із сайтів...</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">Помилка автентифікації PPP через неправильне ім’я користувача або пароль</translation>
 <translation id="7974566588408714340">Повторити спробу, використовуючи розширення <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Облямівка екрана</translation>
-<translation id="79766959863778284">Сервіс ізольованих пристроїв XR</translation>
 <translation id="7978412674231730200">Секретний ключ</translation>
 <translation id="7978450511781612192">Ви вийдете з облікових записів Google. Закладки, історія, паролі тощо більше не синхронізуватимуться.</translation>
 <translation id="7979036127916589816">Помилка синхронізації</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">Створити ярлики в таких розташуваннях:</translation>
 <translation id="9140067245205650184">Використовується непідтримувана позначка функції: <ph name="BAD_FLAG" />. Це вплине на стабільність і безпеку.</translation>
 <translation id="9143298529634201539">Видалити пропозицію?</translation>
-<translation id="9147304170847707004">Спосіб автентифікації</translation>
 <translation id="9147392381910171771">&amp;Параметри</translation>
 <translation id="9148058034647219655">Вийти</translation>
 <translation id="9148126808321036104">Увійти знову</translation>
diff --git a/chrome/app/resources/generated_resources_vi.xtb b/chrome/app/resources/generated_resources_vi.xtb
index e35eb2b0..3c41a78 100644
--- a/chrome/app/resources/generated_resources_vi.xtb
+++ b/chrome/app/resources/generated_resources_vi.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">Tối ưu hóa</translation>
 <translation id="236141728043665931">Luôn chặn quyền truy cập micrô</translation>
 <translation id="2365507699358342471">Trang web này có thể xem văn bản và hình ảnh đã sao chép sang khay nhớ tạm.</translation>
-<translation id="2367199180085172140">Thêm Chia sẻ tệp</translation>
 <translation id="2367972762794486313">Hiển thị ứng dụng</translation>
 <translation id="2369105924912929484">Bật/tắt nội dung trang đã lọc</translation>
 <translation id="2371076942591664043">Mở khi &amp;hoàn tất</translation>
@@ -3299,7 +3298,6 @@
 <translation id="6026047032548434446">Cài đặt ứng dụng?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - Đã kết nối thiết bị USB</translation>
 <translation id="6029027682598229313">Đã hoàn tất quá trình cài đặt Linux.</translation>
-<translation id="6029292188939175871">xóa [<ph name="FINGERPRINT_NAME" />], nút</translation>
 <translation id="6029587122245504742">Chậm nhất</translation>
 <translation id="6032912588568283682">Hệ thống tệp</translation>
 <translation id="6038929619733116134">Chặn nếu trang web hiển thị quảng cáo xâm nhập hoặc quảng cáo gây hiểu nhầm</translation>
@@ -4600,7 +4598,6 @@
 <translation id="7973962044839454485">Xác thực PPP không thành công do tên người dùng hoặc mật khẩu không đúng</translation>
 <translation id="7974566588408714340">Thử lại bằng <ph name="EXTENSIONNAME" /></translation>
 <translation id="7974936243149753750">Quét thừa</translation>
-<translation id="79766959863778284">Dịch vụ dành cho thiết bị cô lập XR</translation>
 <translation id="7978412674231730200">Khoá cá nhân</translation>
 <translation id="7978450511781612192">Việc này này sẽ đăng xuất bạn khỏi các Tài khoản Google. Dấu trang, lịch sử, mật khẩu và các dữ liệu khác của bạn sẽ không còn đồng bộ hóa nữa.</translation>
 <translation id="7979036127916589816">Lỗi Đồng bộ hóa</translation>
@@ -5369,7 +5366,6 @@
 <translation id="9138978632494473300">Thêm lối tắt cho các vị trí sau đây:</translation>
 <translation id="9140067245205650184">Bạn đang sử dụng cờ của tính năng không được hỗ trợ: <ph name="BAD_FLAG" />. Độ ổn định và tính bảo mật sẽ bị ảnh hưởng.</translation>
 <translation id="9143298529634201539">Xóa đề xuất?</translation>
-<translation id="9147304170847707004">Phương thức xác thực</translation>
 <translation id="9147392381910171771">&amp;Tuỳ chọn</translation>
 <translation id="9148058034647219655">Thoát</translation>
 <translation id="9148126808321036104">Đăng nhập lại</translation>
diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/resources/generated_resources_zh-CN.xtb
index eb31f06..42134e8 100644
--- a/chrome/app/resources/generated_resources_zh-CN.xtb
+++ b/chrome/app/resources/generated_resources_zh-CN.xtb
@@ -912,7 +912,6 @@
 <translation id="236117173274098341">优化</translation>
 <translation id="236141728043665931">始终禁止使用麦克风</translation>
 <translation id="2365507699358342471">此页面可以查看复制到剪贴板的文字和图片。</translation>
-<translation id="2367199180085172140">添加文件共享</translation>
 <translation id="2367972762794486313">显示应用</translation>
 <translation id="2369105924912929484">切换已提取的网页内容</translation>
 <translation id="2371076942591664043">完成时打开(&amp;D)</translation>
@@ -1099,7 +1098,7 @@
 <translation id="2649045351178520408">Base64 编码 ASCII,证书链</translation>
 <translation id="2653033005692233957">搜索失败</translation>
 <translation id="2653266418988778031">如果您删除了某个证书授权中心 (CA) 证书,则浏览器不会再信任该 CA 颁发的任何证书。</translation>
-<translation id="2653275834716714682">替换文字</translation>
+<translation id="2653275834716714682">文本替换</translation>
 <translation id="2653659639078652383">提交</translation>
 <translation id="265390580714150011">字段值</translation>
 <translation id="2654166010170466751">允许网站安装付款处理程序</translation>
@@ -2815,7 +2814,7 @@
 <translation id="5301751748813680278">以访客身份登录。</translation>
 <translation id="5301954838959518834">知道了</translation>
 <translation id="5302048478445481009">语言</translation>
-<translation id="5302932258331363306">显示替换项目</translation>
+<translation id="5302932258331363306">显示替换</translation>
 <translation id="5305688511332277257">未安装任何证书</translation>
 <translation id="5307030433605830021">来源不受支持</translation>
 <translation id="5308380583665731573">连接</translation>
@@ -3291,7 +3290,6 @@
 <translation id="6026047032548434446">安装应用?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - 已连接 USB 设备</translation>
 <translation id="6029027682598229313">Linux 已安装完毕。</translation>
-<translation id="6029292188939175871">删除[<ph name="FINGERPRINT_NAME" />],按钮</translation>
 <translation id="6029587122245504742">最慢</translation>
 <translation id="6032912588568283682">文件系统</translation>
 <translation id="6038929619733116134">屏蔽会展示侵扰性或误导性广告的网站</translation>
@@ -4590,7 +4588,6 @@
 <translation id="7973962044839454485">用户名或密码不正确,导致 PPP 身份验证失败</translation>
 <translation id="7974566588408714340">使用“<ph name="EXTENSIONNAME" />”重试</translation>
 <translation id="7974936243149753750">过扫描</translation>
-<translation id="79766959863778284">XR 隔离设备服务</translation>
 <translation id="7978412674231730200">私有密钥</translation>
 <translation id="7978450511781612192">执行此操作会使您退出 Google 帐号。您的书签、历史记录、密码及其他设置将不再保持同步。</translation>
 <translation id="7979036127916589816">同步错误</translation>
@@ -5156,7 +5153,7 @@
 <translation id="8827752199525959199">更多操作,<ph name="USERNAME" /> 在 <ph name="DOMAIN" /> 上的密码</translation>
 <translation id="882854468542856424">不允许任何网站发现附近的蓝牙设备</translation>
 <translation id="8828933418460119530">DNS 名称</translation>
-<translation id="883062543841130884">替换项目</translation>
+<translation id="883062543841130884">替换</translation>
 <translation id="8830796635868321089">无法使用当前代理设置来检查更新,请调整您的<ph name="PROXY_SETTINGS_LINK_START" />代理设置<ph name="PROXY_SETTINGS_LINK_END" />。</translation>
 <translation id="8831664945713891930">打开扩展程序设置</translation>
 <translation id="8834039744648160717">网络配置由 <ph name="USER_EMAIL" /> 控制。</translation>
@@ -5358,7 +5355,6 @@
 <translation id="9138978632494473300">在以下位置添加快捷方式:</translation>
 <translation id="9140067245205650184">您使用的是不受支持的功能标记:<ph name="BAD_FLAG" />。稳定性和安全性会有所下降。</translation>
 <translation id="9143298529634201539">要移除推荐内容吗?</translation>
-<translation id="9147304170847707004">身份验证方法</translation>
 <translation id="9147392381910171771">选项(&amp;O)</translation>
 <translation id="9148058034647219655">退出</translation>
 <translation id="9148126808321036104">重新登录</translation>
diff --git a/chrome/app/resources/generated_resources_zh-TW.xtb b/chrome/app/resources/generated_resources_zh-TW.xtb
index 78371f3..0b5586c 100644
--- a/chrome/app/resources/generated_resources_zh-TW.xtb
+++ b/chrome/app/resources/generated_resources_zh-TW.xtb
@@ -915,7 +915,6 @@
 <translation id="236117173274098341">最佳化</translation>
 <translation id="236141728043665931">一律封鎖存取麥克風</translation>
 <translation id="2365507699358342471">這個網站可以讀取已複製到剪貼簿的文字和圖片。</translation>
-<translation id="2367199180085172140">新增檔案共用</translation>
 <translation id="2367972762794486313">顯示應用程式</translation>
 <translation id="2369105924912929484">切換摘要網頁內容</translation>
 <translation id="2371076942591664043">完成後開啟(&amp;D)</translation>
@@ -3297,7 +3296,6 @@
 <translation id="6026047032548434446">要安裝應用程式嗎?</translation>
 <translation id="6026819612896463875"><ph name="WINDOW_TITLE" /> - 已連接 USB 裝置</translation>
 <translation id="6029027682598229313">Linux 安裝完成。</translation>
-<translation id="6029292188939175871">刪除[<ph name="FINGERPRINT_NAME" />],按鈕</translation>
 <translation id="6029587122245504742">最慢</translation>
 <translation id="6032912588568283682">檔案系統</translation>
 <translation id="6038929619733116134">封鎖干擾性或誤導性的網站廣告</translation>
@@ -4597,7 +4595,6 @@
 <translation id="7973962044839454485">使用者名稱或密碼錯誤,導致 PPP 驗證失敗</translation>
 <translation id="7974566588408714340">使用「<ph name="EXTENSIONNAME" />」重試</translation>
 <translation id="7974936243149753750">遮視區域</translation>
-<translation id="79766959863778284">XR 獨立裝置服務</translation>
 <translation id="7978412674231730200">秘密金鑰</translation>
 <translation id="7978450511781612192">你將登出自己的 Google 帳戶。你的書籤、歷史記錄、密碼和其他設定將不再保持同步。</translation>
 <translation id="7979036127916589816">同步功能發生錯誤</translation>
@@ -5365,7 +5362,6 @@
 <translation id="9138978632494473300">在以下位置新增捷徑:</translation>
 <translation id="9140067245205650184">你正在使用不受支援的功能標幟:<ph name="BAD_FLAG" />。這可能會危及穩定性與安全性。</translation>
 <translation id="9143298529634201539">要移除建議嗎?</translation>
-<translation id="9147304170847707004">驗證方式</translation>
 <translation id="9147392381910171771">選項 (&amp;O)</translation>
 <translation id="9148058034647219655">結束</translation>
 <translation id="9148126808321036104">重新登入</translation>
diff --git a/chrome/app/resources/google_chrome_strings_es.xtb b/chrome/app/resources/google_chrome_strings_es.xtb
index b07b85c9d..3fa1f4a 100644
--- a/chrome/app/resources/google_chrome_strings_es.xtb
+++ b/chrome/app/resources/google_chrome_strings_es.xtb
@@ -35,7 +35,7 @@
 
 <ph name="USER_DATA_DIRECTORY" /></translation>
 <translation id="1698376642261615901">Google Chrome es un navegador web que ejecuta aplicaciones y páginas web a gran velocidad. Es rápido, estable y fácil de utilizar. Navega por la Web de forma más segura gracias a la protección integrada contra suplantación de identidad y software malicioso.</translation>
-<translation id="1713301662689114961">{0,plural, =1{Chrome se reiniciará en el transcurso de 1 hora}other{Chrome se reiniciará en el transcurso de # horas}}</translation>
+<translation id="1713301662689114961">{0,plural, =1{Chrome se reiniciará dentro de 1 hora}other{Chrome se reiniciará dentro de # horas}}</translation>
 <translation id="1718131156967340976">Selecciona <ph name="BEGIN_BOLD" />Google Chrome<ph name="END_BOLD" /> <ph name="SMALL_PRODUCT_LOGO" /></translation>
 <translation id="1734234790201236882">Chrome guardará esta contraseña en tu cuenta de Google para que no tengas que recordarla.</translation>
 <translation id="174539241580958092">Google Chrome no ha podido sincronizar los datos debido a un error de inicio de sesión.</translation>
@@ -97,7 +97,7 @@
 Consulta tu correo electrónico (<ph name="ACCOUNT_EMAIL" />) para obtener más información.</translation>
 <translation id="3282568296779691940">Iniciar sesión en Chrome</translation>
 <translation id="3360895254066713204">Ayudante de Chrome</translation>
-<translation id="3379938682270551431">{0,plural, =0{Chrome se reiniciará ahora}=1{Chrome se reiniciará en el transcurso de 1 segundo}other{Chrome se reiniciará en el transcurso de # segundos}}</translation>
+<translation id="3379938682270551431">{0,plural, =0{Chrome se reiniciará ahora}=1{Chrome se reiniciará dentro de 1 segundo}other{Chrome se reiniciará dentro de # segundos}}</translation>
 <translation id="3395323229510056640">Obtener ayuda de Chrome OS</translation>
 <translation id="3396977131400919238">Se ha producido un error de sistema operativo durante el proceso de instalación. Vuelve a descargar Google Chrome.</translation>
 <translation id="3398288718845740432">Ocultar en el menú de Chrome</translation>
@@ -275,7 +275,7 @@
 <translation id="8568392309447938879">Para usar las aplicaciones, debes iniciar sesión en Chrome. De ese modo, Chrome podrá sincronizar tus aplicaciones, tus marcadores, tu historial, tus contraseñas y otras opciones en distintos dispositivos.</translation>
 <translation id="8606668294522778825">Google Chrome puede utilizar servicios web para mejorar tu experiencia de navegación. Puedes habilitar o inhabilitar estos servicios. <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" /></translation>
 <translation id="8614913330719544658">Google Chrome no responde. ¿Quieres reiniciarlo ahora?</translation>
-<translation id="8625237574518804553">{0,plural, =1{Chrome se reiniciará en el transcurso de 1 minuto}other{Chrome se reiniciará en el transcurso de # minutos}}</translation>
+<translation id="8625237574518804553">{0,plural, =1{Chrome se reiniciará dentro de 1 minuto}other{Chrome se reiniciará dentro de # minutos}}</translation>
 <translation id="8667808506758191620">El dispositivo <ph name="DEVICE_TYPE" /> está actualizado.</translation>
 <translation id="8669527147644353129">Ayudante de Google Chrome</translation>
 <translation id="8679801911857917785">También controla qué página se muestra al iniciar Chrome.</translation>
diff --git a/chrome/app/resources/locale_settings.grd b/chrome/app/resources/locale_settings.grd
index 957e6ef..83aea70 100644
--- a/chrome/app/resources/locale_settings.grd
+++ b/chrome/app/resources/locale_settings.grd
@@ -134,25 +134,6 @@
         55
       </message>
 
-      <if expr="chromeos">
-        <!-- The width and height of the Join Wi-Fi network dialog box in -->
-        <!-- characters and lines (See above). -->
-        <message name="IDS_JOIN_WIFI_NETWORK_DIALOG_WIDTH_CHARS" use_name_for_id="true">
-          60
-        </message>
-        <message name="IDS_JOIN_WIFI_NETWORK_DIALOG_MINIMUM_HEIGHT_LINES" use_name_for_id="true">
-          4
-        </message>
-
-        <!-- The height of the advanced Join Wi-Fi network dialog box in lines. -->
-        <message name="IDS_JOIN_WIFI_NETWORK_DIALOG_ADVANCED_WIDTH_CHARS" use_name_for_id="true">
-          100
-        </message>
-        <message name="IDS_JOIN_WIFI_NETWORK_DIALOG_ADVANCED_MINIMUM_HEIGHT_LINES" use_name_for_id="true">
-          30
-        </message>
-      </if>
-
       <!-- The width and height of the Edit Bookmark dialog box in characters and lines -->
       <!-- (See above). -->
       <message name="IDS_EDITBOOKMARK_DIALOG_WIDTH_CHARS" use_name_for_id="true">
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 92654cb7..6373323 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -162,6 +162,78 @@
   </if>
 
   <!-- Accessibility Page -->
+  <message name="IDS_SETTINGS_CAPTIONS" desc="Name of the settings page which displays caption preferences.">
+    Captions
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_SIZE" desc="Name of the settings page which displays caption text size preferences.">
+    Text size
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_FONT" desc="Name of the settings page which displays caption text font preferences.">
+    Text font
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_COLOR" desc="Name of the settings page which displays caption text color preferences.">
+    Text color
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_OPACITY" desc="Name of the settings page which displays caption text opacity preferences.">
+    Text opacity
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_BACKGROUND_OPACITY" desc="Name of the settings page which displays caption background opacity preferences.">
+    Background opacity
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_OPACITY_MIN" desc="Value of the minimum captions text opacity setting.">
+    0
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_OPACITY_MAX" desc="Value of the maximum captions text opacity setting.">
+    100
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_SHADOW" desc="Name of the settings page which displays caption text shadow preferences.">
+    Text shadow
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_NONE" desc="Name of the None option for the caption text shadow.">
+    None
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_RAISED" desc="Name of the Raised option for the caption text shadow.">
+    Raised
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_DEPRESSED" desc="Name of the Depressed option for the caption text shadow.">
+    Depressed
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_UNIFORM" desc="Name of the Uniform option for the caption text shadow.">
+    Uniform
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_DROP_SHADOW" desc="Name of the Drop shadow option for the caption text shadow.">
+    Drop shadow
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_BACKGROUND_COLOR" desc="Name of the settings page which displays caption background color preferences.">
+    Background color
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_COLOR_BLACK" desc="Name of the Black color for the caption text or background.">
+    Black
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_COLOR_WHITE" desc="Name of the White color for the caption text or background.">
+    White
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_COLOR_RED" desc="Name of the Red color for the caption text or background.">
+    Red
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_COLOR_GREEN" desc="Name of the Green color for the caption text or background.">
+    Green
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_COLOR_BLUE" desc="Name of the Blue color for the caption text or background.">
+    Blue
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_COLOR_YELLOW" desc="Name of the Yellow color for the caption text or background.">
+    Yellow
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_COLOR_CYAN" desc="Name of the Cyan color for the caption text or background.">
+    Cyan
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_COLOR_MAGENTA" desc="Name of the Magenta color for the caption text or background.">
+    Magenta
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_DEFAULT_SETTING" desc="Name of the default setting for the caption text.">
+    Default
+  </message>
   <message name="IDS_SETTINGS_ACCESSIBILITY" desc="Name of the settings page which displays accessibility preferences.">
     Accessibility
   </message>
@@ -1904,6 +1976,9 @@
     <message name="IDS_SETTINGS_INTERNET_CONFIG_SHARE" desc="Settings > Internet > Network config: Label for setting allowing other device users to use the network configuration.">
       Allow other users of this device to use this network
     </message>
+    <message name="IDS_SETTINGS_HIDDEN_NETWORK_WARNING" desc="Settings > Internet > Network config: Text shown when auto connect to the WiFi network is enabled, warning about the dangers of auto-connecting to hidden networks.">
+      Automatically connecting to a hidden network allows others to see your device and some network settings, and is not recommended.
+    </message>
     <message name="IDS_SETTINGS_INTERNET_CONFIG_SAVE_CREDENTIALS" desc="Settings > Internet > Network config: Label for the setting to save identity and password.">
       Save identity and password
     </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1
new file mode 100644
index 0000000..4ba1991
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1
@@ -0,0 +1 @@
+3e05aa8562cd7a68d00d313431aca4dfe7effc5e
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_HIDDEN_NETWORK_WARNING.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_HIDDEN_NETWORK_WARNING.png.sha1
new file mode 100644
index 0000000..fd39eb1
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_HIDDEN_NETWORK_WARNING.png.sha1
@@ -0,0 +1 @@
+7b67f22ff91633353d0c6e3e61cd2bdfd75aab9a
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 247b74b..a9d3dec 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -38,10 +38,6 @@
   import("//tools/grit/grit_rule.gni")
 }
 
-if (is_chromeos) {
-  import("//chrome/browser/chromeos/kiosk_next_home/kiosk_next.gni")
-}
-
 additional_modules_list_file =
     "$root_gen_dir/chrome/browser/internal/additional_modules_list.txt"
 
@@ -280,8 +276,6 @@
     "chrome_browser_main_mac.mm",
     "chrome_browser_main_win.cc",
     "chrome_browser_main_win.h",
-    "chrome_child_process_watcher.cc",
-    "chrome_child_process_watcher.h",
     "chrome_content_browser_client.cc",
     "chrome_content_browser_client.h",
     "chrome_content_browser_client_parts.h",
@@ -321,6 +315,8 @@
     "component_updater/file_type_policies_component_installer.h",
     "component_updater/mei_preload_component_installer.cc",
     "component_updater/mei_preload_component_installer.h",
+    "component_updater/on_device_head_suggest_component_installer.cc",
+    "component_updater/on_device_head_suggest_component_installer.h",
     "component_updater/optimization_hints_component_installer.cc",
     "component_updater/optimization_hints_component_installer.h",
     "component_updater/origin_trials_component_installer.cc",
@@ -1034,6 +1030,9 @@
     "performance_manager/graph/graph.cc",
     "performance_manager/graph/graph_impl.cc",
     "performance_manager/graph/graph_impl.h",
+    "performance_manager/graph/graph_impl_operations.cc",
+    "performance_manager/graph/graph_impl_operations.h",
+    "performance_manager/graph/graph_operations.cc",
     "performance_manager/graph/node.cc",
     "performance_manager/graph/node_attached_data.cc",
     "performance_manager/graph/node_attached_data.h",
@@ -1071,6 +1070,8 @@
     "performance_manager/persistence/site_data/feature_usage.h",
     "performance_manager/persistence/site_data/leveldb_site_data_store.cc",
     "performance_manager/persistence/site_data/leveldb_site_data_store.h",
+    "performance_manager/persistence/site_data/non_recording_site_data_cache.cc",
+    "performance_manager/persistence/site_data/non_recording_site_data_cache.h",
     "performance_manager/persistence/site_data/noop_site_data_writer.cc",
     "performance_manager/persistence/site_data/noop_site_data_writer.h",
     "performance_manager/persistence/site_data/site_data_cache.h",
@@ -1089,6 +1090,7 @@
     "performance_manager/persistence/site_data/tab_visibility.h",
     "performance_manager/public/graph/frame_node.h",
     "performance_manager/public/graph/graph.h",
+    "performance_manager/public/graph/graph_operations.h",
     "performance_manager/public/graph/node.h",
     "performance_manager/public/graph/node_attached_data.h",
     "performance_manager/public/graph/page_node.h",
@@ -1996,6 +1998,7 @@
     "//components/sessions",
     "//components/signin/core/browser",
     "//components/signin/core/browser:signin_buildflags",
+    "//components/signin/core/browser/webdata",
     "//components/spellcheck:buildflags",
     "//components/ssl_errors",
     "//components/startup_metric_utils/browser:host",
@@ -3536,7 +3539,6 @@
     deps += [
       "//ash/public/cpp",
       "//chrome/browser/chromeos",
-      "//chrome/browser/chromeos/kiosk_next_home/mojom",
       "//chrome/services/cups_proxy",
       "//chromeos/services/assistant/public:feature_flags",
       "//chromeos/services/cellular_setup",
@@ -3842,6 +3844,20 @@
     ]
   }
 
+  if (is_mac) {
+    sources += [
+      "accessibility/caption_settings_dialog.h",
+      "accessibility/caption_settings_dialog_mac.mm",
+    ]
+  }
+
+  if (is_win) {
+    sources += [
+      "accessibility/caption_settings_dialog.h",
+      "accessibility/caption_settings_dialog_win.cc",
+    ]
+  }
+
   if (is_win || is_mac || is_desktop_linux) {
     sources += [
       "browser_switcher/alternative_browser_driver.h",
@@ -4515,10 +4531,6 @@
       "offline_pages/offline_page_origin_utils.h",
       "offline_pages/offline_page_request_handler.cc",
       "offline_pages/offline_page_request_handler.h",
-      "offline_pages/offline_page_request_interceptor.cc",
-      "offline_pages/offline_page_request_interceptor.h",
-      "offline_pages/offline_page_request_job.cc",
-      "offline_pages/offline_page_request_job.h",
       "offline_pages/offline_page_tab_helper.cc",
       "offline_pages/offline_page_tab_helper.h",
       "offline_pages/offline_page_url_loader.cc",
@@ -4591,6 +4603,8 @@
         "offline_pages/android/prefetch_background_task_scheduler_android.cc",
         "offline_pages/android/prefetch_configuration_impl_android.cc",
         "offline_pages/android/prefetched_pages_notifier_android.cc",
+        "offline_pages/android/request_coordinator_bridge.cc",
+        "offline_pages/android/request_coordinator_bridge.h",
         "offline_pages/android/request_coordinator_factory.cc",
       ]
     }
@@ -5114,10 +5128,6 @@
     defines += [ "enable_hangout_services_extension" ]
   }
 
-  if (is_chromeos && enable_kiosk_next) {
-    defines += [ "_kiosk_next" ]
-  }
-
   output_dir = "$root_gen_dir/chrome"
   outputs = [
     "grit/browser_resources.h",
@@ -5366,10 +5376,7 @@
   ]
 
   if (!is_android && !is_fuchsia) {
-    sources += [
-      "policy/test/local_policy_test_server.cc",
-      "policy/test/local_policy_test_server.h",
-    ]
+    deps += [ "//components/policy/test_support" ]
   }
 
   if (is_android) {
@@ -5378,6 +5385,8 @@
       "android/download/mock_download_controller.h",
       "autofill/mock_address_accessory_controller.cc",
       "autofill/mock_address_accessory_controller.h",
+      "autofill/mock_credit_card_accessory_controller.cc",
+      "autofill/mock_credit_card_accessory_controller.h",
       "autofill/mock_manual_filling_view.cc",
       "autofill/mock_manual_filling_view.h",
       "autofill/mock_password_accessory_controller.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9d328725..5a5d7b4 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2499,6 +2499,10 @@
      flag_descriptions::kOmniboxTabSwitchSuggestionsName,
      flag_descriptions::kOmniboxTabSwitchSuggestionsDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxTabSwitchSuggestions)},
+    {"omnibox-wrap-popup-position",
+     flag_descriptions::kOmniboxWrapPopupPositionName,
+     flag_descriptions::kOmniboxWrapPopupPositionDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(omnibox::kOmniboxWrapPopupPosition)},
     {"omnibox-pedal-suggestions",
      flag_descriptions::kOmniboxPedalSuggestionsName,
      flag_descriptions::kOmniboxPedalSuggestionsDescription, kOsDesktop,
@@ -2623,7 +2627,8 @@
 
     {"omnibox-ui-max-autocomplete-matches",
      flag_descriptions::kOmniboxUIMaxAutocompleteMatchesName,
-     flag_descriptions::kOmniboxUIMaxAutocompleteMatchesDescription, kOsDesktop,
+     flag_descriptions::kOmniboxUIMaxAutocompleteMatchesDescription,
+     kOsDesktop | kOsAndroid,
      FEATURE_WITH_PARAMS_VALUE_TYPE(
          omnibox::kUIExperimentMaxAutocompleteMatches,
          kOmniboxUIMaxAutocompleteMatchesVariations,
@@ -3452,6 +3457,13 @@
      flag_descriptions::kEnableAssistantKeyRemappingName,
      flag_descriptions::kEnableAssistantKeyRemappingDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::assistant::features::kAssistantKeyRemapping)},
+
+    {"enable-assistant-media-session-integration",
+     flag_descriptions::kEnableAssistantMediaSessionIntegrationName,
+     flag_descriptions::kEnableAssistantMediaSessionIntegrationDescription,
+     kOsCrOS,
+     FEATURE_VALUE_TYPE(
+         chromeos::assistant::features::kEnableMediaSessionIntegration)},
 #endif  // defined(OS_CHROMEOS)
 
     {"click-to-call-receiver", flag_descriptions::kClickToCallReceiverName,
diff --git a/chrome/browser/accessibility/caption_settings_dialog.h b/chrome/browser/accessibility/caption_settings_dialog.h
new file mode 100644
index 0000000..d6f08fa
--- /dev/null
+++ b/chrome/browser/accessibility/caption_settings_dialog.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 CHROME_BROWSER_ACCESSIBILITY_CAPTION_SETTINGS_DIALOG_H_
+#define CHROME_BROWSER_ACCESSIBILITY_CAPTION_SETTINGS_DIALOG_H_
+
+#include "base/macros.h"
+
+namespace captions {
+
+// An abstraction of a caption settings dialog. This is used for the captions
+// sub-section of Settings.
+class CaptionSettingsDialog {
+ public:
+  // Displays the native captions manager dialog.
+  static void ShowCaptionSettingsDialog();
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(CaptionSettingsDialog);
+};
+
+}  // namespace captions
+
+#endif  // CHROME_BROWSER_ACCESSIBILITY_CAPTION_SETTINGS_DIALOG_H_
diff --git a/chrome/browser/accessibility/caption_settings_dialog_mac.mm b/chrome/browser/accessibility/caption_settings_dialog_mac.mm
new file mode 100644
index 0000000..ae9c4aa
--- /dev/null
+++ b/chrome/browser/accessibility/caption_settings_dialog_mac.mm
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/accessibility/caption_settings_dialog.h"
+
+#import <AppKit/AppKit.h>
+
+namespace captions {
+
+static NSString* kCaptionSettingsUrlString =
+    @"x-apple.systempreferences:com.apple.preference.universalaccess?"
+    @"Captioning";
+
+void CaptionSettingsDialog::ShowCaptionSettingsDialog() {
+  [[NSWorkspace sharedWorkspace]
+      openURL:[NSURL URLWithString:kCaptionSettingsUrlString]];
+}
+
+}  // namespace captions
diff --git a/chrome/browser/accessibility/caption_settings_dialog_win.cc b/chrome/browser/accessibility/caption_settings_dialog_win.cc
new file mode 100644
index 0000000..aef787a
--- /dev/null
+++ b/chrome/browser/accessibility/caption_settings_dialog_win.cc
@@ -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.
+
+#include "chrome/browser/accessibility/caption_settings_dialog.h"
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include "base/bind.h"
+#include "base/task/post_task.h"
+#include "base/win/windows_version.h"
+
+namespace {
+
+// A helper callback that opens the caption settings dialog.
+void CaptionSettingsDialogCallback() {
+  if (base::win::GetVersion() >= base::win::Version::WIN10) {
+    ShellExecute(NULL, L"open", L"ms-settings:easeofaccess-closedcaptioning",
+                 NULL, NULL, SW_SHOWNORMAL);
+  }
+}
+
+}  // namespace
+
+namespace captions {
+
+void CaptionSettingsDialog::ShowCaptionSettingsDialog() {
+  base::PostTaskWithTraits(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+      base::BindOnce(CaptionSettingsDialogCallback));
+}
+
+}  // namespace captions
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 140323d..6fb30d55 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -137,12 +137,12 @@
   if (ui_delegate->GetState() != AutofillAssistantState::INACTIVE) {
     // The UI was created for an existing Controller.
     OnStatusMessageChanged(ui_delegate->GetStatusMessage());
+    OnBubbleMessageChanged(ui_delegate->GetBubbleMessage());
     OnProgressChanged(ui_delegate->GetProgress());
     OnProgressVisibilityChanged(ui_delegate->GetProgressVisible());
     OnInfoBoxChanged(ui_delegate_->GetInfoBox());
     OnDetailsChanged(ui_delegate->GetDetails());
-    OnSuggestionsChanged(ui_delegate->GetSuggestions());
-    UpdateActions();
+    OnUserActionsChanged(ui_delegate_->GetUserActions());
     OnPaymentRequestOptionsChanged(ui_delegate->GetPaymentRequestOptions());
     OnPaymentRequestInformationChanged(
         ui_delegate->GetPaymentRequestInformation());
@@ -195,7 +195,7 @@
 }
 
 void UiControllerAndroid::SetupForState() {
-  UpdateActions();
+  UpdateActions(ui_delegate_->GetUserActions());
   AutofillAssistantState state = ui_delegate_->GetState();
   switch (state) {
     case AutofillAssistantState::STARTING:
@@ -260,6 +260,15 @@
   }
 }
 
+void UiControllerAndroid::OnBubbleMessageChanged(const std::string& message) {
+  if (!message.empty()) {
+    JNIEnv* env = AttachCurrentThread();
+    Java_AssistantHeaderModel_setBubbleMessage(
+        env, GetHeaderModel(),
+        base::android::ConvertUTF8ToJavaString(env, message));
+  }
+}
+
 void UiControllerAndroid::OnProgressChanged(int progress) {
   Java_AssistantHeaderModel_setProgress(AttachCurrentThread(), GetHeaderModel(),
                                         progress);
@@ -387,78 +396,79 @@
 
 // Suggestions and actions carousels related methods.
 
-void UiControllerAndroid::OnSuggestionsChanged(
-    const std::vector<Chip>& suggestions) {
+void UiControllerAndroid::UpdateSuggestions(
+    const std::vector<UserAction>& user_actions) {
   JNIEnv* env = AttachCurrentThread();
-  std::vector<int> icons(suggestions.size());
-  std::vector<std::string> texts(suggestions.size());
-  bool disabled[suggestions.size()];
-  for (size_t i = 0; i < suggestions.size(); i++) {
-    icons[i] = suggestions[i].icon;
-    texts[i] = suggestions[i].text;
-    disabled[i] = suggestions[i].disabled;
+  auto chips = Java_AutofillAssistantUiController_createChipList(env);
+  int user_action_count = static_cast<int>(user_actions.size());
+  for (int i = 0; i < user_action_count; i++) {
+    const auto& user_action = user_actions[i];
+    if (user_action.chip.type != SUGGESTION)
+      continue;
+
+    Java_AutofillAssistantUiController_addSuggestion(
+        env, java_object_, chips,
+        base::android::ConvertUTF8ToJavaString(env, user_action.chip.text), i,
+        user_action.chip.icon, !user_action.enabled);
   }
-  Java_AutofillAssistantUiController_setSuggestions(
-      env, java_object_, base::android::ToJavaIntArray(env, icons),
-      base::android::ToJavaArrayOfStrings(env, texts),
-      base::android::ToJavaBooleanArray(env, disabled, suggestions.size()));
+  Java_AutofillAssistantUiController_setSuggestions(env, java_object_, chips);
 }
 
-void UiControllerAndroid::UpdateActions() {
+void UiControllerAndroid::UpdateActions(
+    const std::vector<UserAction>& user_actions) {
   DCHECK(ui_delegate_);
 
   JNIEnv* env = AttachCurrentThread();
 
   bool has_close_or_cancel = false;
   auto chips = Java_AutofillAssistantUiController_createChipList(env);
-  const auto& actions = ui_delegate_->GetActions();
-  int action_count = static_cast<int>(actions.size());
-  for (int i = 0; i < action_count; i++) {
-    const auto& action = actions[i];
-    auto text = base::android::ConvertUTF8ToJavaString(env, action.text);
-    int icon = action.icon;
-    switch (action.type) {
+  int user_action_count = static_cast<int>(user_actions.size());
+  for (int i = 0; i < user_action_count; i++) {
+    const auto& action = user_actions[i];
+    const Chip& chip = action.chip;
+    switch (chip.type) {
+      default:  // Ignore actions with other chip types or with no chips.
+        break;
+
       case HIGHLIGHTED_ACTION:
         Java_AutofillAssistantUiController_addHighlightedActionButton(
-            env, java_object_, chips, icon, text, i, action.disabled,
-            action.sticky);
+            env, java_object_, chips, chip.icon,
+            base::android::ConvertUTF8ToJavaString(env, chip.text), i,
+            !action.enabled, chip.sticky);
         break;
 
       case NORMAL_ACTION:
         Java_AutofillAssistantUiController_addActionButton(
-            env, java_object_, chips, icon, text, i, action.disabled,
-            action.sticky);
+            env, java_object_, chips, chip.icon,
+            base::android::ConvertUTF8ToJavaString(env, chip.text), i,
+            !action.enabled, chip.sticky);
         break;
 
       case CANCEL_ACTION:
         // A Cancel button sneaks in an UNDO snackbar before executing the
         // action, while a close button behaves like a normal button.
         Java_AutofillAssistantUiController_addCancelButton(
-            env, java_object_, chips, icon, text, i, action.disabled,
-            action.sticky);
+            env, java_object_, chips, chip.icon,
+            base::android::ConvertUTF8ToJavaString(env, chip.text), i,
+            !action.enabled, chip.sticky);
         has_close_or_cancel = true;
         break;
 
       case CLOSE_ACTION:
         Java_AutofillAssistantUiController_addActionButton(
-            env, java_object_, chips, icon, text, i, action.disabled,
-            action.sticky);
+            env, java_object_, chips, chip.icon,
+            base::android::ConvertUTF8ToJavaString(env, chip.text), i,
+            !action.enabled, chip.sticky);
         has_close_or_cancel = true;
         break;
 
       case DONE_ACTION:
         Java_AutofillAssistantUiController_addHighlightedActionButton(
-            env, java_object_, chips, icon, text, i, action.disabled,
-            action.sticky);
+            env, java_object_, chips, chip.icon,
+            base::android::ConvertUTF8ToJavaString(env, chip.text), i,
+            !action.enabled, chip.sticky);
         has_close_or_cancel = true;
         break;
-
-      case SUGGESTION:         // Suggestions are handled separately.
-      case UNKNOWN_CHIP_TYPE:  // Ignore unknown types
-        break;
-
-        // Default intentionally left out to cause a compilation error when a
-        // new type is added.
     }
   }
 
@@ -479,24 +489,18 @@
   Java_AutofillAssistantUiController_setActions(env, java_object_, chips);
 }
 
-void UiControllerAndroid::OnActionsChanged(const std::vector<Chip>& actions) {
-  UpdateActions();
+void UiControllerAndroid::OnUserActionsChanged(
+    const std::vector<UserAction>& actions) {
+  UpdateActions(actions);
+  UpdateSuggestions(actions);
 }
 
-void UiControllerAndroid::OnSuggestionSelected(
+void UiControllerAndroid::OnUserActionSelected(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
     jint index) {
   if (ui_delegate_)
-    ui_delegate_->SelectSuggestion(index);
-}
-
-void UiControllerAndroid::OnActionSelected(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    jint index) {
-  if (ui_delegate_)
-    ui_delegate_->SelectAction(index);
+    ui_delegate_->PerformUserAction(index);
 }
 
 void UiControllerAndroid::OnCancelButtonClicked(
@@ -522,7 +526,7 @@
     Shutdown(Metrics::SHEET_CLOSED);
     return;
   }
-  ui_delegate_->SelectAction(action_index);
+  ui_delegate_->PerformUserAction(action_index);
 }
 
 void UiControllerAndroid::OnCloseButtonClicked(
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 4c5ae6a9..0d1a654 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -22,6 +22,7 @@
 #include "components/autofill_assistant/browser/metrics.h"
 #include "components/autofill_assistant/browser/overlay_state.h"
 #include "components/autofill_assistant/browser/ui_controller.h"
+#include "components/autofill_assistant/browser/user_action.h"
 
 namespace autofill_assistant {
 // Class implements UiController, Client and starts the Controller.
@@ -59,9 +60,9 @@
   // Overrides UiController:
   void OnStateChanged(AutofillAssistantState new_state) override;
   void OnStatusMessageChanged(const std::string& message) override;
+  void OnBubbleMessageChanged(const std::string& message) override;
   void WillShutdown(Metrics::DropOutReason reason) override;
-  void OnSuggestionsChanged(const std::vector<Chip>& suggestions) override;
-  void OnActionsChanged(const std::vector<Chip>& actions) override;
+  void OnUserActionsChanged(const std::vector<UserAction>& actions) override;
   void OnPaymentRequestOptionsChanged(
       const PaymentRequestOptions* options) override;
   void OnPaymentRequestInformationChanged(
@@ -119,12 +120,9 @@
   base::android::ScopedJavaLocalRef<jstring> GetPrimaryAccountName(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller);
-  void OnSuggestionSelected(JNIEnv* env,
+  void OnUserActionSelected(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& jcaller,
                             jint index);
-  void OnActionSelected(JNIEnv* env,
-                        const base::android::JavaParamRef<jobject>& jcaller,
-                        jint index);
   void OnCancelButtonClicked(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
@@ -165,7 +163,8 @@
   std::string GetDebugContext();
   void DestroySelf();
   void Shutdown(Metrics::DropOutReason reason);
-  void UpdateActions();
+  void UpdateActions(const std::vector<UserAction>& GetUserActions);
+  void UpdateSuggestions(const std::vector<UserAction>& GetUserActions);
 
   // Hide the UI, show a snackbar with an undo button, and execute the given
   // action after a short delay unless the user taps the undo button.
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc
index 128f0b6e..28a0df4 100644
--- a/chrome/browser/android/download/download_manager_service.cc
+++ b/chrome/browser/android/download/download_manager_service.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/offline_item_utils.h"
 #include "chrome/browser/download/simple_download_manager_coordinator_factory.h"
-#include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_constants.h"
 #include "components/download/network/android/network_status_listener_android.h"
@@ -43,9 +42,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item_utils.h"
 #include "content/public/browser/download_request_utils.h"
-#include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/notification_service.h"
-#include "content/public/browser/system_connector.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 #include "url/origin.h"
 
@@ -77,12 +74,6 @@
       item->GetFileExternallyRemoved());
 }
 
-// For download that doesn't require full browser process, origin security check
-// is skipped as the download is either resumption or from a secure source.
-bool IgnoreOriginSecurityCheck(const GURL& url) {
-  return true;
-}
-
 void RenameItemCallback(
     const base::android::ScopedJavaGlobalRef<jobject> j_callback,
     download::DownloadItem::DownloadRenameResult result) {
@@ -243,19 +234,6 @@
   }
 }
 
-download::InProgressDownloadManager*
-DownloadManagerService::RetriveInProgressDownloadManager(
-    content::BrowserContext* context) {
-  if (in_progress_manager_) {
-    DCHECK(!context->IsOffTheRecord());
-    // Set |is_pending_downloads_loaded_| to false so that we need to wait for
-    // download history to initialize before performing new download actions.
-    is_pending_downloads_loaded_ = false;
-    return in_progress_manager_.release();
-  }
-  return nullptr;
-}
-
 void DownloadManagerService::ShowDownloadManager(bool show_prefetched_content) {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_DownloadManagerService_showDownloadManager(
@@ -346,9 +324,14 @@
 void DownloadManagerService::GetAllDownloads(JNIEnv* env,
                                              const JavaParamRef<jobject>& obj,
                                              bool is_off_the_record) {
-  if (is_manager_initialized_)
+  if (is_manager_initialized_) {
     GetAllDownloadsInternal(is_off_the_record);
-  else if (is_off_the_record)
+    return;
+  }
+
+  // Full download manager is required for this call.
+  GetDownloadManager(is_off_the_record);
+  if (is_off_the_record)
     pending_get_downloads_actions_ |= OFF_THE_RECORD;
   else
     pending_get_downloads_actions_ |= REGULAR;
@@ -655,55 +638,23 @@
     resume_callback_for_testing_.Run(false);
 }
 
-void DownloadManagerService::GetAllDownloads(
-    content::DownloadManager::DownloadVector* all_items,
-    bool is_off_the_record) {
-  if (in_progress_manager_) {
-    in_progress_manager_->GetAllDownloads(all_items);
-  } else {
-    content::DownloadManager* manager = GetDownloadManager(is_off_the_record);
-    if (manager)
-      manager->GetAllDownloads(all_items);
-  }
-}
-
 download::DownloadItem* DownloadManagerService::GetDownload(
     const std::string& download_guid,
     bool is_off_the_record) {
-  if (in_progress_manager_) {
-    DCHECK(!is_off_the_record);
-    return in_progress_manager_->GetDownloadByGuid(download_guid);
-  }
-
-  content::DownloadManager* manager = GetDownloadManager(is_off_the_record);
-  if (manager)
-    return manager->GetDownloadByGuid(download_guid);
-  return nullptr;
+  download::SimpleDownloadManagerCoordinator* coordinator =
+      GetCoordinator(is_off_the_record);
+  return coordinator ? coordinator->GetDownloadByGuid(download_guid) : nullptr;
 }
 
 void DownloadManagerService::CreateInProgressDownloadManager() {
-  if (in_progress_manager_)
-    return;
-  base::FilePath data_dir;
-  base::android::GetDataDirectory(&data_dir);
-  service_manager::Connector* connector = content::GetSystemConnector();
-  in_progress_manager_ = std::make_unique<download::InProgressDownloadManager>(
-      nullptr, data_dir.Append(chrome::kInitialProfile),
-      base::BindRepeating(&IgnoreOriginSecurityCheck),
-      base::BindRepeating(&content::DownloadRequestUtils::IsURLSafe),
-      connector);
-  content::GetNetworkServiceFromConnector(connector);
-  scoped_refptr<network::SharedURLLoaderFactory> factory =
-      SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory();
-  in_progress_manager_->set_url_loader_factory_getter(
-      base::MakeRefCounted<download::DownloadURLLoaderFactoryGetterImpl>(
-          factory->Clone()));
-  in_progress_manager_->set_download_start_observer(
+  download::InProgressDownloadManager* in_progress_manager =
+      DownloadManagerUtils::GetInProgressDownloadManager(
+          ProfileKeyStartupAccessor::GetInstance()->profile_key());
+  in_progress_manager->set_download_start_observer(
       DownloadControllerBase::Get());
   download::SimpleDownloadManagerCoordinator* coordinator =
       SimpleDownloadManagerCoordinatorFactory::GetForKey(
           ProfileKeyStartupAccessor::GetInstance()->profile_key());
-  coordinator->SetSimpleDownloadManager(in_progress_manager_.get(), false);
   UpdateCoordinator(coordinator, false);
 }
 
@@ -721,21 +672,11 @@
 }
 
 void DownloadManagerService::OnPendingDownloadsLoaded() {
-  // If |in_progress_manager_| is null, wait for DownloadManager to initialize
-  // before performing any pending actions.
-  if (!in_progress_manager_ && !is_manager_initialized_)
-    return;
   is_pending_downloads_loaded_ = true;
 
   // Kick-off the auto-resumption handler.
   content::DownloadManager::DownloadVector all_items;
-  if (in_progress_manager_) {
-    in_progress_manager_->GetAllDownloads(&all_items);
-  } else {
-    content::DownloadManager* manager = GetDownloadManager(false);
-    if (manager)
-      manager->GetAllDownloads(&all_items);
-  }
+  original_coordinator_->GetAllDownloads(&all_items);
 
   if (!download::AutoResumptionHandler::Get())
     CreateAutoResumptionHandler();
@@ -796,6 +737,12 @@
   }
 }
 
+download::SimpleDownloadManagerCoordinator*
+DownloadManagerService::GetCoordinator(bool is_off_the_record) {
+  return is_off_the_record ? off_the_record_coordinator_
+                           : original_coordinator_;
+}
+
 void DownloadManagerService::RenameDownload(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
@@ -829,15 +776,15 @@
     const JavaParamRef<jstring>& jurl,
     const JavaParamRef<jstring>& jdownload_guid,
     const JavaParamRef<jstring>& jtarget_path) {
-  if (!in_progress_manager_)
-    return;
+  download::InProgressDownloadManager* in_progress_manager =
+      DownloadManagerUtils::GetInProgressDownloadManager(
+          ProfileKeyStartupAccessor::GetInstance()->profile_key());
   std::vector<GURL> url_chain;
   url_chain.emplace_back(ConvertJavaStringToUTF8(env, jurl));
   base::FilePath target_path(ConvertJavaStringToUTF8(env, jtarget_path));
-  in_progress_manager_->AddInProgressDownloadForTest(
+  in_progress_manager->AddInProgressDownloadForTest(
       std::make_unique<download::DownloadItemImpl>(
-          in_progress_manager_.get(),
-          ConvertJavaStringToUTF8(env, jdownload_guid), 1,
+          in_progress_manager, ConvertJavaStringToUTF8(env, jdownload_guid), 1,
           target_path.AddExtension("crdownload"), target_path, url_chain,
           GURL(), GURL(), GURL(), GURL(), url::Origin(), "", "", base::Time(),
           base::Time(), "", "", 0, -1, 0, "",
diff --git a/chrome/browser/android/download/download_manager_service.h b/chrome/browser/android/download/download_manager_service.h
index 9b280f2e..4451c151 100644
--- a/chrome/browser/android/download/download_manager_service.h
+++ b/chrome/browser/android/download/download_manager_service.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "chrome/browser/android/download/download_controller.h"
+#include "chrome/browser/download/download_manager_utils.h"
 #include "components/download/public/common/all_download_event_notifier.h"
 #include "components/download/public/common/in_progress_download_manager.h"
 #include "content/public/browser/download_manager.h"
@@ -173,10 +174,6 @@
   download::InProgressDownloadManager* RetriveInProgressDownloadManager(
       content::BrowserContext* context);
 
-  // Get all downloads from DownloadManager or InProgressManager.
-  void GetAllDownloads(content::DownloadManager::DownloadVector* all_items,
-                       bool is_off_the_record);
-
   // Gets a download item from DownloadManager or InProgressManager.
   download::DownloadItem* GetDownload(const std::string& download_guid,
                                       bool is_off_the_record);
@@ -193,10 +190,6 @@
       const JavaParamRef<jstring>& jdownload_guid,
       jboolean download_started);
 
- protected:
-  // Called to get the content::DownloadManager instance.
-  virtual content::DownloadManager* GetDownloadManager(bool is_off_the_record);
-
  private:
   // For testing.
   friend class DownloadManagerServiceTest;
@@ -249,6 +242,13 @@
       download::SimpleDownloadManagerCoordinator* coordinator,
       bool is_off_the_record);
 
+  // Called to get the content::DownloadManager instance.
+  content::DownloadManager* GetDownloadManager(bool is_off_the_record);
+
+  // Retrieves the SimpleDownloadManagerCoordinator this object is listening to.
+  download::SimpleDownloadManagerCoordinator* GetCoordinator(
+      bool is_off_the_record);
+
   // Reference to the Java object.
   base::android::ScopedJavaGlobalRef<jobject> java_ref_;
 
@@ -287,10 +287,6 @@
   // The Registrar used to register for notifications.
   content::NotificationRegistrar registrar_;
 
-  // In-progress download manager when download is running as a service. Will
-  // pass this object to DownloadManagerImpl once it is created.
-  std::unique_ptr<download::InProgressDownloadManager> in_progress_manager_;
-
   download::SimpleDownloadManagerCoordinator* original_coordinator_;
   download::SimpleDownloadManagerCoordinator* off_the_record_coordinator_;
 
diff --git a/chrome/browser/android/download/download_manager_service_unittest.cc b/chrome/browser/android/download/download_manager_service_unittest.cc
index 1fdb2ddc..65a476d 100644
--- a/chrome/browser/android/download/download_manager_service_unittest.cc
+++ b/chrome/browser/android/download/download_manager_service_unittest.cc
@@ -23,42 +23,19 @@
 
 using ::testing::_;
 
-class MockDownloadManagerService : public DownloadManagerService {
- public:
-  MockDownloadManagerService() : DownloadManagerService() {
-    ON_CALL(manager_, GetDownloadByGuid(_)).WillByDefault(
-        ::testing::Invoke(this,
-                          &MockDownloadManagerService::GetDownloadByGuid));
-  }
-
-  void CreateDownloadItem(bool can_resume) {
-    download_.reset(new download::MockDownloadItem());
-    ON_CALL(*download_, CanResume()).WillByDefault(
-        ::testing::Return(can_resume));
-  }
-
- protected:
-  download::DownloadItem* GetDownloadByGuid(const std::string&) {
-    return download_.get();
-  }
-
-  content::DownloadManager* GetDownloadManager(
-      bool is_off_the_record) override {
-    return &manager_;
-  }
-
- private:
-  std::unique_ptr<download::MockDownloadItem> download_;
-  content::MockDownloadManager manager_;
-};
-
 class DownloadManagerServiceTest : public testing::Test {
  public:
   DownloadManagerServiceTest()
-      : service_(new MockDownloadManagerService()),
+      : service_(new DownloadManagerService()),
         coordinator_(base::NullCallback()),
         finished_(false),
-        success_(false) {}
+        success_(false) {
+    ON_CALL(manager_, GetDownloadByGuid(_))
+        .WillByDefault(::testing::Invoke(
+            this, &DownloadManagerServiceTest::GetDownloadByGuid));
+    coordinator_.SetSimpleDownloadManager(&manager_, false);
+    service_->UpdateCoordinator(&coordinator_, false);
+  }
 
   void OnResumptionDone(bool success) {
     finished_ = true;
@@ -81,10 +58,22 @@
       base::RunLoop().RunUntilIdle();
   }
 
+  void CreateDownloadItem(bool can_resume) {
+    download_.reset(new download::MockDownloadItem());
+    ON_CALL(*download_, CanResume())
+        .WillByDefault(::testing::Return(can_resume));
+  }
+
  protected:
+  download::DownloadItem* GetDownloadByGuid(const std::string&) {
+    return download_.get();
+  }
+
   base::test::ScopedTaskEnvironment scoped_task_environment_;
-  MockDownloadManagerService* service_;
+  DownloadManagerService* service_;
   download::SimpleDownloadManagerCoordinator coordinator_;
+  std::unique_ptr<download::MockDownloadItem> download_;
+  content::MockDownloadManager manager_;
   bool finished_;
   bool success_;
 
@@ -94,14 +83,14 @@
 // Test that resumption succeeds if the download item is found and can be
 // resumed.
 TEST_F(DownloadManagerServiceTest, ResumptionWithResumableItem) {
-  service_->CreateDownloadItem(true);
+  CreateDownloadItem(true);
   StartDownload("0000");
   EXPECT_TRUE(success_);
 }
 
 // Test that resumption fails if the target download item is not resumable.
 TEST_F(DownloadManagerServiceTest, ResumptionWithNonResumableItem) {
-  service_->CreateDownloadItem(false);
+  CreateDownloadItem(false);
   StartDownload("0000");
   EXPECT_FALSE(success_);
 }
diff --git a/chrome/browser/android/feature_utilities.cc b/chrome/browser/android/feature_utilities.cc
index 0a2ab01b..60030c3 100644
--- a/chrome/browser/android/feature_utilities.cc
+++ b/chrome/browser/android/feature_utilities.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/android/chrome_jni_headers/FeatureUtilities_jni.h"
 
+#include "base/android/jni_string.h"
 #include "chrome/browser/ntp_snippets/content_suggestions_service_factory.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/ntp_snippets/content_suggestions_service.h"
@@ -13,7 +14,9 @@
 #include "content/public/common/network_service_util.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 
+using base::android::ConvertJavaStringToUTF8;
 using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
 
 namespace {
 bool custom_tab_visible = false;
@@ -42,6 +45,13 @@
   return Java_FeatureUtilities_isNoTouchModeEnabled(env);
 }
 
+std::string GetReachedCodeProfilerTrialGroup() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> group =
+      Java_FeatureUtilities_getReachedCodeProfilerTrialGroup(env);
+  return ConvertJavaStringToUTF8(env, group);
+}
+
 } // namespace android
 } // namespace chrome
 
diff --git a/chrome/browser/android/feature_utilities.h b/chrome/browser/android/feature_utilities.h
index f3f20bcf..606b6ee 100644
--- a/chrome/browser/android/feature_utilities.h
+++ b/chrome/browser/android/feature_utilities.h
@@ -7,6 +7,8 @@
 
 #include <jni.h>
 
+#include <string>
+
 namespace chrome {
 namespace android {
 
@@ -24,6 +26,10 @@
 
 bool IsNoTouchModeEnabled();
 
+// Returns a finch group name currently used for the reached code profiler.
+// Returns an empty string if the group isn't specified.
+std::string GetReachedCodeProfilerTrialGroup();
+
 } // namespace android
 } // namespace chrome
 
diff --git a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
index b4274247..fe5752c87 100644
--- a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
+++ b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/web_data_service_factory.h"
+#include "components/payments/content/payment_event_response_util.h"
 #include "components/payments/content/payment_handler_host.h"
 #include "components/payments/content/payment_manifest_web_data_service.h"
 #include "components/payments/content/service_worker_payment_app_factory.h"
@@ -168,7 +169,10 @@
   Java_ServiceWorkerPaymentAppBridge_onPaymentAppInvoked(
       env, jcallback,
       ConvertUTF8ToJavaString(env, handler_response->method_name),
-      ConvertUTF8ToJavaString(env, handler_response->stringified_details));
+      ConvertUTF8ToJavaString(env, handler_response->stringified_details),
+      ConvertUTF8ToJavaString(
+          env, payments::ConvertPaymentEventResponseTypeToErrorString(
+                   handler_response->response_type)));
 }
 
 void OnPaymentAppAborted(const JavaRef<jobject>& jweb_contents,
diff --git a/chrome/browser/android/signin/chrome_signin_manager_delegate.cc b/chrome/browser/android/signin/chrome_signin_manager_delegate.cc
index ce35edfa..ab165ca 100644
--- a/chrome/browser/android/signin/chrome_signin_manager_delegate.cc
+++ b/chrome/browser/android/signin/chrome_signin_manager_delegate.cc
@@ -7,12 +7,18 @@
 #include "base/android/callback_android.h"
 #include "base/android/jni_string.h"
 #include "chrome/android/chrome_jni_headers/ChromeSigninManagerDelegate_jni.h"
+#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
 #include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
 #include "chrome/browser/policy/cloud/user_policy_signin_service_mobile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "components/google/core/common/google_util.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_pref_names.h"
+#include "content/public/browser/browsing_data_filter_builder.h"
+#include "content/public/browser/browsing_data_remover.h"
 #include "content/public/browser/storage_partition.h"
 #include "services/identity/public/cpp/identity_manager.h"
 
@@ -20,6 +26,73 @@
 
 namespace {
 
+// A BrowsingDataRemover::Observer that clears Profile data and then invokes
+// a callback and deletes itself. It can be configured to delete all data
+// (for enterprise users) or only Google's service workers (for all users).
+class ProfileDataRemover : public content::BrowsingDataRemover::Observer {
+ public:
+  ProfileDataRemover(Profile* profile,
+                     bool all_data,
+                     base::OnceClosure callback)
+      : profile_(profile),
+        all_data_(all_data),
+        callback_(std::move(callback)),
+        origin_runner_(base::ThreadTaskRunnerHandle::Get()),
+        remover_(content::BrowserContext::GetBrowsingDataRemover(profile)) {
+    remover_->AddObserver(this);
+
+    if (all_data) {
+      remover_->RemoveAndReply(
+          base::Time(), base::Time::Max(),
+          ChromeBrowsingDataRemoverDelegate::ALL_DATA_TYPES,
+          ChromeBrowsingDataRemoverDelegate::ALL_ORIGIN_TYPES, this);
+    } else {
+      std::unique_ptr<content::BrowsingDataFilterBuilder> google_tld_filter =
+          content::BrowsingDataFilterBuilder::Create(
+              content::BrowsingDataFilterBuilder::WHITELIST);
+
+      // TODO(msramek): BrowsingDataFilterBuilder was not designed for
+      // large filters. Optimize it.
+      for (const std::string& domain :
+           google_util::GetGoogleRegistrableDomains()) {
+        google_tld_filter->AddRegisterableDomain(domain);
+      }
+
+      remover_->RemoveWithFilterAndReply(
+          base::Time(), base::Time::Max(),
+          content::BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE,
+          ChromeBrowsingDataRemoverDelegate::ALL_ORIGIN_TYPES,
+          std::move(google_tld_filter), this);
+    }
+  }
+
+  ~ProfileDataRemover() override {}
+
+  void OnBrowsingDataRemoverDone() override {
+    remover_->RemoveObserver(this);
+
+    if (all_data_) {
+      // All the Profile data has been wiped. Clear the last signed in username
+      // as well, so that the next signin doesn't trigger the account
+      // change dialog.
+      profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesLastAccountId);
+      profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesLastUsername);
+    }
+
+    origin_runner_->PostTask(FROM_HERE, std::move(callback_));
+    origin_runner_->DeleteSoon(FROM_HERE, this);
+  }
+
+ private:
+  Profile* profile_;
+  bool all_data_;
+  base::OnceClosure callback_;
+  scoped_refptr<base::SingleThreadTaskRunner> origin_runner_;
+  content::BrowsingDataRemover* remover_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProfileDataRemover);
+};
+
 // Returns whether the user is a managed user or not.
 bool ShouldLoadPolicyForUser(const std::string& username) {
   return !policy::BrowserPolicyConnector::IsNonEnterpriseUser(username);
@@ -27,14 +100,12 @@
 
 }  // namespace
 
-ChromeSigninManagerDelegate::ChromeSigninManagerDelegate(JNIEnv* env,
-                                                         jobject obj)
+ChromeSigninManagerDelegate::ChromeSigninManagerDelegate(JNIEnv* env)
     : profile_(ProfileManager::GetActiveUserProfile()),
       identity_manager_(IdentityManagerFactory::GetForProfile(profile_)),
       user_cloud_policy_manager_(profile_->GetUserCloudPolicyManager()),
       user_policy_signin_service_(
           policy::UserPolicySigninServiceFactory::GetForProfile(profile_)),
-      java_ref_(env, obj),
       weak_factory_(this) {
   DCHECK(profile_);
   DCHECK(identity_manager_);
@@ -44,8 +115,9 @@
 
 ChromeSigninManagerDelegate::~ChromeSigninManagerDelegate() {}
 
-void ChromeSigninManagerDelegate::Destroy(JNIEnv* env,
-                                          const JavaParamRef<jobject>& obj) {
+void ChromeSigninManagerDelegate::Destroy(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
   delete this;
 }
 
@@ -173,11 +245,37 @@
   return domain;
 }
 
-// instantiates ChromeSigninManagerDelegate
-static jlong JNI_ChromeSigninManagerDelegate_Init(
+void ChromeSigninManagerDelegate::WipeProfileData(
     JNIEnv* env,
-    const JavaParamRef<jobject>& obj) {
+    const JavaParamRef<jobject>& obj,
+    const JavaParamRef<jobject>& j_callback) {
+  WipeData(
+      profile_, true /* all data */,
+      base::BindOnce(base::android::RunRunnableAndroid,
+                     base::android::ScopedJavaGlobalRef<jobject>(j_callback)));
+}
+
+void ChromeSigninManagerDelegate::WipeGoogleServiceWorkerCaches(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
+    const JavaParamRef<jobject>& j_callback) {
+  WipeData(
+      profile_, false /* only Google service worker caches */,
+      base::BindOnce(base::android::RunRunnableAndroid,
+                     base::android::ScopedJavaGlobalRef<jobject>(j_callback)));
+}
+
+// static
+void ChromeSigninManagerDelegate::WipeData(Profile* profile,
+                                           bool all_data,
+                                           base::OnceClosure callback) {
+  // The ProfileDataRemover deletes itself once done.
+  new ProfileDataRemover(profile, all_data, std::move(callback));
+}
+
+// instantiates ChromeSigninManagerDelegate
+static jlong JNI_ChromeSigninManagerDelegate_Init(JNIEnv* env) {
   ChromeSigninManagerDelegate* chrome_signin_manager_delegate =
-      new ChromeSigninManagerDelegate(env, obj);
+      new ChromeSigninManagerDelegate(env);
   return reinterpret_cast<intptr_t>(chrome_signin_manager_delegate);
 }
diff --git a/chrome/browser/android/signin/chrome_signin_manager_delegate.h b/chrome/browser/android/signin/chrome_signin_manager_delegate.h
index 35a77b1a..dd9e940 100644
--- a/chrome/browser/android/signin/chrome_signin_manager_delegate.h
+++ b/chrome/browser/android/signin/chrome_signin_manager_delegate.h
@@ -25,7 +25,7 @@
 // dependencies.
 class ChromeSigninManagerDelegate {
  public:
-  ChromeSigninManagerDelegate(JNIEnv* env, jobject obj);
+  explicit ChromeSigninManagerDelegate(JNIEnv* env);
 
   void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
 
@@ -49,7 +49,22 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
 
+  // Delete all data for this profile.
+  void WipeProfileData(JNIEnv* env,
+                       const base::android::JavaParamRef<jobject>& obj,
+                       const base::android::JavaParamRef<jobject>& j_callback);
+
+  // Delete service worker caches for google.<eTLD>.
+  void WipeGoogleServiceWorkerCaches(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& j_callback);
+
  private:
+  friend class ChromeSigninManagerDelegateTest;
+  FRIEND_TEST_ALL_PREFIXES(ChromeSigninManagerDelegateTest,
+                           DeleteGoogleServiceWorkerCaches);
+
   struct ManagementCredentials {
     ManagementCredentials(const std::string& dm_token,
                           const std::string& client_id)
@@ -82,13 +97,15 @@
                                base::OnceCallback<void()> policy_callback,
                                const ManagementCredentials& credentials);
 
+  static void WipeData(Profile* profile,
+                       bool all_data,
+                       base::OnceClosure callback);
+
   Profile* const profile_ = nullptr;
 
   identity::IdentityManager* const identity_manager_ = nullptr;
   policy::UserCloudPolicyManager* const user_cloud_policy_manager_ = nullptr;
   policy::UserPolicySigninService* const user_policy_signin_service_ = nullptr;
-  // A reference to the Java counterpart of this object.
-  const base::android::ScopedJavaGlobalRef<jobject> java_ref_;
 
   base::WeakPtrFactory<ChromeSigninManagerDelegate> weak_factory_;
 };
diff --git a/chrome/browser/android/signin/signin_manager_android_unittest.cc b/chrome/browser/android/signin/chrome_signin_manager_delegate_unittest.cc
similarity index 89%
rename from chrome/browser/android/signin/signin_manager_android_unittest.cc
rename to chrome/browser/android/signin/chrome_signin_manager_delegate_unittest.cc
index 36c96e4..40312a9 100644
--- a/chrome/browser/android/signin/signin_manager_android_unittest.cc
+++ b/chrome/browser/android/signin/chrome_signin_manager_delegate_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 "signin_manager_android.h"
+#include "chrome_signin_manager_delegate.h"
 
 #include <memory>
 #include <set>
@@ -50,11 +50,11 @@
 
 }  // namespace
 
-class SigninManagerAndroidTest : public ::testing::Test {
+class ChromeSigninManagerDelegateTest : public ::testing::Test {
  public:
-  SigninManagerAndroidTest()
+  ChromeSigninManagerDelegateTest()
       : profile_manager_(TestingBrowserProcess::GetGlobal()) {}
-  ~SigninManagerAndroidTest() override {}
+  ~ChromeSigninManagerDelegateTest() override {}
 
   void SetUp() override {
     ASSERT_TRUE(profile_manager_.SetUp());
@@ -92,8 +92,8 @@
   // Calls SigninManager::WipeData(|all_data|) and waits for its completion.
   void WipeData(bool all_data) {
     std::unique_ptr<base::RunLoop> run_loop(new base::RunLoop());
-    SigninManagerAndroid::WipeData(profile(), all_data,
-                                   run_loop->QuitClosure());
+    ChromeSigninManagerDelegate::WipeData(profile(), all_data,
+                                          run_loop->QuitClosure());
     run_loop->Run();
   }
 
@@ -102,13 +102,14 @@
   TestingProfileManager profile_manager_;
   TestingProfile* profile_;  // Owned by |profile_manager_|.
 
-  DISALLOW_COPY_AND_ASSIGN(SigninManagerAndroidTest);
+  DISALLOW_COPY_AND_ASSIGN(ChromeSigninManagerDelegateTest);
 };
 
 // TODO(crbug.com/929456): This test does not actually test anything; the
 // CannedBrowsingDataCacheStorageHelper isn't hooked up to observe any
 // deletions. Disabled to allow refactoring of browsing data code.
-TEST_F(SigninManagerAndroidTest, DISABLED_DeleteGoogleServiceWorkerCaches) {
+TEST_F(ChromeSigninManagerDelegateTest,
+       DISABLED_DeleteGoogleServiceWorkerCaches) {
   struct TestCase {
     std::string worker_url;
     bool should_be_deleted;
@@ -146,9 +147,9 @@
 
   // Delete service workers and wait for completion.
   base::RunLoop run_loop;
-  SigninManagerAndroid::WipeData(profile(),
-                                 false /* only Google service worker caches */,
-                                 run_loop.QuitClosure());
+  ChromeSigninManagerDelegate::WipeData(
+      profile(), false /* only Google service worker caches */,
+      run_loop.QuitClosure());
   run_loop.Run();
 
   // Test whether the correct service worker caches were deleted.
@@ -167,7 +168,7 @@
 }
 
 // Tests that wiping all data also deletes bookmarks.
-TEST_F(SigninManagerAndroidTest, DeleteBookmarksWhenWipingAllData) {
+TEST_F(ChromeSigninManagerDelegateTest, DeleteBookmarksWhenWipingAllData) {
   bookmarks::BookmarkModel* bookmark_model = AddTestBookmarks();
   ASSERT_GE(bookmark_model->bookmark_bar_node()->children().size(), 0u);
   WipeData(true);
@@ -175,7 +176,8 @@
 }
 
 // Tests that wiping Google service worker caches does not delete bookmarks.
-TEST_F(SigninManagerAndroidTest, DontDeleteBookmarksWhenDeletingSWCaches) {
+TEST_F(ChromeSigninManagerDelegateTest,
+       DontDeleteBookmarksWhenDeletingSWCaches) {
   bookmarks::BookmarkModel* bookmark_model = AddTestBookmarks();
   size_t num_bookmarks = bookmark_model->bookmark_bar_node()->children().size();
   ASSERT_GE(num_bookmarks, 0u);
diff --git a/chrome/browser/android/signin/signin_manager_android.cc b/chrome/browser/android/signin/signin_manager_android.cc
index 7577171..4db70d6 100644
--- a/chrome/browser/android/signin/signin_manager_android.cc
+++ b/chrome/browser/android/signin/signin_manager_android.cc
@@ -7,119 +7,31 @@
 #include <utility>
 #include <vector>
 
-#include "base/android/callback_android.h"
-#include "base/android/jni_android.h"
-#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/android/chrome_jni_headers/SigninManager_jni.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
-#include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
-#include "chrome/browser/policy/cloud/user_policy_signin_service_mobile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/pref_names.h"
-#include "components/google/core/common/google_util.h"
-#include "components/policy/core/common/cloud/cloud_policy_core.h"
-#include "components/policy/core/common/cloud/cloud_policy_store.h"
-#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/primary_account_manager.h"
 #include "components/signin/core/browser/signin_pref_names.h"
-#include "content/public/browser/browsing_data_filter_builder.h"
-#include "content/public/browser/browsing_data_remover.h"
-#include "content/public/browser/storage_partition.h"
 #include "google_apis/gaia/gaia_auth_util.h"
-#include "google_apis/gaia/gaia_constants.h"
 #include "services/identity/public/cpp/primary_account_mutator.h"
 
 using base::android::JavaParamRef;
 
 namespace {
-
 // Clears the information about the last signed-in user from |profile|.
 void ClearLastSignedInUserForProfile(Profile* profile) {
   profile->GetPrefs()->ClearPref(prefs::kGoogleServicesLastAccountId);
   profile->GetPrefs()->ClearPref(prefs::kGoogleServicesLastUsername);
 }
-
-// A BrowsingDataRemover::Observer that clears Profile data and then invokes
-// a callback and deletes itself. It can be configured to delete all data
-// (for enterprise users) or only Google's service workers (for all users).
-class ProfileDataRemover : public content::BrowsingDataRemover::Observer {
- public:
-  ProfileDataRemover(Profile* profile,
-                     bool all_data,
-                     base::OnceClosure callback)
-      : profile_(profile),
-        all_data_(all_data),
-        callback_(std::move(callback)),
-        origin_runner_(base::ThreadTaskRunnerHandle::Get()),
-        remover_(content::BrowserContext::GetBrowsingDataRemover(profile)) {
-    remover_->AddObserver(this);
-
-    if (all_data) {
-      remover_->RemoveAndReply(
-          base::Time(), base::Time::Max(),
-          ChromeBrowsingDataRemoverDelegate::ALL_DATA_TYPES,
-          ChromeBrowsingDataRemoverDelegate::ALL_ORIGIN_TYPES, this);
-    } else {
-      std::unique_ptr<content::BrowsingDataFilterBuilder> google_tld_filter =
-          content::BrowsingDataFilterBuilder::Create(
-              content::BrowsingDataFilterBuilder::WHITELIST);
-
-      // TODO(msramek): BrowsingDataFilterBuilder was not designed for
-      // large filters. Optimize it.
-      for (const std::string& domain :
-           google_util::GetGoogleRegistrableDomains()) {
-        google_tld_filter->AddRegisterableDomain(domain);
-      }
-
-      remover_->RemoveWithFilterAndReply(
-          base::Time(), base::Time::Max(),
-          content::BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE,
-          ChromeBrowsingDataRemoverDelegate::ALL_ORIGIN_TYPES,
-          std::move(google_tld_filter), this);
-    }
-  }
-
-  ~ProfileDataRemover() override {}
-
-  void OnBrowsingDataRemoverDone() override {
-    remover_->RemoveObserver(this);
-
-    if (all_data_) {
-      // All the Profile data has been wiped. Clear the last signed in username
-      // as well, so that the next signin doesn't trigger the account
-      // change dialog.
-      ClearLastSignedInUserForProfile(profile_);
-    }
-
-    origin_runner_->PostTask(FROM_HERE, std::move(callback_));
-    origin_runner_->DeleteSoon(FROM_HERE, this);
-  }
-
- private:
-  Profile* profile_;
-  bool all_data_;
-  base::OnceClosure callback_;
-  scoped_refptr<base::SingleThreadTaskRunner> origin_runner_;
-  content::BrowsingDataRemover* remover_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProfileDataRemover);
-};
-
 }  // namespace
 
 SigninManagerAndroid::SigninManagerAndroid(JNIEnv* env, jobject obj)
-    : profile_(NULL),
-      weak_factory_(this) {
+    : profile_(NULL) {
   java_signin_manager_.Reset(env, obj);
   profile_ = ProfileManager::GetActiveUserProfile();
   DCHECK(profile_);
@@ -167,26 +79,6 @@
       signin_metrics::SignoutDelete::IGNORE_METRIC);
 }
 
-void SigninManagerAndroid::WipeProfileData(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jobject>& j_callback) {
-  WipeData(
-      profile_, true /* all data */,
-      base::BindOnce(base::android::RunRunnableAndroid,
-                     base::android::ScopedJavaGlobalRef<jobject>(j_callback)));
-}
-
-void SigninManagerAndroid::WipeGoogleServiceWorkerCaches(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jobject>& j_callback) {
-  WipeData(
-      profile_, false /* only Google service worker caches */,
-      base::BindOnce(base::android::RunRunnableAndroid,
-                     base::android::ScopedJavaGlobalRef<jobject>(j_callback)));
-}
-
 void SigninManagerAndroid::ClearLastSignedInUser(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
@@ -236,14 +128,6 @@
       profile_->GetPrefs()->GetBoolean(prefs::kSigninAllowed));
 }
 
-// static
-void SigninManagerAndroid::WipeData(Profile* profile,
-                                    bool all_data,
-                                    base::OnceClosure callback) {
-  // The ProfileDataRemover deletes itself once done.
-  new ProfileDataRemover(profile, all_data, std::move(callback));
-}
-
 static jlong JNI_SigninManager_Init(JNIEnv* env,
                                     const JavaParamRef<jobject>& obj) {
   SigninManagerAndroid* signin_manager_android =
diff --git a/chrome/browser/android/signin/signin_manager_android.h b/chrome/browser/android/signin/signin_manager_android.h
index 68c3d18..bf6220f 100644
--- a/chrome/browser/android/signin/signin_manager_android.h
+++ b/chrome/browser/android/signin/signin_manager_android.h
@@ -10,7 +10,6 @@
 
 #include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "services/identity/public/cpp/identity_manager.h"
@@ -39,17 +38,6 @@
                const base::android::JavaParamRef<jobject>& obj,
                jint signoutReason);
 
-  // Delete all data for this profile.
-  void WipeProfileData(JNIEnv* env,
-                       const base::android::JavaParamRef<jobject>& obj,
-                       const base::android::JavaParamRef<jobject>& j_callback);
-
-  // Delete service worker caches for google.<eTLD>.
-  void WipeGoogleServiceWorkerCaches(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jobject>& j_callback);
-
   void LogInSignedInUser(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& obj);
 
@@ -72,20 +60,10 @@
       const CoreAccountInfo& previous_primary_account_info) override;
 
  private:
-  friend class SigninManagerAndroidTest;
-  FRIEND_TEST_ALL_PREFIXES(SigninManagerAndroidTest,
-                           DeleteGoogleServiceWorkerCaches);
-
   ~SigninManagerAndroid() override;
 
-  void OnBrowsingDataRemoverDone();
-
   void OnSigninAllowedPrefChanged();
 
-  static void WipeData(Profile* profile,
-                       bool all_data,
-                       base::OnceClosure callback);
-
   Profile* profile_;
 
   // Java-side SigninManager object.
@@ -95,8 +73,6 @@
 
   base::ThreadChecker thread_checker_;
 
-  base::WeakPtrFactory<SigninManagerAndroid> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(SigninManagerAndroid);
 };
 
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
index 1476dd0..5c00b09c 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -554,6 +554,13 @@
       &ArCoreGl::OnBindingDisconnect, weak_ptr_factory_.GetWeakPtr()));
 }
 
+void ArCoreGl::SetInputSourceButtonListener(
+    device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo) {
+  // Input eventing is not supported. This call should not
+  // be made on this device.
+  mojo::ReportBadMessage("Input eventing is not supported.");
+}
+
 void ArCoreGl::RequestHitTest(
     mojom::XRRayPtr ray,
     mojom::XREnvironmentIntegrationProvider::RequestHitTestCallback callback) {
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.h b/chrome/browser/android/vr/arcore_device/arcore_gl.h
index 1735b49..0e13a647 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.h
@@ -85,6 +85,8 @@
   void GetEnvironmentIntegrationProvider(
       mojom::XREnvironmentIntegrationProviderAssociatedRequest
           environment_provider) override;
+  void SetInputSourceButtonListener(
+      device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo) override;
 
   // XRPresentationProvider
   void SubmitFrameMissing(int16_t frame_index, const gpu::SyncToken&) override;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.cc b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
index 8b99335..1054860 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
@@ -30,8 +30,9 @@
 
   device::mojom::VRPosePtr result = device::mojom::VRPose::New();
 
-  result->orientation.emplace(pose_raw, pose_raw + 4);
-  result->position.emplace(pose_raw + 4, pose_raw + 7);
+  result->orientation =
+      gfx::Quaternion(pose_raw[0], pose_raw[1], pose_raw[2], pose_raw[3]);
+  result->position = gfx::Point3F(pose_raw[4], pose_raw[5], pose_raw[6]);
 
   return result;
 }
@@ -498,10 +499,21 @@
       }
     }
 
+    std::array<float, 16> matrix;
+    ArPose_getMatrix(arcore_session_.get(), arcore_pose.get(), matrix.data());
+
     mojom::XRHitResultPtr mojo_hit = mojom::XRHitResult::New();
-    mojo_hit.get()->hit_matrix.resize(16);
-    ArPose_getMatrix(arcore_session_.get(), arcore_pose.get(),
-                     mojo_hit.get()->hit_matrix.data());
+
+    // ArPose_getMatrix returns the matrix in WebGL style column-major order
+    // and gfx::Transform expects row major order.
+    // clang-format off
+    mojo_hit->hit_matrix = gfx::Transform(
+      matrix[0], matrix[4], matrix[8],  matrix[12],
+      matrix[1], matrix[5], matrix[9],  matrix[13],
+      matrix[2], matrix[6], matrix[10], matrix[14],
+      matrix[3], matrix[7], matrix[11], matrix[15]
+    );
+    // clang-format on
 
     // Insert new results at head to preserver order from ArCore
     hit_results->insert(hit_results->begin(), std::move(mojo_hit));
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
index de1dd36..ee351e7d5 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -190,15 +190,8 @@
 
   // 1m up from the origin, neutral orientation facing forward.
   mojom::VRPosePtr pose = mojom::VRPose::New();
-  pose->position.emplace(3);
-  pose->position.value()[0] = 0;
-  pose->position.value()[1] = 1;
-  pose->position.value()[2] = 0;
-  pose->orientation.emplace(4);
-  pose->orientation.value()[0] = 0;
-  pose->orientation.value()[1] = 0;
-  pose->orientation.value()[2] = 0;
-  pose->orientation.value()[3] = 1;
+  pose->position = gfx::Point3F(0.0, 1.0, 0.0);
+  pose->orientation = gfx::Quaternion();
 
   return pose;
 }
@@ -207,12 +200,8 @@
     const mojom::XRRayPtr& ray,
     std::vector<mojom::XRHitResultPtr>* hit_results) {
   mojom::XRHitResultPtr hit = mojom::XRHitResult::New();
-  hit->hit_matrix.resize(16);
   // Identity matrix - no translation and default orientation.
-  hit->hit_matrix.data()[0] = 1;
-  hit->hit_matrix.data()[5] = 1;
-  hit->hit_matrix.data()[10] = 1;
-  hit->hit_matrix.data()[15] = 1;
+  hit->hit_matrix = gfx::Transform();
   hit_results->push_back(std::move(hit));
 
   return true;
@@ -223,15 +212,8 @@
 
   // 1m ahead of the origin, neutral orientation facing forward.
   mojom::VRPosePtr pose = mojom::VRPose::New();
-  pose->position.emplace(3);
-  pose->position.value()[0] = 0;
-  pose->position.value()[1] = 0;
-  pose->position.value()[2] = -1;
-  pose->orientation.emplace(4);
-  pose->orientation.value()[0] = 0;
-  pose->orientation.value()[1] = 0;
-  pose->orientation.value()[2] = 0;
-  pose->orientation.value()[3] = 1;
+  pose->position = gfx::Point3F(0.0, 0.0, -1.0);
+  pose->orientation = gfx::Quaternion();
 
   // some random triangle
   std::vector<mojom::XRPlanePointDataPtr> vertices;
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.cc b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
index 09428de3..5db1082 100644
--- a/chrome/browser/android/vr/gvr_scheduler_delegate.cc
+++ b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
@@ -1330,4 +1330,11 @@
   mojo::ReportBadMessage("Environment integration is not supported.");
 }
 
+void GvrSchedulerDelegate::SetInputSourceButtonListener(
+    device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo) {
+  // Input eventing is not supported. This call should not
+  // be made on this device.
+  mojo::ReportBadMessage("Input eventing is not supported.");
+}
+
 }  // namespace vr
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.h b/chrome/browser/android/vr/gvr_scheduler_delegate.h
index 58c2a62..ecf688c 100644
--- a/chrome/browser/android/vr/gvr_scheduler_delegate.h
+++ b/chrome/browser/android/vr/gvr_scheduler_delegate.h
@@ -140,6 +140,8 @@
   void GetEnvironmentIntegrationProvider(
       device::mojom::XREnvironmentIntegrationProviderAssociatedRequest
           environment_provider) override;
+  void SetInputSourceButtonListener(
+      device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo) override;
 
   // XRPresentationProvider
   void SubmitFrameMissing(int16_t frame_index, const gpu::SyncToken&) override;
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index 7c228900..2d91b23f 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -53,6 +53,9 @@
 // Icon which has Cross-Origin-Resource-Policy: same-origin set.
 const char* kBestPrimaryIconCorpUrl = "/banners/image-512px-corp.png";
 
+// Timeout for getting response from WebAPK server.
+const int kWebApkServerRequestTimeoutMs = 1000;
+
 // Token from the WebAPK server. In production, the token is sent to Google
 // Play. Google Play uses the token to retrieve the WebAPK from the WebAPK
 // server.
@@ -100,52 +103,36 @@
 // Runs the WebApkInstaller installation process/update and blocks till done.
 class WebApkInstallerRunner {
  public:
-  WebApkInstallerRunner(content::BrowserContext* browser_context,
-                        const GURL& start_url,
-                        const GURL& best_primary_icon_url,
-                        const GURL& best_badge_icon_url,
-                        SpaceStatus test_space_status)
-      : browser_context_(browser_context),
-        start_url_(start_url),
-        best_primary_icon_url_(best_primary_icon_url),
-        best_badge_icon_url_(best_badge_icon_url),
-        test_space_status_(test_space_status) {}
+  WebApkInstallerRunner() {}
 
   ~WebApkInstallerRunner() {}
 
-  void RunInstallWebApk() {
+  void RunInstallWebApk(std::unique_ptr<WebApkInstaller> installer,
+                        const ShortcutInfo& info) {
     base::RunLoop run_loop;
     on_completed_callback_ = run_loop.QuitClosure();
 
-    ShortcutInfo info(start_url_);
-    info.best_primary_icon_url = best_primary_icon_url_;
-    info.best_badge_icon_url = best_badge_icon_url_;
-    WebApkInstaller::InstallAsyncForTesting(
-        CreateWebApkInstaller(), info, SkBitmap(), SkBitmap(),
-        base::BindOnce(&WebApkInstallerRunner::OnCompleted,
-                       base::Unretained(this)));
-
-    run_loop.Run();
-  }
-
-  void RunUpdateWebApk(const base::FilePath& update_request_path) {
-    base::RunLoop run_loop;
-    on_completed_callback_ = run_loop.QuitClosure();
-
-    WebApkInstaller::UpdateAsyncForTesting(
-        CreateWebApkInstaller(), update_request_path,
-        base::BindOnce(&WebApkInstallerRunner::OnCompleted,
-                       base::Unretained(this)));
-
-    run_loop.Run();
-  }
-
-  WebApkInstaller* CreateWebApkInstaller() {
     // WebApkInstaller owns itself.
-    WebApkInstaller* installer =
-        new TestWebApkInstaller(browser_context_, test_space_status_);
-    installer->SetTimeoutMs(100);
-    return installer;
+    WebApkInstaller::InstallAsyncForTesting(
+        installer.release(), info, SkBitmap(), SkBitmap(),
+        base::BindOnce(&WebApkInstallerRunner::OnCompleted,
+                       base::Unretained(this)));
+
+    run_loop.Run();
+  }
+
+  void RunUpdateWebApk(std::unique_ptr<WebApkInstaller> installer,
+                       const base::FilePath& update_request_path) {
+    base::RunLoop run_loop;
+    on_completed_callback_ = run_loop.QuitClosure();
+
+    // WebApkInstaller owns itself.
+    WebApkInstaller::UpdateAsyncForTesting(
+        installer.release(), update_request_path,
+        base::BindOnce(&WebApkInstallerRunner::OnCompleted,
+                       base::Unretained(this)));
+
+    run_loop.Run();
   }
 
   WebApkInstallResult result() { return result_; }
@@ -158,17 +145,6 @@
     on_completed_callback_.Run();
   }
 
-  content::BrowserContext* browser_context_;
-
-  const GURL start_url_;
-
-  // The Web Manifest's icon URLs.
-  const GURL best_primary_icon_url_;
-  const GURL best_badge_icon_url_;
-
-  // The space status used in tests.
-  SpaceStatus test_space_status_;
-
   // Called after the installation process has succeeded or failed.
   base::Closure on_completed_callback_;
 
@@ -312,17 +288,18 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  // Sets the Web Manifest's start URL.
-  void SetStartUrl(const GURL& start_url) { start_url_ = start_url; }
-
-  // Sets the best Web Manifest's primary icon URL.
-  void SetBestPrimaryIconUrl(const GURL& best_primary_icon_url) {
-    best_primary_icon_url_ = best_primary_icon_url;
+  std::unique_ptr<WebApkInstaller> CreateDefaultWebApkInstaller() {
+    auto installer = std::unique_ptr<WebApkInstaller>(
+        new TestWebApkInstaller(profile_.get(), SpaceStatus::ENOUGH_SPACE));
+    installer->SetTimeoutMs(kWebApkServerRequestTimeoutMs);
+    return installer;
   }
 
-  // Sets the best Web Manifest's badge icon URL.
-  void SetBestBadgeIconUrl(const GURL& best_badge_icon_url) {
-    best_badge_icon_url_ = best_badge_icon_url;
+  ShortcutInfo DefaultShortcutInfo() {
+    ShortcutInfo info(test_server_.GetURL(kStartUrl));
+    info.best_primary_icon_url = test_server_.GetURL(kBestPrimaryIconUrl);
+    info.best_badge_icon_url = test_server_.GetURL(kBestBadgeIconUrl);
+    return info;
   }
 
   // Sets the URL to send the webapk::CreateWebApkRequest to. WebApkInstaller
@@ -338,31 +315,18 @@
     webapk_response_builder_ = builder;
   }
 
-  // Sets the function that should be used to build the response to the
-  // WebAPK creation request.
-  void SetSpaceStatus(const SpaceStatus status) { test_space_status_ = status; }
-
-  std::unique_ptr<WebApkInstallerRunner> CreateWebApkInstallerRunner() {
-    return std::unique_ptr<WebApkInstallerRunner>(new WebApkInstallerRunner(
-        profile_.get(), start_url_, best_primary_icon_url_,
-        best_badge_icon_url_, test_space_status_));
-  }
-
   std::unique_ptr<BuildProtoRunner> CreateBuildProtoRunner() {
     return std::unique_ptr<BuildProtoRunner>(new BuildProtoRunner());
   }
 
+  Profile* profile() { return profile_.get(); }
   net::test_server::EmbeddedTestServer* test_server() { return &test_server_; }
 
  private:
   // Sets default configuration for running WebApkInstaller.
   void SetDefaults() {
-    SetStartUrl(test_server_.GetURL(kStartUrl));
-    SetBestPrimaryIconUrl(test_server_.GetURL(kBestPrimaryIconUrl));
-    SetBestBadgeIconUrl(test_server_.GetURL(kBestBadgeIconUrl));
     SetWebApkServerUrl(test_server_.GetURL(kServerUrl));
     SetWebApkResponseBuilder(base::Bind(&BuildValidWebApkResponse, kToken));
-    SetSpaceStatus(SpaceStatus::ENOUGH_SPACE);
   }
 
   std::unique_ptr<net::test_server::HttpResponse> HandleWebApkRequest(
@@ -376,77 +340,77 @@
   content::TestBrowserThreadBundle thread_bundle_;
   net::EmbeddedTestServer test_server_;
 
-  // Web Manifest's start URL.
-  GURL start_url_;
-
-  // Web Manifest's icon URLs.
-  GURL best_primary_icon_url_;
-  GURL best_badge_icon_url_;
-
   // Builds response to the WebAPK creation request.
   WebApkResponseBuilder webapk_response_builder_;
 
-  // The space status used in tests.
-  SpaceStatus test_space_status_;
-
   DISALLOW_COPY_AND_ASSIGN(WebApkInstallerTest);
 };
 
 // Test installation succeeding.
 TEST_F(WebApkInstallerTest, Success) {
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunInstallWebApk();
-  EXPECT_EQ(WebApkInstallResult::SUCCESS, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunInstallWebApk(CreateDefaultWebApkInstaller(),
+                          DefaultShortcutInfo());
+  EXPECT_EQ(WebApkInstallResult::SUCCESS, runner.result());
 }
 
 // Test that installation fails if there is not enough space on device.
 TEST_F(WebApkInstallerTest, FailOnLowSpace) {
-  SetSpaceStatus(SpaceStatus::NOT_ENOUGH_SPACE);
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunInstallWebApk();
-  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+  std::unique_ptr<WebApkInstaller> installer(
+      new TestWebApkInstaller(profile(), SpaceStatus::NOT_ENOUGH_SPACE));
+  installer->SetTimeoutMs(kWebApkServerRequestTimeoutMs);
+  WebApkInstallerRunner runner;
+  runner.RunInstallWebApk(std::move(installer), DefaultShortcutInfo());
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner.result());
 }
 
 // Test that installation succeeds when the primary icon is guarded by
 // a Cross-Origin-Resource-Policy: same-origin header and the icon is
 // same-origin with the start URL.
 TEST_F(WebApkInstallerTest, CrossOriginResourcePolicySameOriginIconSuccess) {
-  SetBestPrimaryIconUrl(test_server()->GetURL(kBestPrimaryIconCorpUrl));
+  ShortcutInfo shortcut_info = DefaultShortcutInfo();
+  shortcut_info.best_primary_icon_url =
+      test_server()->GetURL(kBestPrimaryIconCorpUrl);
 
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunInstallWebApk();
-  EXPECT_EQ(WebApkInstallResult::SUCCESS, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunInstallWebApk(CreateDefaultWebApkInstaller(), shortcut_info);
+  EXPECT_EQ(WebApkInstallResult::SUCCESS, runner.result());
 }
 
 // Test that installation fails if fetching the bitmap at the best primary icon
 // URL returns no content. In a perfect world the fetch would always succeed
 // because the fetch for the same icon succeeded recently.
 TEST_F(WebApkInstallerTest, BestPrimaryIconUrlDownloadTimesOut) {
-  SetBestPrimaryIconUrl(test_server()->GetURL("/nocontent"));
+  ShortcutInfo shortcut_info = DefaultShortcutInfo();
+  shortcut_info.best_primary_icon_url = test_server()->GetURL("/nocontent");
 
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunInstallWebApk();
-  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunInstallWebApk(CreateDefaultWebApkInstaller(), shortcut_info);
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner.result());
 }
 
 // Test that installation fails if fetching the bitmap at the best badge icon
 // URL returns no content. In a perfect world the fetch would always succeed
 // because the fetch for the same icon succeeded recently.
 TEST_F(WebApkInstallerTest, BestBadgeIconUrlDownloadTimesOut) {
-  SetBestBadgeIconUrl(test_server()->GetURL("/nocontent"));
+  ShortcutInfo shortcut_info = DefaultShortcutInfo();
+  shortcut_info.best_badge_icon_url = test_server()->GetURL("/nocontent");
 
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunInstallWebApk();
-  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunInstallWebApk(CreateDefaultWebApkInstaller(), shortcut_info);
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner.result());
 }
 
 // Test that installation fails if the WebAPK creation request times out.
 TEST_F(WebApkInstallerTest, CreateWebApkRequestTimesOut) {
   SetWebApkServerUrl(test_server()->GetURL("/slow?1000"));
+  std::unique_ptr<WebApkInstaller> installer(
+      new TestWebApkInstaller(profile(), SpaceStatus::ENOUGH_SPACE));
+  installer->SetTimeoutMs(100);
 
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunInstallWebApk();
-  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunInstallWebApk(std::move(installer), DefaultShortcutInfo());
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner.result());
 }
 
 namespace {
@@ -468,21 +432,22 @@
 TEST_F(WebApkInstallerTest, UnparsableCreateWebApkResponse) {
   SetWebApkResponseBuilder(base::BindRepeating(&BuildUnparsableWebApkResponse));
 
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunInstallWebApk();
-  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunInstallWebApk(CreateDefaultWebApkInstaller(),
+                          DefaultShortcutInfo());
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner.result());
 }
 
 // Test update succeeding.
-TEST_F(WebApkInstallerTest, DISABLED_UpdateSuccess) {
+TEST_F(WebApkInstallerTest, UpdateSuccess) {
   ScopedTempFile scoped_file;
   base::FilePath update_request_path = scoped_file.GetFilePath();
   UpdateRequestStorer().StoreSync(update_request_path);
   ASSERT_TRUE(base::PathExists(update_request_path));
 
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunUpdateWebApk(update_request_path);
-  EXPECT_EQ(WebApkInstallResult::SUCCESS, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunUpdateWebApk(CreateDefaultWebApkInstaller(), update_request_path);
+  EXPECT_EQ(WebApkInstallResult::SUCCESS, runner.result());
 }
 
 // Test that an update suceeds if the WebAPK server returns a HTTP response with
@@ -499,9 +464,9 @@
   base::FilePath update_request_path = scoped_file.GetFilePath();
   UpdateRequestStorer().StoreSync(update_request_path);
   ASSERT_TRUE(base::PathExists(update_request_path));
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunUpdateWebApk(update_request_path);
-  EXPECT_EQ(WebApkInstallResult::SUCCESS, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunUpdateWebApk(CreateDefaultWebApkInstaller(), update_request_path);
+  EXPECT_EQ(WebApkInstallResult::SUCCESS, runner.result());
 }
 
 // Test that an update fails if the "update request path" points to an update
@@ -511,9 +476,9 @@
   base::FilePath update_request_path = scoped_file.GetFilePath();
   base::WriteFile(update_request_path, "😀", 1);
 
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunUpdateWebApk(update_request_path);
-  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunUpdateWebApk(CreateDefaultWebApkInstaller(), update_request_path);
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner.result());
 }
 
 // Test that an update fails if the "update request path" points to a
@@ -526,9 +491,9 @@
   }
   ASSERT_FALSE(base::PathExists(update_request_path));
 
-  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunUpdateWebApk(update_request_path);
-  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+  WebApkInstallerRunner runner;
+  runner.RunUpdateWebApk(CreateDefaultWebApkInstaller(), update_request_path);
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner.result());
 }
 
 // Test that StoreUpdateRequestToFile() creates directories if needed when
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc
index 9068a74..73b21c0 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.cc
+++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/browser/autofill/captured_sites_test_utils.h"
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
@@ -550,6 +554,9 @@
     } else if (base::CompareCaseInsensitiveASCII(type, "click") == 0) {
       if (!ExecuteClickAction(*action))
         return false;
+    } else if (base::CompareCaseInsensitiveASCII(type, "coolOff") == 0) {
+      if (!ExecuteCoolOffAction(*action))
+        return false;
     } else if (base::CompareCaseInsensitiveASCII(type, "executeScript") == 0) {
       if (!ExecuteRunCommandAction(*action))
         return false;
@@ -557,10 +564,15 @@
       if (!ExecuteHoverAction(*action))
         return false;
     } else if (base::CompareCaseInsensitiveASCII(type, "loadPage") == 0) {
+      if (!ExecuteForceLoadPage(*action))
+        return false;
       // Load page is an no-op action.
     } else if (base::CompareCaseInsensitiveASCII(type, "pressEnter") == 0) {
       if (!ExecutePressEnterAction(*action))
         return false;
+    } else if (base::CompareCaseInsensitiveASCII(type, "pressEscape") == 0) {
+      if (!ExecutePressEscapeAction(*action))
+        return false;
     } else if (base::CompareCaseInsensitiveASCII(type, "savePassword") == 0) {
       if (!ExecuteSavePasswordAction(*action))
         return false;
@@ -607,7 +619,7 @@
 // Functions for deserializing and executing actions from the test recipe
 // JSON object.
 bool TestRecipeReplayer::InitializeBrowserToExecuteRecipe(
-    std::unique_ptr<base::DictionaryValue>& recipe) {
+    const std::unique_ptr<base::DictionaryValue>& recipe) {
   // Setup any saved address and credit card at the start of the test.
   const base::Value* autofill_profile_container =
       recipe->FindKey("autofillProfile");
@@ -733,6 +745,28 @@
   return true;
 }
 
+bool TestRecipeReplayer::ExecuteCoolOffAction(
+    const base::DictionaryValue& action) {
+  base::RunLoop heart_beat;
+  base::TimeDelta cool_off_time = cool_off_action_timeout;
+  const base::Value* pause_time_container = action.FindKey("pauseTimeSec");
+  if (pause_time_container) {
+    if (pause_time_container->type() != base::Value::Type::INTEGER) {
+      ADD_FAILURE() << "Pause time is not an integer!";
+      return false;
+    }
+    int seconds = pause_time_container->GetInt();
+    cool_off_time = base::TimeDelta::FromSeconds(seconds);
+  }
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, heart_beat.QuitClosure(), cool_off_time);
+  VLOG(1) << "Pausing execution for '" << cool_off_time.InSeconds()
+          << "' seconds";
+  heart_beat.Run();
+
+  return true;
+}
+
 bool TestRecipeReplayer::ExecuteHoverAction(
     const base::DictionaryValue& action) {
   std::string xpath;
@@ -772,6 +806,38 @@
   return true;
 }
 
+bool TestRecipeReplayer::ExecuteForceLoadPage(
+    const base::DictionaryValue& action) {
+  const base::Value* force_load_container = action.FindKey("force");
+  const base::Value* url_container = action.FindKey("url");
+  if (!force_load_container) {
+    // Nothing to do, load should have been made by previous action
+    return true;
+  }
+
+  if (force_load_container->type() != base::Value::Type::BOOLEAN) {
+    ADD_FAILURE() << "Force load is not a bool!";
+    return false;
+  }
+
+  bool shouldForce = force_load_container->GetBool();
+  if (!shouldForce)
+    return true;
+
+  if (!url_container || url_container->type() != base::Value::Type::STRING) {
+    ADD_FAILURE() << "Force load url could not be parsed";
+    return false;
+  }
+  std::string url = url_container->GetString();
+  VLOG(1) << "Making explicit URL redirect to '" << url << "'";
+  ui_test_utils::NavigateToURL(browser_, GURL(url));
+
+  PageActivityObserver page_activity_observer(GetWebContents());
+  page_activity_observer.WaitTillPageIsIdle();
+
+  return true;
+}
+
 bool TestRecipeReplayer::ExecutePressEnterAction(
     const base::DictionaryValue& action) {
   std::string xpath;
@@ -794,7 +860,7 @@
   if (!WaitForElementToBeReady(xpath, visibility_enum_val, frame))
     return false;
 
-  VLOG(1) << "Press 'Enter' on `" << xpath << "`.";
+  VLOG(1) << "Pressing 'Enter' on `" << xpath << "`.";
   PageActivityObserver page_activity_observer(frame);
   if (!PlaceFocusOnElement(xpath, frame_path, frame))
     return false;
@@ -808,6 +874,19 @@
   return true;
 }
 
+bool TestRecipeReplayer::ExecutePressEscapeAction(
+    const base::DictionaryValue& action) {
+  ui::DomKey key = ui::DomKey::ESCAPE;
+  ui::KeyboardCode key_code = ui::NonPrintableDomKeyToKeyboardCode(key);
+  ui::DomCode code = ui::UsLayoutKeyboardCodeToDomCode(key_code);
+  SimulateKeyPress(GetWebContents(), key, code, key_code, false, false, false,
+                   false);
+  VLOG(1) << "Pressing 'Esc' in the current frame";
+  PageActivityObserver page_activity_observer(GetWebContents());
+  page_activity_observer.WaitTillPageIsIdle();
+  return true;
+}
+
 bool TestRecipeReplayer::ExecuteRunCommandAction(
     const base::DictionaryValue& action) {
   // Extract the list of JavaScript commands into a vector.
@@ -1068,6 +1147,13 @@
   if (!GetTargetFrameFromAction(action, &frame))
     return false;
 
+  // If we're just validating we don't care about on_top-ness, as copied from
+  // chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js
+  // to TestRecipeReplayer::DomElementReadyState enum
+  // So remove (DomElementReadyState::kReadyStateOnTop)
+  if (visibility_enum_val & kReadyStateOnTop)
+    visibility_enum_val -= kReadyStateOnTop;
+
   if (!WaitForElementToBeReady(xpath, visibility_enum_val, frame))
     return false;
 
diff --git a/chrome/browser/autofill/captured_sites_test_utils.h b/chrome/browser/autofill/captured_sites_test_utils.h
index 5ff7ec7..61528c60 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.h
+++ b/chrome/browser/autofill/captured_sites_test_utils.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_AUTOFILL_CAPTURED_SITES_TEST_UTILS_H_
 #define CHROME_BROWSER_AUTOFILL_CAPTURED_SITES_TEST_UTILS_H_
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "chrome/browser/ui/browser.h"
@@ -34,6 +38,9 @@
 // an action. The Captured Site Automation Framework uses this timeout to
 // break out of a wait loop after a hover action.
 const base::TimeDelta visual_update_timeout = base::TimeDelta::FromSeconds(20);
+// Some times, tests tend to need a break that can't be read from the elements
+// play status
+const base::TimeDelta cool_off_action_timeout = base::TimeDelta::FromSeconds(1);
 
 std::string FilePathToUTF8(const base::FilePath::StringType& str);
 
@@ -198,6 +205,13 @@
   static const int kHostHttpPort = 8080;
   static const int kHostHttpsPort = 8081;
 
+  enum DomElementReadyState {
+    kReadyStatePresent = 0,
+    kReadyStateVisible = 1 << 0,
+    kReadyStateEnabled = 1 << 1,
+    kReadyStateOnTop   = 1 << 2
+  };
+
   TestRecipeReplayer(
       Browser* browser,
       TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor);
@@ -253,11 +267,14 @@
                            base::Process* process);
   bool ReplayRecordedActions(const base::FilePath& recipe_file_path);
   bool InitializeBrowserToExecuteRecipe(
-      std::unique_ptr<base::DictionaryValue>& recipe);
+      const std::unique_ptr<base::DictionaryValue>& recipe);
   bool ExecuteAutofillAction(const base::DictionaryValue& action);
   bool ExecuteClickAction(const base::DictionaryValue& action);
+  bool ExecuteCoolOffAction(const base::DictionaryValue& action);
   bool ExecuteHoverAction(const base::DictionaryValue& action);
+  bool ExecuteForceLoadPage(const base::DictionaryValue& action);
   bool ExecutePressEnterAction(const base::DictionaryValue& action);
+  bool ExecutePressEscapeAction(const base::DictionaryValue& action);
   bool ExecuteRunCommandAction(const base::DictionaryValue& action);
   bool ExecuteSavePasswordAction(const base::DictionaryValue& action);
   bool ExecuteSelectDropdownAction(const base::DictionaryValue& action);
diff --git a/chrome/browser/autofill/credit_card_accessory_controller.h b/chrome/browser/autofill/credit_card_accessory_controller.h
index 39f87c4..4bcdb04 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller.h
+++ b/chrome/browser/autofill/credit_card_accessory_controller.h
@@ -5,7 +5,10 @@
 #ifndef CHROME_BROWSER_AUTOFILL_CREDIT_CARD_ACCESSORY_CONTROLLER_H_
 #define CHROME_BROWSER_AUTOFILL_CREDIT_CARD_ACCESSORY_CONTROLLER_H_
 
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/autofill/accessory_controller.h"
+#include "components/autofill/core/browser/personal_data_manager_observer.h"
 
 namespace content {
 class WebContents;
@@ -15,10 +18,17 @@
 
 // Interface for credit card-specific keyboard accessory controller between the
 // ManualFillingController and Autofill backend logic.
-class CreditCardAccessoryController : public AccessoryController {
+class CreditCardAccessoryController
+    : public base::SupportsWeakPtr<CreditCardAccessoryController>,
+      public AccessoryController,
+      public PersonalDataManagerObserver {
  public:
   CreditCardAccessoryController() = default;
   ~CreditCardAccessoryController() override = default;
+  // Disallow copy and assign
+  CreditCardAccessoryController(const CreditCardAccessoryController&) = delete;
+  CreditCardAccessoryController& operator=(
+      const CreditCardAccessoryController&) = delete;
 
   // Returns true if the accessory controller may exist for |web_contents|.
   // Otherwise it returns false.
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
index 3a9ea01..795bf02 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
+#include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/strings/grit/components_strings.h"
@@ -25,11 +26,13 @@
 
 namespace {
 
-base::string16 GetTitle(bool unused) {
-  return l10n_util::GetStringUTF16(IDS_MANUAL_FILLING_CREDIT_CARD_SHEET_TITLE);
+base::string16 GetTitle(bool has_suggestions) {
+  return l10n_util::GetStringUTF16(
+      has_suggestions ? IDS_MANUAL_FILLING_CREDIT_CARD_SHEET_TITLE
+                      : IDS_MANUAL_FILLING_CREDIT_CARD_SHEET_EMPTY_MESSAGE);
 }
 
-void AddField(const base::string16& data, UserInfo* user_info) {
+void AddSimpleField(const base::string16& data, UserInfo* user_info) {
   user_info->add_field(UserInfo::Field(data, data,
                                        /*is_password=*/false,
                                        /*selectable=*/true));
@@ -40,20 +43,24 @@
 
   UserInfo user_info;
 
-  AddField(data->ObfuscatedLastFourDigits(), &user_info);
+  base::string16 obfuscated_number = data->ObfuscatedLastFourDigits();
+  user_info.add_field(UserInfo::Field(obfuscated_number, obfuscated_number,
+                                      data->guid(), /*is_password=*/false,
+                                      /*selectable=*/true));
 
   if (data->HasValidExpirationDate()) {
-    AddField(data->ExpirationMonthAsString(), &user_info);
-    AddField(data->Expiration4DigitYearAsString(), &user_info);
+    AddSimpleField(data->ExpirationMonthAsString(), &user_info);
+    AddSimpleField(data->Expiration4DigitYearAsString(), &user_info);
   } else {
-    AddField(base::string16(), &user_info);
-    AddField(base::string16(), &user_info);
+    AddSimpleField(base::string16(), &user_info);
+    AddSimpleField(base::string16(), &user_info);
   }
 
   if (data->HasNameOnCard()) {
-    AddField(data->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL), &user_info);
+    AddSimpleField(data->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL),
+                   &user_info);
   } else {
-    AddField(base::string16(), &user_info);
+    AddSimpleField(base::string16(), &user_info);
   }
 
   return user_info;
@@ -61,8 +68,42 @@
 
 }  // namespace
 
-CreditCardAccessoryControllerImpl::~CreditCardAccessoryControllerImpl() =
-    default;
+CreditCardAccessoryControllerImpl::~CreditCardAccessoryControllerImpl() {
+  if (personal_data_manager_)
+    personal_data_manager_->RemoveObserver(this);
+}
+
+void CreditCardAccessoryControllerImpl::OnFillingTriggered(
+    const UserInfo::Field& selection) {
+  autofill::ContentAutofillDriver* driver =
+      autofill::ContentAutofillDriver::GetForRenderFrameHost(
+          web_contents_->GetFocusedFrame());
+  if (!driver)
+    return;
+
+  // Credit card number fields have a GUID populated to allow deobfuscation
+  // before filling.
+  if (selection.id().empty()) {
+    driver->RendererShouldFillFieldWithValue(selection.display_text());
+    return;
+  }
+
+  auto card_iter = std::find_if(cards_cache_.begin(), cards_cache_.end(),
+                                [&selection](const auto* card) {
+                                  return card->guid() == selection.id();
+                                });
+
+  DCHECK(card_iter != cards_cache_.end())
+      << "Tried to fill card with unknown GUID";
+
+  CreditCard* matching_card = *card_iter;
+  if (matching_card->record_type() ==
+      CreditCard::RecordType::MASKED_SERVER_CARD) {
+    // Unmasking server cards is not yet supported
+    return;
+  }
+  driver->RendererShouldFillFieldWithValue(matching_card->number());
+}
 
 void CreditCardAccessoryControllerImpl::OnOptionSelected(
     AccessoryAction selected_action) {
@@ -104,9 +145,9 @@
 }
 
 void CreditCardAccessoryControllerImpl::RefreshSuggestions() {
-  const std::vector<CreditCard*> suggestions = GetSuggestions();
+  FetchSuggestionsFromPersonalDataManager();
   std::vector<UserInfo> info_to_add;
-  std::transform(suggestions.begin(), suggestions.end(),
+  std::transform(cards_cache_.begin(), cards_cache_.end(),
                  std::back_inserter(info_to_add), &TranslateCard);
 
   const std::vector<FooterCommand> footer_commands = {FooterCommand(
@@ -114,12 +155,17 @@
       AccessoryAction::MANAGE_CREDIT_CARDS)};
 
   bool has_suggestions = !info_to_add.empty();
+
   GetManualFillingController()->RefreshSuggestions(
       autofill::CreateAccessorySheetData(
           AccessoryTabType::CREDIT_CARDS, GetTitle(has_suggestions),
           std::move(info_to_add), std::move(footer_commands)));
 }
 
+void CreditCardAccessoryControllerImpl::OnPersonalDataChanged() {
+  RefreshSuggestions();
+}
+
 // static
 void CreditCardAccessoryControllerImpl::CreateForWebContentsForTesting(
     content::WebContents* web_contents,
@@ -138,7 +184,13 @@
 CreditCardAccessoryControllerImpl::CreditCardAccessoryControllerImpl(
     content::WebContents* web_contents)
     : web_contents_(web_contents),
-      personal_data_manager_for_testing_(nullptr) {}
+      personal_data_manager_(
+          autofill::PersonalDataManagerFactory::GetForProfile(
+              Profile::FromBrowserContext(
+                  web_contents_->GetBrowserContext()))) {
+  if (personal_data_manager_)
+    personal_data_manager_->AddObserver(this);
+}
 
 CreditCardAccessoryControllerImpl::CreditCardAccessoryControllerImpl(
     content::WebContents* web_contents,
@@ -146,21 +198,19 @@
     PersonalDataManager* personal_data_manager)
     : web_contents_(web_contents),
       mf_controller_(mf_controller),
-      personal_data_manager_for_testing_(personal_data_manager) {}
+      personal_data_manager_(personal_data_manager) {
+  if (personal_data_manager_)
+    personal_data_manager_->AddObserver(this);
+}
 
-const std::vector<CreditCard*>
-CreditCardAccessoryControllerImpl::GetSuggestions() {
-  const PersonalDataManager* personal_data_manager =
-      personal_data_manager_for_testing_;
-  if (!personal_data_manager) {
-    personal_data_manager = autofill::PersonalDataManagerFactory::GetForProfile(
-        Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
+void CreditCardAccessoryControllerImpl::
+    FetchSuggestionsFromPersonalDataManager() {
+  if (!personal_data_manager_) {
+    cards_cache_.clear();  // No data available.
+  } else {
+    cards_cache_ = personal_data_manager_->GetCreditCardsToSuggest(
+        /*include_server_cards=*/true);
   }
-  if (!personal_data_manager) {
-    return {};  // No data available.
-  }
-  return personal_data_manager->GetCreditCardsToSuggest(
-      /*include_server_cards=*/true);
 }
 
 base::WeakPtr<ManualFillingController>
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl.h b/chrome/browser/autofill/credit_card_accessory_controller_impl.h
index 1ccaadb..e9ac50f8 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl.h
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl.h
@@ -7,7 +7,6 @@
 
 #include "chrome/browser/autofill/credit_card_accessory_controller.h"
 
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -26,13 +25,15 @@
   ~CreditCardAccessoryControllerImpl() override;
 
   // AccessoryController:
-  // TODO(crbug.com/902425): Implement filling logic.
-  void OnFillingTriggered(const UserInfo::Field& selection) override {}
+  void OnFillingTriggered(const UserInfo::Field& selection) override;
   void OnOptionSelected(AccessoryAction selected_action) override;
 
   // CreditCardAccessoryController:
   void RefreshSuggestions() override;
 
+  // PersonalDataManagerObserver:
+  void OnPersonalDataChanged() override;
+
   static void CreateForWebContentsForTesting(
       content::WebContents* web_contents,
       base::WeakPtr<ManualFillingController> mf_controller,
@@ -50,15 +51,16 @@
       base::WeakPtr<ManualFillingController> mf_controller,
       PersonalDataManager* personal_data_manager);
 
-  const std::vector<CreditCard*> GetSuggestions();
+  void FetchSuggestionsFromPersonalDataManager();
   base::WeakPtr<ManualFillingController> GetManualFillingController();
 
+  // Pointers to cards owned by PersonalDataManager.
+  std::vector<CreditCard*> cards_cache_;
   content::WebContents* web_contents_;
   base::WeakPtr<ManualFillingController> mf_controller_;
-  const PersonalDataManager* personal_data_manager_for_testing_;
+  PersonalDataManager* const personal_data_manager_;
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
-  DISALLOW_COPY_AND_ASSIGN(CreditCardAccessoryControllerImpl);
 };
 
 }  // namespace autofill
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
index 3ec1e78d..d709487 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
@@ -58,7 +58,7 @@
   }
 
  protected:
-  testing::StrictMock<MockManualFillingController> mock_mf_controller_;
+  testing::NiceMock<MockManualFillingController> mock_mf_controller_;
   autofill::TestPersonalDataManager data_manager_;
   TestingProfile profile_;
 };
@@ -67,7 +67,7 @@
   autofill::CreditCard card = test::GetCreditCard();
   data_manager_.AddCreditCard(card);
 
-  autofill::AccessorySheetData result(autofill::AccessoryTabType::PASSWORDS,
+  autofill::AccessorySheetData result(autofill::AccessoryTabType::CREDIT_CARDS,
                                       base::string16());
 
   EXPECT_CALL(mock_mf_controller_, RefreshSuggestions(_))
@@ -77,14 +77,18 @@
   ASSERT_TRUE(cc_controller);
   cc_controller->RefreshSuggestions();
 
-  ASSERT_EQ(result, CreditCardAccessorySheetDataBuilder()
-                        .AddUserInfo()
-                        .AppendSimpleField(card.ObfuscatedLastFourDigits())
-                        .AppendSimpleField(card.ExpirationMonthAsString())
-                        .AppendSimpleField(card.Expiration4DigitYearAsString())
-                        .AppendSimpleField(
-                            card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL))
-                        .Build());
+  ASSERT_EQ(
+      result,
+      CreditCardAccessorySheetDataBuilder()
+          .AddUserInfo()
+          .AppendField(card.ObfuscatedLastFourDigits(),
+                       card.ObfuscatedLastFourDigits(), card.guid(),
+                       /*is_obfuscated=*/false,
+                       /*selectable=*/true)
+          .AppendSimpleField(card.ExpirationMonthAsString())
+          .AppendSimpleField(card.Expiration4DigitYearAsString())
+          .AppendSimpleField(card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL))
+          .Build());
 }
 
 }  // namespace autofill
diff --git a/chrome/browser/autofill/manual_filling_controller.h b/chrome/browser/autofill/manual_filling_controller.h
index 7023fcd..39cf80f 100644
--- a/chrome/browser/autofill/manual_filling_controller.h
+++ b/chrome/browser/autofill/manual_filling_controller.h
@@ -46,7 +46,8 @@
     AUTOFILL,
     PASSWORD_FALLBACKS,
     CREDIT_CARD_FALLBACKS,
-    ADDRESS_FALLBACKS
+    ADDRESS_FALLBACKS,
+    TOUCH_TO_FILL,
   };
 
   ManualFillingController() = default;
@@ -84,10 +85,6 @@
   virtual void UpdateSourceAvailability(FillingSource source,
                                         bool has_suggestions) = 0;
 
-  // Requests to show the touch to fill sheet.
-  virtual void ShowTouchToFillSheet(
-      const autofill::AccessorySheetData& data) = 0;
-
   // Explicitly hides all manual filling UI without checking any filling source.
   // E.g. after autofilling suggestions, or generating a password.
   virtual void Hide() = 0;
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.cc b/chrome/browser/autofill/manual_filling_controller_impl.cc
index 0fe14a4c..27c52609 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl.cc
@@ -9,6 +9,7 @@
 #include "base/callback.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/autofill/address_accessory_controller.h"
+#include "chrome/browser/autofill/credit_card_accessory_controller.h"
 #include "chrome/browser/password_manager/password_accessory_controller.h"
 #include "chrome/browser/password_manager/password_accessory_metrics_util.h"
 #include "chrome/browser/password_manager/touch_to_fill_controller.h"
@@ -21,6 +22,7 @@
 using autofill::AccessorySheetData;
 using autofill::AccessoryTabType;
 using autofill::AddressAccessoryController;
+using autofill::CreditCardAccessoryController;
 using autofill::mojom::FillingStatus;
 using autofill::mojom::FocusedFieldType;
 
@@ -37,6 +39,7 @@
     case AccessoryTabType::ADDRESSES:
       return FillingSource::ADDRESS_FALLBACKS;
     case AccessoryTabType::TOUCH_TO_FILL:
+      return FillingSource::TOUCH_TO_FILL;
     case AccessoryTabType::ALL:
     case AccessoryTabType::COUNT:
       break;  // Intentional failure.
@@ -68,17 +71,21 @@
     content::WebContents* web_contents,
     base::WeakPtr<PasswordAccessoryController> pwd_controller,
     base::WeakPtr<AddressAccessoryController> address_controller,
+    base::WeakPtr<CreditCardAccessoryController> cc_controller,
     std::unique_ptr<ManualFillingViewInterface> view) {
   DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
   DCHECK(!FromWebContents(web_contents)) << "Controller already attached!";
   DCHECK(pwd_controller);
   DCHECK(address_controller);
+  DCHECK(cc_controller);
   DCHECK(view);
 
-  web_contents->SetUserData(
-      UserDataKey(), base::WrapUnique(new ManualFillingControllerImpl(
-                         web_contents, std::move(pwd_controller),
-                         std::move(address_controller), std::move(view))));
+  web_contents->SetUserData(UserDataKey(),
+                            // Using `new` to access a non-public constructor.
+                            base::WrapUnique(new ManualFillingControllerImpl(
+                                web_contents, std::move(pwd_controller),
+                                std::move(address_controller),
+                                std::move(cc_controller), std::move(view))));
 
   FromWebContents(web_contents)->Initialize();
 }
@@ -136,12 +143,6 @@
   UpdateVisibility();
 }
 
-void ManualFillingControllerImpl::ShowTouchToFillSheet(
-    const AccessorySheetData& data) {
-  view_->OnItemsAvailable(data);
-  view_->ShowTouchToFillSheet();
-}
-
 void ManualFillingControllerImpl::Hide() {
   view_->Hide();
 }
@@ -180,6 +181,8 @@
   DCHECK(FromWebContents(web_contents_)) << "Don't call from constructor!";
   if (address_controller_)
     address_controller_->RefreshSuggestions();
+  if (cc_controller_)
+    cc_controller_->RefreshSuggestions();
 }
 
 ManualFillingControllerImpl::ManualFillingControllerImpl(
@@ -195,6 +198,11 @@
         AddressAccessoryController::GetOrCreate(web_contents)->AsWeakPtr();
     DCHECK(address_controller_);
   }
+  if (CreditCardAccessoryController::AllowedForWebContents(web_contents)) {
+    cc_controller_ =
+        CreditCardAccessoryController::GetOrCreate(web_contents)->AsWeakPtr();
+    DCHECK(cc_controller_);
+  }
   if (TouchToFillController::AllowedForWebContents(web_contents)) {
     touch_to_fill_controller_ =
         TouchToFillController::GetOrCreate(web_contents)->AsWeakPtr();
@@ -206,10 +214,12 @@
     content::WebContents* web_contents,
     base::WeakPtr<PasswordAccessoryController> pwd_controller,
     base::WeakPtr<AddressAccessoryController> address_controller,
+    base::WeakPtr<CreditCardAccessoryController> cc_controller,
     std::unique_ptr<ManualFillingViewInterface> view)
     : web_contents_(web_contents),
       pwd_controller_(std::move(pwd_controller)),
       address_controller_(std::move(address_controller)),
+      cc_controller_(std::move(cc_controller)),
       view_(std::move(view)) {}
 
 bool ManualFillingControllerImpl::ShouldShowAccessory() const {
@@ -249,7 +259,10 @@
 
 void ManualFillingControllerImpl::UpdateVisibility() {
   if (ShouldShowAccessory()) {
-    view_->ShowWhenKeyboardIsVisible();
+    if (available_sources_.contains(FillingSource::TOUCH_TO_FILL))
+      view_->ShowTouchToFillSheet();
+    else
+      view_->ShowWhenKeyboardIsVisible();
   } else {
     view_->Hide();
   }
@@ -263,7 +276,7 @@
     case AccessoryTabType::PASSWORDS:
       return pwd_controller_.get();
     case AccessoryTabType::CREDIT_CARDS:
-      // TODO(crbug.com/902425): return credit card controller.
+      return cc_controller_.get();
     case AccessoryTabType::TOUCH_TO_FILL:
       return touch_to_fill_controller_.get();
     case AccessoryTabType::ALL:
@@ -284,7 +297,7 @@
     case AccessoryAction::MANAGE_ADDRESSES:
       return address_controller_.get();
     case AccessoryAction::MANAGE_CREDIT_CARDS:
-      // TODO(crbug.com/902425): Return credit card controller.
+      return cc_controller_.get();
     case AccessoryAction::AUTOFILL_SUGGESTION:
     case AccessoryAction::COUNT:
       break;  // Intentional failure;
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.h b/chrome/browser/autofill/manual_filling_controller_impl.h
index fed10891..435facf 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.h
+++ b/chrome/browser/autofill/manual_filling_controller_impl.h
@@ -17,7 +17,9 @@
 
 namespace autofill {
 class AddressAccessoryController;
+class CreditCardAccessoryController;
 }
+
 class AccessoryController;
 class PasswordAccessoryController;
 class TouchToFillController;
@@ -37,7 +39,6 @@
   void OnFilledIntoFocusedField(autofill::mojom::FillingStatus status) override;
   void UpdateSourceAvailability(FillingSource source,
                                 bool has_suggestions) override;
-  void ShowTouchToFillSheet(const autofill::AccessorySheetData& data) override;
   void Hide() override;
   void OnAutomaticGenerationStatusChanged(bool available) override;
   void OnFillingTriggered(autofill::AccessoryTabType type,
@@ -59,6 +60,7 @@
       content::WebContents* web_contents,
       base::WeakPtr<PasswordAccessoryController> pwd_controller,
       base::WeakPtr<autofill::AddressAccessoryController> address_controller,
+      base::WeakPtr<autofill::CreditCardAccessoryController> cc_controller,
       std::unique_ptr<ManualFillingViewInterface> test_view);
 
 #if defined(UNIT_TEST)
@@ -90,6 +92,7 @@
       content::WebContents* web_contents,
       base::WeakPtr<PasswordAccessoryController> pwd_controller,
       base::WeakPtr<autofill::AddressAccessoryController> address_controller,
+      base::WeakPtr<autofill::CreditCardAccessoryController> cc_controller,
       std::unique_ptr<ManualFillingViewInterface> view);
 
   // Returns true if the keyboard accessory needs to be shown.
@@ -115,11 +118,11 @@
   autofill::mojom::FocusedFieldType focused_field_type_ =
       autofill::mojom::FocusedFieldType::kUnknown;
 
-  // The password accessory controller object to forward view requests to.
+  // Controllers which handle events relating to a specific tab and the
+  // associated data.
   base::WeakPtr<PasswordAccessoryController> pwd_controller_;
-
-  // The address accessory controller object to forward view requests to.
   base::WeakPtr<autofill::AddressAccessoryController> address_controller_;
+  base::WeakPtr<autofill::CreditCardAccessoryController> cc_controller_;
 
   // The touch to fill controller object to forward view requests to.
   base::WeakPtr<TouchToFillController> touch_to_fill_controller_;
diff --git a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
index 8adaae8..34c292c 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/autofill/mock_address_accessory_controller.h"
+#include "chrome/browser/autofill/mock_credit_card_accessory_controller.h"
 #include "chrome/browser/autofill/mock_manual_filling_view.h"
 #include "chrome/browser/autofill/mock_password_accessory_controller.h"
 #include "chrome/browser/password_manager/password_accessory_controller.h"
@@ -61,7 +62,7 @@
     NavigateAndCommit(GURL(kExampleSite));
     ManualFillingControllerImpl::CreateForWebContentsForTesting(
         web_contents(), mock_pwd_controller_.AsWeakPtr(),
-        mock_address_controller_.AsWeakPtr(),
+        mock_address_controller_.AsWeakPtr(), mock_cc_controller_.AsWeakPtr(),
         std::make_unique<NiceMock<MockManualFillingView>>());
     NavigateAndCommit(GURL(kExampleSite));
   }
@@ -91,6 +92,7 @@
  protected:
   NiceMock<MockPasswordAccessoryController> mock_pwd_controller_;
   NiceMock<MockAddressAccessoryController> mock_address_controller_;
+  NiceMock<MockCreditCardAccessoryController> mock_cc_controller_;
 };
 
 TEST_F(ManualFillingControllerTest, IsNotRecreatedForSameWebContents) {
@@ -149,6 +151,17 @@
   controller()->RefreshSuggestions(empty_passwords_sheet());
 }
 
+TEST_F(ManualFillingControllerTest, TouchToFillSheetHasPreference) {
+  SetSuggestionsAndClearExpectations(
+      populate_sheet(AccessoryTabType::TOUCH_TO_FILL));
+  SetSuggestionsAndClearExpectations(
+      populate_sheet(AccessoryTabType::PASSWORDS));
+  FocusFieldAndClearExpectations(FocusedFieldType::kFillablePasswordField);
+
+  EXPECT_CALL(*view(), ShowTouchToFillSheet());
+  controller()->RefreshSuggestions(empty_passwords_sheet());
+}
+
 TEST_F(ManualFillingControllerTest,
        HidesAccessoryWithoutSuggestionsOnNonPasswordFields) {
   SetSuggestionsAndClearExpectations(
@@ -225,12 +238,6 @@
                                          /*has_suggestions=*/false);
 }
 
-TEST_F(ManualFillingControllerTest, RelaysShowTouchToFillSheet) {
-  EXPECT_CALL(*view(), OnItemsAvailable(empty_passwords_sheet()));
-  EXPECT_CALL(*view(), ShowTouchToFillSheet);
-  controller()->ShowTouchToFillSheet(empty_passwords_sheet());
-}
-
 TEST_F(ManualFillingControllerTest, HidesAccessoryWithoutAvailableSources) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(
@@ -296,6 +303,12 @@
   controller()->OnOptionSelected(AccessoryAction::MANAGE_ADDRESSES);
 }
 
+TEST_F(ManualFillingControllerTest, ForwardsCreditCardManagingToController) {
+  EXPECT_CALL(mock_cc_controller_,
+              OnOptionSelected(AccessoryAction::MANAGE_CREDIT_CARDS));
+  controller()->OnOptionSelected(AccessoryAction::MANAGE_CREDIT_CARDS);
+}
+
 TEST_F(ManualFillingControllerTest, OnAutomaticGenerationRequested) {
   EXPECT_CALL(mock_pwd_controller_,
               OnOptionSelected(AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
diff --git a/chrome/browser/autofill/mock_credit_card_accessory_controller.cc b/chrome/browser/autofill/mock_credit_card_accessory_controller.cc
new file mode 100644
index 0000000..639f9fa
--- /dev/null
+++ b/chrome/browser/autofill/mock_credit_card_accessory_controller.cc
@@ -0,0 +1,10 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/autofill/mock_credit_card_accessory_controller.h"
+
+MockCreditCardAccessoryController::MockCreditCardAccessoryController() =
+    default;
+MockCreditCardAccessoryController::~MockCreditCardAccessoryController() =
+    default;
diff --git a/chrome/browser/autofill/mock_credit_card_accessory_controller.h b/chrome/browser/autofill/mock_credit_card_accessory_controller.h
new file mode 100644
index 0000000..b870d90
--- /dev/null
+++ b/chrome/browser/autofill/mock_credit_card_accessory_controller.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOFILL_MOCK_CREDIT_CARD_ACCESSORY_CONTROLLER_H_
+#define CHROME_BROWSER_AUTOFILL_MOCK_CREDIT_CARD_ACCESSORY_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/autofill/credit_card_accessory_controller.h"
+#include "components/autofill/core/browser/ui/accessory_sheet_data.h"
+#include "components/autofill/core/browser/ui/accessory_sheet_enums.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+class MockCreditCardAccessoryController
+    : public autofill::CreditCardAccessoryController {
+ public:
+  MockCreditCardAccessoryController();
+  ~MockCreditCardAccessoryController() override;
+  MockCreditCardAccessoryController(const MockCreditCardAccessoryController&) =
+      delete;
+  MockCreditCardAccessoryController& operator=(
+      const MockCreditCardAccessoryController&) = delete;
+
+  MOCK_METHOD1(OnFillingTriggered, void(const autofill::UserInfo::Field&));
+  MOCK_METHOD1(OnOptionSelected, void(autofill::AccessoryAction));
+  MOCK_METHOD0(RefreshSuggestions, void());
+  MOCK_METHOD0(OnPersonalDataChanged, void());
+};
+
+#endif  // CHROME_BROWSER_AUTOFILL_MOCK_CREDIT_CARD_ACCESSORY_CONTROLLER_H_
diff --git a/chrome/browser/autofill/mock_manual_filling_controller.h b/chrome/browser/autofill/mock_manual_filling_controller.h
index 1a64d529..6bd06a15 100644
--- a/chrome/browser/autofill/mock_manual_filling_controller.h
+++ b/chrome/browser/autofill/mock_manual_filling_controller.h
@@ -22,7 +22,6 @@
   MOCK_METHOD1(OnFilledIntoFocusedField, void(autofill::mojom::FillingStatus));
   MOCK_METHOD2(UpdateSourceAvailability,
                void(ManualFillingController::FillingSource, bool));
-  MOCK_METHOD1(ShowTouchToFillSheet, void(const autofill::AccessorySheetData&));
   MOCK_METHOD0(Hide, void());
   MOCK_METHOD1(OnAutomaticGenerationStatusChanged, void(bool));
   MOCK_METHOD2(OnFillingTriggered,
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index cf7baa3..ddfd33e3 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -36,7 +36,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/battery/battery_metrics.h"
 #include "chrome/browser/chrome_browser_main.h"
-#include "chrome/browser/chrome_child_process_watcher.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/component_updater/chrome_component_updater_configurator.h"
@@ -400,8 +399,6 @@
     profile_manager_.reset();
   }
 
-  child_process_watcher_.reset();
-
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   media_file_system_registry_.reset();
   // Remove the global instance of the Storage Monitor now. Otherwise the
@@ -1154,8 +1151,6 @@
           ->Clone());
 #endif
 
-  child_process_watcher_ = std::make_unique<ChromeChildProcessWatcher>();
-
   CacheDefaultWebClientState();
 
   platform_part_->PreMainMessageLoopRun();
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 81faf8dd91..8029837 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -36,7 +36,6 @@
 #include "services/network/public/mojom/network_service.mojom-forward.h"
 
 class BatteryMetrics;
-class ChromeChildProcessWatcher;
 class ChromeFeatureListCreator;
 class ChromeMetricsServicesManagerClient;
 class DevToolsAutoOpener;
@@ -393,8 +392,6 @@
 
   std::unique_ptr<gcm::GCMDriver> gcm_driver_;
 
-  std::unique_ptr<ChromeChildProcessWatcher> child_process_watcher_;
-
   shell_integration::DefaultWebClientState cached_default_web_client_state_ =
       shell_integration::UNKNOWN_DEFAULT;
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 15d6745bd..33a1c337 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -216,11 +216,12 @@
         <include name="IDR_APP_MANAGEMENT_CHROME_APP_PERMISSION_VIEW_JS" file="resources\app_management\chrome_app_permission_view.js" type="BINDATA"/>
         <include name="IDR_APP_MANAGEMENT_CONSTANTS_HTML" file="resources\app_management\constants.html" type="BINDATA"/>
         <include name="IDR_APP_MANAGEMENT_CONSTANTS_JS" file="resources\app_management\constants.js" type="BINDATA"/>
-        <include name="IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_HTML" file="resources\app_management\expandable_app_list.html" type="BINDATA"/>
-        <include name="IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_JS" file="resources\app_management\expandable_app_list.js" type="BINDATA"/>
         <include name="IDR_APP_MANAGEMENT_DOM_SWITCH_HTML" file="resources\app_management\dom_switch.html" type="BINDATA"/>
         <include name="IDR_APP_MANAGEMENT_DOM_SWITCH_JS" file="resources\app_management\dom_switch.js" type="BINDATA"/>
+        <include name="IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_HTML" file="resources\app_management\expandable_app_list.html" type="BINDATA"/>
+        <include name="IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_JS" file="resources\app_management\expandable_app_list.js" type="BINDATA"/>
         <include name="IDR_APP_MANAGEMENT_FAKE_PAGE_HANDLER_JS" file="resources\app_management\fake_page_handler.js" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_ICONS_HTML" file="resources\app_management\icons.html" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_INDEX_HTML" file="resources\app_management\index.html" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_MAIN_VIEW_HTML" file="resources\app_management\main_view.html" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_MAIN_VIEW_JS" file="resources\app_management\main_view.js" type="BINDATA" />
@@ -484,6 +485,8 @@
         <include name="IDR_PASSWORD_CHANGE_JS" file="resources\chromeos\password_change\password_change.js" flattenhtml="true" type="BINDATA" />
         <include name="IDR_PASSWORD_CHANGE_CSS" file="resources\chromeos\password_change\password_change.css" flattenhtml="true" type="BINDATA" />
         <include name="IDR_PASSWORD_CHANGE_AUTHENTICATOR_JS" file="resources\gaia_auth_host\password_change_authenticator.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_CONFIRM_PASSWORD_CHANGE_HTML" file="resources\chromeos\password_change\confirm_password_change.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
+        <include name="IDR_CONFIRM_PASSWORD_CHANGE_JS" file="resources\chromeos\password_change\confirm_password_change.js" type="chrome_html" />
 
         <include name="IDR_CROSH_BUILTIN_MANIFEST" file="resources\chromeos\crosh_builtin\manifest.json" type="BINDATA" />
         <include name="IDR_CRYPTOHOME_HTML" file="resources\chromeos\cryptohome.html" flattenhtml="true" type="BINDATA" />
@@ -530,9 +533,6 @@
         <include name="IDR_QUICKOFFICE_MANIFEST" file="resources\chromeos\quickoffice\manifest.json" type="BINDATA" />
         <include name="IDR_PRODUCT_CHROMEOS_SYNC_CONSENT_SCREEN_ICONS" file="internal\resources\chromeos-sync-consent-icons.html" type="BINDATA" />
       </if>
-      <if expr="_kiosk_next">
-        <include name="IDR_KIOSK_NEXT_HOME_MANIFEST" file="resources\chromeos\kiosk_next_home\manifest.json" type="BINDATA" />
-      </if>
       <if expr="is_win">
         <include name="IDR_SET_AS_DEFAULT_BROWSER_JS" file="resources\set_as_default_browser.js" flattenhtml="true" type="BINDATA" />
         <include name="IDR_SET_AS_DEFAULT_BROWSER_HTML" file="resources\set_as_default_browser.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
@@ -683,7 +683,8 @@
         <include name="IDR_SYS_INTERNALS_IMAGE_CPU_SVG" file="resources\chromeos\sys_internals\img\cpu.svg" type="BINDATA" />
         <include name="IDR_SYS_INTERNALS_IMAGE_MEMORY_SVG" file="resources\chromeos\sys_internals\img\memory.svg" type="BINDATA" />
         <include name="IDR_SYS_INTERNALS_IMAGE_ZRAM_SVG" file="resources\chromeos\sys_internals\img\zram.svg" type="BINDATA" />
-        <include name="IDR_ADD_SUPERVISION_HTML" file="resources\chromeos\add_supervision\add_supervision.html" compress="gzip" type="BINDATA" />
+        <include name="IDR_ADD_SUPERVISION_HTML" file="resources\chromeos\add_supervision\add_supervision.html" compress="gzip" type="chrome_html" />
+        <include name="IDR_ADD_SUPERVISION_NETWORK_UNAVAILABLE_SVG" file="resources\chromeos\add_supervision\images\network_unavailable.svg" compress="gzip" type="BINDATA" />
         <include name="IDR_ADD_SUPERVISION_JS" file="resources\chromeos\add_supervision\add_supervision.js" compress="gzip" type="BINDATA" />
         <include name="IDR_ADD_SUPERVISION_API_SERVER_JS" file="resources\chromeos\add_supervision\add_supervision_api_server.js" compress="gzip" type="BINDATA" />
         <include name="IDR_ADD_SUPERVISION_POST_MESSAGE_API_JS" file="resources\chromeos\add_supervision\post_message_api.js" compress="gzip" type="BINDATA" />
diff --git a/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc b/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
index 5248d47..870bed89 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
@@ -50,6 +50,12 @@
     "<rules version=\"1\"><docMode><domain docMode=\"9\">"
     "yahoo.com</domain></docMode></rules>";
 
+#if defined(OS_WIN)
+const char kYetAnotherSitelistXml[] =
+    "<rules version=\"1\"><docMode><domain docMode=\"9\">"
+    "greylist.invalid.com</domain></docMode></rules>";
+#endif
+
 bool ReturnValidXml(content::URLLoaderInterceptor::RequestParams* params) {
   std::string headers = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n";
   content::URLLoaderInterceptor::WriteResponse(
@@ -128,7 +134,21 @@
   }
 
 #if defined(OS_WIN)
-  const base::FilePath& appdata_dir() { return fake_appdata_dir_.GetPath(); }
+  const base::FilePath& appdata_dir() const {
+    return fake_appdata_dir_.GetPath();
+  }
+
+  const base::FilePath cache_dir() const {
+    return appdata_dir().AppendASCII("Google").AppendASCII("BrowserSwitcher");
+  }
+
+  const base::FilePath cache_file_path() const {
+    return cache_dir().AppendASCII("cache.dat");
+  }
+
+  const base::FilePath sitelist_cache_file_path() const {
+    return cache_dir().AppendASCII("sitelistcache.dat");
+  }
 #endif
 
  private:
@@ -654,20 +674,16 @@
   policy_provider().UpdateChromePolicy(policies);
   base::RunLoop().RunUntilIdle();
 
-  base::FilePath cache_file_path = appdata_dir()
-                                       .AppendASCII("Google")
-                                       .AppendASCII("BrowserSwitcher")
-                                       .AppendASCII("cache.dat");
-
   // Execute everything and check "cache.dat" file contents.
   BrowserSwitcherServiceFactory::GetForBrowserContext(browser()->profile());
   base::RunLoop run_loop;
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(
-          [](base::FilePath path, base::OnceClosure quit) {
+          [](base::FilePath cache_file_path,
+             base::FilePath sitelist_cache_file_path, base::OnceClosure quit) {
             base::ScopedAllowBlockingForTesting allow_blocking;
-            base::File file(path,
+            base::File file(cache_file_path,
                             base::File::FLAG_OPEN | base::File::FLAG_READ);
             ASSERT_TRUE(file.IsValid());
 
@@ -685,12 +701,15 @@
             std::unique_ptr<char[]> buffer(new char[file.GetLength() + 1]);
             buffer.get()[file.GetLength()] = '\0';
             file.Read(0, buffer.get(), file.GetLength());
-            // Check that there's no space in the URL (i.e. replaced with %20).
             EXPECT_EQ(std::string(expected_output), std::string(buffer.get()));
 
+            // Check that sitelistcache.dat doesn't exist.
+            EXPECT_FALSE(base::PathExists(sitelist_cache_file_path));
+
             std::move(quit).Run();
           },
-          cache_file_path, run_loop.QuitClosure()),
+          cache_file_path(), sitelist_cache_file_path(),
+          run_loop.QuitClosure()),
       action_timeout());
   run_loop.Run();
 }
@@ -709,11 +728,19 @@
   base::WriteFile(external_sitelist_path, kOtherSitelistXml,
                   strlen(kOtherSitelistXml));
 
+  base::FilePath external_greylist_path =
+      dir.GetPath().AppendASCII("external_greylist.xml");
+  base::WriteFile(external_greylist_path, kYetAnotherSitelistXml,
+                  strlen(kYetAnotherSitelistXml));
+
   policy::PolicyMap policies;
   EnableBrowserSwitcher(&policies);
   SetPolicy(&policies, policy::key::kBrowserSwitcherExternalSitelistUrl,
             std::make_unique<base::Value>(
                 net::FilePathToFileURL(external_sitelist_path).spec()));
+  SetPolicy(&policies, policy::key::kBrowserSwitcherExternalGreylistUrl,
+            std::make_unique<base::Value>(
+                net::FilePathToFileURL(external_greylist_path).spec()));
   SetPolicy(&policies, policy::key::kBrowserSwitcherUseIeSitelist,
             std::make_unique<base::Value>(true));
   policy_provider().UpdateChromePolicy(policies);
@@ -721,39 +748,72 @@
   BrowserSwitcherServiceWin::SetIeemSitelistUrlForTesting(
       net::FilePathToFileURL(ieem_sitelist_path).spec());
 
-  base::FilePath cache_file_path = appdata_dir()
-                                       .AppendASCII("Google")
-                                       .AppendASCII("BrowserSwitcher")
-                                       .AppendASCII("sitelistcache.dat");
-
-  // Execute everything and check "sitelistcache.dat" file contents. It should
+  // Execute everything and check "cache.dat" file contents. It should
   // contain the *union* of both sitelists, not just one of them.
   BrowserSwitcherServiceFactory::GetForBrowserContext(browser()->profile());
   base::RunLoop run_loop;
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(
-          [](base::FilePath path, base::OnceClosure quit) {
+          [](base::FilePath cache_file_path,
+             base::FilePath sitelist_cache_file_path, base::OnceClosure quit) {
             base::ScopedAllowBlockingForTesting allow_blocking;
-            base::File file(path,
+            base::File file(cache_file_path,
                             base::File::FLAG_OPEN | base::File::FLAG_READ);
             ASSERT_TRUE(file.IsValid());
 
             const char expected_output[] =
                 "1\n"
+                "\n"
+                "\n"
+                "\n"
+                "\n"
                 "2\n"
                 "docs.google.com\n"
-                "yahoo.com\n";
+                "yahoo.com\n"
+                "1\n"
+                "greylist.invalid.com\n";
 
             std::unique_ptr<char[]> buffer(new char[file.GetLength() + 1]);
             buffer.get()[file.GetLength()] = '\0';
             file.Read(0, buffer.get(), file.GetLength());
-            // Check that there's no space in the URL (i.e. replaced with %20).
             EXPECT_EQ(std::string(expected_output), std::string(buffer.get()));
 
+            // Check that sitelistcache.dat doesn't exist.
+            EXPECT_FALSE(base::PathExists(sitelist_cache_file_path));
+
             std::move(quit).Run();
           },
-          cache_file_path, run_loop.QuitClosure()),
+          cache_file_path(), sitelist_cache_file_path(),
+          run_loop.QuitClosure()),
+      action_timeout());
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserSwitcherServiceTest,
+                       DeletesSitelistCacheOnStartup) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  policy::PolicyMap policies;
+  EnableBrowserSwitcher(&policies);
+  policy_provider().UpdateChromePolicy(policies);
+  base::RunLoop().RunUntilIdle();
+
+  base::CreateDirectory(cache_dir());
+  base::WriteFile(sitelist_cache_file_path(), "", 0);
+  ASSERT_TRUE(base::PathExists(sitelist_cache_file_path()));
+
+  // Check that "sitelistcache.dat" got cleaned up on startup.
+  BrowserSwitcherServiceFactory::GetForBrowserContext(browser()->profile());
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::FilePath sitelist_cache_file_path, base::OnceClosure quit) {
+            EXPECT_FALSE(base::PathExists(sitelist_cache_file_path));
+            std::move(quit).Run();
+          },
+          sitelist_cache_file_path(), run_loop.QuitClosure()),
       action_timeout());
   run_loop.Run();
 }
@@ -761,14 +821,8 @@
 IN_PROC_BROWSER_TEST_F(BrowserSwitcherServiceTest, WritesNothingIfDisabled) {
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  base::ScopedTempDir dir;
-  ASSERT_TRUE(dir.CreateUniqueTempDir());
-
   // No policies configured.
 
-  base::FilePath cache_dir =
-      appdata_dir().AppendASCII("Google").AppendASCII("BrowserSwitcher");
-
   // Check that "cache.dat" and "sitelistcache.dat" don't exist when LBS is not
   // configured.
   BrowserSwitcherServiceFactory::GetForBrowserContext(browser()->profile());
@@ -776,13 +830,15 @@
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(
-          [](base::FilePath cache_dir, base::OnceClosure quit) {
-            EXPECT_FALSE(base::PathExists(cache_dir.AppendASCII("cache.dat")));
-            EXPECT_FALSE(
-                base::PathExists(cache_dir.AppendASCII("sitelistcache.dat")));
+          [](base::FilePath cache_dir, base::FilePath cache_file_path,
+             base::FilePath sitelist_cache_file_path, base::OnceClosure quit) {
+            EXPECT_FALSE(base::PathExists(cache_dir));
+            EXPECT_FALSE(base::PathExists(cache_file_path));
+            EXPECT_FALSE(base::PathExists(sitelist_cache_file_path));
             std::move(quit).Run();
           },
-          cache_dir, run_loop.QuitClosure()),
+          cache_dir(), cache_file_path(), sitelist_cache_file_path(),
+          run_loop.QuitClosure()),
       action_timeout());
   run_loop.Run();
 }
diff --git a/chrome/browser/browser_switcher/browser_switcher_service_win.cc b/chrome/browser/browser_switcher/browser_switcher_service_win.cc
index 9d5961d..1866d44 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service_win.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service_win.cc
@@ -36,6 +36,7 @@
 
 const int kCurrentFileVersion = 1;
 
+// Returns "AppData\Local\Google\BrowserSwitcher".
 base::FilePath GetCacheDir() {
   base::FilePath path;
   if (!base::PathService::Get(base::DIR_LOCAL_APP_DATA, &path))
@@ -45,8 +46,27 @@
   return path;
 }
 
+// Creates a RuleSet that is the concatenation of all 3 sources.
+RuleSet GetRules(const BrowserSwitcherPrefs& prefs,
+                 const BrowserSwitcherSitelist* sitelist) {
+  const RuleSet* source_rulesets[] = {
+      &prefs.GetRules(),
+      sitelist->GetIeemSitelist(),
+      sitelist->GetExternalSitelist(),
+  };
+  RuleSet rules;
+  for (const RuleSet* source : source_rulesets) {
+    rules.sitelist.insert(rules.sitelist.end(), source->sitelist.begin(),
+                          source->sitelist.end());
+    rules.greylist.insert(rules.greylist.end(), source->greylist.begin(),
+                          source->greylist.end());
+  }
+  return rules;
+}
+
 // Serialize prefs to a string for writing to cache.dat.
-std::string SerializeCacheFile(const BrowserSwitcherPrefs& prefs) {
+std::string SerializeCacheFile(const BrowserSwitcherPrefs& prefs,
+                               const BrowserSwitcherSitelist* sitelist) {
   std::ostringstream buffer;
 
   buffer << kCurrentFileVersion << std::endl;
@@ -58,7 +78,8 @@
   buffer << prefs.GetChromePath() << std::endl;
   buffer << base::JoinString(prefs.GetChromeParameters(), " ") << std::endl;
 
-  const auto& rules = prefs.GetRules();
+  const RuleSet rules = GetRules(prefs, sitelist);
+
   buffer << rules.sitelist.size() << std::endl;
   if (!rules.sitelist.empty())
     buffer << base::JoinString(rules.sitelist, "\n") << std::endl;
@@ -70,24 +91,6 @@
   return buffer.str();
 }
 
-std::string SerializeSitelistCacheFile(
-    const BrowserSwitcherSitelist* sitelist) {
-  const auto* ieem = sitelist->GetIeemSitelist();
-  const auto* external = sitelist->GetExternalSitelist();
-
-  std::ostringstream buffer;
-
-  buffer << kCurrentFileVersion << std::endl;
-
-  buffer << (ieem->sitelist.size() + external->sitelist.size()) << std::endl;
-  if (!ieem->sitelist.empty())
-    buffer << base::JoinString(ieem->sitelist, "\n") << std::endl;
-  if (!external->sitelist.empty())
-    buffer << base::JoinString(external->sitelist, "\n") << std::endl;
-
-  return buffer.str();
-}
-
 void SaveDataToFile(const std::string& data, base::StringPiece file_name) {
   base::FilePath dir = GetCacheDir();
 
@@ -118,14 +121,15 @@
   UMA_HISTOGRAM_BOOLEAN("BrowserSwitcher.CacheFile.MoveSuccess", success);
 }
 
-void DoRemovePrefsFile() {
+// Delete the file at "AppData\Local\Google\BrowserSwitcher\<file_name>".
+void DoRemoveFileFromCacheDir(std::string file_name) {
   base::FilePath dir = GetCacheDir();
 
   if (dir.empty())
     return;
 
   // Ignore errors while deleting.
-  base::FilePath dest_path = dir.AppendASCII("cache.dat");
+  base::FilePath dest_path = dir.AppendASCII(file_name);
   base::DeleteFile(dest_path, false);
 }
 
@@ -139,11 +143,19 @@
 }  // namespace
 
 BrowserSwitcherServiceWin::BrowserSwitcherServiceWin(Profile* profile)
-    : BrowserSwitcherService(profile), weak_ptr_factory_(this) {
+    : BrowserSwitcherService(profile),
+      sequenced_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
+      weak_ptr_factory_(this) {
   if (prefs().IsEnabled())
     SavePrefsToFile();
   else
     DeletePrefsFile();
+
+  // Clean up sitelistcache.dat from the extension, or from a previous Chrome
+  // version.
+  DeleteSitelistCacheFile();
 }
 
 BrowserSwitcherServiceWin::~BrowserSwitcherServiceWin() = default;
@@ -188,12 +200,7 @@
   if (!prefs().IsEnabled())
     return;
 
-  base::PostTaskWithTraits(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
-      base::BindOnce(&SaveDataToFile, SerializeSitelistCacheFile(sitelist()),
-                     "sitelistcache.dat"));
+  SavePrefsToFile();
 }
 
 GURL BrowserSwitcherServiceWin::GetIeemSitelistUrl() {
@@ -229,19 +236,21 @@
 }
 
 void BrowserSwitcherServiceWin::SavePrefsToFile() {
-  base::PostTaskWithTraits(
+  sequenced_task_runner_->PostTask(
       FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
-      base::BindOnce(&SaveDataToFile, SerializeCacheFile(prefs()),
+      base::BindOnce(&SaveDataToFile, SerializeCacheFile(prefs(), sitelist()),
                      "cache.dat"));
 }
 
 void BrowserSwitcherServiceWin::DeletePrefsFile() const {
-  base::PostTaskWithTraits(FROM_HERE,
-                           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-                            base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
-                           base::BindOnce(&DoRemovePrefsFile));
+  sequenced_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&DoRemoveFileFromCacheDir, "cache.dat"));
+}
+
+void BrowserSwitcherServiceWin::DeleteSitelistCacheFile() const {
+  sequenced_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&DoRemoveFileFromCacheDir, "sitelistcache.dat"));
 }
 
 }  // namespace browser_switcher
diff --git a/chrome/browser/browser_switcher/browser_switcher_service_win.h b/chrome/browser/browser_switcher/browser_switcher_service_win.h
index ed15cf7..be16004 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service_win.h
+++ b/chrome/browser/browser_switcher/browser_switcher_service_win.h
@@ -49,8 +49,11 @@
   // Delete the "cache.dat" file created by |SavePrefsToFile()|. This call does
   // not block, it only posts a task to a worker thread.
   void DeletePrefsFile() const;
+  // Delete the "sitelistcache.dat" file that might be left from the LBS
+  // extension, or from a previous Chrome version. Called during initialization.
+  void DeleteSitelistCacheFile() const;
 
-  std::unique_ptr<XmlDownloader> ieem_downloader_;
+  scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
 
   base::WeakPtrFactory<BrowserSwitcherServiceWin> weak_ptr_factory_;
 
diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc
index d42e450..e03ae78 100644
--- a/chrome/browser/captive_portal/captive_portal_browsertest.cc
+++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
@@ -67,6 +67,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/url_loader_interceptor.h"
@@ -157,6 +158,12 @@
 // captive portal.
 const char* const kInternetConnectedTitle = "Title Of Awesomeness";
 
+BrowserThread::ID GetInterceptorThreadID() {
+  return base::FeatureList::IsEnabled(features::kNavigationLoaderOnUI)
+             ? BrowserThread::UI
+             : BrowserThread::IO;
+}
+
 // Creates a server-side redirect for use with the TestServer.
 std::string CreateServerRedirect(const std::string& dest_url) {
   const char* const kServerRedirectBase = "/server-redirect?";
@@ -216,6 +223,7 @@
   // True if WaitForNavigations has been called, until
   // |num_navigations_to_wait_for_| have been observed.
   bool waiting_for_navigation_;
+  std::unique_ptr<base::RunLoop> run_loop_;
 
   content::NotificationRegistrar registrar_;
 
@@ -241,7 +249,8 @@
   if (num_navigations_ < num_navigations_to_wait_for) {
     num_navigations_to_wait_for_ = num_navigations_to_wait_for;
     waiting_for_navigation_ = true;
-    content::RunMessageLoop();
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
     EXPECT_FALSE(waiting_for_navigation_);
   }
   EXPECT_EQ(num_navigations_, num_navigations_to_wait_for);
@@ -267,7 +276,8 @@
   if (waiting_for_navigation_ &&
       num_navigations_to_wait_for_ == num_navigations_) {
     waiting_for_navigation_ = false;
-    base::RunLoop::QuitCurrentWhenIdleDeprecated();
+    if (run_loop_)
+      run_loop_->Quit();
   }
 }
 
@@ -305,6 +315,7 @@
   // True if WaitForNavigations has been called, until
   // |tabs_navigated_to_final_destination_| equals |tabs_needing_navigation_|.
   bool waiting_for_navigation_;
+  std::unique_ptr<base::RunLoop> run_loop_;
 
   content::NotificationRegistrar registrar_;
 
@@ -332,7 +343,8 @@
   if (tabs_needing_navigation_.size() !=
           tabs_navigated_to_final_destination_.size()) {
     waiting_for_navigation_ = true;
-    content::RunMessageLoop();
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
     EXPECT_FALSE(waiting_for_navigation_);
   }
   EXPECT_EQ(tabs_needing_navigation_.size(),
@@ -359,7 +371,8 @@
       tabs_needing_navigation_.size() ==
           tabs_navigated_to_final_destination_.size()) {
     waiting_for_navigation_ = false;
-    base::RunLoop::QuitCurrentWhenIdleDeprecated();
+    if (run_loop_)
+      run_loop_->Quit();
   }
 }
 
@@ -394,6 +407,7 @@
   int num_results_to_wait_for_;
 
   bool waiting_for_result_;
+  std::unique_ptr<base::RunLoop> run_loop_;
 
   Profile* profile_;
 
@@ -427,7 +441,8 @@
   if (num_results_received_ < num_results_to_wait_for) {
     num_results_to_wait_for_ = num_results_to_wait_for;
     waiting_for_result_ = true;
-    content::RunMessageLoop();
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
     EXPECT_FALSE(waiting_for_result_);
   }
   EXPECT_EQ(num_results_to_wait_for, num_results_received_);
@@ -453,7 +468,8 @@
   if (waiting_for_result_ &&
       num_results_to_wait_for_ == num_results_received_) {
     waiting_for_result_ = false;
-    base::RunLoop::QuitCurrentWhenIdleDeprecated();
+    if (run_loop_)
+      run_loop_->Quit();
   }
 }
 
@@ -746,12 +762,13 @@
 
   // Waits for exactly |num_jobs| kMockHttps* requests.
   void WaitForJobs(int num_jobs) {
-    if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+    if (!BrowserThread::CurrentlyOn(GetInterceptorThreadID())) {
       base::PostTaskWithTraits(
-          FROM_HERE, {content::BrowserThread::IO},
+          FROM_HERE, {GetInterceptorThreadID()},
           base::BindOnce(&CaptivePortalBrowserTest::WaitForJobs,
                          base::Unretained(this), num_jobs));
-      content::RunMessageLoop();
+      run_loop_ = std::make_unique<base::RunLoop>();
+      run_loop_->Run();
       return;
     }
 
@@ -760,7 +777,8 @@
     if (num_jobs == static_cast<int>(ongoing_mock_requests_.size())) {
       base::PostTaskWithTraits(
           FROM_HERE, {BrowserThread::UI},
-          base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+          base::BindOnce(&CaptivePortalBrowserTest::QuitRunLoop,
+                         base::Unretained(this)));
     } else {
       num_jobs_to_wait_for_ = num_jobs;
     }
@@ -771,9 +789,9 @@
   // failure.  The only way to guarantee this is with an earlier call to
   // WaitForJobs, so makes sure there has been a matching WaitForJobs call.
   void FailJobs(int expected_num_jobs) {
-    if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    if (!BrowserThread::CurrentlyOn(GetInterceptorThreadID())) {
       base::PostTaskWithTraits(
-          FROM_HERE, {BrowserThread::IO},
+          FROM_HERE, {GetInterceptorThreadID()},
           base::BindOnce(&CaptivePortalBrowserTest::FailJobs,
                          base::Unretained(this), expected_num_jobs));
       return;
@@ -792,9 +810,9 @@
   // |expected_num_jobs| behaves just as in FailJobs.
   void FailJobsWithCertError(int expected_num_jobs,
                              const net::SSLInfo& ssl_info) {
-    if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    if (!BrowserThread::CurrentlyOn(GetInterceptorThreadID())) {
       base::PostTaskWithTraits(
-          FROM_HERE, {BrowserThread::IO},
+          FROM_HERE, {GetInterceptorThreadID()},
           base::BindOnce(&CaptivePortalBrowserTest::FailJobsWithCertError,
                          base::Unretained(this), expected_num_jobs, ssl_info));
       return;
@@ -834,9 +852,9 @@
   // Abandon all active kMockHttps* requests.  |expected_num_jobs|
   // behaves just as in FailJobs.
   void AbandonJobs(int expected_num_jobs) {
-    if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    if (!BrowserThread::CurrentlyOn(GetInterceptorThreadID())) {
       base::PostTaskWithTraits(
-          FROM_HERE, {BrowserThread::IO},
+          FROM_HERE, {GetInterceptorThreadID()},
           base::BindOnce(&CaptivePortalBrowserTest::AbandonJobs,
                          base::Unretained(this), expected_num_jobs));
       return;
@@ -860,8 +878,15 @@
     return contents;
   }
 
+  void QuitRunLoop() {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
  protected:
   std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_;
+  std::unique_ptr<base::RunLoop> run_loop_;
   // Only accessed on the IO thread.
   int num_jobs_to_wait_for_ = 0;
   std::vector<content::URLLoaderInterceptor::RequestParams>
@@ -906,7 +931,7 @@
     content::URLLoaderInterceptor::RequestParams* params) {
   if (params->url_request.url.path() == kMockHttpsBadCertPath &&
       intercept_bad_cert_) {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    CHECK(BrowserThread::CurrentlyOn(GetInterceptorThreadID()));
     ongoing_mock_requests_.emplace_back(std::move(*params));
     return true;
   }
@@ -929,7 +954,7 @@
   if (url_string == kMockHttpsUrl || url_string == kMockHttpsUrl2 ||
       url_string == kMockHttpsQuickTimeoutUrl ||
       params->url_request.url.path() == kRedirectToMockHttpsPath) {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    CHECK(BrowserThread::CurrentlyOn(GetInterceptorThreadID()));
     if (params->url_request.url.path() == kRedirectToMockHttpsPath) {
       net::RedirectInfo redirect_info;
       redirect_info.new_url = GURL(kMockHttpsUrl);
@@ -963,7 +988,8 @@
           num_jobs_to_wait_for_ = 0;
           base::PostTaskWithTraits(
               FROM_HERE, {BrowserThread::UI},
-              base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+              base::BindOnce(&CaptivePortalBrowserTest::QuitRunLoop,
+                             base::Unretained(this)));
         }
       }
     } else {
diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc
index eafed7a3..e48bad2 100644
--- a/chrome/browser/chrome_browser_field_trials.cc
+++ b/chrome/browser/chrome_browser_field_trials.cc
@@ -27,7 +27,7 @@
 #include "components/version_info/version_info.h"
 
 #if defined(OS_ANDROID)
-#include "base/android/reached_code_profiler.h"
+#include "chrome/browser/android/feature_utilities.h"
 #include "chrome/browser/chrome_browser_field_trials_mobile.h"
 #else
 #include "chrome/browser/chrome_browser_field_trials_desktop.h"
@@ -100,17 +100,13 @@
 
 void ChromeBrowserFieldTrials::RegisterSyntheticTrials() {
 #if defined(OS_ANDROID)
-  static constexpr char kEnabledGroup[] = "Enabled";
-  static constexpr char kDisabledGroup[] = "Disabled";
-
   static constexpr char kReachedCodeProfilerTrial[] =
-      "ReachedCodeProfilerSynthetic";
-  if (base::android::IsReachedCodeProfilerEnabled()) {
+      "ReachedCodeProfilerSynthetic2";
+  std::string reached_code_profiler_group =
+      chrome::android::GetReachedCodeProfilerTrialGroup();
+  if (!reached_code_profiler_group.empty()) {
     ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-        kReachedCodeProfilerTrial, kEnabledGroup);
-  } else {
-    ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-        kReachedCodeProfilerTrial, kDisabledGroup);
+        kReachedCodeProfilerTrial, reached_code_profiler_group);
   }
 #endif  // defined(OS_ANDROID)
 }
diff --git a/chrome/browser/chrome_child_process_watcher.cc b/chrome/browser/chrome_child_process_watcher.cc
deleted file mode 100644
index f1e6c9d2..0000000
--- a/chrome/browser/chrome_child_process_watcher.cc
+++ /dev/null
@@ -1,42 +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 "chrome/browser/chrome_child_process_watcher.h"
-
-#include "base/command_line.h"
-#include "base/metrics/histogram_macros.h"
-#include "chrome/common/chrome_result_codes.h"
-#include "chrome/common/chrome_switches.h"
-#include "content/public/browser/browser_child_process_observer.h"
-#include "content/public/browser/child_process_data.h"
-#include "content/public/browser/child_process_termination_info.h"
-
-namespace {
-
-void AnalyzeCrash(int exit_code) {
-  if (exit_code == chrome::RESULT_CODE_INVALID_SANDBOX_STATE) {
-    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-    const bool no_startup_window =
-        command_line->HasSwitch(switches::kNoStartupWindow);
-    UMA_HISTOGRAM_BOOLEAN(
-        "ChildProcess.InvalidSandboxStateCrash.NoStartupWindow",
-        no_startup_window);
-  }
-}
-
-}  // namespace
-
-ChromeChildProcessWatcher::ChromeChildProcessWatcher() {
-  BrowserChildProcessObserver::Add(this);
-}
-
-ChromeChildProcessWatcher::~ChromeChildProcessWatcher() {
-  BrowserChildProcessObserver::Remove(this);
-}
-
-void ChromeChildProcessWatcher::BrowserChildProcessCrashed(
-    const content::ChildProcessData& data,
-    const content::ChildProcessTerminationInfo& info) {
-  AnalyzeCrash(info.exit_code);
-}
diff --git a/chrome/browser/chrome_child_process_watcher.h b/chrome/browser/chrome_child_process_watcher.h
deleted file mode 100644
index c9a1352..0000000
--- a/chrome/browser/chrome_child_process_watcher.h
+++ /dev/null
@@ -1,26 +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 CHROME_BROWSER_CHROME_CHILD_PROCESS_WATCHER_H_
-#define CHROME_BROWSER_CHROME_CHILD_PROCESS_WATCHER_H_
-
-#include "base/macros.h"
-#include "content/public/browser/browser_child_process_observer.h"
-
-// ChromeChildProcessWatcher watches for crashed child processes.
-class ChromeChildProcessWatcher : public content::BrowserChildProcessObserver {
- public:
-  ChromeChildProcessWatcher();
-  ~ChromeChildProcessWatcher() override;
-
- private:
-  // content::BrowserChildProcessObserver:
-  void BrowserChildProcessCrashed(
-      const content::ChildProcessData& data,
-      const content::ChildProcessTerminationInfo& info) override;
-
-  DISALLOW_COPY_AND_ASSIGN(ChromeChildProcessWatcher);
-};
-
-#endif  // CHROME_BROWSER_CHROME_CHILD_PROCESS_WATCHER_H_
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 12f1c99..51a0b01b2 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -205,6 +205,7 @@
 #include "components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
@@ -1640,17 +1641,6 @@
 #endif
 }
 
-void ChromeContentBrowserClient::LogInitiatorSchemeBypassingDocumentBlocking(
-    const url::Origin& initiator_origin,
-    int render_process_id,
-    content::ResourceType resource_type) {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  ChromeContentBrowserClientExtensionsPart::
-      LogInitiatorSchemeBypassingDocumentBlocking(
-          initiator_origin, render_process_id, resource_type);
-#endif
-}
-
 network::mojom::URLLoaderFactoryPtrInfo
 ChromeContentBrowserClient::CreateURLLoaderFactoryForNetworkRequests(
     content::RenderProcessHost* process,
@@ -3154,19 +3144,25 @@
 
   style.text_size = prefs->GetString(prefs::kAccessibilityCaptionsTextSize);
   style.font_family = prefs->GetString(prefs::kAccessibilityCaptionsTextFont);
-  style.text_color = base::StringPrintf(
-      "rgba(%s,%s)",
-      prefs->GetString(prefs::kAccessibilityCaptionsTextColor).c_str(),
-      base::NumberToString(
-          prefs->GetInteger(prefs::kAccessibilityCaptionsTextOpacity) / 100.0)
-          .c_str());
-  style.background_color = base::StringPrintf(
-      "rgba(%s,%s)",
-      prefs->GetString(prefs::kAccessibilityCaptionsBackgroundColor).c_str(),
-      base::NumberToString(
-          prefs->GetInteger(prefs::kAccessibilityCaptionsBackgroundOpacity) /
-          100.0)
-          .c_str());
+  if (!prefs->GetString(prefs::kAccessibilityCaptionsTextColor).empty()) {
+    style.text_color = base::StringPrintf(
+        "rgba(%s,%s)",
+        prefs->GetString(prefs::kAccessibilityCaptionsTextColor).c_str(),
+        base::NumberToString(
+            prefs->GetInteger(prefs::kAccessibilityCaptionsTextOpacity) / 100.0)
+            .c_str());
+  }
+
+  if (!prefs->GetString(prefs::kAccessibilityCaptionsBackgroundColor).empty()) {
+    style.background_color = base::StringPrintf(
+        "rgba(%s,%s)",
+        prefs->GetString(prefs::kAccessibilityCaptionsBackgroundColor).c_str(),
+        base::NumberToString(
+            prefs->GetInteger(prefs::kAccessibilityCaptionsBackgroundOpacity) /
+            100.0)
+            .c_str());
+  }
+
   style.text_shadow = prefs->GetString(prefs::kAccessibilityCaptionsTextShadow);
 
   return style;
@@ -4791,6 +4787,32 @@
   ChromeNavigationUIData* chrome_navigation_ui_data =
       static_cast<ChromeNavigationUIData*>(navigation_ui_data);
 
+  auto* drp_settings =
+      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+          browser_context);
+  if (chrome_navigation_ui_data && !profile->IsIncognitoProfile() &&
+      data_reduction_proxy::params::IsEnabledWithNetworkService() &&
+      drp_settings) {
+    if (!data_reduction_proxy_throttle_manager_) {
+      data_reduction_proxy_throttle_manager_ = std::unique_ptr<
+          data_reduction_proxy::DataReductionProxyThrottleManager,
+          base::OnTaskRunnerDeleter>(
+          new data_reduction_proxy::DataReductionProxyThrottleManager(
+              drp_settings->data_reduction_proxy_service(),
+              data_reduction_proxy::DataReductionProxyThrottleManager::
+                  CreateConfig(drp_settings->proxies_for_http())),
+          base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));
+    }
+    net::HttpRequestHeaders headers;
+    data_reduction_proxy::DataReductionProxyRequestOptions::
+        AddPageIDRequestHeader(
+            &headers,
+            chrome_navigation_ui_data->data_reduction_proxy_page_id());
+    result.push_back(std::make_unique<
+                     data_reduction_proxy::DataReductionProxyURLLoaderThrottle>(
+        headers, data_reduction_proxy_throttle_manager_.get()));
+  }
+
   if (chrome_navigation_ui_data &&
       chrome_navigation_ui_data->prerender_mode() != prerender::NO_PRERENDER) {
     result.push_back(std::make_unique<prerender::PrerenderURLLoaderThrottle>(
@@ -5560,7 +5582,7 @@
     }
   }
 
-  // Evaluate client LoFi, Offline, NoScript, and ResourceBlocking previews.
+  // Evaluate Offline, NoScript, and ResourceBlocking previews.
   previews_state |= previews::DetermineAllowedClientPreviewsState(
       previews_data, previews_triggering_logic_already_ran,
       data_reduction_proxy_settings->IsDataReductionProxyEnabled(),
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 52f60d5..4cd801e4 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -151,10 +151,6 @@
   bool ShouldLockToOrigin(content::BrowserContext* browser_context,
                           const GURL& effective_site_url) override;
   const char* GetInitiatorSchemeBypassingDocumentBlocking() override;
-  void LogInitiatorSchemeBypassingDocumentBlocking(
-      const url::Origin& initiator_origin,
-      int render_process_id,
-      content::ResourceType resource_type) override;
   network::mojom::URLLoaderFactoryPtrInfo
   CreateURLLoaderFactoryForNetworkRequests(
       content::RenderProcessHost* process,
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index 46129190..f8dc255 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -72,10 +72,6 @@
   // Source<ThemeService>. There are no details.
   NOTIFICATION_BROWSER_THEME_CHANGED,
 
-  // Sent when the renderer returns focus to the browser, as part of focus
-  // traversal. The source is the browser, there are no details.
-  NOTIFICATION_FOCUS_RETURNED_TO_BROWSER,
-
   // Application-wide ----------------------------------------------------------
 
   // This message is sent when the application is terminating (the last
@@ -192,9 +188,6 @@
   // AutocompleteController, the details not used.
   NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
 
-  // This is sent from Instant when the omnibox focus state changes.
-  NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
-
   // Printing ----------------------------------------------------------------
 
   // Notification from PrintJob that an event occurred. It can be that a page
@@ -208,10 +201,6 @@
 
   // Content Settings --------------------------------------------------------
 
-  // Sent when the collect cookies dialog is shown. The source is a
-  // TabSpecificContentSettings object, there are no details.
-  NOTIFICATION_COLLECTED_COOKIES_SHOWN,
-
   // Sent when content settings change for a tab. The source is a
   // content::WebContents object, the details are None.
   NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 7c18b16..1b0480d 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -58,7 +58,6 @@
     "//chrome/app:command_ids",
     "//chrome/browser/apps/platform_apps",
     "//chrome/browser/apps/platform_apps/api",
-    "//chrome/browser/chromeos/kiosk_next_home/mojom",
     "//chrome/browser/chromeos/power/ml/smart_dim",
     "//chrome/browser/chromeos/supervision/mojom",
     "//chrome/browser/devtools",
@@ -191,6 +190,7 @@
     "//components/safe_browsing/db:metadata_proto",
     "//components/session_manager/core",
     "//components/signin/core/browser",
+    "//components/signin/core/browser/webdata",
     "//components/storage_monitor",
     "//components/strings",
     "//components/sync",
@@ -735,10 +735,6 @@
     "crostini/crostini_remover.h",
     "crostini/crostini_reporting_util.cc",
     "crostini/crostini_reporting_util.h",
-    "crostini/crostini_share_path.cc",
-    "crostini/crostini_share_path.h",
-    "crostini/crostini_share_path_factory.cc",
-    "crostini/crostini_share_path_factory.h",
     "crostini/crostini_util.cc",
     "crostini/crostini_util.h",
     "crostini/crosvm_metrics.cc",
@@ -763,6 +759,8 @@
     "dbus/chrome_features_service_provider.h",
     "dbus/component_updater_service_provider.cc",
     "dbus/component_updater_service_provider.h",
+    "dbus/cryptohome_key_delegate_service_provider.cc",
+    "dbus/cryptohome_key_delegate_service_provider.h",
     "dbus/dbus_helper.cc",
     "dbus/dbus_helper.h",
     "dbus/drive_file_stream_service_provider.cc",
@@ -1051,6 +1049,12 @@
     "first_run/steps/app_list_step.h",
     "first_run/steps/tray_step.cc",
     "first_run/steps/tray_step.h",
+    "guest_os/guest_os_pref_names.cc",
+    "guest_os/guest_os_pref_names.h",
+    "guest_os/guest_os_share_path.cc",
+    "guest_os/guest_os_share_path.h",
+    "guest_os/guest_os_share_path_factory.cc",
+    "guest_os/guest_os_share_path_factory.h",
     "hats/hats_dialog.cc",
     "hats/hats_dialog.h",
     "hats/hats_finch_helper.cc",
@@ -1087,20 +1091,6 @@
     "kerberos/kerberos_credentials_manager.h",
     "kiosk_next/kiosk_next_browser_factory.cc",
     "kiosk_next/kiosk_next_browser_factory.h",
-    "kiosk_next_home/app_controller_service.cc",
-    "kiosk_next_home/app_controller_service.h",
-    "kiosk_next_home/app_controller_service_factory.cc",
-    "kiosk_next_home/app_controller_service_factory.h",
-    "kiosk_next_home/identity_controller_impl.cc",
-    "kiosk_next_home/identity_controller_impl.h",
-    "kiosk_next_home/intent_config_helper.cc",
-    "kiosk_next_home/intent_config_helper.h",
-    "kiosk_next_home/kiosk_next_home_interface_broker_impl.cc",
-    "kiosk_next_home/kiosk_next_home_interface_broker_impl.h",
-    "kiosk_next_home/metrics_helper.cc",
-    "kiosk_next_home/metrics_helper.h",
-    "kiosk_next_home/website_controller_impl.cc",
-    "kiosk_next_home/website_controller_impl.h",
     "language_preferences.cc",
     "language_preferences.h",
     "launcher_search_provider/error_reporter.cc",
@@ -1234,6 +1224,8 @@
     "login/lock_screen_utils.h",
     "login/login_auth_recorder.cc",
     "login/login_auth_recorder.h",
+    "login/login_client_cert_usage_observer.cc",
+    "login/login_client_cert_usage_observer.h",
     "login/login_wizard.h",
     "login/mojo_system_info_dispatcher.cc",
     "login/mojo_system_info_dispatcher.h",
@@ -2121,12 +2113,12 @@
     "extensions/file_system_provider/provider_function.h",
     "extensions/input_method_api.cc",
     "extensions/input_method_api.h",
-    "extensions/login/login_api.cc",
-    "extensions/login/login_api.h",
-    "extensions/login_screen_ui/login_screen_extension_ui_handler.cc",
-    "extensions/login_screen_ui/login_screen_extension_ui_handler.h",
-    "extensions/login_screen_ui/login_screen_ui_api.cc",
-    "extensions/login_screen_ui/login_screen_ui_api.h",
+    "extensions/login_screen/login/login_api.cc",
+    "extensions/login_screen/login/login_api.h",
+    "extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc",
+    "extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h",
+    "extensions/login_screen/login_screen_ui/login_screen_ui_api.cc",
+    "extensions/login_screen/login_screen_ui/login_screen_ui_api.h",
     "extensions/media_player_api.cc",
     "extensions/media_player_api.h",
     "extensions/quick_unlock_private/quick_unlock_private_api.cc",
@@ -2385,7 +2377,6 @@
     "crostini/crostini_package_service_unittest.cc",
     "crostini/crostini_registry_service_unittest.cc",
     "crostini/crostini_reporting_util_unittest.cc",
-    "crostini/crostini_share_path_unittest.cc",
     "crostini/crosvm_metrics_unittest.cc",
     "crostini/crosvm_process_list_unittest.cc",
     "cryptauth/client_app_metadata_provider_service_unittest.cc",
@@ -2411,8 +2402,8 @@
     "extensions/file_manager/job_event_router_unittest.cc",
     "extensions/gfx_utils_unittest.cc",
     "extensions/install_limiter_unittest.cc",
-    "extensions/login/login_api_unittest.cc",
-    "extensions/login_screen_ui/login_screen_extension_ui_handler_unittest.cc",
+    "extensions/login_screen/login/login_api_unittest.cc",
+    "extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_unittest.cc",
     "extensions/permissions_updater_delegate_chromeos_unittest.cc",
     "extensions/public_session_permission_helper_unittest.cc",
     "extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc",
@@ -2478,6 +2469,7 @@
     "fileapi/recent_disk_source_unittest.cc",
     "fileapi/recent_model_unittest.cc",
     "fileapi/test/fake_recent_source.cc",
+    "guest_os/guest_os_share_path_unittest.cc",
     "hats/hats_finch_helper_unittest.cc",
     "hats/hats_notification_controller_unittest.cc",
     "input_method/browser_state_monitor_unittest.cc",
@@ -2485,9 +2477,6 @@
     "input_method/input_method_engine_unittest.cc",
     "input_method/input_method_manager_impl_unittest.cc",
     "input_method/input_method_persistence_unittest.cc",
-    "kiosk_next_home/app_controller_service_unittest.cc",
-    "kiosk_next_home/identity_controller_impl_unittest.cc",
-    "kiosk_next_home/intent_config_helper_unittest.cc",
     "locale_change_guard_unittest.cc",
     "lock_screen_apps/app_manager_impl_unittest.cc",
     "lock_screen_apps/lock_screen_profile_creator_impl_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.cc b/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.cc
index cf95d0e..6eaf6f2 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.cc
@@ -65,7 +65,6 @@
       imm_bridge_(imm_bridge),
       input_context_id_(input_context_id),
       binding_(this),
-      composing_text_(),
       state_update_timer_() {}
 
 InputConnectionImpl::~InputConnectionImpl() = default;
@@ -130,7 +129,6 @@
   if (!ime_engine_->CommitText(input_context_id_,
                                base::UTF16ToUTF8(text).c_str(), &error))
     LOG(ERROR) << "CommitText failed: error=\"" << error << "\"";
-  composing_text_.clear();
 }
 
 void InputConnectionImpl::DeleteSurroundingText(int before, int after) {
@@ -159,12 +157,6 @@
 void InputConnectionImpl::FinishComposingText() {
   StartStateUpdateTimer();
 
-  if (composing_text_.empty()) {
-    // There is no ongoing composing. Do nothing.
-    UpdateTextInputState(true);
-    return;
-  }
-
   ui::TextInputClient* client = GetTextInputClient();
   if (!client)
     return;
@@ -172,14 +164,22 @@
   client->GetEditableSelectionRange(&selection_range);
   client->GetCompositionTextRange(&composition_range);
 
+  if (composition_range.is_empty()) {
+    // There is no ongoing composing. Do nothing.
+    UpdateTextInputState(true);
+    return;
+  }
+
+  base::string16 composing_text;
+  client->GetTextFromRange(composition_range, &composing_text);
+
   std::string error;
   if (!ime_engine_->CommitText(input_context_id_,
-                               base::UTF16ToUTF8(composing_text_).c_str(),
+                               base::UTF16ToUTF8(composing_text).c_str(),
                                &error)) {
     LOG(ERROR) << "FinishComposingText: CommitText() failed, error=\"" << error
                << "\"";
   }
-  composing_text_.clear();
 
   if (selection_range.start() == selection_range.end() &&
       selection_range.start() == composition_range.end()) {
@@ -229,7 +229,6 @@
                << ", error=\"" << error << "\"";
     return;
   }
-  composing_text_ = text;
 }
 
 void InputConnectionImpl::RequestTextInputState(
diff --git a/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.h b/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.h
index 5c65d89..5057a14 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.h
+++ b/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.h
@@ -65,8 +65,6 @@
 
   mojo::Binding<mojom::InputConnection> binding_;
 
-  base::string16 composing_text_;
-
   base::OneShotTimer state_update_timer_;
 
   DISALLOW_COPY_AND_ASSIGN(InputConnectionImpl);
diff --git a/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl_unittest.cc b/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl_unittest.cc
index c4829403..a0458c6e 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl_unittest.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/input_connection_impl_unittest.cc
@@ -104,6 +104,43 @@
   DISALLOW_COPY_AND_ASSIGN(TestIMEInputContextHandler);
 };
 
+class MockTextInputClient : public ui::DummyTextInputClient {
+ public:
+  void SetText(const std::string& text) { text_ = text; }
+
+  void SetCursorPos(int pos) { cursor_pos_ = pos; }
+
+  void SetCompositionRange(const gfx::Range& range) {
+    composition_range_ = range;
+  }
+
+  bool GetTextRange(gfx::Range* range) const override {
+    *range = gfx::Range(0, base::ASCIIToUTF16(text_).length());
+    return true;
+  }
+
+  bool GetTextFromRange(const gfx::Range& range,
+                        base::string16* text) const override {
+    *text = base::ASCIIToUTF16(text_.substr(range.start(), range.end()));
+    return true;
+  }
+
+  bool GetEditableSelectionRange(gfx::Range* range) const override {
+    *range = gfx::Range(cursor_pos_, cursor_pos_);
+    return true;
+  }
+
+  bool GetCompositionTextRange(gfx::Range* range) const override {
+    *range = composition_range_;
+    return true;
+  }
+
+ private:
+  std::string text_;
+  int cursor_pos_ = 0;
+  gfx::Range composition_range_ = gfx::Range(0, 0);
+};
+
 class InputConnectionImplTest : public testing::Test {
  public:
   InputConnectionImplTest() = default;
@@ -118,7 +155,7 @@
 
   TestIMEInputContextHandler* context_handler() { return &context_handler_; }
 
-  ui::DummyTextInputClient* client() { return &text_input_client_; }
+  MockTextInputClient* client() { return &text_input_client_; }
 
   ui::IMEEngineHandlerInterface::InputContext context() {
     return ui::IMEEngineHandlerInterface::InputContext{
@@ -160,7 +197,7 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<TestInputMethodManagerBridge> bridge_;
   std::unique_ptr<chromeos::InputMethodEngine> engine_;
-  ui::DummyTextInputClient text_input_client_;
+  MockTextInputClient text_input_client_;
   ui::MockInputMethod input_method_{nullptr};
   TestIMEInputContextHandler context_handler_{&input_method_};
   std::unique_ptr<ChromeKeyboardControllerClientTestHelper>
@@ -218,11 +255,17 @@
   context_handler()->Reset();
   connection->SetComposingText(base::ASCIIToUTF16("composing"), 0,
                                base::nullopt);
+  client()->SetText("composing");
+  client()->SetCompositionRange(gfx::Range(0, 9));
   EXPECT_EQ(0, context_handler()->commit_text_call_count());
   connection->FinishComposingText();
   EXPECT_EQ(1, context_handler()->commit_text_call_count());
   EXPECT_EQ("composing", context_handler()->last_commit_text());
 
+  client()->SetCompositionRange(gfx::Range(0, 0));
+  connection->FinishComposingText();
+  EXPECT_EQ(1, context_handler()->commit_text_call_count());
+
   engine()->FocusOut();
 }
 
diff --git a/chrome/browser/chromeos/base/file_flusher.cc b/chrome/browser/chromeos/base/file_flusher.cc
index 7983e88..258ba39 100644
--- a/chrome/browser/chromeos/base/file_flusher.cc
+++ b/chrome/browser/chromeos/base/file_flusher.cc
@@ -11,7 +11,7 @@
 #include "base/files/file.h"
 #include "base/files/file_enumerator.h"
 #include "base/logging.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task/post_task.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -70,7 +70,7 @@
   const base::Closure callback_;
 
   bool started_ = false;
-  base::CancellationFlag cancel_flag_;
+  base::AtomicFlag cancel_flag_;
   bool finish_scheduled_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(Job);
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 8f384fe2..549df91 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -47,6 +47,7 @@
 #include "chrome/browser/chromeos/boot_times_recorder.h"
 #include "chrome/browser/chromeos/dbus/chrome_features_service_provider.h"
 #include "chrome/browser/chromeos/dbus/component_updater_service_provider.h"
+#include "chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.h"
 #include "chrome/browser/chromeos/dbus/dbus_helper.h"
 #include "chrome/browser/chromeos/dbus/drive_file_stream_service_provider.h"
 #include "chrome/browser/chromeos/dbus/kiosk_info_service_provider.h"
@@ -60,7 +61,7 @@
 #include "chrome/browser/chromeos/display/quirks_manager_delegate_impl.h"
 #include "chrome/browser/chromeos/events/event_rewriter_delegate_impl.h"
 #include "chrome/browser/chromeos/extensions/default_app_order.h"
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h"
 #include "chrome/browser/chromeos/external_metrics.h"
 #include "chrome/browser/chromeos/input_method/input_method_configuration.h"
 #include "chrome/browser/chromeos/language_preferences.h"
@@ -137,6 +138,7 @@
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/cryptohome/homedir_methods.h"
 #include "chromeos/cryptohome/system_salt_getter.h"
+#include "chromeos/dbus/constants/cryptohome_key_delegate_constants.h"
 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/power_manager_client.h"
@@ -355,6 +357,12 @@
         CrosDBusService::CreateServiceProviderList(
             std::make_unique<DriveFileStreamServiceProvider>()));
 
+    cryptohome_key_delegate_service_ = CrosDBusService::Create(
+        system_bus, cryptohome::kCryptohomeKeyDelegateServiceName,
+        dbus::ObjectPath(cryptohome::kCryptohomeKeyDelegateServicePath),
+        CrosDBusService::CreateServiceProviderList(
+            std::make_unique<CryptohomeKeyDelegateServiceProvider>()));
+
     if (arc::IsArcVmEnabled()) {
       libvda_service_ = CrosDBusService::Create(
           system_bus, libvda::kLibvdaServiceName,
@@ -402,6 +410,7 @@
     chrome_features_service_.reset();
     vm_applications_service_.reset();
     drive_file_stream_service_.reset();
+    cryptohome_key_delegate_service_.reset();
     ProcessDataCollector::Shutdown();
     PowerDataCollector::Shutdown();
     PowerPolicyController::Shutdown();
@@ -419,6 +428,7 @@
   std::unique_ptr<CrosDBusService> chrome_features_service_;
   std::unique_ptr<CrosDBusService> vm_applications_service_;
   std::unique_ptr<CrosDBusService> drive_file_stream_service_;
+  std::unique_ptr<CrosDBusService> cryptohome_key_delegate_service_;
   std::unique_ptr<CrosDBusService> libvda_service_;
 
   DISALLOW_COPY_AND_ASSIGN(DBusServices);
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import.cc b/chrome/browser/chromeos/crostini/crostini_export_import.cc
index d17d8700..dc2ea3be 100644
--- a/chrome/browser/chromeos/crostini/crostini_export_import.cc
+++ b/chrome/browser/chromeos/crostini/crostini_export_import.cc
@@ -12,9 +12,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager_factory.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
 #include "chrome/common/pref_names.h"
@@ -47,7 +47,7 @@
       : BrowserContextKeyedServiceFactory(
             "CrostiniExportImportService",
             BrowserContextDependencyManager::GetInstance()) {
-    DependsOn(CrostiniSharePathFactory::GetInstance());
+    DependsOn(guest_os::GuestOsSharePathFactory::GetInstance());
     DependsOn(CrostiniManagerFactory::GetInstance());
   }
 
@@ -168,14 +168,14 @@
 
   switch (type) {
     case ExportImportType::EXPORT:
-      CrostiniSharePath::GetForProfile(profile_)->SharePath(
+      guest_os::GuestOsSharePath::GetForProfile(profile_)->SharePath(
           kCrostiniDefaultVmName, path.DirName(), false,
           base::BindOnce(&CrostiniExportImport::ExportAfterSharing,
                          weak_ptr_factory_.GetWeakPtr(), container_id,
                          path.BaseName(), std::move(callback)));
       break;
     case ExportImportType::IMPORT:
-      CrostiniSharePath::GetForProfile(profile_)->SharePath(
+      guest_os::GuestOsSharePath::GetForProfile(profile_)->SharePath(
           kCrostiniDefaultVmName, path, false,
           base::BindOnce(&CrostiniExportImport::ImportAfterSharing,
                          weak_ptr_factory_.GetWeakPtr(), container_id,
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index af019cbb..ca00bdd 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -15,7 +15,6 @@
 #include "base/no_destructor.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
-#include "base/system/sys_info.h"
 #include "base/task/post_task.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
@@ -24,9 +23,9 @@
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_remover.h"
 #include "chrome/browser/chromeos/crostini/crostini_reporting_util.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/usb/cros_usb_detector.h"
 #include "chrome/browser/profiles/profile.h"
@@ -65,6 +64,7 @@
 namespace crostini {
 
 namespace {
+
 const char kSeparator[] = "--";
 
 chromeos::CiceroneClient* GetCiceroneClient() {
@@ -75,9 +75,8 @@
   return chromeos::DBusThreadManager::Get()->GetConciergeClient();
 }
 
-void OnConciergeServiceAvailable(
-    CrostiniManager::StartConciergeCallback callback,
-    bool success) {
+void OnConciergeServiceAvailable(CrostiniManager::BoolCallback callback,
+                                 bool success) {
   if (!success) {
     LOG(ERROR) << "Concierge service did not become available";
     std::move(callback).Run(success);
@@ -116,12 +115,12 @@
                     base::WeakPtr<CrostiniManager> crostini_manager,
                     std::string vm_name,
                     std::string container_name,
-                    CrostiniManager::RestartCrostiniCallback callback)
+                    CrostiniManager::CrostiniResultCallback callback)
       : profile_(profile),
         crostini_manager_(crostini_manager),
         vm_name_(std::move(vm_name)),
         container_name_(std::move(container_name)),
-        callback_(std::move(callback)),
+        completed_callback_(std::move(callback)),
         restart_id_(next_restart_id_++) {}
 
   void Restart() {
@@ -129,7 +128,7 @@
     if (!IsCrostiniUIAllowedForProfile(profile_)) {
       LOG(ERROR) << "Crostini UI not allowed for profile "
                  << profile_->GetProfileUserName();
-      std::move(callback_).Run(CrostiniResult::NOT_ALLOWED);
+      std::move(completed_callback_).Run(CrostiniResult::NOT_ALLOWED);
       return;
     }
     if (is_aborted_) {
@@ -154,9 +153,11 @@
     observer_list_.AddObserver(observer);
   }
 
-  void RunCallback(CrostiniResult result) { std::move(callback_).Run(result); }
+  void RunCallback(CrostiniResult result) {
+    std::move(completed_callback_).Run(result);
+  }
 
-  void Abort(CrostiniManager::AbortRestartCallback callback) {
+  void Abort(base::OnceClosure callback) {
     is_aborted_ = true;
     observer_list_.Clear();
     abort_callback_ = std::move(callback);
@@ -166,7 +167,6 @@
     if (!is_running_) {
       return;
     }
-    // Tell observers.
     for (auto& observer : observer_list_) {
       observer.OnContainerDownloading(download_percent);
     }
@@ -181,7 +181,7 @@
   friend class base::RefCountedThreadSafe<CrostiniRestarter>;
 
   ~CrostiniRestarter() override {
-    if (callback_) {
+    if (completed_callback_) {
       LOG(ERROR) << "Destroying without having called the callback.";
     }
   }
@@ -191,7 +191,6 @@
   }
 
   void LoadComponentFinished(CrostiniResult result) {
-    // Tell observers.
     for (auto& observer : observer_list_) {
       observer.OnComponentLoaded(result);
     }
@@ -211,38 +210,20 @@
 
   void ConciergeStarted(bool is_started) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    CrostiniResult result = is_started ? CrostiniResult::SUCCESS
-                                       : CrostiniResult::CONTAINER_START_FAILED;
-    // Tell observers.
     for (auto& observer : observer_list_) {
-      observer.OnConciergeStarted(result);
+      observer.OnConciergeStarted(is_started);
     }
     if (is_aborted_) {
       std::move(abort_callback_).Run();
       return;
     }
     if (!is_started) {
-      LOG(ERROR) << "Failed to start Concierge service.";
-      FinishRestart(result);
+      FinishRestart(CrostiniResult::CONTAINER_START_FAILED);
       return;
     }
 
-    base::PostTaskWithTraitsAndReplyWithResult(
-        FROM_HERE, {base::MayBlock()},
-        base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace,
-                       base::FilePath(kHomeDirectory)),
-        base::BindOnce(&CrostiniRestarter::CreateDiskImageAfterSizeCheck,
-                       this));
-  }
-
-  void CreateDiskImageAfterSizeCheck(int64_t free_disk_bytes) {
-    // Unlike other functions, this isn't called from a crostini_manager_
-    // function, so crostini_manager_ could have been deleted.
-    if (!crostini_manager_) {
-      return;
-    }
-
-    int64_t disk_size_available = (free_disk_bytes * 9) / 10;
+    // Allow concierge to choose an appropriate disk image size.
+    int64_t disk_size_available = 0;
     // If we have an already existing disk, CreateDiskImage will just return its
     // path so we can pass it to StartTerminaVm.
     crostini_manager_->CreateDiskImage(
@@ -254,21 +235,19 @@
   }
 
   void CreateDiskImageFinished(int64_t disk_size_available,
-                               CrostiniResult result,
+                               bool success,
                                vm_tools::concierge::DiskImageStatus status,
                                const base::FilePath& result_path) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    // Tell observers.
     for (auto& observer : observer_list_) {
-      observer.OnDiskImageCreated(result, status, disk_size_available);
+      observer.OnDiskImageCreated(success, status, disk_size_available);
     }
     if (is_aborted_) {
       std::move(abort_callback_).Run();
       return;
     }
-    if (result != CrostiniResult::SUCCESS) {
-      LOG(ERROR) << "Failed to create disk image.";
-      FinishRestart(result);
+    if (!success) {
+      FinishRestart(CrostiniResult::CREATE_DISK_IMAGE_FAILED);
       return;
     }
     crostini_manager_->StartTerminaVm(
@@ -276,19 +255,17 @@
         base::BindOnce(&CrostiniRestarter::StartTerminaVmFinished, this));
   }
 
-  void StartTerminaVmFinished(CrostiniResult result) {
+  void StartTerminaVmFinished(bool success) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    // Tell observers.
     for (auto& observer : observer_list_) {
-      observer.OnVmStarted(result);
+      observer.OnVmStarted(success);
     }
     if (is_aborted_) {
       std::move(abort_callback_).Run();
       return;
     }
-    if (result != CrostiniResult::SUCCESS) {
-      LOG(ERROR) << "Failed to Start Termina VM.";
-      FinishRestart(result);
+    if (!success) {
+      FinishRestart(CrostiniResult::VM_START_FAILED);
       return;
     }
     crostini_manager_->CreateLxdContainer(
@@ -298,7 +275,6 @@
 
   void CreateLxdContainerFinished(CrostiniResult result) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    // Tell observers.
     for (auto& observer : observer_list_) {
       observer.OnContainerCreated(result);
     }
@@ -317,20 +293,18 @@
                        this));
   }
 
-  void SetUpLxdContainerUserFinished(CrostiniResult result) {
+  void SetUpLxdContainerUserFinished(bool success) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-    // Tell observers.
     for (auto& observer : observer_list_) {
-      observer.OnContainerSetup(result);
+      observer.OnContainerSetup(success);
     }
     if (is_aborted_) {
       std::move(abort_callback_).Run();
       return;
     }
-    if (result != CrostiniResult::SUCCESS) {
-      LOG(ERROR) << "Failed to set up Lxd Container user.";
-      FinishRestart(result);
+    if (!success) {
+      FinishRestart(CrostiniResult::CONTAINER_START_FAILED);
       return;
     }
 
@@ -349,7 +323,6 @@
     }
 
     CloseCrostiniUpgradeContainerView();
-    // Tell observers.
     for (auto& observer : observer_list_) {
       observer.OnContainerStarted(result);
     }
@@ -378,22 +351,20 @@
   }
 
   void GetContainerSshKeysFinished(const std::string& container_username,
-                                   crostini::CrostiniResult result,
+                                   bool success,
                                    const std::string& container_public_key,
                                    const std::string& host_private_key,
                                    const std::string& hostname) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    // Tell observers.
     for (auto& observer : observer_list_) {
-      observer.OnSshKeysFetched(result);
+      observer.OnSshKeysFetched(success);
     }
     if (is_aborted_) {
       std::move(abort_callback_).Run();
       return;
     }
-    if (result != crostini::CrostiniResult::SUCCESS) {
-      LOG(ERROR) << "Failed to get ssh keys.";
-      FinishRestart(result);
+    if (!success) {
+      FinishRestart(CrostiniResult::GET_CONTAINER_SSH_KEYS_FAILED);
       return;
     }
 
@@ -465,8 +436,8 @@
   std::string vm_name_;
   std::string container_name_;
   std::string source_path_;
-  CrostiniManager::RestartCrostiniCallback callback_;
-  CrostiniManager::AbortRestartCallback abort_callback_;
+  CrostiniManager::CrostiniResultCallback completed_callback_;
+  base::OnceClosure abort_callback_;
   base::ObserverList<CrostiniManager::RestartObserver>::Unchecked
       observer_list_;
   CrostiniManager::RestartId restart_id_;
@@ -483,7 +454,7 @@
 // it is updated via MaybeUpgradeCrostini.
 bool CrostiniManager::is_dev_kvm_present_ = true;
 
-void CrostiniManager::SetVmState(std::string vm_name, VmState vm_state) {
+void CrostiniManager::UpdateVmState(std::string vm_name, VmState vm_state) {
   auto vm_info = running_vms_.find(std::move(vm_name));
   if (vm_info != running_vms_.end()) {
     vm_info->second.state = vm_state;
@@ -762,15 +733,14 @@
   return success;
 }
 
-void CrostiniManager::StartConcierge(StartConciergeCallback callback) {
+void CrostiniManager::StartConcierge(BoolCallback callback) {
   VLOG(1) << "Starting Concierge service";
   chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->StartConcierge(
       base::BindOnce(&CrostiniManager::OnStartConcierge,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void CrostiniManager::OnStartConcierge(StartConciergeCallback callback,
-                                       bool success) {
+void CrostiniManager::OnStartConcierge(BoolCallback callback, bool success) {
   if (!success) {
     LOG(ERROR) << "Failed to start Concierge service";
     std::move(callback).Run(success);
@@ -783,15 +753,14 @@
       base::BindOnce(&OnConciergeServiceAvailable, std::move(callback)));
 }
 
-void CrostiniManager::StopConcierge(StopConciergeCallback callback) {
+void CrostiniManager::StopConcierge(BoolCallback callback) {
   VLOG(1) << "Stopping Concierge service";
   chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->StopConcierge(
       base::BindOnce(&CrostiniManager::OnStopConcierge,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void CrostiniManager::OnStopConcierge(StopConciergeCallback callback,
-                                      bool success) {
+void CrostiniManager::OnStopConcierge(BoolCallback callback, bool success) {
   if (!success) {
     LOG(ERROR) << "Failed to stop Concierge service";
   } else {
@@ -809,7 +778,7 @@
   if (disk_path_string.empty()) {
     LOG(ERROR) << "Disk path cannot be empty";
     std::move(callback).Run(
-        CrostiniResult::CLIENT_ERROR,
+        /*success=*/false,
         vm_tools::concierge::DiskImageStatus::DISK_STATUS_UNKNOWN,
         base::FilePath());
     return;
@@ -825,7 +794,7 @@
     LOG(ERROR) << "'" << storage_location
                << "' is not a valid storage location";
     std::move(callback).Run(
-        CrostiniResult::CLIENT_ERROR,
+        /*success=*/false,
         vm_tools::concierge::DiskImageStatus::DISK_STATUS_UNKNOWN,
         base::FilePath());
     return;
@@ -840,13 +809,12 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void CrostiniManager::DestroyDiskImage(
-    const base::FilePath& disk_path,
-    DestroyDiskImageCallback callback) {
+void CrostiniManager::DestroyDiskImage(const base::FilePath& disk_path,
+                                       BoolCallback callback) {
   std::string disk_path_string = disk_path.AsUTF8Unsafe();
   if (disk_path_string.empty()) {
     LOG(ERROR) << "Disk path cannot be empty";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
 
@@ -873,17 +841,17 @@
 
 void CrostiniManager::StartTerminaVm(std::string name,
                                      const base::FilePath& disk_path,
-                                     StartTerminaVmCallback callback) {
+                                     BoolCallback callback) {
   if (name.empty()) {
     LOG(ERROR) << "name is required";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
 
   std::string disk_path_string = disk_path.AsUTF8Unsafe();
   if (disk_path_string.empty()) {
     LOG(ERROR) << "Disk path cannot be empty";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
 
@@ -906,14 +874,15 @@
                               std::move(callback)));
 }
 
-void CrostiniManager::StopVm(std::string name, StopVmCallback callback) {
+void CrostiniManager::StopVm(std::string name,
+                             CrostiniResultCallback callback) {
   if (name.empty()) {
     LOG(ERROR) << "name is required";
     std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
     return;
   }
 
-  SetVmState(name, VmState::STOPPING);
+  UpdateVmState(name, VmState::STOPPING);
 
   vm_tools::concierge::StopVmRequest request;
   request.set_owner_id(owner_id_);
@@ -961,22 +930,22 @@
 
 void CrostiniManager::DeleteLxdContainer(std::string vm_name,
                                          std::string container_name,
-                                         CrostiniResultCallback callback) {
+                                         BoolCallback callback) {
   if (vm_name.empty()) {
     LOG(ERROR) << "vm_name is required";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
   if (container_name.empty()) {
     LOG(ERROR) << "container_name is required";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
   if (!GetCiceroneClient()->IsLxdContainerDeletedSignalConnected()) {
     LOG(ERROR)
         << "Async call to DeleteLxdContainer can't complete when signals "
            "are not connected.";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
 
@@ -994,29 +963,29 @@
 void CrostiniManager::OnDeleteLxdContainer(
     std::string vm_name,
     std::string container_name,
-    CrostiniResultCallback callback,
-    base::Optional<vm_tools::cicerone::DeleteLxdContainerResponse> reply) {
-  if (!reply.has_value()) {
+    BoolCallback callback,
+    base::Optional<vm_tools::cicerone::DeleteLxdContainerResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to delete lxd container in vm. Empty response.";
-    std::move(callback).Run(CrostiniResult::UNKNOWN_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
-  vm_tools::cicerone::DeleteLxdContainerResponse response = reply.value();
-  if (response.status() ==
+
+  if (response->status() ==
       vm_tools::cicerone::DeleteLxdContainerResponse::DELETING) {
     VLOG(1) << "Awaiting LxdContainerDeletedSignal for " << vm_name << ", "
             << container_name;
     delete_lxd_container_callbacks_.emplace(
         std::make_tuple(vm_name, container_name), std::move(callback));
 
-  } else if (response.status() ==
+  } else if (response->status() ==
              vm_tools::cicerone::DeleteLxdContainerResponse::DOES_NOT_EXIST) {
     RemoveLxdContainerFromPrefs(profile_, vm_name, container_name);
-    std::move(callback).Run(CrostiniResult::SUCCESS);
+    std::move(callback).Run(/*success=*/true);
 
   } else {
-    LOG(ERROR) << "Failed to delete container: " << response.failure_reason();
-    std::move(callback).Run(CrostiniResult::UNKNOWN_ERROR);
+    LOG(ERROR) << "Failed to delete container: " << response->failure_reason();
+    std::move(callback).Run(/*success=*/false);
   }
 }
 
@@ -1063,20 +1032,20 @@
 void CrostiniManager::SetUpLxdContainerUser(std::string vm_name,
                                             std::string container_name,
                                             std::string container_username,
-                                            CrostiniResultCallback callback) {
+                                            BoolCallback callback) {
   if (vm_name.empty()) {
     LOG(ERROR) << "vm_name is required";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
   if (container_name.empty()) {
     LOG(ERROR) << "container_name is required";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
   if (container_username.empty()) {
     LOG(ERROR) << "container_username is required";
-    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
   vm_tools::cicerone::SetUpLxdContainerUserRequest request;
@@ -1180,7 +1149,7 @@
     std::string desktop_file_id,
     const std::vector<std::string>& files,
     bool display_scaled,
-    LaunchContainerApplicationCallback callback) {
+    BoolCallback callback) {
   vm_tools::cicerone::LaunchContainerApplicationRequest request;
   request.set_owner_id(owner_id_);
   request.set_vm_name(std::move(vm_name));
@@ -1315,7 +1284,7 @@
     std::string vm_name,
     std::string container_name,
     std::string desktop_file_id,
-    UninstallPackageOwningFileCallback callback) {
+    CrostiniResultCallback callback) {
   if (!GetCiceroneClient()->IsUninstallPackageProgressSignalConnected()) {
     // Technically we could still start the uninstall, but we wouldn't be able
     // to detect when the uninstall completes, successfully or otherwise.
@@ -1405,27 +1374,26 @@
     const std::string& vm_name,
     device::mojom::UsbDeviceInfoPtr device,
     AttachUsbDeviceCallback callback,
-    base::Optional<vm_tools::concierge::AttachUsbDeviceResponse> reply) {
-  if (reply.has_value()) {
-    vm_tools::concierge::AttachUsbDeviceResponse response = reply.value();
-    if (response.success()) {
-      std::move(callback).Run(response.guest_port(), CrostiniResult::SUCCESS);
-    } else {
-      LOG(ERROR) << "Failed to attach USB device, " << response.reason();
-      std::move(callback).Run(chromeos::kInvalidUsbPortNumber,
-                              CrostiniResult::ATTACH_USB_FAILED);
-    }
-  } else {
+    base::Optional<vm_tools::concierge::AttachUsbDeviceResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to attach USB device, empty dbus response";
-    std::move(callback).Run(chromeos::kInvalidUsbPortNumber,
-                            CrostiniResult::DBUS_ERROR);
+    std::move(callback).Run(/*success=*/false, chromeos::kInvalidUsbPortNumber);
+    return;
   }
+
+  if (!response->success()) {
+    LOG(ERROR) << "Failed to attach USB device, " << response->reason();
+    std::move(callback).Run(/*success=*/false, chromeos::kInvalidUsbPortNumber);
+    return;
+  }
+
+  std::move(callback).Run(/*success=*/true, response->guest_port());
 }
 
 void CrostiniManager::DetachUsbDevice(const std::string& vm_name,
                                       device::mojom::UsbDeviceInfoPtr device,
                                       uint8_t guest_port,
-                                      DetachUsbDeviceCallback callback) {
+                                      BoolCallback callback) {
   vm_tools::concierge::DetachUsbDeviceRequest request;
   request.set_vm_name(vm_name);
   request.set_owner_id(CryptohomeIdForProfile(profile_));
@@ -1442,20 +1410,21 @@
     const std::string& vm_name,
     uint8_t guest_port,
     device::mojom::UsbDeviceInfoPtr device,
-    DetachUsbDeviceCallback callback,
-    base::Optional<vm_tools::concierge::DetachUsbDeviceResponse> reply) {
-  if (reply.has_value()) {
-    vm_tools::concierge::DetachUsbDeviceResponse response = reply.value();
-    if (response.success()) {
-      std::move(callback).Run(CrostiniResult::SUCCESS);
-    } else {
-      LOG(ERROR) << "Failed to detach USB device, " << response.reason();
-      std::move(callback).Run(CrostiniResult::DETACH_USB_FAILED);
-    }
-  } else {
+    BoolCallback callback,
+    base::Optional<vm_tools::concierge::DetachUsbDeviceResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to detach USB device, empty dbus response";
-    std::move(callback).Run(CrostiniResult::DBUS_ERROR);
+    std::move(callback).Run(/*success=*/false);
+    return;
   }
+
+  if (!response->success()) {
+    LOG(ERROR) << "Failed to detach USB device, " << response->reason();
+    std::move(callback).Run(/*success=*/false);
+    return;
+  }
+
+  std::move(callback).Run(/*success=*/true);
 }
 
 void CrostiniManager::ListUsbDevices(const std::string& vm_name,
@@ -1473,23 +1442,24 @@
 void CrostiniManager::OnListUsbDevices(
     const std::string& vm_name,
     ListUsbDevicesCallback callback,
-    base::Optional<vm_tools::concierge::ListUsbDeviceResponse> reply) {
-  if (reply.has_value()) {
-    vm_tools::concierge::ListUsbDeviceResponse response = reply.value();
-    if (response.success()) {
-      std::vector<std::pair<std::string, uint8_t>> mount_points;
-      for (const auto& dev : response.usb_devices()) {
-        mount_points.push_back(std::make_pair(vm_name, dev.guest_port()));
-      }
-      std::move(callback).Run(CrostiniResult::SUCCESS, std::move(mount_points));
-    } else {
-      LOG(ERROR) << "Failed to list USB devices";
-      std::move(callback).Run(CrostiniResult::LIST_USB_FAILED, {});
-    }
-  } else {
+    base::Optional<vm_tools::concierge::ListUsbDeviceResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to list USB devices, empty dbus response";
-    std::move(callback).Run(CrostiniResult::DBUS_ERROR, {});
+    std::move(callback).Run(/*success=*/false, {});
+    return;
   }
+
+  if (!response->success()) {
+    LOG(ERROR) << "Failed to list USB devices";
+    std::move(callback).Run(/*success=*/false, {});
+    return;
+  }
+
+  std::vector<std::pair<std::string, uint8_t>> mount_points;
+  for (const auto& dev : response->usb_devices()) {
+    mount_points.push_back(std::make_pair(vm_name, dev.guest_port()));
+  }
+  std::move(callback).Run(/*success=*/true, std::move(mount_points));
 }
 
 // static
@@ -1589,7 +1559,7 @@
 CrostiniManager::RestartId CrostiniManager::RestartCrostini(
     std::string vm_name,
     std::string container_name,
-    RestartCrostiniCallback callback,
+    CrostiniResultCallback callback,
     RestartObserver* observer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // Currently, |remove_crostini_callbacks_| is only used just before running
@@ -1621,7 +1591,7 @@
 
 void CrostiniManager::AbortRestartCrostini(
     CrostiniManager::RestartId restart_id,
-    AbortRestartCallback callback) {
+    base::OnceClosure callback) {
   auto restarter_it = restarters_by_id_.find(restart_id);
   if (restarter_it == restarters_by_id_.end()) {
     // This can happen if a user cancels the install flow at the exact right
@@ -1636,7 +1606,7 @@
 
 void CrostiniManager::OnAbortRestartCrostini(
     CrostiniManager::RestartId restart_id,
-    AbortRestartCallback callback) {
+    base::OnceClosure callback) {
   auto restarter_it = restarters_by_id_.find(restart_id);
   auto key = std::make_pair(restarter_it->second->vm_name(),
                             restarter_it->second->container_name());
@@ -1671,7 +1641,7 @@
 void CrostiniManager::AddShutdownContainerCallback(
     std::string vm_name,
     std::string container_name,
-    ShutdownContainerCallback shutdown_callback) {
+    base::OnceClosure shutdown_callback) {
   shutdown_container_callbacks_.emplace(ContainerId(vm_name, container_name),
                                         std::move(shutdown_callback));
 }
@@ -1730,63 +1700,60 @@
 
 void CrostiniManager::OnCreateDiskImage(
     CreateDiskImageCallback callback,
-    base::Optional<vm_tools::concierge::CreateDiskImageResponse> reply) {
-  if (!reply.has_value()) {
+    base::Optional<vm_tools::concierge::CreateDiskImageResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to create disk image. Empty response.";
-    std::move(callback).Run(CrostiniResult::CREATE_DISK_IMAGE_FAILED,
+    std::move(callback).Run(/*success=*/false,
                             vm_tools::concierge::DISK_STATUS_UNKNOWN,
                             base::FilePath());
     return;
   }
-  vm_tools::concierge::CreateDiskImageResponse response = reply.value();
 
-  if (response.status() != vm_tools::concierge::DISK_STATUS_EXISTS &&
-      response.status() != vm_tools::concierge::DISK_STATUS_CREATED) {
-    LOG(ERROR) << "Failed to create disk image: " << response.failure_reason();
-    std::move(callback).Run(CrostiniResult::CREATE_DISK_IMAGE_FAILED,
-                            response.status(), base::FilePath());
+  if (response->status() != vm_tools::concierge::DISK_STATUS_EXISTS &&
+      response->status() != vm_tools::concierge::DISK_STATUS_CREATED) {
+    LOG(ERROR) << "Failed to create disk image: " << response->failure_reason();
+    std::move(callback).Run(/*success=*/false, response->status(),
+                            base::FilePath());
     return;
   }
 
-  std::move(callback).Run(CrostiniResult::SUCCESS, response.status(),
-                          base::FilePath(response.disk_path()));
+  std::move(callback).Run(/*success=*/true, response->status(),
+                          base::FilePath(response->disk_path()));
 }
 
 void CrostiniManager::OnDestroyDiskImage(
-    DestroyDiskImageCallback callback,
-    base::Optional<vm_tools::concierge::DestroyDiskImageResponse> reply) {
-  if (!reply.has_value()) {
+    BoolCallback callback,
+    base::Optional<vm_tools::concierge::DestroyDiskImageResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to destroy disk image. Empty response.";
-    std::move(callback).Run(CrostiniResult::DESTROY_DISK_IMAGE_FAILED);
-    return;
-  }
-  vm_tools::concierge::DestroyDiskImageResponse response =
-      std::move(reply).value();
-
-  if (response.status() != vm_tools::concierge::DISK_STATUS_DESTROYED &&
-      response.status() != vm_tools::concierge::DISK_STATUS_DOES_NOT_EXIST) {
-    LOG(ERROR) << "Failed to destroy disk image: " << response.failure_reason();
-    std::move(callback).Run(CrostiniResult::DESTROY_DISK_IMAGE_FAILED);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
 
-  std::move(callback).Run(CrostiniResult::SUCCESS);
+  if (response->status() != vm_tools::concierge::DISK_STATUS_DESTROYED &&
+      response->status() != vm_tools::concierge::DISK_STATUS_DOES_NOT_EXIST) {
+    LOG(ERROR) << "Failed to destroy disk image: "
+               << response->failure_reason();
+    std::move(callback).Run(/*success=*/false);
+    return;
+  }
+
+  std::move(callback).Run(/*success=*/true);
 }
 
 void CrostiniManager::OnListVmDisks(
     ListVmDisksCallback callback,
-    base::Optional<vm_tools::concierge::ListVmDisksResponse> reply) {
-  if (!reply.has_value()) {
+    base::Optional<vm_tools::concierge::ListVmDisksResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to get list of VM disks. Empty response.";
     std::move(callback).Run(
         CrostiniResult::LIST_VM_DISKS_FAILED,
         profile_->GetPrefs()->GetInt64(prefs::kCrostiniLastDiskSize));
     return;
   }
-  vm_tools::concierge::ListVmDisksResponse response = std::move(reply).value();
 
-  if (!response.success()) {
-    LOG(ERROR) << "Failed to list VM disks: " << response.failure_reason();
+  if (!response->success()) {
+    LOG(ERROR) << "Failed to list VM disks: " << response->failure_reason();
     std::move(callback).Run(
         CrostiniResult::LIST_VM_DISKS_FAILED,
         profile_->GetPrefs()->GetInt64(prefs::kCrostiniLastDiskSize));
@@ -1794,20 +1761,19 @@
   }
 
   profile_->GetPrefs()->SetInt64(prefs::kCrostiniLastDiskSize,
-                                 response.total_size());
-  std::move(callback).Run(CrostiniResult::SUCCESS, response.total_size());
+                                 response->total_size());
+  std::move(callback).Run(CrostiniResult::SUCCESS, response->total_size());
 }
 
 void CrostiniManager::OnStartTerminaVm(
     std::string vm_name,
-    StartTerminaVmCallback callback,
-    base::Optional<vm_tools::concierge::StartVmResponse> reply) {
-  if (!reply.has_value()) {
+    BoolCallback callback,
+    base::Optional<vm_tools::concierge::StartVmResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to start termina vm. Empty response.";
-    std::move(callback).Run(CrostiniResult::VM_START_FAILED);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
-  vm_tools::concierge::StartVmResponse response = reply.value();
 
   // Any pending backup or restore callbacks can be marked as failed.
   InvokeAndErasePendingCallbacks(
@@ -1817,31 +1783,31 @@
       &import_lxd_container_callbacks_, vm_name,
       CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STARTED);
 
-  if (response.status() == vm_tools::concierge::VM_STATUS_FAILURE ||
-      response.status() == vm_tools::concierge::VM_STATUS_UNKNOWN) {
-    LOG(ERROR) << "Failed to start VM: " << response.failure_reason();
+  if (response->status() == vm_tools::concierge::VM_STATUS_FAILURE ||
+      response->status() == vm_tools::concierge::VM_STATUS_UNKNOWN) {
+    LOG(ERROR) << "Failed to start VM: " << response->failure_reason();
     // If we thought vms and containers were running before, they aren't now.
     running_vms_.erase(vm_name);
     running_containers_.erase(vm_name);
-    std::move(callback).Run(CrostiniResult::VM_START_FAILED);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
 
   // If the vm is already marked "running" run the callback.
-  if (response.status() == vm_tools::concierge::VM_STATUS_RUNNING) {
+  if (response->status() == vm_tools::concierge::VM_STATUS_RUNNING) {
     running_vms_[vm_name] =
-        VmInfo{VmState::STARTED, std::move(response.vm_info())};
-    std::move(callback).Run(CrostiniResult::SUCCESS);
+        VmInfo{VmState::STARTED, std::move(response->vm_info())};
+    std::move(callback).Run(/*success=*/true);
     return;
   }
 
   // Otherwise, record the container start and run the callback after the VM
   // starts.
-  DCHECK_EQ(response.status(), vm_tools::concierge::VM_STATUS_STARTING);
+  DCHECK_EQ(response->status(), vm_tools::concierge::VM_STATUS_STARTING);
   VLOG(1) << "Awaiting TremplinStartedSignal for " << owner_id_ << ", "
           << vm_name;
   running_vms_[vm_name] =
-      VmInfo{VmState::STARTING, std::move(response.vm_info())};
+      VmInfo{VmState::STARTING, std::move(response->vm_info())};
   // If we thought a container was running for this VM, we're wrong. This can
   // happen if the vm was formerly running, then stopped via crosh.
   running_containers_.erase(vm_name);
@@ -1849,50 +1815,48 @@
   tremplin_started_callbacks_.emplace(
       vm_name, base::BindOnce(&CrostiniManager::OnStartTremplin,
                               weak_ptr_factory_.GetWeakPtr(), vm_name,
-                              std::move(callback), CrostiniResult::SUCCESS));
+                              std::move(callback)));
 
   // Share folders from Downloads, etc with VM.
-  CrostiniSharePath::GetForProfile(profile_)->SharePersistedPaths(
+  guest_os::GuestOsSharePath::GetForProfile(profile_)->SharePersistedPaths(
       vm_name, base::DoNothing());
 }
 
 void CrostiniManager::OnStartTremplin(std::string vm_name,
-                                      StartTerminaVmCallback callback,
-                                      CrostiniResult result) {
+                                      BoolCallback callback) {
   // Record the running vm.
   VLOG(1) << "Received TremplinStartedSignal, VM: " << owner_id_ << ", "
           << vm_name;
-  SetVmState(vm_name, VmState::STARTED);
+  UpdateVmState(vm_name, VmState::STARTED);
 
   // Run the original callback.
-  std::move(callback).Run(result);
+  std::move(callback).Run(/*success=*/true);
 }
 
 void CrostiniManager::OnStopVm(
     std::string vm_name,
-    StopVmCallback callback,
-    base::Optional<vm_tools::concierge::StopVmResponse> reply) {
-  if (!reply.has_value()) {
+    CrostiniResultCallback callback,
+    base::Optional<vm_tools::concierge::StopVmResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to stop termina vm. Empty response.";
     std::move(callback).Run(CrostiniResult::VM_STOP_FAILED);
     return;
   }
-  vm_tools::concierge::StopVmResponse response = reply.value();
 
-  if (!response.success()) {
-    LOG(ERROR) << "Failed to stop VM: " << response.failure_reason();
+  if (!response->success()) {
+    LOG(ERROR) << "Failed to stop VM: " << response->failure_reason();
     // TODO(rjwright): Change the service so that "Requested VM does not
     // exist" is not an error. "Requested VM does not exist" means that there
     // is a disk image for the VM but it is not running, either because it has
     // not been started or it has already been stopped. There's no need for
     // this to be an error, and making it a success will save us having to
     // discriminate on failure_reason here.
-    if (response.failure_reason() != "Requested VM does not exist") {
+    if (response->failure_reason() != "Requested VM does not exist") {
       std::move(callback).Run(CrostiniResult::VM_STOP_FAILED);
       return;
     }
   }
-  // Notify observers
+
   for (auto& observer : vm_shutdown_observers_) {
     observer.OnVmShutdown(vm_name);
   }
@@ -2033,26 +1997,24 @@
 }
 
 void CrostiniManager::OnUninstallPackageOwningFile(
-    UninstallPackageOwningFileCallback callback,
+    CrostiniResultCallback callback,
     base::Optional<vm_tools::cicerone::UninstallPackageOwningFileResponse>
-        reply) {
-  if (!reply.has_value()) {
+        response) {
+  if (!response) {
     LOG(ERROR) << "Failed to uninstall Linux package. Empty response.";
     std::move(callback).Run(CrostiniResult::UNINSTALL_PACKAGE_FAILED);
     return;
   }
-  vm_tools::cicerone::UninstallPackageOwningFileResponse response =
-      reply.value();
 
-  if (response.status() ==
+  if (response->status() ==
       vm_tools::cicerone::UninstallPackageOwningFileResponse::FAILED) {
     LOG(ERROR) << "Failed to uninstall Linux package: "
-               << response.failure_reason();
+               << response->failure_reason();
     std::move(callback).Run(CrostiniResult::UNINSTALL_PACKAGE_FAILED);
     return;
   }
 
-  if (response.status() ==
+  if (response->status() ==
       vm_tools::cicerone::UninstallPackageOwningFileResponse::
           BLOCKING_OPERATION_IN_PROGRESS) {
     LOG(WARNING) << "Failed to uninstall Linux package, another operation is "
@@ -2068,14 +2030,14 @@
     std::string vm_name,
     std::string container_name,
     CrostiniResultCallback callback,
-    base::Optional<vm_tools::cicerone::CreateLxdContainerResponse> reply) {
-  if (!reply.has_value()) {
+    base::Optional<vm_tools::cicerone::CreateLxdContainerResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to create lxd container in vm. Empty response.";
     std::move(callback).Run(CrostiniResult::CONTAINER_START_FAILED);
     return;
   }
-  vm_tools::cicerone::CreateLxdContainerResponse response = reply.value();
-  if (response.status() ==
+
+  if (response->status() ==
       vm_tools::cicerone::CreateLxdContainerResponse::CREATING) {
     VLOG(1) << "Awaiting LxdContainerCreatedSignal for " << owner_id_ << ", "
             << vm_name << ", " << container_name;
@@ -2085,9 +2047,9 @@
         ContainerId(vm_name, container_name), std::move(callback));
     return;
   }
-  if (response.status() !=
+  if (response->status() !=
       vm_tools::cicerone::CreateLxdContainerResponse::EXISTS) {
-    LOG(ERROR) << "Failed to start container: " << response.failure_reason();
+    LOG(ERROR) << "Failed to start container: " << response->failure_reason();
     std::move(callback).Run(CrostiniResult::CONTAINER_START_FAILED);
     return;
   }
@@ -2098,18 +2060,17 @@
     std::string vm_name,
     std::string container_name,
     CrostiniResultCallback callback,
-    base::Optional<vm_tools::cicerone::StartLxdContainerResponse> reply) {
-  if (!reply.has_value()) {
+    base::Optional<vm_tools::cicerone::StartLxdContainerResponse> response) {
+  if (!response) {
     VLOG(1) << "Failed to start lxd container in vm. Empty response.";
     std::move(callback).Run(CrostiniResult::CONTAINER_START_FAILED);
     return;
   }
-  vm_tools::cicerone::StartLxdContainerResponse response = reply.value();
 
-  switch (response.status()) {
+  switch (response->status()) {
     case vm_tools::cicerone::StartLxdContainerResponse::UNKNOWN:
     case vm_tools::cicerone::StartLxdContainerResponse::FAILED:
-      LOG(ERROR) << "Failed to start container: " << response.failure_reason();
+      LOG(ERROR) << "Failed to start container: " << response->failure_reason();
       std::move(callback).Run(CrostiniResult::CONTAINER_START_FAILED);
       break;
 
@@ -2143,25 +2104,25 @@
 void CrostiniManager::OnSetUpLxdContainerUser(
     std::string vm_name,
     std::string container_name,
-    CrostiniResultCallback callback,
-    base::Optional<vm_tools::cicerone::SetUpLxdContainerUserResponse> reply) {
-  if (!reply.has_value()) {
+    BoolCallback callback,
+    base::Optional<vm_tools::cicerone::SetUpLxdContainerUserResponse>
+        response) {
+  if (!response) {
     LOG(ERROR) << "Failed to set up lxd container user. Empty response.";
-    std::move(callback).Run(CrostiniResult::CONTAINER_START_FAILED);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
-  vm_tools::cicerone::SetUpLxdContainerUserResponse response = reply.value();
 
-  if (!(response.status() ==
-            vm_tools::cicerone::SetUpLxdContainerUserResponse::SUCCESS ||
-        response.status() ==
-            vm_tools::cicerone::SetUpLxdContainerUserResponse::EXISTS)) {
+  if (response->status() !=
+          vm_tools::cicerone::SetUpLxdContainerUserResponse::SUCCESS &&
+      response->status() !=
+          vm_tools::cicerone::SetUpLxdContainerUserResponse::EXISTS) {
     LOG(ERROR) << "Failed to set up container user: "
-               << response.failure_reason();
-    std::move(callback).Run(CrostiniResult::CONTAINER_START_FAILED);
+               << response->failure_reason();
+    std::move(callback).Run(/*success=*/false);
     return;
   }
-  std::move(callback).Run(CrostiniResult::SUCCESS);
+  std::move(callback).Run(/*success=*/true);
 }
 
 void CrostiniManager::OnLxdContainerCreated(
@@ -2205,31 +2166,22 @@
     const vm_tools::cicerone::LxdContainerDeletedSignal& signal) {
   if (signal.owner_id() != owner_id_)
     return;
-  CrostiniResult result;
 
-  switch (signal.status()) {
-    case vm_tools::cicerone::LxdContainerDeletedSignal::UNKNOWN:
-      result = CrostiniResult::UNKNOWN_ERROR;
-      break;
-    case vm_tools::cicerone::LxdContainerDeletedSignal::DELETED:
-      result = CrostiniResult::SUCCESS;
-      RemoveLxdContainerFromPrefs(profile_, signal.vm_name(),
-                                  signal.container_name());
-      break;
-    case vm_tools::cicerone::LxdContainerDeletedSignal::CANCELLED:
-    case vm_tools::cicerone::LxdContainerDeletedSignal::FAILED:
-    default:
-      result = CrostiniResult::UNKNOWN_ERROR;
-      LOG(ERROR) << "Failed to delete container (" << signal.vm_name() << ","
-                 << signal.container_name()
-                 << ") : " << signal.failure_reason();
-      break;
+  bool success =
+      signal.status() == vm_tools::cicerone::LxdContainerDeletedSignal::DELETED;
+  if (success) {
+    RemoveLxdContainerFromPrefs(profile_, signal.vm_name(),
+                                signal.container_name());
+  } else {
+    LOG(ERROR) << "Failed to delete container (" << signal.vm_name() << ","
+               << signal.container_name() << ") : " << signal.failure_reason();
   }
+
   // Find the callbacks to call, then erase them from the map.
   auto range = delete_lxd_container_callbacks_.equal_range(
       std::make_tuple(signal.vm_name(), signal.container_name()));
   for (auto it = range.first; it != range.second; ++it) {
-    std::move(it->second).Run(result);
+    std::move(it->second).Run(success);
   }
   delete_lxd_container_callbacks_.erase(range.first, range.second);
 }
@@ -2297,50 +2249,47 @@
 }
 
 void CrostiniManager::OnLaunchContainerApplication(
-    LaunchContainerApplicationCallback callback,
+    BoolCallback callback,
     base::Optional<vm_tools::cicerone::LaunchContainerApplicationResponse>
-        reply) {
-  if (!reply.has_value()) {
+        response) {
+  if (!response) {
     LOG(ERROR) << "Failed to launch application. Empty response.";
-    std::move(callback).Run(
-        CrostiniResult::LAUNCH_CONTAINER_APPLICATION_FAILED);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
-  vm_tools::cicerone::LaunchContainerApplicationResponse response =
-      reply.value();
 
-  if (!response.success()) {
-    LOG(ERROR) << "Failed to launch application: " << response.failure_reason();
-    std::move(callback).Run(
-        CrostiniResult::LAUNCH_CONTAINER_APPLICATION_FAILED);
+  if (!response->success()) {
+    LOG(ERROR) << "Failed to launch application: "
+               << response->failure_reason();
+    std::move(callback).Run(/*success=*/false);
     return;
   }
-  std::move(callback).Run(CrostiniResult::SUCCESS);
+  std::move(callback).Run(/*success=*/true);
 }
 
 void CrostiniManager::OnGetContainerAppIcons(
     GetContainerAppIconsCallback callback,
-    base::Optional<vm_tools::cicerone::ContainerAppIconResponse> reply) {
+    base::Optional<vm_tools::cicerone::ContainerAppIconResponse> response) {
   std::vector<Icon> icons;
-  if (!reply.has_value()) {
+  if (!response) {
     LOG(ERROR) << "Failed to get container application icons. Empty response.";
-    std::move(callback).Run(CrostiniResult::DBUS_ERROR, icons);
+    std::move(callback).Run(/*success=*/false, icons);
     return;
   }
-  vm_tools::cicerone::ContainerAppIconResponse response = reply.value();
-  for (auto& icon : *response.mutable_icons()) {
+
+  for (auto& icon : *response->mutable_icons()) {
     icons.emplace_back(
         Icon{.desktop_file_id = std::move(*icon.mutable_desktop_file_id()),
              .content = std::move(*icon.mutable_icon())});
   }
-  std::move(callback).Run(CrostiniResult::SUCCESS, icons);
+  std::move(callback).Run(/*success=*/true, icons);
 }
 
 void CrostiniManager::OnGetLinuxPackageInfo(
     GetLinuxPackageInfoCallback callback,
-    base::Optional<vm_tools::cicerone::LinuxPackageInfoResponse> reply) {
+    base::Optional<vm_tools::cicerone::LinuxPackageInfoResponse> response) {
   LinuxPackageInfo result;
-  if (!reply.has_value()) {
+  if (!response) {
     LOG(ERROR) << "Failed to get Linux package info. Empty response.";
     result.success = false;
     // The error message is currently only used in a console message. If we
@@ -2349,13 +2298,12 @@
     std::move(callback).Run(result);
     return;
   }
-  vm_tools::cicerone::LinuxPackageInfoResponse response = reply.value();
 
-  if (!response.success()) {
+  if (!response->success()) {
     LOG(ERROR) << "Failed to get Linux package info: "
-               << response.failure_reason();
+               << response->failure_reason();
     result.success = false;
-    result.failure_reason = response.failure_reason();
+    result.failure_reason = response->failure_reason();
     std::move(callback).Run(result);
     return;
   }
@@ -2363,10 +2311,10 @@
   // The |package_id| field is formatted like "name;version;arch;data". We're
   // currently only interested in name and version.
   std::vector<std::string> split = base::SplitString(
-      response.package_id(), ";", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+      response->package_id(), ";", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
   if (split.size() < 2 || split[0].empty() || split[1].empty()) {
     LOG(ERROR) << "Linux package info contained invalid package id: \""
-               << response.package_id() << '"';
+               << response->package_id() << '"';
     result.success = false;
     result.failure_reason = "Linux package info contained invalid package id.";
     std::move(callback).Run(result);
@@ -2374,35 +2322,33 @@
   }
 
   result.success = true;
-  result.package_id = response.package_id();
+  result.package_id = response->package_id();
   result.name = split[0];
   result.version = split[1];
-  result.description = response.description();
-  result.summary = response.summary();
+  result.description = response->description();
+  result.summary = response->summary();
 
   std::move(callback).Run(result);
 }
 
 void CrostiniManager::OnInstallLinuxPackage(
     InstallLinuxPackageCallback callback,
-    base::Optional<vm_tools::cicerone::InstallLinuxPackageResponse> reply) {
-  if (!reply.has_value()) {
+    base::Optional<vm_tools::cicerone::InstallLinuxPackageResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to install Linux package. Empty response.";
-    std::move(callback).Run(
-        CrostiniResult::LAUNCH_CONTAINER_APPLICATION_FAILED);
-    return;
-  }
-  vm_tools::cicerone::InstallLinuxPackageResponse response = reply.value();
-
-  if (response.status() ==
-      vm_tools::cicerone::InstallLinuxPackageResponse::FAILED) {
-    LOG(ERROR) << "Failed to install Linux package: "
-               << response.failure_reason();
     std::move(callback).Run(CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED);
     return;
   }
 
-  if (response.status() ==
+  if (response->status() ==
+      vm_tools::cicerone::InstallLinuxPackageResponse::FAILED) {
+    LOG(ERROR) << "Failed to install Linux package: "
+               << response->failure_reason();
+    std::move(callback).Run(CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED);
+    return;
+  }
+
+  if (response->status() ==
       vm_tools::cicerone::InstallLinuxPackageResponse::INSTALL_ALREADY_ACTIVE) {
     LOG(WARNING) << "Failed to install Linux package, install already active.";
     std::move(callback).Run(CrostiniResult::BLOCKING_OPERATION_ALREADY_ACTIVE);
@@ -2414,16 +2360,14 @@
 
 void CrostiniManager::OnGetContainerSshKeys(
     GetContainerSshKeysCallback callback,
-    base::Optional<vm_tools::concierge::ContainerSshKeysResponse> reply) {
-  if (!reply.has_value()) {
+    base::Optional<vm_tools::concierge::ContainerSshKeysResponse> response) {
+  if (!response) {
     LOG(ERROR) << "Failed to get ssh keys. Empty response.";
-    std::move(callback).Run(CrostiniResult::DBUS_ERROR, "", "", "");
+    std::move(callback).Run(/*success=*/false, "", "", "");
     return;
   }
-  vm_tools::concierge::ContainerSshKeysResponse response = reply.value();
-  std::move(callback).Run(CrostiniResult::SUCCESS,
-                          response.container_public_key(),
-                          response.host_private_key(), response.hostname());
+  std::move(callback).Run(/*success=*/true, response->container_public_key(),
+                          response->host_private_key(), response->hostname());
 }
 
 void CrostiniManager::RemoveCrostini(std::string vm_name,
@@ -2475,15 +2419,14 @@
 
 void CrostiniManager::OnSearchApp(
     SearchAppCallback callback,
-    base::Optional<vm_tools::cicerone::AppSearchResponse> reply) {
+    base::Optional<vm_tools::cicerone::AppSearchResponse> response) {
   std::vector<std::string> package_names;
-  if (!reply.has_value()) {
+  if (!response) {
     LOG(ERROR) << "Failed to SearchApp. Empty response.";
     std::move(callback).Run(package_names);
     return;
   }
-  vm_tools::cicerone::AppSearchResponse response = reply.value();
-  for (auto& package : response.packages())
+  for (auto& package : response->packages())
     package_names.push_back(package.package_name());
   std::move(callback).Run(package_names);
 }
@@ -2491,7 +2434,7 @@
 void CrostiniManager::OnExportLxdContainer(
     std::string vm_name,
     std::string container_name,
-    base::Optional<vm_tools::cicerone::ExportLxdContainerResponse> reply) {
+    base::Optional<vm_tools::cicerone::ExportLxdContainerResponse> response) {
   ContainerId key(vm_name, container_name);
   auto it = export_lxd_container_callbacks_.find(key);
   if (it == export_lxd_container_callbacks_.end()) {
@@ -2500,21 +2443,20 @@
     return;
   }
 
-  if (!reply.has_value()) {
+  if (!response) {
     LOG(ERROR) << "Failed to export lxd container. Empty response.";
     std::move(it->second).Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED);
     export_lxd_container_callbacks_.erase(it);
     return;
   }
-  vm_tools::cicerone::ExportLxdContainerResponse response = reply.value();
 
   // If export has started, the callback will be invoked when the
   // ExportLxdContainerProgressSignal signal indicates that export is complete,
   // otherwise this is an error.
-  if (response.status() !=
+  if (response->status() !=
       vm_tools::cicerone::ExportLxdContainerResponse::EXPORTING) {
-    LOG(ERROR) << "Failed to export container: status=" << response.status()
-               << ", failure_reason=" << response.failure_reason();
+    LOG(ERROR) << "Failed to export container: status=" << response->status()
+               << ", failure_reason=" << response->failure_reason();
     std::move(it->second).Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED);
     export_lxd_container_callbacks_.erase(it);
   }
@@ -2547,7 +2489,6 @@
                  << ", " << signal.failure_reason();
   }
 
-  // If we are still exporting, call progress observers.
   if (exporting) {
     for (auto& observer : export_container_progress_observers_) {
       observer.OnExportContainerProgress(
@@ -2572,7 +2513,7 @@
 void CrostiniManager::OnImportLxdContainer(
     std::string vm_name,
     std::string container_name,
-    base::Optional<vm_tools::cicerone::ImportLxdContainerResponse> reply) {
+    base::Optional<vm_tools::cicerone::ImportLxdContainerResponse> response) {
   ContainerId key(vm_name, container_name);
   auto it = import_lxd_container_callbacks_.find(key);
   if (it == import_lxd_container_callbacks_.end()) {
@@ -2581,20 +2522,19 @@
     return;
   }
 
-  if (!reply.has_value()) {
+  if (!response) {
     LOG(ERROR) << "Failed to import lxd container. Empty response.";
     std::move(it->second).Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED);
     import_lxd_container_callbacks_.erase(it);
     return;
   }
-  vm_tools::cicerone::ImportLxdContainerResponse response = reply.value();
 
   // If import has started, the callback will be invoked when the
   // ImportLxdContainerProgressSignal signal indicates that import is complete,
   // otherwise this is an error.
-  if (response.status() !=
+  if (response->status() !=
       vm_tools::cicerone::ImportLxdContainerResponse::IMPORTING) {
-    LOG(ERROR) << "Failed to import container: " << response.failure_reason();
+    LOG(ERROR) << "Failed to import container: " << response->failure_reason();
     std::move(it->second).Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED);
     import_lxd_container_callbacks_.erase(it);
   }
@@ -2642,7 +2582,6 @@
                  << ", " << signal.failure_reason();
   }
 
-  // Call progress observers.
   if (call_observers) {
     for (auto& observer : import_container_progress_observers_) {
       observer.OnImportContainerProgress(
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h
index f66e6f9b..34e3f5c 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.h
+++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -30,49 +30,50 @@
 
 namespace crostini {
 
-// Result types for CrostiniManager::StartTerminaVmCallback etc.
+// Result types for various callbacks etc.
 
 // WARNING: Do not remove or re-order these values, as they are used in user
 // visible error messages and logs. New entries should only be added to the end.
 // This message was added during development of M74, error codes from prior
 // versions may differ from the numbering here.
 enum class CrostiniResult {
-  SUCCESS,
-  DBUS_ERROR,
-  UNPARSEABLE_RESPONSE,
-  INSUFFICIENT_DISK,
-  CREATE_DISK_IMAGE_FAILED,
-  VM_START_FAILED,
-  VM_STOP_FAILED,
-  DESTROY_DISK_IMAGE_FAILED,
-  LIST_VM_DISKS_FAILED,
-  CLIENT_ERROR,
-  DISK_TYPE_ERROR,
-  CONTAINER_DOWNLOAD_TIMED_OUT,
-  CONTAINER_CREATE_CANCELLED,
-  CONTAINER_CREATE_FAILED,
-  CONTAINER_START_CANCELLED,
-  CONTAINER_START_FAILED,
-  LAUNCH_CONTAINER_APPLICATION_FAILED,
-  INSTALL_LINUX_PACKAGE_FAILED,
-  BLOCKING_OPERATION_ALREADY_ACTIVE,
-  UNINSTALL_PACKAGE_FAILED,
-  SSHFS_MOUNT_ERROR,
-  OFFLINE_WHEN_UPGRADE_REQUIRED,
-  LOAD_COMPONENT_FAILED,
-  PERMISSION_BROKER_ERROR,
-  ATTACH_USB_FAILED,
-  DETACH_USB_FAILED,
-  LIST_USB_FAILED,
-  CROSTINI_UNINSTALLER_RUNNING,
-  UNKNOWN_USB_DEVICE,
-  UNKNOWN_ERROR,
-  CONTAINER_EXPORT_IMPORT_FAILED,
-  CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED,
-  CONTAINER_EXPORT_IMPORT_FAILED_VM_STARTED,
-  CONTAINER_EXPORT_IMPORT_FAILED_ARCHITECTURE,
-  NOT_ALLOWED,
-  CONTAINER_EXPORT_IMPORT_FAILED_SPACE,
+  SUCCESS = 0,
+  // DBUS_ERROR = 1,
+  // UNPARSEABLE_RESPONSE = 2,
+  // INSUFFICIENT_DISK = 3,
+  CREATE_DISK_IMAGE_FAILED = 4,
+  VM_START_FAILED = 5,
+  VM_STOP_FAILED = 6,
+  DESTROY_DISK_IMAGE_FAILED = 7,
+  LIST_VM_DISKS_FAILED = 8,
+  CLIENT_ERROR = 9,
+  // DISK_TYPE_ERROR = 10,
+  CONTAINER_DOWNLOAD_TIMED_OUT = 11,
+  CONTAINER_CREATE_CANCELLED = 12,
+  CONTAINER_CREATE_FAILED = 13,
+  CONTAINER_START_CANCELLED = 14,
+  CONTAINER_START_FAILED = 15,
+  // LAUNCH_CONTAINER_APPLICATION_FAILED = 16,
+  INSTALL_LINUX_PACKAGE_FAILED = 17,
+  BLOCKING_OPERATION_ALREADY_ACTIVE = 18,
+  UNINSTALL_PACKAGE_FAILED = 19,
+  // SSHFS_MOUNT_ERROR = 20,
+  OFFLINE_WHEN_UPGRADE_REQUIRED = 21,
+  LOAD_COMPONENT_FAILED = 22,
+  // PERMISSION_BROKER_ERROR = 23,
+  // ATTACH_USB_FAILED = 24,
+  // DETACH_USB_FAILED = 25,
+  // LIST_USB_FAILED = 26,
+  CROSTINI_UNINSTALLER_RUNNING = 27,
+  // UNKNOWN_USB_DEVICE = 28,
+  UNKNOWN_ERROR = 29,
+  CONTAINER_EXPORT_IMPORT_FAILED = 30,
+  CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED = 31,
+  CONTAINER_EXPORT_IMPORT_FAILED_VM_STARTED = 32,
+  CONTAINER_EXPORT_IMPORT_FAILED_ARCHITECTURE = 33,
+  NOT_ALLOWED = 34,
+  CONTAINER_EXPORT_IMPORT_FAILED_SPACE = 35,
+  GET_CONTAINER_SSH_KEYS_FAILED = 36,
 };
 
 enum class InstallLinuxPackageProgressStatus {
@@ -230,85 +231,24 @@
  public:
   using CrostiniResultCallback =
       base::OnceCallback<void(CrostiniResult result)>;
-  using BoolCallback = base::OnceCallback<void(bool)>;
-
-  // The type of the callback for CrostiniManager::StartConcierge.
-  using StartConciergeCallback = BoolCallback;
-  // The type of the callback for CrostiniManager::StopConcierge.
-  using StopConciergeCallback = BoolCallback;
-  // The type of the callback for CrostiniManager::StartTerminaVm.
-  using StartTerminaVmCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::CreateDiskImage.
-  using CreateDiskImageCallback =
-      base::OnceCallback<void(CrostiniResult result,
-                              vm_tools::concierge::DiskImageStatus,
-                              const base::FilePath& disk_path)>;
-  // The type of the callback for CrostiniManager::DestroyDiskImage.
-  using DestroyDiskImageCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::ListVmDisks.
-  using ListVmDisksCallback =
-      base::OnceCallback<void(CrostiniResult result, int64_t total_size)>;
-  // The type of the callback for CrostiniManager::StopVm.
-  using StopVmCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::StartContainer.
-  using StartContainerCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::ShutdownContainer.
-  using ShutdownContainerCallback = base::OnceClosure;
-  // The type of the callback for CrostiniManager::LaunchContainerApplication.
-  using LaunchContainerApplicationCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::GetContainerAppIcons.
-  using GetContainerAppIconsCallback =
-      base::OnceCallback<void(CrostiniResult result,
-                              const std::vector<Icon>& icons)>;
-  // The type of the callback for CrostiniManager::GetLinuxPackageInfo.
-  using GetLinuxPackageInfoCallback =
-      base::OnceCallback<void(const LinuxPackageInfo&)>;
-  // The type of the callback for CrostiniManager::InstallLinuxPackage.
-  using InstallLinuxPackageCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::UninstallPackageOwningFile.
-  using UninstallPackageOwningFileCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::GetContainerSshKeys.
-  using GetContainerSshKeysCallback =
-      base::OnceCallback<void(CrostiniResult result,
-                              const std::string& container_public_key,
-                              const std::string& host_private_key,
-                              const std::string& hostname)>;
-  // The type of the callback for CrostiniManager::RestartCrostini.
-  using RestartCrostiniCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::RemoveCrostini.
-  using RemoveCrostiniCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::AttachUsbDevice
-  // Note: The guest_port is only valid when the result is ::SUCCESS.
-  using AttachUsbDeviceCallback =
-      base::OnceCallback<void(uint8_t guest_port, CrostiniResult result)>;
-  // The type of the callback for CrostiniManager::DetachUsbDevice
-  using DetachUsbDeviceCallback = CrostiniResultCallback;
-  // The type of the callback for CrostiniManager::ListUsbDevices
-  using ListUsbDevicesCallback = base::OnceCallback<void(
-      CrostiniResult result,
-      std::vector<std::pair<std::string, uint8_t>> devices)>;
-
-  // The type of the callback for CrostiniManager::SearchApp.
-  using SearchAppCallback =
-      base::OnceCallback<void(const std::vector<std::string>& package_names)>;
-
-  using AbortRestartCallback = base::OnceCallback<void()>;
+  // Callback indicating success or failure
+  using BoolCallback = base::OnceCallback<void(bool success)>;
 
   // Observer class for the Crostini restart flow.
   class RestartObserver {
    public:
     virtual ~RestartObserver() {}
     virtual void OnComponentLoaded(CrostiniResult result) = 0;
-    virtual void OnConciergeStarted(CrostiniResult result) = 0;
-    virtual void OnDiskImageCreated(CrostiniResult result,
+    virtual void OnConciergeStarted(bool success) = 0;
+    virtual void OnDiskImageCreated(bool success,
                                     vm_tools::concierge::DiskImageStatus status,
                                     int64_t disk_size_available) = 0;
-    virtual void OnVmStarted(CrostiniResult result) = 0;
+    virtual void OnVmStarted(bool success) = 0;
     virtual void OnContainerDownloading(int32_t download_percent) = 0;
     virtual void OnContainerCreated(CrostiniResult result) = 0;
-    virtual void OnContainerSetup(CrostiniResult result) = 0;
+    virtual void OnContainerSetup(bool success) = 0;
     virtual void OnContainerStarted(CrostiniResult result) = 0;
-    virtual void OnSshKeysFetched(CrostiniResult result) = 0;
+    virtual void OnSshKeysFetched(bool success) = 0;
   };
 
   static CrostiniManager* GetForProfile(Profile* profile);
@@ -344,16 +284,20 @@
 
   // Starts the Concierge service. |callback| is called after the method call
   // finishes.
-  void StartConcierge(StartConciergeCallback callback);
+  void StartConcierge(BoolCallback callback);
 
   // Stops the Concierge service. |callback| is called after the method call
   // finishes.
-  void StopConcierge(StopConciergeCallback callback);
+  void StopConcierge(BoolCallback callback);
 
   // Checks the arguments for creating a new Termina VM disk image. Creates a
   // disk image for a Termina VM via ConciergeClient::CreateDiskImage.
   // |callback| is called if the arguments are bad, or after the method call
   // finishes.
+  using CreateDiskImageCallback =
+      base::OnceCallback<void(bool success,
+                              vm_tools::concierge::DiskImageStatus,
+                              const base::FilePath& disk_path)>;
   void CreateDiskImage(
       // The path to the disk image, including the name of
       // the image itself. The image name should match the
@@ -370,11 +314,12 @@
   // |callback| is called if the arguments are bad, or after the method call
   // finishes.
   void DestroyDiskImage(
-      // The path to the disk image, including the name of
-      // the image itself.
+      // The path to the disk image, including the name of the image itself.
       const base::FilePath& disk_path,
-      DestroyDiskImageCallback callback);
+      BoolCallback callback);
 
+  using ListVmDisksCallback =
+      base::OnceCallback<void(CrostiniResult result, int64_t total_size)>;
   void ListVmDisks(ListVmDisksCallback callback);
 
   // Checks the arguments for starting a Termina VM. Starts a Termina VM via
@@ -385,12 +330,12 @@
       std::string name,
       // Path to the disk image on the host.
       const base::FilePath& disk_path,
-      StartTerminaVmCallback callback);
+      BoolCallback callback);
 
   // Checks the arguments for stopping a Termina VM. Stops the Termina VM via
   // ConciergeClient::StopVm. |callback| is called if the arguments are bad,
   // or after the method call finishes.
-  void StopVm(std::string name, StopVmCallback callback);
+  void StopVm(std::string name, CrostiniResultCallback callback);
 
   // Checks the arguments for creating an Lxd container via
   // CiceroneClient::CreateLxdContainer. |callback| is called immediately if the
@@ -404,7 +349,7 @@
   // arguments are bad, or once the container has been deleted.
   void DeleteLxdContainer(std::string vm_name,
                           std::string container_name,
-                          CrostiniResultCallback callback);
+                          BoolCallback callback);
 
   // Checks the arguments for starting an Lxd container via
   // CiceroneClient::StartLxdContainer. |callback| is called immediately if the
@@ -419,7 +364,7 @@
   void SetUpLxdContainerUser(std::string vm_name,
                              std::string container_name,
                              std::string container_username,
-                             CrostiniResultCallback callback);
+                             BoolCallback callback);
 
   // Checks the arguments for exporting an Lxd container via
   // CiceroneClient::ExportLxdContainer. |callback| is called immediately if the
@@ -438,17 +383,17 @@
                           CrostiniResultCallback callback);
 
   // Asynchronously launches an app as specified by its desktop file id.
-  // |callback| is called with SUCCESS when the relevant process is started
-  // or LAUNCH_CONTAINER_APPLICATION_FAILED if there was an error somewhere.
   void LaunchContainerApplication(std::string vm_name,
                                   std::string container_name,
                                   std::string desktop_file_id,
                                   const std::vector<std::string>& files,
                                   bool display_scaled,
-                                  LaunchContainerApplicationCallback callback);
+                                  BoolCallback callback);
 
   // Asynchronously gets app icons as specified by their desktop file ids.
   // |callback| is called after the method call finishes.
+  using GetContainerAppIconsCallback =
+      base::OnceCallback<void(bool success, const std::vector<Icon>& icons)>;
   void GetContainerAppIcons(std::string vm_name,
                             std::string container_name,
                             std::vector<std::string> desktop_file_ids,
@@ -458,6 +403,8 @@
 
   // Asynchronously retrieve information about a Linux Package (.deb) inside the
   // container.
+  using GetLinuxPackageInfoCallback =
+      base::OnceCallback<void(const LinuxPackageInfo&)>;
   void GetLinuxPackageInfo(Profile* profile,
                            std::string vm_name,
                            std::string container_name,
@@ -474,6 +421,7 @@
   // Begin installation of a Linux Package inside the container. If the
   // installation is successfully started, further updates will be sent to
   // added LinuxPackageOperationProgressObservers.
+  using InstallLinuxPackageCallback = CrostiniResultCallback;
   void InstallLinuxPackage(std::string vm_name,
                            std::string container_name,
                            std::string package_path,
@@ -497,17 +445,24 @@
   void UninstallPackageOwningFile(std::string vm_name,
                                   std::string container_name,
                                   std::string desktop_file_id,
-                                  UninstallPackageOwningFileCallback callback);
+                                  CrostiniResultCallback callback);
 
   // Asynchronously gets SSH server public key of container and trusted SSH
   // client private key which can be used to connect to the container.
   // |callback| is called after the method call finishes.
+  using GetContainerSshKeysCallback =
+      base::OnceCallback<void(bool success,
+                              const std::string& container_public_key,
+                              const std::string& host_private_key,
+                              const std::string& hostname)>;
   void GetContainerSshKeys(std::string vm_name,
                            std::string container_name,
                            GetContainerSshKeysCallback callback);
 
-  // Called when a USB device should be attached into the VM.
-  // Should only ever be called on user action.
+  // Called when a USB device should be attached into the VM. Should only ever
+  // be called on user action. The guest_port is only valid on success.
+  using AttachUsbDeviceCallback =
+      base::OnceCallback<void(bool success, uint8_t guest_port)>;
   void AttachUsbDevice(const std::string& vm_name,
                        device::mojom::UsbDeviceInfoPtr device,
                        base::ScopedFD fd,
@@ -518,11 +473,13 @@
   void DetachUsbDevice(const std::string& vm_name,
                        device::mojom::UsbDeviceInfoPtr device,
                        uint8_t guest_port,
-                       DetachUsbDeviceCallback callback);
+                       BoolCallback callback);
 
   // Lists USB devices attached to a guest VM.
   // TODO(jopra): Rename to reflect that this now lists the mount points for USB
   // devices.
+  using ListUsbDevicesCallback = base::OnceCallback<
+      void(bool success, std::vector<std::pair<std::string, uint8_t>> devices)>;
   void ListUsbDevices(const std::string& vm_name,
                       ListUsbDevicesCallback callback);
 
@@ -545,6 +502,8 @@
 
   // Searches for not installed packages that have names matching the passed
   // plaintext search query and returns a vector containing their names.
+  using SearchAppCallback =
+      base::OnceCallback<void(const std::vector<std::string>& package_names)>;
   void SearchApp(const std::string& vm_name,
                  const std::string& container_name,
                  const std::string& query,
@@ -556,26 +515,25 @@
   // The optional |observer| tracks progress.
   RestartId RestartCrostini(std::string vm_name,
                             std::string container_name,
-                            RestartCrostiniCallback callback,
+                            CrostiniResultCallback callback,
                             RestartObserver* observer = nullptr);
 
   // Aborts a restart. A "next" restarter with the same <vm_name,
   // container_name> will run, if there is one. |callback| will be called once
   // the restart has finished aborting
-  void AbortRestartCrostini(RestartId restart_id,
-                            AbortRestartCallback callback);
+  void AbortRestartCrostini(RestartId restart_id, base::OnceClosure callback);
 
   // Returns true if the Restart corresponding to |restart_id| is not yet
   // complete.
   bool IsRestartPending(RestartId restart_id);
 
   // Adds a callback to receive notification of container shutdown.
-  void AddShutdownContainerCallback(
-      std::string vm_name,
-      std::string container_name,
-      ShutdownContainerCallback shutdown_callback);
+  void AddShutdownContainerCallback(std::string vm_name,
+                                    std::string container_name,
+                                    base::OnceClosure shutdown_callback);
 
   // Adds a callback to receive uninstall notification.
+  using RemoveCrostiniCallback = CrostiniResultCallback;
   void AddRemoveCrostiniCallback(RemoveCrostiniCallback remove_callback);
 
   // Add/remove observers for package install and uninstall progress.
@@ -640,7 +598,7 @@
 
   void RemoveCrostini(std::string vm_name, RemoveCrostiniCallback callback);
 
-  void SetVmState(std::string vm_name, VmState vm_state);
+  void UpdateVmState(std::string vm_name, VmState vm_state);
   bool IsVmRunning(std::string vm_name);
   // Returns null if VM is not running.
   base::Optional<VmInfo> GetVmInfo(std::string vm_name);
@@ -683,19 +641,19 @@
   // service method finishes.
   void OnCreateDiskImage(
       CreateDiskImageCallback callback,
-      base::Optional<vm_tools::concierge::CreateDiskImageResponse> reply);
+      base::Optional<vm_tools::concierge::CreateDiskImageResponse> response);
 
   // Callback for ConciergeClient::DestroyDiskImage. Called after the Concierge
   // service method finishes.
   void OnDestroyDiskImage(
-      DestroyDiskImageCallback callback,
-      base::Optional<vm_tools::concierge::DestroyDiskImageResponse> reply);
+      BoolCallback callback,
+      base::Optional<vm_tools::concierge::DestroyDiskImageResponse> response);
 
   // Callback for ConciergeClient::ListVmDisks. Called after the Concierge
   // service method finishes.
   void OnListVmDisks(
       ListVmDisksCallback callback,
-      base::Optional<vm_tools::concierge::ListVmDisksResponse> reply);
+      base::Optional<vm_tools::concierge::ListVmDisksResponse> response);
 
   // Callback for ConciergeClient::StartTerminaVm. Called after the Concierge
   // service method finishes.  Updates running containers list then calls the
@@ -703,21 +661,19 @@
   // callback to OnStartTremplin.
   void OnStartTerminaVm(
       std::string vm_name,
-      StartTerminaVmCallback callback,
-      base::Optional<vm_tools::concierge::StartVmResponse> reply);
+      BoolCallback callback,
+      base::Optional<vm_tools::concierge::StartVmResponse> response);
 
   // Callback for ConciergeClient::TremplinStartedSignal. Called after the
   // Tremplin service starts. Updates running containers list and then calls the
-  // |callback|.
-  void OnStartTremplin(std::string vm_name,
-                       StartTerminaVmCallback callback,
-                       CrostiniResult result);
+  // |callback| with true, indicating success.
+  void OnStartTremplin(std::string vm_name, BoolCallback callback);
 
   // Callback for ConciergeClient::StopVm. Called after the Concierge
   // service method finishes.
   void OnStopVm(std::string vm_name,
-                StopVmCallback callback,
-                base::Optional<vm_tools::concierge::StopVmResponse> reply);
+                CrostiniResultCallback callback,
+                base::Optional<vm_tools::concierge::StopVmResponse> response);
 
   // Callback for CrostiniManager::InstallCrostiniComponent. Must be called on
   // the UI thread.
@@ -729,11 +685,11 @@
 
   // Callback for CrostiniClient::StartConcierge. Called after the
   // DebugDaemon service method finishes.
-  void OnStartConcierge(StartConciergeCallback callback, bool success);
+  void OnStartConcierge(BoolCallback callback, bool success);
 
   // Callback for CrostiniClient::StopConcierge. Called after the
   // DebugDaemon service method finishes.
-  void OnStopConcierge(StopConciergeCallback callback, bool success);
+  void OnStopConcierge(BoolCallback callback, bool success);
 
   // Callback for CiceroneClient::CreateLxdContainer. May indicate the container
   // is still being created, in which case we will wait for an
@@ -742,107 +698,102 @@
       std::string vm_name,
       std::string container_name,
       CrostiniResultCallback callback,
-      base::Optional<vm_tools::cicerone::CreateLxdContainerResponse> reply);
+      base::Optional<vm_tools::cicerone::CreateLxdContainerResponse> response);
 
   // Callback for CiceroneClient::DeleteLxdContainer.
   void OnDeleteLxdContainer(
       std::string vm_name,
       std::string container_name,
-      CrostiniResultCallback callback,
-      base::Optional<vm_tools::cicerone::DeleteLxdContainerResponse> reply);
+      BoolCallback callback,
+      base::Optional<vm_tools::cicerone::DeleteLxdContainerResponse> response);
 
   // Callback for CiceroneClient::StartLxdContainer.
   void OnStartLxdContainer(
       std::string vm_name,
       std::string container_name,
       CrostiniResultCallback callback,
-      base::Optional<vm_tools::cicerone::StartLxdContainerResponse> reply);
+      base::Optional<vm_tools::cicerone::StartLxdContainerResponse> response);
 
   // Callback for CiceroneClient::SetUpLxdContainerUser.
   void OnSetUpLxdContainerUser(
       std::string vm_name,
       std::string container_name,
-      CrostiniResultCallback callback,
-      base::Optional<vm_tools::cicerone::SetUpLxdContainerUserResponse> reply);
+      BoolCallback callback,
+      base::Optional<vm_tools::cicerone::SetUpLxdContainerUserResponse>
+          response);
 
   // Callback for CiceroneClient::ExportLxdContainer.
   void OnExportLxdContainer(
       std::string vm_name,
       std::string container_name,
-      base::Optional<vm_tools::cicerone::ExportLxdContainerResponse> reply);
+      base::Optional<vm_tools::cicerone::ExportLxdContainerResponse> response);
 
   // Callback for CiceroneClient::ImportLxdContainer.
   void OnImportLxdContainer(
       std::string vm_name,
       std::string container_name,
-      base::Optional<vm_tools::cicerone::ImportLxdContainerResponse> reply);
+      base::Optional<vm_tools::cicerone::ImportLxdContainerResponse> response);
 
   // Callback for CrostiniManager::LaunchContainerApplication.
   void OnLaunchContainerApplication(
-      LaunchContainerApplicationCallback callback,
+      BoolCallback callback,
       base::Optional<vm_tools::cicerone::LaunchContainerApplicationResponse>
-          reply);
+          response);
 
   // Callback for CrostiniManager::GetContainerAppIcons. Called after the
   // Concierge service finishes.
   void OnGetContainerAppIcons(
       GetContainerAppIconsCallback callback,
-      base::Optional<vm_tools::cicerone::ContainerAppIconResponse> reply);
+      base::Optional<vm_tools::cicerone::ContainerAppIconResponse> response);
 
   // Callback for CrostiniManager::GetLinuxPackageInfo and
   // CrostiniManager::GetLinuxPackageInfoFromApt.
   void OnGetLinuxPackageInfo(
       GetLinuxPackageInfoCallback callback,
-      base::Optional<vm_tools::cicerone::LinuxPackageInfoResponse> reply);
+      base::Optional<vm_tools::cicerone::LinuxPackageInfoResponse> response);
 
   // Callback for CrostiniManager::InstallLinuxPackage.
   void OnInstallLinuxPackage(
       InstallLinuxPackageCallback callback,
-      base::Optional<vm_tools::cicerone::InstallLinuxPackageResponse> reply);
+      base::Optional<vm_tools::cicerone::InstallLinuxPackageResponse> response);
 
   // Callback for CrostiniManager::UninstallPackageOwningFile.
   void OnUninstallPackageOwningFile(
-      UninstallPackageOwningFileCallback callback,
+      CrostiniResultCallback callback,
       base::Optional<vm_tools::cicerone::UninstallPackageOwningFileResponse>
-          reply);
+          response);
 
   // Callback for CrostiniManager::GetContainerSshKeys. Called after the
   // Concierge service finishes.
   void OnGetContainerSshKeys(
       GetContainerSshKeysCallback callback,
-      base::Optional<vm_tools::concierge::ContainerSshKeysResponse> reply);
+      base::Optional<vm_tools::concierge::ContainerSshKeysResponse> response);
 
   // Callback for CrostiniManager::OnAttachUsbDeviceOpen
   void OnAttachUsbDevice(
       const std::string& vm_name,
       device::mojom::UsbDeviceInfoPtr device,
       AttachUsbDeviceCallback callback,
-      base::Optional<vm_tools::concierge::AttachUsbDeviceResponse> reply);
+      base::Optional<vm_tools::concierge::AttachUsbDeviceResponse> response);
 
   // Callback for CrostiniManager::DetachUsbDevice
   void OnDetachUsbDevice(
       const std::string& vm_name,
       uint8_t guest_port,
       device::mojom::UsbDeviceInfoPtr device,
-      DetachUsbDeviceCallback callback,
-      base::Optional<vm_tools::concierge::DetachUsbDeviceResponse> reply);
+      BoolCallback callback,
+      base::Optional<vm_tools::concierge::DetachUsbDeviceResponse> response);
 
   // Callback for CrostiniManager::ListUsbDevices
   void OnListUsbDevices(
       const std::string& vm_name,
       ListUsbDevicesCallback callback,
-      base::Optional<vm_tools::concierge::ListUsbDeviceResponse> reply);
-
-  // Callback for CrostiniManager::OnListUsbDevices
-  void OnListUsbDeviceInfoPtrs(
-      const std::string& vm_name,
-      vm_tools::concierge::ListUsbDeviceResponse response,
-      ListUsbDevicesCallback callback,
-      std::vector<device::mojom::UsbDeviceInfoPtr> device_info);
+      base::Optional<vm_tools::concierge::ListUsbDeviceResponse> response);
 
   // Callback for CrostiniManager::SearchApp.
-  void OnSearchApp(SearchAppCallback callback,
-                   base::Optional<vm_tools::cicerone::AppSearchResponse> reply);
+  void OnSearchApp(
+      SearchAppCallback callback,
+      base::Optional<vm_tools::cicerone::AppSearchResponse> response);
 
   // Helper for CrostiniManager::MaybeUpgradeCrostini. Makes blocking calls to
   // check for file paths and registered components.
@@ -855,8 +806,7 @@
   void FinishRestart(CrostiniRestarter* restarter, CrostiniResult result);
 
   // Callback for CrostiniManager::AbortRestartCrostini
-  void OnAbortRestartCrostini(RestartId restart_id,
-                              AbortRestartCallback callback);
+  void OnAbortRestartCrostini(RestartId restart_id, base::OnceClosure callback);
 
   // Callback for CrostiniManager::RemoveCrostini.
   void OnRemoveCrostini(CrostiniResult result);
@@ -873,35 +823,13 @@
   bool termina_update_check_needed_ = false;
   static bool is_dev_kvm_present_;
 
-  // Pending container started callbacks are keyed by <vm_name, container_name>
-  // string pairs.
-  std::multimap<ContainerId, StartContainerCallback> start_container_callbacks_;
-
-  // Pending ShutdownContainer callbacks are keyed by <vm_name, container_name>
-  // string pairs.
-  std::multimap<ContainerId, ShutdownContainerCallback>
-      shutdown_container_callbacks_;
-
-  // Pending CreateLxdContainer callbacks are keyed by <vm_name, container_name>
-  // string pairs. These are used if CreateLxdContainer indicates we need to
-  // wait for an LxdContainerCreate signal.
+  // Callbacks that are waiting on a signal
+  std::multimap<ContainerId, CrostiniResultCallback> start_container_callbacks_;
+  std::multimap<ContainerId, base::OnceClosure> shutdown_container_callbacks_;
   std::multimap<ContainerId, CrostiniResultCallback>
       create_lxd_container_callbacks_;
-
-  // Pending DeleteLxdContainer callbacks are keyed by <vm_name, container_name>
-  // string pairs. These are used if DeleteLxdContainer indicates we need to
-  // wait for an LxdContainerDelete signal.
-  std::multimap<ContainerId, CrostiniResultCallback>
-      delete_lxd_container_callbacks_;
-
-  // Pending ExportLxdContainer callbacks are keyed by <vm_name, container_name>
-  // string pairs. They are invoked once ExportLxdContainerProgressSignal signal
-  // indicates that export is finished.
+  std::multimap<ContainerId, BoolCallback> delete_lxd_container_callbacks_;
   std::map<ContainerId, CrostiniResultCallback> export_lxd_container_callbacks_;
-
-  // Pending ImportLxdContainer callbacks are keyed by <vm_name, container_name>
-  // string pairs. They are invoked once ImportLxdContainerProgressSignal signal
-  // indicates that import is finished.
   std::map<ContainerId, CrostiniResultCallback> import_lxd_container_callbacks_;
 
   // Callbacks to run after Tremplin is started, keyed by vm_name. These are
@@ -929,9 +857,9 @@
 
   base::ObserverList<VmShutdownObserver> vm_shutdown_observers_;
 
-  // Restarts by <vm_name, container_name>. Only one restarter flow is actually
-  // running for a given container, other restarters will just have their
-  // callback called when the running restarter completes.
+  // Only one restarter flow is actually running for a given container, other
+  // restarters will just have their callback called when the running restarter
+  // completes.
   std::multimap<ContainerId, CrostiniManager::RestartId>
       restarters_by_container_;
 
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index 36ce9d42..9cb7eee 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -29,68 +29,53 @@
 namespace crostini {
 
 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
 const uint8_t kUsbPort = 0x01;
+
+void ExpectFailure(base::OnceClosure closure, bool success) {
+  EXPECT_FALSE(success);
+  std::move(closure).Run();
+}
+
+void ExpectSuccess(base::OnceClosure closure, bool success) {
+  EXPECT_TRUE(success);
+  std::move(closure).Run();
+}
+
+void ExpectCrostiniResult(base::OnceClosure closure,
+                          CrostiniResult expected_result,
+                          CrostiniResult result) {
+  EXPECT_EQ(expected_result, result);
+  std::move(closure).Run();
+}
+
 }  // namespace
 
 class CrostiniManagerTest : public testing::Test {
  public:
-  void CreateDiskImageClientErrorCallback(
+  void CreateDiskImageFailureCallback(
       base::OnceClosure closure,
-      CrostiniResult result,
+      bool success,
       vm_tools::concierge::DiskImageStatus status,
       const base::FilePath& file_path) {
     EXPECT_FALSE(fake_concierge_client_->create_disk_image_called());
-    EXPECT_EQ(result, CrostiniResult::CLIENT_ERROR);
+    EXPECT_FALSE(success);
     EXPECT_EQ(status,
               vm_tools::concierge::DiskImageStatus::DISK_STATUS_UNKNOWN);
     std::move(closure).Run();
   }
 
-  void DestroyDiskImageClientErrorCallback(base::OnceClosure closure,
-                                           CrostiniResult result) {
-    EXPECT_FALSE(fake_concierge_client_->destroy_disk_image_called());
-    EXPECT_EQ(result, CrostiniResult::CLIENT_ERROR);
-    std::move(closure).Run();
-  }
-
-  void ListVmDisksClientErrorCallback(base::OnceClosure closure,
-                                      CrostiniResult result,
-                                      int64_t total_size) {
-    EXPECT_FALSE(fake_concierge_client_->list_vm_disks_called());
-    EXPECT_EQ(result, CrostiniResult::CLIENT_ERROR);
-    std::move(closure).Run();
-  }
-
-  void StartTerminaVmClientErrorCallback(base::OnceClosure closure,
-                                         CrostiniResult result) {
-    EXPECT_FALSE(fake_concierge_client_->start_termina_vm_called());
-    EXPECT_EQ(result, CrostiniResult::CLIENT_ERROR);
-    std::move(closure).Run();
-  }
-
-  void StopVmClientErrorCallback(base::OnceClosure closure,
-                                 CrostiniResult result) {
-    EXPECT_FALSE(fake_concierge_client_->stop_vm_called());
-    EXPECT_EQ(result, CrostiniResult::CLIENT_ERROR);
-    std::move(closure).Run();
-  }
-
   void CreateDiskImageSuccessCallback(
       base::OnceClosure closure,
-      CrostiniResult result,
+      bool success,
       vm_tools::concierge::DiskImageStatus status,
       const base::FilePath& file_path) {
     EXPECT_TRUE(fake_concierge_client_->create_disk_image_called());
-    std::move(closure).Run();
-  }
-
-  void DestroyDiskImageSuccessCallback(base::OnceClosure closure,
-                                       CrostiniResult result) {
-    EXPECT_TRUE(fake_concierge_client_->destroy_disk_image_called());
+    EXPECT_TRUE(success);
     std::move(closure).Run();
   }
 
@@ -101,38 +86,6 @@
     std::move(closure).Run();
   }
 
-  void StartTerminaVmSuccessCallback(base::OnceClosure closure,
-                                     CrostiniResult result) {
-    EXPECT_TRUE(fake_concierge_client_->start_termina_vm_called());
-    std::move(closure).Run();
-  }
-
-  void OnStartTremplinRecordsRunningVmCallback(base::OnceClosure closure,
-                                               CrostiniResult result) {
-    // Check that running_vms_ contains the running vm.
-    EXPECT_TRUE(crostini_manager()->IsVmRunning(kVmName));
-    std::move(closure).Run();
-  }
-
-  void StopVmSuccessCallback(base::OnceClosure closure, CrostiniResult result) {
-    EXPECT_TRUE(fake_concierge_client_->stop_vm_called());
-    std::move(closure).Run();
-  }
-
-  void CreateContainerFailsCallback(base::OnceClosure closure,
-                                    CrostiniResult result) {
-    create_container_fails_callback_called_ = true;
-    EXPECT_EQ(result, CrostiniResult::UNKNOWN_ERROR);
-    std::move(closure).Run();
-  }
-
-  void CrostiniResultCallback(base::OnceClosure closure,
-                              CrostiniResult expected_result,
-                              CrostiniResult result) {
-    EXPECT_EQ(expected_result, result);
-    std::move(closure).Run();
-  }
-
   base::ScopedFD TestFileDescriptor() {
     base::File file(base::FilePath("/dev/null"),
                     base::File::FLAG_OPEN | base::File::FLAG_WRITE);
@@ -141,32 +94,32 @@
   }
 
   void AttachUsbDeviceCallback(base::OnceClosure closure,
-                               CrostiniResult expected_result,
-                               uint8_t guest_port,
-                               CrostiniResult result) {
+                               bool expected_success,
+                               bool success,
+                               uint8_t guest_port) {
     EXPECT_TRUE(fake_concierge_client_->attach_usb_device_called());
-    EXPECT_EQ(expected_result, result);
+    EXPECT_EQ(expected_success, success);
     std::move(closure).Run();
   }
 
   void DetachUsbDeviceCallback(base::OnceClosure closure,
                                bool expected_called,
-                               CrostiniResult expected_result,
-                               CrostiniResult result) {
+                               bool expected_success,
+                               bool success) {
     EXPECT_EQ(fake_concierge_client_->detach_usb_device_called(),
               expected_called);
-    EXPECT_EQ(expected_result, result);
+    EXPECT_EQ(expected_success, success);
     std::move(closure).Run();
   }
 
   void ListUsbDevicesCallback(
       base::OnceClosure closure,
-      CrostiniResult expected_result,
+      bool expected_success,
       size_t expected_size,
-      CrostiniResult result,
+      bool success,
       std::vector<std::pair<std::string, uint8_t>> devices) {
     EXPECT_TRUE(fake_concierge_client_->list_usb_devices_called());
-    EXPECT_EQ(expected_result, result);
+    EXPECT_EQ(expected_success, success);
     EXPECT_EQ(devices.size(), expected_size);
     std::move(closure).Run();
   }
@@ -243,7 +196,6 @@
       run_loop_;  // run_loop_ must be created on the UI thread.
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<CrostiniManager> crostini_manager_;
-  bool create_container_fails_callback_called_ = false;
   device::FakeUsbDeviceManager fake_usb_manager_;
 
  private:
@@ -258,7 +210,7 @@
 
   crostini_manager()->CreateDiskImage(
       disk_path, vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT, kDiskSizeBytes,
-      base::BindOnce(&CrostiniManagerTest::CreateDiskImageClientErrorCallback,
+      base::BindOnce(&CrostiniManagerTest::CreateDiskImageFailureCallback,
                      base::Unretained(this), run_loop()->QuitClosure()));
   run_loop()->Run();
 }
@@ -270,7 +222,7 @@
       disk_path,
       vm_tools::concierge::StorageLocation_INT_MIN_SENTINEL_DO_NOT_USE_,
       kDiskSizeBytes,
-      base::BindOnce(&CrostiniManagerTest::CreateDiskImageClientErrorCallback,
+      base::BindOnce(&CrostiniManagerTest::CreateDiskImageFailureCallback,
                      base::Unretained(this), run_loop()->QuitClosure()));
   run_loop()->Run();
 }
@@ -289,20 +241,18 @@
   const base::FilePath& disk_path = base::FilePath("");
 
   crostini_manager()->DestroyDiskImage(
-      disk_path,
-      base::BindOnce(&CrostiniManagerTest::DestroyDiskImageClientErrorCallback,
-                     base::Unretained(this), run_loop()->QuitClosure()));
+      disk_path, base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
+  EXPECT_FALSE(fake_concierge_client_->destroy_disk_image_called());
 }
 
 TEST_F(CrostiniManagerTest, DestroyDiskImageSuccess) {
   const base::FilePath& disk_path = base::FilePath(kVmName);
 
   crostini_manager()->DestroyDiskImage(
-      disk_path,
-      base::BindOnce(&CrostiniManagerTest::DestroyDiskImageSuccessCallback,
-                     base::Unretained(this), run_loop()->QuitClosure()));
+      disk_path, base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
   run_loop()->Run();
+  EXPECT_TRUE(fake_concierge_client_->destroy_disk_image_called());
 }
 
 TEST_F(CrostiniManagerTest, ListVmDisksSuccess) {
@@ -316,10 +266,9 @@
   const base::FilePath& disk_path = base::FilePath(kVmName);
 
   crostini_manager()->StartTerminaVm(
-      "", disk_path,
-      base::BindOnce(&CrostiniManagerTest::StartTerminaVmClientErrorCallback,
-                     base::Unretained(this), run_loop()->QuitClosure()));
+      "", disk_path, base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
+  EXPECT_FALSE(fake_concierge_client_->start_termina_vm_called());
 }
 
 TEST_F(CrostiniManagerTest, StartTerminaVmDiskPathError) {
@@ -327,9 +276,9 @@
 
   crostini_manager()->StartTerminaVm(
       kVmName, disk_path,
-      base::BindOnce(&CrostiniManagerTest::StartTerminaVmClientErrorCallback,
-                     base::Unretained(this), run_loop()->QuitClosure()));
+      base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
+  EXPECT_FALSE(fake_concierge_client_->start_termina_vm_called());
 }
 
 TEST_F(CrostiniManagerTest, StartTerminaVmSuccess) {
@@ -337,9 +286,9 @@
 
   crostini_manager()->StartTerminaVm(
       kVmName, disk_path,
-      base::BindOnce(&CrostiniManagerTest::StartTerminaVmSuccessCallback,
-                     base::Unretained(this), run_loop()->QuitClosure()));
+      base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
   run_loop()->Run();
+  EXPECT_TRUE(fake_concierge_client_->start_termina_vm_called());
 }
 
 TEST_F(CrostiniManagerTest, OnStartTremplinRecordsRunningVm) {
@@ -349,29 +298,28 @@
   // Start the Vm.
   crostini_manager()->StartTerminaVm(
       kVmName, disk_path,
-      base::BindOnce(
-          &CrostiniManagerTest::OnStartTremplinRecordsRunningVmCallback,
-          base::Unretained(this), run_loop()->QuitClosure()));
+      base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
 
-  // Check that the Vm start is not recorded (without tremplin start).
+  // Check that the Vm start is not recorded until tremplin starts.
   EXPECT_FALSE(crostini_manager()->IsVmRunning(kVmName));
-
   run_loop()->Run();
+  EXPECT_TRUE(crostini_manager()->IsVmRunning(kVmName));
 }
 
 TEST_F(CrostiniManagerTest, StopVmNameError) {
   crostini_manager()->StopVm(
-      "", base::BindOnce(&CrostiniManagerTest::StopVmClientErrorCallback,
-                         base::Unretained(this), run_loop()->QuitClosure()));
+      "", base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
+                         CrostiniResult::CLIENT_ERROR));
   run_loop()->Run();
+  EXPECT_FALSE(fake_concierge_client_->stop_vm_called());
 }
 
 TEST_F(CrostiniManagerTest, StopVmSuccess) {
   crostini_manager()->StopVm(
-      kVmName,
-      base::BindOnce(&CrostiniManagerTest::StopVmSuccessCallback,
-                     base::Unretained(this), run_loop()->QuitClosure()));
+      kVmName, base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
+                              CrostiniResult::SUCCESS));
   run_loop()->Run();
+  EXPECT_TRUE(fake_concierge_client_->stop_vm_called());
 }
 
 TEST_F(CrostiniManagerTest, InstallLinuxPackageSignalNotConnectedError) {
@@ -379,8 +327,7 @@
       false);
   crostini_manager()->InstallLinuxPackage(
       kVmName, kContainerName, "/tmp/package.deb",
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED));
   run_loop()->Run();
 }
@@ -391,8 +338,7 @@
   fake_cicerone_client_->set_install_linux_package_response(response);
   crostini_manager()->InstallLinuxPackage(
       kVmName, kContainerName, "/tmp/package.deb",
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
   run_loop()->Run();
 }
@@ -405,8 +351,7 @@
   fake_cicerone_client_->set_install_linux_package_response(response);
   crostini_manager()->InstallLinuxPackage(
       kVmName, kContainerName, "/tmp/package.deb",
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED));
   run_loop()->Run();
 }
@@ -418,8 +363,7 @@
   fake_cicerone_client_->set_install_linux_package_response(response);
   crostini_manager()->InstallLinuxPackage(
       kVmName, kContainerName, "/tmp/package.deb",
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::BLOCKING_OPERATION_ALREADY_ACTIVE));
   run_loop()->Run();
 }
@@ -428,8 +372,7 @@
   fake_cicerone_client_->set_uninstall_package_progress_signal_connected(false);
   crostini_manager()->UninstallPackageOwningFile(
       kVmName, kContainerName, "emacs",
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::UNINSTALL_PACKAGE_FAILED));
   run_loop()->Run();
 }
@@ -441,8 +384,7 @@
   fake_cicerone_client_->set_uninstall_package_owning_file_response(response);
   crostini_manager()->UninstallPackageOwningFile(
       kVmName, kContainerName, "emacs",
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
   run_loop()->Run();
 }
@@ -455,8 +397,7 @@
   fake_cicerone_client_->set_uninstall_package_owning_file_response(response);
   crostini_manager()->UninstallPackageOwningFile(
       kVmName, kContainerName, "emacs",
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::UNINSTALL_PACKAGE_FAILED));
   run_loop()->Run();
 }
@@ -468,8 +409,7 @@
   fake_cicerone_client_->set_uninstall_package_owning_file_response(response);
   crostini_manager()->UninstallPackageOwningFile(
       kVmName, kContainerName, "emacs",
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::BLOCKING_OPERATION_ALREADY_ACTIVE));
   run_loop()->Run();
 }
@@ -486,7 +426,7 @@
       kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(),
-                     CrostiniResult::SUCCESS));
+                     /*expected_success=*/true));
   run_loop()->Run();
   fake_usb_manager_.RemoveDevice(guid);
 }
@@ -503,7 +443,7 @@
       kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(),
-                     CrostiniResult::ATTACH_USB_FAILED));
+                     /*expected_success=*/false));
   run_loop()->Run();
   fake_usb_manager_.RemoveDevice(guid);
 }
@@ -525,13 +465,13 @@
       kVmName, fake_usb.Clone(), kUsbPort,
       base::BindOnce(&CrostiniManagerTest::DetachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(), true,
-                     CrostiniResult::SUCCESS));
+                     /*expected_success=*/true));
 
   crostini_manager()->AttachUsbDevice(
       kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), std::move(detach_usb),
-                     CrostiniResult::SUCCESS));
+                     /*expected_success=*/true));
   run_loop()->Run();
   fake_usb_manager_.RemoveDevice(guid);
 }
@@ -553,13 +493,13 @@
       kVmName, fake_usb.Clone(), kUsbPort,
       base::BindOnce(&CrostiniManagerTest::DetachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(), true,
-                     CrostiniResult::DETACH_USB_FAILED));
+                     /*expected_success=*/false));
 
   crostini_manager()->AttachUsbDevice(
       kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), std::move(detach_usb),
-                     CrostiniResult::SUCCESS));
+                     /*expected_success=*/true));
   run_loop()->Run();
   fake_usb_manager_.RemoveDevice(guid);
 }
@@ -572,7 +512,7 @@
   crostini_manager()->ListUsbDevices(
       kVmName, base::BindOnce(&CrostiniManagerTest::ListUsbDevicesCallback,
                               base::Unretained(this), run_loop()->QuitClosure(),
-                              CrostiniResult::LIST_USB_FAILED, 0));
+                              /*expected_success=*/false, 0));
   run_loop()->Run();
 }
 
@@ -584,7 +524,7 @@
   crostini_manager()->ListUsbDevices(
       kVmName, base::BindOnce(&CrostiniManagerTest::ListUsbDevicesCallback,
                               base::Unretained(this), run_loop()->QuitClosure(),
-                              CrostiniResult::SUCCESS, 0));
+                              /*expected_success=*/true, 0));
   run_loop()->Run();
 }
 
@@ -601,7 +541,7 @@
       kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(),
-                     CrostiniResult::SUCCESS));
+                     /*expected_success=*/true));
   run_loop()->Run();
 
   vm_tools::concierge::ListUsbDeviceResponse response;
@@ -614,7 +554,7 @@
   crostini_manager()->ListUsbDevices(
       kVmName, base::BindOnce(&CrostiniManagerTest::ListUsbDevicesCallback,
                               base::Unretained(this), run_loop2.QuitClosure(),
-                              CrostiniResult::SUCCESS, 1));
+                              /*expected_success=*/true, 1));
   run_loop2.Run();
 
   fake_usb_manager_.RemoveDevice(guid);
@@ -642,13 +582,13 @@
     }
   }
 
-  void OnConciergeStarted(CrostiniResult result) override {
+  void OnConciergeStarted(bool success) override {
     if (abort_on_concierge_started_) {
       Abort();
     }
   }
 
-  void OnDiskImageCreated(CrostiniResult result,
+  void OnDiskImageCreated(bool success,
                           vm_tools::concierge::DiskImageStatus status,
                           int64_t disk_size_available) override {
     if (abort_on_disk_image_created_) {
@@ -656,7 +596,7 @@
     }
   }
 
-  void OnVmStarted(CrostiniResult result) override {
+  void OnVmStarted(bool success) override {
     if (abort_on_vm_started_) {
       Abort();
     }
@@ -676,13 +616,13 @@
     }
   }
 
-  void OnContainerSetup(CrostiniResult result) override {
+  void OnContainerSetup(bool success) override {
     if (abort_on_container_setup_) {
       Abort();
     }
   }
 
-  void OnSshKeysFetched(CrostiniResult result) override {
+  void OnSshKeysFetched(bool success) override {
     if (abort_on_ssh_keys_fetched_) {
       Abort();
     }
@@ -821,12 +761,11 @@
   // Use termina/penguin names to allow fetch ssh keys.
   restart_id_ = crostini_manager()->RestartCrostini(
       kCrostiniDefaultVmName, kCrostiniDefaultContainerName,
-      base::BindOnce(&CrostiniManagerTest::CreateContainerFailsCallback,
-                     base::Unretained(this), run_loop()->QuitClosure()),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
+                     CrostiniResult::UNKNOWN_ERROR),
       this);
   run_loop()->Run();
 
-  EXPECT_TRUE(create_container_fails_callback_called_);
   EXPECT_TRUE(fake_concierge_client_->create_disk_image_called());
   EXPECT_TRUE(fake_concierge_client_->start_termina_vm_called());
   EXPECT_FALSE(fake_concierge_client_->get_container_ssh_keys_called());
@@ -977,9 +916,9 @@
   base::RunLoop run_loop2;
   crostini_manager()->StartTerminaVm(
       kVmName, disk_path,
-      base::BindOnce(&CrostiniManagerTest::StartTerminaVmSuccessCallback,
-                     base::Unretained(this), run_loop2.QuitClosure()));
+      base::BindOnce(&ExpectSuccess, run_loop2.QuitClosure()));
   run_loop2.Run();
+  EXPECT_TRUE(fake_concierge_client_->start_termina_vm_called());
   EXPECT_TRUE(crostini_manager()->IsVmRunning(kVmName));
   EXPECT_FALSE(crostini_manager()->GetContainerInfo(kVmName, kContainerName));
 }
@@ -1079,8 +1018,7 @@
 TEST_F(CrostiniManagerTest, ExportContainerSuccess) {
   crostini_manager()->ExportLxdContainer(
       kVmName, kContainerName, base::FilePath("export_path"),
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
 
   // Send signals, PACK, DOWNLOAD, DONE.
@@ -1108,15 +1046,13 @@
   // 1st call succeeds.
   crostini_manager()->ExportLxdContainer(
       kVmName, kContainerName, base::FilePath("export_path"),
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
 
   // 2nd call fails since 1st call is in progress.
   crostini_manager()->ExportLxdContainer(
       kVmName, kContainerName, base::FilePath("export_path"),
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), base::DoNothing::Once(),
+      base::BindOnce(&ExpectCrostiniResult, base::DoNothing::Once(),
                      CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED));
 
   // Send signal to indicate 1st call is done.
@@ -1134,8 +1070,7 @@
 TEST_F(CrostiniManagerTest, ExportContainerFailFromSignal) {
   crostini_manager()->ExportLxdContainer(
       kVmName, kContainerName, base::FilePath("export_path"),
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED));
 
   // Send signal with FAILED.
@@ -1155,8 +1090,7 @@
   crostini_manager()->ExportLxdContainer(
       kVmName, kContainerName, base::FilePath("export_path"),
       base::BindOnce(
-          &CrostiniManagerTest::CrostiniResultCallback, base::Unretained(this),
-          run_loop()->QuitClosure(),
+          &ExpectCrostiniResult, run_loop()->QuitClosure(),
           CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED));
   crostini_manager()->StopVm(kVmName, base::DoNothing());
   run_loop()->Run();
@@ -1165,8 +1099,7 @@
 TEST_F(CrostiniManagerTest, ImortContainerSuccess) {
   crostini_manager()->ImportLxdContainer(
       kVmName, kContainerName, base::FilePath("import_path"),
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
 
   // Send signals, UPLOAD, UNPACK, DONE.
@@ -1195,15 +1128,13 @@
   // 1st call succeeds.
   crostini_manager()->ImportLxdContainer(
       kVmName, kContainerName, base::FilePath("import_path"),
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
 
   // 2nd call fails since 1st call is in progress.
   crostini_manager()->ImportLxdContainer(
       kVmName, kContainerName, base::FilePath("import_path"),
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), base::DoNothing::Once(),
+      base::BindOnce(ExpectCrostiniResult, base::DoNothing::Once(),
                      CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED));
 
   // Send signal to indicate 1st call is done.
@@ -1222,8 +1153,7 @@
   crostini_manager()->ImportLxdContainer(
       kVmName, kContainerName, base::FilePath("import_path"),
       base::BindOnce(
-          &CrostiniManagerTest::CrostiniResultCallback, base::Unretained(this),
-          run_loop()->QuitClosure(),
+          &ExpectCrostiniResult, run_loop()->QuitClosure(),
           CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_ARCHITECTURE));
 
   // Send signal with FAILED_ARCHITECTURE.
@@ -1244,8 +1174,7 @@
 TEST_F(CrostiniManagerTest, ImportContainerFailFromSignal) {
   crostini_manager()->ImportLxdContainer(
       kVmName, kContainerName, base::FilePath("import_path"),
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED));
 
   // Send signal with FAILED.
@@ -1265,8 +1194,7 @@
   crostini_manager()->ImportLxdContainer(
       kVmName, kContainerName, base::FilePath("import_path"),
       base::BindOnce(
-          &CrostiniManagerTest::CrostiniResultCallback, base::Unretained(this),
-          run_loop()->QuitClosure(),
+          &ExpectCrostiniResult, run_loop()->QuitClosure(),
           CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED));
   crostini_manager()->StopVm(kVmName, base::DoNothing());
   run_loop()->Run();
@@ -1363,8 +1291,7 @@
       false);
   crostini_manager()->InstallLinuxPackageFromApt(
       kVmName, kContainerName, kPackageID,
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED));
   run_loop()->Run();
 }
@@ -1375,8 +1302,7 @@
   fake_cicerone_client_->set_install_linux_package_response(response);
   crostini_manager()->InstallLinuxPackageFromApt(
       kVmName, kContainerName, kPackageID,
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
   run_loop()->Run();
 }
@@ -1389,8 +1315,7 @@
   fake_cicerone_client_->set_install_linux_package_response(response);
   crostini_manager()->InstallLinuxPackageFromApt(
       kVmName, kContainerName, kPackageID,
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED));
   run_loop()->Run();
 }
@@ -1402,8 +1327,7 @@
   fake_cicerone_client_->set_install_linux_package_response(response);
   crostini_manager()->InstallLinuxPackageFromApt(
       kVmName, kContainerName, kPackageID,
-      base::BindOnce(&CrostiniManagerTest::CrostiniResultCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
+      base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::BLOCKING_OPERATION_ALREADY_ACTIVE));
   run_loop()->Run();
 }
diff --git a/chrome/browser/chromeos/crostini/crostini_pref_names.cc b/chrome/browser/chromeos/crostini/crostini_pref_names.cc
index 950bca1..938e2c72 100644
--- a/chrome/browser/chromeos/crostini/crostini_pref_names.cc
+++ b/chrome/browser/chromeos/crostini/crostini_pref_names.cc
@@ -19,10 +19,6 @@
 const char kCrostiniEnabled[] = "crostini.enabled";
 const char kCrostiniMimeTypes[] = "crostini.mime_types";
 const char kCrostiniRegistry[] = "crostini.registry";
-// List of filesystem paths that are shared with the crostini container.
-// TODO(crbug.com/946273): Remove crostini.shared_paths and migration code after
-// M77.
-const char kCrostiniSharedPaths[] = "crostini.shared_paths";
 // List of USB devices with their system guid, a name/description and their
 // enabled state for use with Crostini.
 const char kCrostiniSharedUsbDevices[] = "crostini.shared_usb_devices";
@@ -49,17 +45,11 @@
 // The value of the last sample of the disk space used by Crostini.
 const char kCrostiniLastDiskSize[] = "crostini.last_disk_size";
 
-// Dictionary of filesystem paths mapped to the list of VMs that the paths are
-// shared with.
-const char kGuestOSPathsSharedToVms[] = "guest_os.paths_shared_to_vms";
-
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterBooleanPref(kCrostiniEnabled, false);
   registry->RegisterDictionaryPref(kCrostiniMimeTypes);
   registry->RegisterDictionaryPref(kCrostiniRegistry);
-  registry->RegisterListPref(kCrostiniSharedPaths);
   registry->RegisterListPref(kCrostiniSharedUsbDevices);
-  registry->RegisterDictionaryPref(kGuestOSPathsSharedToVms);
 
   // Set a default value for crostini.containers to ensure that we track the
   // default container even if its creation predates this preference. This
diff --git a/chrome/browser/chromeos/crostini/crostini_pref_names.h b/chrome/browser/chromeos/crostini/crostini_pref_names.h
index ae2a24d..8b3f6e4e0 100644
--- a/chrome/browser/chromeos/crostini/crostini_pref_names.h
+++ b/chrome/browser/chromeos/crostini/crostini_pref_names.h
@@ -13,7 +13,6 @@
 extern const char kCrostiniEnabled[];
 extern const char kCrostiniMimeTypes[];
 extern const char kCrostiniRegistry[];
-extern const char kCrostiniSharedPaths[];
 extern const char kCrostiniSharedUsbDevices[];
 extern const char kCrostiniContainers[];
 extern const char kVmKey[];
@@ -27,8 +26,6 @@
 extern const char kCrostiniLastLaunchTimeWindowStart[];
 extern const char kCrostiniLastDiskSize[];
 
-extern const char kGuestOSPathsSharedToVms[];
-
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
 }  // namespace prefs
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.cc b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
index 6c5ad4a..e017ce1 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.cc
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
@@ -944,9 +944,9 @@
 void CrostiniRegistryService::OnContainerAppIcon(
     const std::string& app_id,
     ui::ScaleFactor scale_factor,
-    CrostiniResult result,
+    bool success,
     const std::vector<Icon>& icons) {
-  if (result != CrostiniResult::SUCCESS) {
+  if (!success) {
     // Add this to the list of retryable icon requests so we redo this when
     // we get feedback from the container that it's available.
     retry_icon_requests_[app_id] |= (1 << scale_factor);
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.h b/chrome/browser/chromeos/crostini/crostini_registry_service.h
index f933a50..4f4c2e8 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.h
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.h
@@ -206,7 +206,7 @@
   // Callback for when we request an icon from the container.
   void OnContainerAppIcon(const std::string& app_id,
                           ui::ScaleFactor scale_factor,
-                          CrostiniResult result,
+                          bool success,
                           const std::vector<Icon>& icons);
   // Callback for our internal call for saving out icon data.
   void OnIconInstalled(const std::string& app_id,
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.cc b/chrome/browser/chromeos/crostini/crostini_remover.cc
index 05b0ebf1..9877dcc8 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.cc
+++ b/chrome/browser/chromeos/crostini/crostini_remover.cc
@@ -78,10 +78,10 @@
       base::BindOnce(&CrostiniRemover::DestroyDiskImageFinished, this));
 }
 
-void CrostiniRemover::DestroyDiskImageFinished(CrostiniResult result) {
+void CrostiniRemover::DestroyDiskImageFinished(bool success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (result != CrostiniResult::SUCCESS) {
-    std::move(callback_).Run(result);
+  if (!success) {
+    std::move(callback_).Run(CrostiniResult::DESTROY_DISK_IMAGE_FAILED);
     return;
   }
   // Only set kCrostiniEnabled to false once cleanup is completely finished.
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.h b/chrome/browser/chromeos/crostini/crostini_remover.h
index 9febb63..e8630e4 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.h
+++ b/chrome/browser/chromeos/crostini/crostini_remover.h
@@ -25,7 +25,7 @@
   void OnComponentLoaded(crostini::CrostiniResult result);
   void OnConciergeStarted(bool is_successful);
   void StopVmFinished(crostini::CrostiniResult result);
-  void DestroyDiskImageFinished(crostini::CrostiniResult result);
+  void DestroyDiskImageFinished(bool success);
   void StopConciergeFinished(bool is_successful);
 
   Profile* profile_;
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path_factory.cc b/chrome/browser/chromeos/crostini/crostini_share_path_factory.cc
deleted file mode 100644
index ad36a2267..0000000
--- a/chrome/browser/chromeos/crostini/crostini_share_path_factory.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/crostini/crostini_share_path_factory.h"
-
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-
-namespace crostini {
-
-// static
-CrostiniSharePath* CrostiniSharePathFactory::GetForProfile(Profile* profile) {
-  return static_cast<CrostiniSharePath*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-// static
-CrostiniSharePathFactory* CrostiniSharePathFactory::GetInstance() {
-  static base::NoDestructor<CrostiniSharePathFactory> factory;
-  return factory.get();
-}
-
-CrostiniSharePathFactory::CrostiniSharePathFactory()
-    : BrowserContextKeyedServiceFactory(
-          "CrostiniSharePath",
-          BrowserContextDependencyManager::GetInstance()) {}
-
-CrostiniSharePathFactory::~CrostiniSharePathFactory() = default;
-
-KeyedService* CrostiniSharePathFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  Profile* profile = Profile::FromBrowserContext(context);
-  return new CrostiniSharePath(profile);
-}
-
-}  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path_factory.h b/chrome/browser/chromeos/crostini/crostini_share_path_factory.h
deleted file mode 100644
index ed8dc01..0000000
--- a/chrome/browser/chromeos/crostini/crostini_share_path_factory.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_FACTORY_H_
-#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/no_destructor.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class Profile;
-
-namespace crostini {
-
-class CrostiniSharePath;
-
-class CrostiniSharePathFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static CrostiniSharePath* GetForProfile(Profile* profile);
-  static CrostiniSharePathFactory* GetInstance();
-
- private:
-  friend class base::NoDestructor<CrostiniSharePathFactory>;
-
-  CrostiniSharePathFactory();
-  ~CrostiniSharePathFactory() override;
-
-  // BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(CrostiniSharePathFactory);
-};
-
-}  // namespace crostini
-
-#endif  // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_FACTORY_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index f8c675e..d2d4156 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -93,9 +93,8 @@
   std::move(callback).Run();
 }
 
-void OnContainerApplicationLaunched(const std::string& app_id,
-                                    crostini::CrostiniResult result) {
-  if (result != crostini::CrostiniResult::SUCCESS)
+void OnContainerApplicationLaunched(const std::string& app_id, bool success) {
+  if (!success)
     OnLaunchFailed(app_id);
 }
 
@@ -280,6 +279,10 @@
       kCrostiniDefaultVmName);
 }
 
+bool IsCrostiniAnsibleInfrastructureEnabled() {
+  return base::FeatureList::IsEnabled(features::kCrostiniAnsibleInfrastructure);
+}
+
 void LaunchCrostiniApp(Profile* profile,
                        const std::string& app_id,
                        int64_t display_id) {
diff --git a/chrome/browser/chromeos/crostini/crostini_util.h b/chrome/browser/chromeos/crostini/crostini_util.h
index fbfe5da..7da03c3 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.h
+++ b/chrome/browser/chromeos/crostini/crostini_util.h
@@ -57,6 +57,10 @@
 // Returns whether the default Crostini VM is running for the user.
 bool IsCrostiniRunning(Profile* profile);
 
+// Returns whether infrastructure for applying Ansible playbook to default
+// Crostini container is enabled.
+bool IsCrostiniAnsibleInfrastructureEnabled();
+
 // Launches the Crostini app with ID of |app_id| on the display with ID of
 // |display_id|. |app_id| should be a valid Crostini app list id.
 void LaunchCrostiniApp(Profile* profile,
diff --git a/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.cc b/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.cc
new file mode 100644
index 0000000..21437613
--- /dev/null
+++ b/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.cc
@@ -0,0 +1,243 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/dbus/cryptohome/key.pb.h"
+#include "chromeos/dbus/cryptohome/rpc.pb.h"
+#include "dbus/message.h"
+#include "net/base/net_errors.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "third_party/cros_system_api/dbus/cryptohome/dbus-constants.h"
+
+namespace chromeos {
+
+namespace {
+
+// Converts the cryptohome challenge algorithm enum into the TLS 1.3
+// SignatureScheme.
+bool ChallengeSignatureAlgorithmToSslAlgorithm(
+    cryptohome::ChallengeSignatureAlgorithm challenge_algorithm,
+    uint16_t* ssl_algorithm) {
+  switch (challenge_algorithm) {
+    case cryptohome::CHALLENGE_RSASSA_PKCS1_V1_5_SHA1:
+      *ssl_algorithm = SSL_SIGN_RSA_PKCS1_SHA1;
+      return true;
+    case cryptohome::CHALLENGE_RSASSA_PKCS1_V1_5_SHA256:
+      *ssl_algorithm = SSL_SIGN_RSA_PKCS1_SHA256;
+      return true;
+    case cryptohome::CHALLENGE_RSASSA_PKCS1_V1_5_SHA384:
+      *ssl_algorithm = SSL_SIGN_RSA_PKCS1_SHA384;
+      return true;
+    case cryptohome::CHALLENGE_RSASSA_PKCS1_V1_5_SHA512:
+      *ssl_algorithm = SSL_SIGN_RSA_PKCS1_SHA512;
+      return true;
+    default:
+      LOG(ERROR) << "Unknown cryptohome key challenge algorithm: "
+                 << challenge_algorithm;
+      return false;
+  }
+}
+
+// Builds the digest of the given input, using the hashing algorithm that is
+// required for the given signature algorithm.
+bool BuildDigestToSign(const std::string& input,
+                       uint16_t ssl_algorithm,
+                       std::vector<uint8_t>* digest) {
+  DCHECK(!input.empty());
+  const EVP_MD* md = SSL_get_signature_algorithm_digest(ssl_algorithm);
+  if (!md)
+    return false;
+  digest->resize(EVP_MAX_MD_SIZE);
+  unsigned digest_len = 0;
+  if (!EVP_Digest(input.data(), input.size(), digest->data(), &digest_len, md,
+                  nullptr)) {
+    digest->clear();
+    return false;
+  }
+  digest->resize(digest_len);
+  return true;
+}
+
+// Completes the "ChallengeKey" D-Bus call of the |CHALLENGE_TYPE_SIGNATURE|
+// type with the given signature, or with an error if the signature wasn't
+// successfully generated.
+void CompleteSignatureKeyChallenge(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender,
+    net::Error error,
+    const std::vector<uint8_t>& signature) {
+  if (error != net::OK || signature.empty()) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_FAILED, "Failed to generate the signature"));
+    return;
+  }
+
+  cryptohome::KeyChallengeResponse challenge_response;
+  challenge_response.mutable_signature_response_data()->set_signature(
+      signature.data(), signature.size());
+
+  std::unique_ptr<dbus::Response> response =
+      dbus::Response::FromMethodCall(method_call);
+  dbus::MessageWriter writer(response.get());
+  writer.AppendProtoAsArrayOfBytes(challenge_response);
+
+  response_sender.Run(std::move(response));
+}
+
+// Handles the "ChallengeKey" D-Bus call for the request of the
+// |CHALLENGE_TYPE_SIGNATURE| type.
+void HandleSignatureKeyChallenge(
+    dbus::MethodCall* method_call,
+    const cryptohome::SignatureKeyChallengeRequestData& challenge_request_data,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  if (challenge_request_data.data_to_sign().empty()) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_INVALID_ARGS, "Missing data to sign"));
+    return;
+  }
+  if (challenge_request_data.public_key_spki_der().empty()) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_INVALID_ARGS, "Missing public key"));
+    return;
+  }
+  if (!challenge_request_data.has_signature_algorithm()) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_INVALID_ARGS, "Missing signature algorithm"));
+    return;
+  }
+
+  uint16_t ssl_algorithm = 0;
+  if (!ChallengeSignatureAlgorithmToSslAlgorithm(
+          challenge_request_data.signature_algorithm(), &ssl_algorithm)) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_FAILED, "Unknown signature algorithm"));
+    return;
+  }
+
+  // Handle the challenge request by delivering it to one of the sign-in
+  // certificateProvider subscribers (e.g., smart card middleware extensions).
+  // The sign-in profile is used since it's where the needed extensions are
+  // installed (e.g., for the smart card based login they are force-installed
+  // via the DeviceLoginScreenExtensions admin policy).
+  Profile* signin_profile = ProfileHelper::GetSigninProfile();
+  CertificateProviderService* certificate_provider_service =
+      CertificateProviderServiceFactory::GetForBrowserContext(signin_profile);
+  if (!certificate_provider_service) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_FAILED,
+        "Missing certificate provider service"));
+    return;
+  }
+
+  std::vector<uint16_t> supported_ssl_algorithms;
+  if (!certificate_provider_service->GetSupportedAlgorithmsBySpki(
+          challenge_request_data.public_key_spki_der(),
+          &supported_ssl_algorithms)) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_FAILED, "Key is unavailable"));
+    return;
+  }
+  if (!base::Contains(supported_ssl_algorithms, ssl_algorithm)) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_FAILED, "Unsupported algorithm"));
+    return;
+  }
+
+  std::vector<uint8_t> digest;
+  if (!BuildDigestToSign(challenge_request_data.data_to_sign(), ssl_algorithm,
+                         &digest)) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_FAILED, "Failed to build digest"));
+    return;
+  }
+
+  certificate_provider_service->RequestSignatureBySpki(
+      challenge_request_data.public_key_spki_der(), ssl_algorithm, digest,
+      base::BindOnce(&CompleteSignatureKeyChallenge,
+                     base::Unretained(method_call), response_sender));
+}
+
+}  // namespace
+
+CryptohomeKeyDelegateServiceProvider::CryptohomeKeyDelegateServiceProvider() =
+    default;
+
+CryptohomeKeyDelegateServiceProvider::~CryptohomeKeyDelegateServiceProvider() =
+    default;
+
+void CryptohomeKeyDelegateServiceProvider::Start(
+    scoped_refptr<dbus::ExportedObject> exported_object) {
+  exported_object->ExportMethod(
+      cryptohome::kCryptohomeKeyDelegateInterface,
+      cryptohome::kCryptohomeKeyDelegateChallengeKey,
+      base::BindRepeating(
+          &CryptohomeKeyDelegateServiceProvider::HandleChallengeKey,
+          weak_ptr_factory_.GetWeakPtr()),
+      base::BindRepeating([](const std::string& interface_name,
+                             const std::string& method_name, bool success) {
+        LOG_IF(ERROR, !success)
+            << "Failed to export " << interface_name << "." << method_name;
+      }));
+}
+
+void CryptohomeKeyDelegateServiceProvider::HandleChallengeKey(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+
+  cryptohome::AccountIdentifier account_identifier;
+  if (!reader.PopArrayOfBytesAsProto(&account_identifier)) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_INVALID_ARGS,
+        "Unable to parse AccountIdentifier from request"));
+    return;
+  }
+  // For now |account_identifier| is not used.
+
+  cryptohome::KeyChallengeRequest request;
+  if (!reader.PopArrayOfBytesAsProto(&request)) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_INVALID_ARGS,
+        "Unable to parse KeyChallengeRequest from request"));
+    return;
+  }
+
+  if (!request.has_challenge_type()) {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, DBUS_ERROR_INVALID_ARGS, "Missing challenge type"));
+    return;
+  }
+
+  if (request.challenge_type() ==
+      cryptohome::KeyChallengeRequest::CHALLENGE_TYPE_SIGNATURE) {
+    if (!request.has_signature_request_data()) {
+      response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+          method_call, DBUS_ERROR_INVALID_ARGS,
+          "Missing signature request data"));
+      return;
+    }
+    HandleSignatureKeyChallenge(method_call, request.signature_request_data(),
+                                response_sender);
+    return;
+  }
+
+  response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+      method_call, DBUS_ERROR_FAILED, "Unknown challenge type"));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.h b/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.h
new file mode 100644
index 0000000..387c7ca
--- /dev/null
+++ b/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_DBUS_CRYPTOHOME_KEY_DELEGATE_SERVICE_PROVIDER_H_
+#define CHROME_BROWSER_CHROMEOS_DBUS_CRYPTOHOME_KEY_DELEGATE_SERVICE_PROVIDER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/services/cros_dbus_service.h"
+#include "dbus/exported_object.h"
+
+namespace dbus {
+class MethodCall;
+}
+
+namespace chromeos {
+
+// Provider for the org.chromium.CryptohomeKeyDelegateInterface service
+// implementation.
+//
+// This service is called by the cryptohomed daemon for operations related to
+// user protection keys. See the interface definition in the Chrome OS repo in
+// src/platform2/cryptohome/dbus_bindings/
+//   org.chromium.CryptohomeKeyDelegateInterface.xml .
+class CryptohomeKeyDelegateServiceProvider final
+    : public CrosDBusService::ServiceProviderInterface {
+ public:
+  CryptohomeKeyDelegateServiceProvider();
+  ~CryptohomeKeyDelegateServiceProvider() override;
+
+  // CrosDBusService::ServiceProviderInterface overrides:
+  void Start(scoped_refptr<dbus::ExportedObject> exported_object) override;
+
+ private:
+  // Implements the "ChallengeKey" D-Bus method.
+  void HandleChallengeKey(dbus::MethodCall* method_call,
+                          dbus::ExportedObject::ResponseSender response_sender);
+
+  // Must be the last member.
+  base::WeakPtrFactory<CryptohomeKeyDelegateServiceProvider> weak_ptr_factory_{
+      this};
+
+  DISALLOW_COPY_AND_ASSIGN(CryptohomeKeyDelegateServiceProvider);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_DBUS_CRYPTOHOME_KEY_DELEGATE_SERVICE_PROVIDER_H_
diff --git a/chrome/browser/chromeos/display/output_protection_delegate.cc b/chrome/browser/chromeos/display/output_protection_delegate.cc
index 379e0d3d..52df6c9 100644
--- a/chrome/browser/chromeos/display/output_protection_delegate.cc
+++ b/chrome/browser/chromeos/display/output_protection_delegate.cc
@@ -6,49 +6,31 @@
 
 #include "base/bind_helpers.h"
 #include "chrome/browser/chromeos/display/output_protection_controller_ash.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
 
 namespace chromeos {
 
-namespace {
-
-bool GetCurrentDisplayId(content::RenderFrameHost* rfh, int64_t* display_id) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(rfh);
-  DCHECK(display_id);
-
-  display::Screen* screen = display::Screen::GetScreen();
-  if (!screen)
-    return false;
-  display::Display display =
-      screen->GetDisplayNearestView(rfh->GetNativeView());
-  *display_id = display.id();
-  DCHECK_NE(*display_id, display::kInvalidDisplayId);
-
-  return true;
-}
-
-}  // namespace
-
 OutputProtectionDelegate::Controller::Controller() = default;
 OutputProtectionDelegate::Controller::~Controller() = default;
 
-OutputProtectionDelegate::OutputProtectionDelegate(int render_process_id,
-                                                   int render_frame_id)
-    : render_process_id_(render_process_id), render_frame_id_(render_frame_id) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+OutputProtectionDelegate::OutputProtectionDelegate(aura::Window* window)
+    : window_(window),
+      display_id_(
+          display::Screen::GetScreen()->GetDisplayNearestWindow(window).id()) {
+  DCHECK(window_);
+
+  window_->AddObserver(this);
   display::Screen::GetScreen()->AddObserver(this);
 }
 
 OutputProtectionDelegate::~OutputProtectionDelegate() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!window_)
+    return;
+
   display::Screen::GetScreen()->RemoveObserver(this);
-  if (window_)
-    window_->RemoveObserver(this);
+  window_->RemoveObserver(this);
 }
 
 void OutputProtectionDelegate::OnDisplayMetricsChanged(
@@ -75,14 +57,13 @@
 
 void OutputProtectionDelegate::OnWindowDestroying(aura::Window* window) {
   DCHECK_EQ(window, window_);
+  display::Screen::GetScreen()->RemoveObserver(this);
   window_->RemoveObserver(this);
   window_ = nullptr;
 }
 
 void OutputProtectionDelegate::QueryStatus(
     Controller::QueryStatusCallback callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
   if (!InitializeControllerIfNecessary()) {
     std::move(callback).Run(/*success=*/false,
                             display::DISPLAY_CONNECTION_TYPE_NONE,
@@ -96,8 +77,6 @@
 void OutputProtectionDelegate::SetProtection(
     uint32_t protection_mask,
     Controller::SetProtectionCallback callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
   if (!InitializeControllerIfNecessary()) {
     std::move(callback).Run(/*success=*/false);
     return;
@@ -108,16 +87,9 @@
 }
 
 void OutputProtectionDelegate::OnWindowMayHaveMovedToAnotherDisplay() {
-  content::RenderFrameHost* rfh =
-      content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
-  if (!rfh) {
-    DLOG(WARNING) << "RenderFrameHost is not alive.";
-    return;
-  }
-
-  int64_t new_display_id = display::kInvalidDisplayId;
-  if (!GetCurrentDisplayId(rfh, &new_display_id))
-    return;
+  DCHECK(window_);
+  int64_t new_display_id =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window_).id();
 
   if (display_id_ == new_display_id)
     return;
@@ -134,31 +106,12 @@
 }
 
 bool OutputProtectionDelegate::InitializeControllerIfNecessary() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  if (controller_)
-    return true;
-
-  content::RenderFrameHost* rfh =
-      content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
-  if (!rfh) {
-    DLOG(WARNING) << "RenderFrameHost is not alive.";
-    return false;
-  }
-
-  int64_t display_id = display::kInvalidDisplayId;
-  if (!GetCurrentDisplayId(rfh, &display_id))
+  if (!window_)
     return false;
 
-  aura::Window* window = rfh->GetNativeView();
-  if (!window)
-    return false;
+  if (!controller_)
+    controller_ = std::make_unique<OutputProtectionControllerAsh>();
 
-  controller_ = std::make_unique<OutputProtectionControllerAsh>();
-
-  display_id_ = display_id;
-  window_ = window;
-  window_->AddObserver(this);
   return true;
 }
 
diff --git a/chrome/browser/chromeos/display/output_protection_delegate.h b/chrome/browser/chromeos/display/output_protection_delegate.h
index 281eb226..27a9cfcf 100644
--- a/chrome/browser/chromeos/display/output_protection_delegate.h
+++ b/chrome/browser/chromeos/display/output_protection_delegate.h
@@ -41,7 +41,7 @@
     DISALLOW_COPY_AND_ASSIGN(Controller);
   };
 
-  OutputProtectionDelegate(int render_process_id, int render_frame_id);
+  explicit OutputProtectionDelegate(aura::Window* window);
   ~OutputProtectionDelegate() override;
 
   // display::DisplayObserver:
@@ -62,15 +62,11 @@
 
   bool InitializeControllerIfNecessary();
 
-  // Used to lookup the WebContents associated with the render frame.
-  const int render_process_id_;
-  const int render_frame_id_;
-
   // Native window being observed.
   aura::Window* window_ = nullptr;
 
   // Display ID of the observed window.
-  int64_t display_id_ = display::kInvalidDisplayId;
+  int64_t display_id_;
 
   // Last requested ContentProtectionMethod bitmask, applied when the observed
   // window moves to another display.
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index 3bb2623..ed6c40e 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -833,8 +833,9 @@
     const extensions::Extension* extension,
     base::string16* error) const {
   if (account_type_ == policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION) {
-    // Allow extension if it is an externally hosted component of Chrome.
-    if (extension->location() == extensions::Manifest::EXTERNAL_COMPONENT) {
+    // Allow extension if it is a component of Chrome.
+    if (extension->location() == extensions::Manifest::EXTERNAL_COMPONENT ||
+        extension->location() == extensions::Manifest::COMPONENT) {
       return true;
     }
 
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc
index 33d0029ff..b82ba69 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc
@@ -59,6 +59,13 @@
                                    extensions::Extension::NO_FLAGS);
 }
 
+scoped_refptr<const extensions::Extension> CreateComponentExtension() {
+  base::DictionaryValue values;
+  return CreateExtensionFromValues(std::string(),
+                                   extensions::Manifest::COMPONENT, &values,
+                                   extensions::Extension::NO_FLAGS);
+}
+
 scoped_refptr<const extensions::Extension> CreateHostedApp() {
   base::DictionaryValue values;
   values.Set(extensions::manifest_keys::kApp,
@@ -106,6 +113,12 @@
   EXPECT_EQ(base::string16(), error);
   error.clear();
 
+  extension = CreateComponentExtension();
+  ASSERT_TRUE(extension.get());
+  EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
+  EXPECT_EQ(base::string16(), error);
+  error.clear();
+
   // Verify that if an extension's type has been whitelisted for use in
   // device-local accounts, the extension can be installed.
   extension = CreateHostedApp();
@@ -597,6 +610,12 @@
   EXPECT_EQ(base::string16(), error);
   error.clear();
 
+  extension = CreateComponentExtension();
+  ASSERT_TRUE(extension.get());
+  EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
+  EXPECT_EQ(base::string16(), error);
+  error.clear();
+
   // Verify that an extension whose type has been whitelisted for use in other
   // types of device-local accounts cannot be installed in a single-app kiosk
   // session.
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.cc b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
index 294acd4..2f93e43 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
@@ -588,10 +588,10 @@
   if (intent_helper)
     intent_helper->AddObserver(this);
 
-  auto* crostini_share_path =
-      crostini::CrostiniSharePath::GetForProfile(profile_);
-  if (crostini_share_path)
-    crostini_share_path->AddObserver(this);
+  auto* guest_os_share_path =
+      guest_os::GuestOsSharePath::GetForProfile(profile_);
+  if (guest_os_share_path)
+    guest_os_share_path->AddObserver(this);
 }
 
 // File watch setup routines.
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.h b/chrome/browser/chromeos/extensions/file_manager/event_router.h
index 4ea030fa..16349012 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.h
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.h
@@ -15,7 +15,6 @@
 #include "base/compiler_specific.h"
 #include "base/files/file_path_watcher.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/chromeos/extensions/file_manager/device_event_router.h"
 #include "chrome/browser/chromeos/extensions/file_manager/drivefs_event_router.h"
@@ -24,6 +23,7 @@
 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 #include "chrome/common/extensions/api/file_manager_private.h"
 #include "chromeos/disks/disk_mount_manager.h"
 #include "chromeos/settings/timezone_settings.h"
@@ -58,7 +58,7 @@
       public VolumeManagerObserver,
       public arc::ArcIntentHelperObserver,
       public drive::DriveIntegrationServiceObserver,
-      public crostini::CrostiniSharePath::Observer {
+      public guest_os::GuestOsSharePath::Observer {
  public:
   typedef base::Callback<void(const base::FilePath& virtual_path,
                               const drive::FileChange* list,
@@ -151,7 +151,7 @@
   // DriveIntegrationServiceObserver override.
   void OnFileSystemMountFailed() override;
 
-  // crostini::CrostiniSharePath::Observer overrides
+  // guest_os::GuestOsSharePath::Observer overrides
   void OnUnshare(const std::string& vm_name,
                  const base::FilePath& path) override;
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
index a241562..e2ea348e 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -560,11 +560,11 @@
     ASSERT_TRUE(base::CreateDirectory(shared1));
     ASSERT_TRUE(base::CreateDirectory(shared2));
   }
-  crostini::CrostiniSharePath* crostini_share_path =
-      crostini::CrostiniSharePath::GetForProfile(browser()->profile());
-  crostini_share_path->RegisterPersistedPath(crostini::kCrostiniDefaultVmName,
+  guest_os::GuestOsSharePath* guest_os_share_path =
+      guest_os::GuestOsSharePath::GetForProfile(browser()->profile());
+  guest_os_share_path->RegisterPersistedPath(crostini::kCrostiniDefaultVmName,
                                              shared1);
-  crostini_share_path->RegisterPersistedPath(crostini::kCrostiniDefaultVmName,
+  guest_os_share_path->RegisterPersistedPath(crostini::kCrostiniDefaultVmName,
                                              shared2);
 
   ASSERT_TRUE(RunComponentExtensionTest("file_browser/crostini_test"));
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
index bf523ea..f97024d6e 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -705,7 +705,9 @@
     return RespondNow(Error("Volume not found"));
 
   DiskMountManager::GetInstance()->FormatMountedDevice(
-      volume->mount_path().AsUTF8Unsafe());
+      volume->mount_path().AsUTF8Unsafe(),
+      api::file_manager_private::ToString(params->filesystem),
+      params->volume_label);
   return RespondNow(NoArguments());
 }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index 643d9ee..1473d3e 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -23,7 +23,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/crostini/crostini_package_service.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
@@ -35,6 +34,7 @@
 #include "chrome/browser/chromeos/file_system_provider/service.h"
 #include "chrome/browser/chromeos/fileapi/recent_file.h"
 #include "chrome/browser/chromeos/fileapi/recent_model.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/devtools/devtools_window.h"
@@ -729,7 +729,7 @@
     paths.emplace_back(cracked.path());
   }
 
-  crostini::CrostiniSharePath::GetForProfile(profile)->SharePaths(
+  guest_os::GuestOsSharePath::GetForProfile(profile)->SharePaths(
       params->vm_name, std::move(paths), params->persist,
       base::BindOnce(&FileManagerPrivateInternalSharePathsWithCrostiniFunction::
                          SharePathsCallback,
@@ -756,7 +756,7 @@
           profile, render_frame_host());
   storage::FileSystemURL cracked =
       file_system_context->CrackURL(GURL(params->url));
-  crostini::CrostiniSharePath::GetForProfile(profile)->UnsharePath(
+  guest_os::GuestOsSharePath::GetForProfile(profile)->UnsharePath(
       params->vm_name, cracked.path(), /*unpersist=*/true,
       base::BindOnce(
           &FileManagerPrivateInternalUnsharePathWithCrostiniFunction::
@@ -779,12 +779,12 @@
   EXTENSION_FUNCTION_VALIDATE(params);
   Profile* profile = Profile::FromBrowserContext(browser_context());
 
-  auto* crostini_share_path =
-      crostini::CrostiniSharePath::GetForProfile(profile);
+  auto* guest_os_share_path =
+      guest_os::GuestOsSharePath::GetForProfile(profile);
   bool first_for_session = params->observe_first_for_session &&
-                           crostini_share_path->GetAndSetFirstForSession();
+                           guest_os_share_path->GetAndSetFirstForSession();
   auto shared_paths =
-      crostini_share_path->GetPersistedSharedPaths(params->vm_name);
+      guest_os_share_path->GetPersistedSharedPaths(params->vm_name);
   auto entries = std::make_unique<base::ListValue>();
   for (const base::FilePath& path : shared_paths) {
     std::string mount_name;
@@ -807,7 +807,7 @@
     // All shared paths should be directories.  Even if this is not true,
     // it is fine for foreground/js/crostini.js class to think so. We
     // verify that the paths are in fact valid directories before calling
-    // seneschal/9p in CrostiniSharePath::CallSeneschalSharePath().
+    // seneschal/9p in GuestOsSharePath::CallSeneschalSharePath().
     entry->SetBoolean("fileIsDirectory", true);
     entries->Append(std::move(entry));
   }
diff --git a/chrome/browser/chromeos/extensions/login/OWNERS b/chrome/browser/chromeos/extensions/login/OWNERS
deleted file mode 100644
index 8b107e4c..0000000
--- a/chrome/browser/chromeos/extensions/login/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://chrome/browser/chromeos/extensions/login_screen_ui/OWNERS
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/OWNERS b/chrome/browser/chromeos/extensions/login_screen/OWNERS
similarity index 100%
rename from chrome/browser/chromeos/extensions/login_screen_ui/OWNERS
rename to chrome/browser/chromeos/extensions/login_screen/OWNERS
diff --git a/chrome/browser/chromeos/extensions/login/login_api.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
similarity index 97%
rename from chrome/browser/chromeos/extensions/login/login_api.cc
rename to chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
index bf6d2bea..5d24ddcd 100644
--- a/chrome/browser/chromeos/extensions/login/login_api.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/login/login_api.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/login_api.h"
 
 #include <memory>
 #include <string>
diff --git a/chrome/browser/chromeos/extensions/login/login_api.h b/chrome/browser/chromeos/extensions/login_screen/login/login_api.h
similarity index 90%
rename from chrome/browser/chromeos/extensions/login/login_api.h
rename to chrome/browser/chromeos/extensions/login_screen/login/login_api.h
index d95008e..20ff168d 100644
--- a/chrome/browser/chromeos/extensions/login/login_api.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_LOGIN_API_H_
-#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_LOGIN_API_H_
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_LOGIN_API_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_LOGIN_API_H_
 
 #include "base/macros.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -89,4 +89,4 @@
 
 }  // namespace extensions
 
-#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_LOGIN_API_H_
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_LOGIN_API_H_
diff --git a/chrome/browser/chromeos/extensions/login/login_api_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc
similarity index 98%
rename from chrome/browser/chromeos/extensions/login/login_api_unittest.cc
rename to chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc
index 7d722d0..7291ebf9 100644
--- a/chrome/browser/chromeos/extensions/login/login_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/login/login_api.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/login_api.h"
 
 #include <memory>
 #include <string>
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc
similarity index 97%
rename from chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.cc
rename to chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc
index 47694ce..10c9708 100644
--- a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h"
 
 #include "ash/public/cpp/login_screen.h"
 #include "ash/public/cpp/login_screen_model.h"
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h
similarity index 90%
rename from chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h
rename to chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h
index 1b852d9..bab8657 100644
--- a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_EXTENSION_UI_HANDLER_H_
-#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_EXTENSION_UI_HANDLER_H_
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_UI_LOGIN_SCREEN_EXTENSION_UI_HANDLER_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_UI_LOGIN_SCREEN_EXTENSION_UI_HANDLER_H_
 
 #include <map>
 #include <memory>
@@ -97,4 +97,4 @@
 };
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_EXTENSION_UI_HANDLER_H_
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_UI_LOGIN_SCREEN_EXTENSION_UI_HANDLER_H_
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler_browsertest.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_browsertest.cc
similarity index 93%
rename from chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler_browsertest.cc
rename to chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_browsertest.cc
index 31d36866..f2740e4 100644
--- a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_browsertest.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h"
 
 #include <memory>
 #include <string>
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h"
 #include "chrome/browser/chromeos/policy/signin_profile_extensions_policy_test_base.h"
 #include "components/version_info/version_info.h"
 #include "extensions/test/extension_test_message_listener.h"
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler_unittest.cc
rename to chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_unittest.cc
index 0493ca1..5abaa946 100644
--- a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h"
 
 #include <memory>
 
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_api.cc
similarity index 88%
rename from chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.cc
rename to chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_api.cc
index 85f9360..d555070 100644
--- a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_api.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_api.h"
 
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h"
 #include "chrome/common/extensions/api/login_screen_ui.h"
 
 namespace login_screen_ui = extensions::api::login_screen_ui;
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.h b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_api.h
similarity index 77%
rename from chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.h
rename to chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_api.h
index 4dfafb4..5c9f342 100644
--- a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_api.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
-#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
 
 #include "extensions/browser/extension_function.h"
 
@@ -43,4 +43,4 @@
 
 }  // namespace extensions
 
-#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
diff --git a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_apitest.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_apitest.cc
similarity index 96%
rename from chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_apitest.cc
rename to chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_apitest.cc
index 1584f2e..56e2d3d 100644
--- a/chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_apitest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_apitest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/version_info/version_info.h"
diff --git a/chrome/browser/chromeos/extensions/wallpaper_function_base.cc b/chrome/browser/chromeos/extensions/wallpaper_function_base.cc
index 89b3a67..0d87a6e 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_function_base.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_function_base.cc
@@ -7,7 +7,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task/lazy_task_runner.h"
 #include "base/task/task_traits.h"
 #include "chrome/browser/image_decoder.h"
@@ -121,7 +121,7 @@
 
  private:
   scoped_refptr<WallpaperFunctionBase> function_;
-  base::CancellationFlag cancel_flag_;
+  base::AtomicFlag cancel_flag_;
 
   DISALLOW_COPY_AND_ASSIGN(UnsafeWallpaperDecoder);
 };
diff --git a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
index 23d69ea..97f0acd 100644
--- a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
+++ b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
@@ -151,8 +151,9 @@
   unmount_errors_[mount_path] = error_code;
 }
 
-void FakeDiskMountManager::FormatMountedDevice(const std::string& mount_path) {
-}
+void FakeDiskMountManager::FormatMountedDevice(const std::string& mount_path,
+                                               const std::string& filesystem,
+                                               const std::string& label) {}
 
 void FakeDiskMountManager::RenameMountedDevice(const std::string& mount_path,
                                                const std::string& volume_name) {
diff --git a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h
index b55a3950..8e1d8e5 100644
--- a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h
+++ b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h
@@ -98,7 +98,9 @@
                    UnmountPathCallback callback) override;
   void RemountAllRemovableDrives(
       chromeos::MountAccessMode access_mode) override;
-  void FormatMountedDevice(const std::string& mount_path) override;
+  void FormatMountedDevice(const std::string& mount_path,
+                           const std::string& filesystem,
+                           const std::string& label) override;
   void RenameMountedDevice(const std::string& mount_path,
                            const std::string& volume_name) override;
   void UnmountDeviceRecursively(
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 45e19d8..6192e68 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -513,11 +513,13 @@
         TestCase("checkContextMenuFocus").EnableMyFilesVolume()));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
-    Delete, /* delete.js */
+    Toolbar, /* toolbar.js */
     FilesAppBrowserTest,
-    ::testing::Values(TestCase("deleteMenuItemNoEntrySelected"),
-                      TestCase("deleteEntryWithToolbar").InGuestMode(),
-                      TestCase("deleteEntryWithToolbar")));
+    ::testing::Values(
+        TestCase("toolbarDeleteWithMenuItemNoEntrySelected"),
+        TestCase("toolbarDeleteEntry").InGuestMode(),
+        TestCase("toolbarDeleteEntry"),
+        TestCase("toolbarRefreshButtonWithSelection").EnableArc()));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     QuickView, /* quick_view.js */
@@ -1062,7 +1064,8 @@
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     Breadcrumbs, /* breadcrumbs.js */
     FilesAppBrowserTest,
-    ::testing::Values(TestCase("breadcrumbsNavigate")));
+    ::testing::Values(TestCase("breadcrumbsNavigate"),
+                      TestCase("breadcrumbsLeafNoFocus")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     FormatDialog, /* format_dialog.js */
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 e9c434e..a2690e6 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -168,7 +168,10 @@
     CROSTINI_VOLUME,
     USB_VOLUME,
     ANDROID_FILES_VOLUME,
-    DOCUMENTS_PROVIDER_VOLUME
+    DOCUMENTS_PROVIDER_VOLUME,
+    MEDIA_VIEW_AUDIO,
+    MEDIA_VIEW_IMAGES,
+    MEDIA_VIEW_VIDEOS
   };
 
   // Represents the different types of entries (e.g. file, folder).
@@ -216,6 +219,12 @@
       *volume = ANDROID_FILES_VOLUME;
     else if (value == "documents_provider")
       *volume = DOCUMENTS_PROVIDER_VOLUME;
+    else if (value == "media_view_audio")
+      *volume = MEDIA_VIEW_AUDIO;
+    else if (value == "media_view_images")
+      *volume = MEDIA_VIEW_IMAGES;
+    else if (value == "media_view_videos")
+      *volume = MEDIA_VIEW_VIDEOS;
     else
       return false;
     return true;
@@ -1960,6 +1969,27 @@
             LOG(FATAL) << "Add entry: but no DocumentsProvider volume.";
           }
           break;
+        case AddEntriesMessage::MEDIA_VIEW_AUDIO:
+          if (media_view_audio_) {
+            media_view_audio_->CreateEntry(*message.entries[i]);
+          } else {
+            LOG(FATAL) << "Add entry: but no MediaView Audio volume.";
+          }
+          break;
+        case AddEntriesMessage::MEDIA_VIEW_IMAGES:
+          if (media_view_images_) {
+            media_view_images_->CreateEntry(*message.entries[i]);
+          } else {
+            LOG(FATAL) << "Add entry: but no MediaView Images volume.";
+          }
+          break;
+        case AddEntriesMessage::MEDIA_VIEW_VIDEOS:
+          if (media_view_videos_) {
+            media_view_videos_->CreateEntry(*message.entries[i]);
+          } else {
+            LOG(FATAL) << "Add entry: but no MediaView Videos volume.";
+          }
+          break;
       }
     }
 
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
index 283b9cd..ea84fde 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -420,6 +420,7 @@
   SET_STRING("CHANGE_DEFAULT_CAPTION", IDS_FILE_BROWSER_CHANGE_DEFAULT_CAPTION);
   SET_STRING("CHANGE_DEFAULT_MENU_ITEM",
              IDS_FILE_BROWSER_CHANGE_DEFAULT_MENU_ITEM);
+  SET_STRING("CLOSE_LABEL", IDS_FILE_BROWSER_CLOSE_LABEL);
   SET_STRING("CLOSE_VOLUME_BUTTON_LABEL",
              IDS_FILE_BROWSER_CLOSE_VOLUME_BUTTON_LABEL);
   SET_STRING("CONFIGURE_VOLUME_BUTTON_LABEL",
@@ -507,6 +508,7 @@
   SET_STRING("ERROR_RENAMING", IDS_FILE_BROWSER_ERROR_RENAMING);
   SET_STRING("ERROR_RESERVED_NAME", IDS_FILE_BROWSER_ERROR_RESERVED_NAME);
   SET_STRING("ERROR_WHITESPACE_NAME", IDS_FILE_BROWSER_ERROR_WHITESPACE_NAME);
+  SET_STRING("EXPAND_LABEL", IDS_FILE_BROWSER_EXPAND_LABEL);
   SET_STRING("EXTERNAL_STORAGE_DISABLED_MESSAGE",
              IDS_EXTERNAL_STORAGE_DISABLED_MESSAGE);
   SET_STRING("FAILED_SPACE_INFO", IDS_FILE_BROWSER_FAILED_SPACE_INFO);
@@ -533,6 +535,7 @@
   SET_STRING("FORMATTING_OF_DEVICE_PENDING_TITLE",
              IDS_FORMATTING_OF_DEVICE_PENDING_TITLE);
   SET_STRING("FORMATTING_WARNING", IDS_FILE_BROWSER_FORMATTING_WARNING);
+  SET_STRING("PAUSE_LABEL", IDS_FILE_BROWSER_PAUSE_LABEL);
   SET_STRING("RENAMING_OF_DEVICE_FINISHED_FAILURE_MESSAGE",
              IDS_RENAMING_OF_DEVICE_FINISHED_FAILURE_MESSAGE);
   SET_STRING("RENAMING_OF_DEVICE_FAILED_TITLE",
diff --git a/chrome/browser/chromeos/guest_os/OWNERS b/chrome/browser/chromeos/guest_os/OWNERS
new file mode 100644
index 0000000..3e30dd27
--- /dev/null
+++ b/chrome/browser/chromeos/guest_os/OWNERS
@@ -0,0 +1,4 @@
+benwells@chromium.org
+joelhockey@chromium.org
+nverne@chromium.org
+timloh@chromium.org
diff --git a/chrome/browser/chromeos/guest_os/README.md b/chrome/browser/chromeos/guest_os/README.md
new file mode 100644
index 0000000..0f2ba8f
--- /dev/null
+++ b/chrome/browser/chromeos/guest_os/README.md
@@ -0,0 +1,10 @@
+# Guest OS
+
+This directory contains code to interact with Chrome OS guest
+[VMs and containers](https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md)
+This directory includes code which is common to all VM types such as file
+sharing.
+
+Code for specific VM types can be found in:
+* Crostini [`chrome/browser/chromeos/crostini`](/chrome/browser/chromeos/crostini/)
+* PluginVm [`chrome/browser/chromeos/plugin_vm`](/chrome/browser/chromeos/plugin_vm/)
diff --git a/chrome/browser/chromeos/guest_os/guest_os_pref_names.cc b/chrome/browser/chromeos/guest_os/guest_os_pref_names.cc
new file mode 100644
index 0000000..3be359f1
--- /dev/null
+++ b/chrome/browser/chromeos/guest_os/guest_os_pref_names.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/guest_os/guest_os_pref_names.h"
+
+#include "components/prefs/pref_registry_simple.h"
+
+namespace guest_os {
+namespace prefs {
+
+// TODO(crbug.com/946273): Remove crostini.shared_paths and migration code after
+// M77.
+const char kCrostiniSharedPaths[] = "crostini.shared_paths";
+// Dictionary of filesystem paths mapped to the list of VMs that the paths are
+// shared with.
+const char kGuestOSPathsSharedToVms[] = "guest_os.paths_shared_to_vms";
+
+void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterListPref(kCrostiniSharedPaths);
+  registry->RegisterDictionaryPref(kGuestOSPathsSharedToVms);
+}
+
+}  // namespace prefs
+}  // namespace guest_os
diff --git a/chrome/browser/chromeos/guest_os/guest_os_pref_names.h b/chrome/browser/chromeos/guest_os/guest_os_pref_names.h
new file mode 100644
index 0000000..6138b5b
--- /dev/null
+++ b/chrome/browser/chromeos/guest_os/guest_os_pref_names.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_PREF_NAMES_H_
+#define CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_PREF_NAMES_H_
+
+class PrefRegistrySimple;
+
+namespace guest_os {
+namespace prefs {
+
+extern const char kCrostiniSharedPaths[];
+extern const char kGuestOSPathsSharedToVms[];
+
+void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+}  // namespace prefs
+}  // namespace guest_os
+
+#endif  // CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_PREF_NAMES_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path.cc b/chrome/browser/chromeos/guest_os/guest_os_share_path.cc
similarity index 87%
rename from chrome/browser/chromeos/crostini/crostini_share_path.cc
rename to chrome/browser/chromeos/guest_os/guest_os_share_path.cc
index 1abb580..fe1a373 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path.cc
+++ b/chrome/browser/chromeos/guest_os/guest_os_share_path.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 
 #include "base/atomic_ref_count.h"
 #include "base/bind.h"
@@ -10,12 +10,12 @@
 #include "base/optional.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
-#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_pref_names.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path_factory.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -35,7 +35,7 @@
 namespace {
 
 void OnSeneschalSharePathResponse(
-    crostini::CrostiniSharePath::SharePathCallback callback,
+    guest_os::GuestOsSharePath::SharePathCallback callback,
     base::Optional<vm_tools::seneschal::SharePathResponse> response) {
   if (!response) {
     std::move(callback).Run(base::FilePath(), false, "System error");
@@ -50,7 +50,7 @@
 void OnVmRestartedForSeneschal(
     Profile* profile,
     const std::string& vm_name,
-    crostini::CrostiniSharePath::SharePathCallback callback,
+    guest_os::GuestOsSharePath::SharePathCallback callback,
     vm_tools::seneschal::SharePathRequest request,
     crostini::CrostiniResult result) {
   auto* crostini_manager = crostini::CrostiniManager::GetForProfile(profile);
@@ -151,7 +151,7 @@
 
 }  // namespace
 
-namespace crostini {
+namespace guest_os {
 
 SharedPathInfo::SharedPathInfo(const std::string& vm_name) {
   vm_names.insert(vm_name);
@@ -159,11 +159,11 @@
 SharedPathInfo::SharedPathInfo(SharedPathInfo&&) = default;
 SharedPathInfo::~SharedPathInfo() = default;
 
-CrostiniSharePath* CrostiniSharePath::GetForProfile(Profile* profile) {
-  return CrostiniSharePathFactory::GetForProfile(profile);
+GuestOsSharePath* GuestOsSharePath::GetForProfile(Profile* profile) {
+  return GuestOsSharePathFactory::GetForProfile(profile);
 }
 
-CrostiniSharePath::CrostiniSharePath(Profile* profile)
+GuestOsSharePath::GuestOsSharePath(Profile* profile)
     : profile_(profile),
       sequenced_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE})),
@@ -182,7 +182,7 @@
   }
 }
 
-CrostiniSharePath::~CrostiniSharePath() {
+GuestOsSharePath::~GuestOsSharePath() {
   for (auto& shared_path : shared_paths_) {
     if (shared_path.second.watcher) {
       sequenced_task_runner_->DeleteSoon(FROM_HERE,
@@ -191,14 +191,14 @@
   }
 }
 
-void CrostiniSharePath::AddObserver(Observer* obs) {
+void GuestOsSharePath::AddObserver(Observer* obs) {
   observers_.AddObserver(obs);
 }
 
-void CrostiniSharePath::CallSeneschalSharePath(const std::string& vm_name,
-                                               const base::FilePath& path,
-                                               bool persist,
-                                               SharePathCallback callback) {
+void GuestOsSharePath::CallSeneschalSharePath(const std::string& vm_name,
+                                              const base::FilePath& path,
+                                              bool persist,
+                                              SharePathCallback callback) {
   // Verify path is in one of the allowable mount points.
   // This logic is similar to DownloadPrefs::SanitizeDownloadTargetPath().
   if (!path.IsAbsolute() || path.ReferencesParent()) {
@@ -232,7 +232,7 @@
       request.set_storage_location(
           vm_tools::seneschal::SharePathRequest::DOWNLOADS);
     }
-    request.set_owner_id(CryptohomeIdForProfile(profile_));
+    request.set_owner_id(crostini::CryptohomeIdForProfile(profile_));
   } else if (base::FeatureList::IsEnabled(chromeos::features::kDriveFs) &&
              integration_service &&
              (drivefs_mount_point_path =
@@ -335,7 +335,7 @@
       base::BindOnce(&OnSeneschalSharePathResponse, std::move(callback)));
 }
 
-void CrostiniSharePath::CallSeneschalUnsharePath(
+void GuestOsSharePath::CallSeneschalUnsharePath(
     const std::string& vm_name,
     const base::FilePath& path,
     base::OnceCallback<void(bool, std::string)> callback) {
@@ -388,15 +388,15 @@
       base::BindOnce(&OnSeneschalUnsharePathResponse, std::move(callback)));
 }
 
-void CrostiniSharePath::SharePath(const std::string& vm_name,
-                                  const base::FilePath& path,
-                                  bool persist,
-                                  SharePathCallback callback) {
+void GuestOsSharePath::SharePath(const std::string& vm_name,
+                                 const base::FilePath& path,
+                                 bool persist,
+                                 SharePathCallback callback) {
   DCHECK(callback);
   CallSeneschalSharePath(vm_name, path, persist, std::move(callback));
 }
 
-void CrostiniSharePath::SharePaths(
+void GuestOsSharePath::SharePaths(
     const std::string& vm_name,
     std::vector<base::FilePath> paths,
     bool persist,
@@ -412,7 +412,7 @@
   }
 }
 
-void CrostiniSharePath::UnsharePath(
+void GuestOsSharePath::UnsharePath(
     const std::string& vm_name,
     const base::FilePath& path,
     bool unpersist,
@@ -429,8 +429,7 @@
 
   if (unpersist) {
     PrefService* pref_service = profile_->GetPrefs();
-    DictionaryPrefUpdate update(pref_service,
-                                crostini::prefs::kGuestOSPathsSharedToVms);
+    DictionaryPrefUpdate update(pref_service, prefs::kGuestOSPathsSharedToVms);
     base::DictionaryValue* shared_paths = update.Get();
     RemovePersistedPathFromPrefs(shared_paths, vm_name, path);
   }
@@ -441,13 +440,13 @@
   }
 }
 
-bool CrostiniSharePath::GetAndSetFirstForSession() {
+bool GuestOsSharePath::GetAndSetFirstForSession() {
   bool result = first_for_session_;
   first_for_session_ = false;
   return result;
 }
 
-std::vector<base::FilePath> CrostiniSharePath::GetPersistedSharedPaths(
+std::vector<base::FilePath> GuestOsSharePath::GetPersistedSharedPaths(
     const std::string& vm_name) {
   std::vector<base::FilePath> result;
   // |shared_paths| format is {'path': ['vm1', vm2']}.
@@ -469,18 +468,17 @@
   return result;
 }
 
-void CrostiniSharePath::SharePersistedPaths(
+void GuestOsSharePath::SharePersistedPaths(
     const std::string& vm_name,
     base::OnceCallback<void(bool, std::string)> callback) {
   SharePaths(vm_name, GetPersistedSharedPaths(vm_name),
              /*persist=*/false, std::move(callback));
 }
 
-void CrostiniSharePath::RegisterPersistedPath(const std::string& vm_name,
-                                              const base::FilePath& path) {
+void GuestOsSharePath::RegisterPersistedPath(const std::string& vm_name,
+                                             const base::FilePath& path) {
   PrefService* pref_service = profile_->GetPrefs();
-  DictionaryPrefUpdate update(pref_service,
-                              crostini::prefs::kGuestOSPathsSharedToVms);
+  DictionaryPrefUpdate update(pref_service, prefs::kGuestOSPathsSharedToVms);
   base::DictionaryValue* shared_paths = update.Get();
   // Check if path is already shared so we know whether we need to add it.
   bool already_shared = false;
@@ -512,7 +510,7 @@
   }
 }
 
-void CrostiniSharePath::MigratePersistedPathsToMultiVM(
+void GuestOsSharePath::MigratePersistedPathsToMultiVM(
     PrefService* profile_prefs) {
   const base::ListValue* shared_paths =
       profile_prefs->GetList(prefs::kCrostiniSharedPaths);
@@ -523,7 +521,8 @@
   base::Value dict(base::Value::Type::DICTIONARY);
   for (const auto& shared_path : *shared_paths) {
     base::Value termina(base::Value::Type::LIST);
-    termina.GetList().emplace_back(base::Value(kCrostiniDefaultVmName));
+    termina.GetList().emplace_back(
+        base::Value(crostini::kCrostiniDefaultVmName));
     dict.SetKey(shared_path.GetString(), std::move(termina));
   }
   profile_prefs->Set(prefs::kGuestOSPathsSharedToVms, std::move(dict));
@@ -531,8 +530,8 @@
   profile_prefs->ClearPref(prefs::kCrostiniSharedPaths);
 }
 
-void CrostiniSharePath::OnVolumeMounted(chromeos::MountError error_code,
-                                        const file_manager::Volume& volume) {
+void GuestOsSharePath::OnVolumeMounted(chromeos::MountError error_code,
+                                       const file_manager::Volume& volume) {
   if (error_code != chromeos::MountError::MOUNT_ERROR_NONE) {
     return;
   }
@@ -549,7 +548,7 @@
     const auto& vms = it.second.GetList();
     for (const auto& vm : vms) {
       RegisterSharedPath(vm.GetString(), path);
-      if (CrostiniManager::GetForProfile(profile_)->IsVmRunning(
+      if (crostini::CrostiniManager::GetForProfile(profile_)->IsVmRunning(
               vm.GetString())) {
         CallSeneschalSharePath(vm.GetString(), path, false,
                                base::BindOnce(mount_event_seneschal_callback_,
@@ -559,8 +558,8 @@
   }
 }
 
-void CrostiniSharePath::OnVolumeUnmounted(chromeos::MountError error_code,
-                                          const file_manager::Volume& volume) {
+void GuestOsSharePath::OnVolumeUnmounted(chromeos::MountError error_code,
+                                         const file_manager::Volume& volume) {
   if (error_code != chromeos::MountError::MOUNT_ERROR_NONE) {
     return;
   }
@@ -584,19 +583,19 @@
   }
 }
 
-void CrostiniSharePath::StartFileWatcher(const base::FilePath& path) {
+void GuestOsSharePath::StartFileWatcher(const base::FilePath& path) {
   auto* info = FindSharedPathInfo(path);
   if (!info || info->watcher) {
     return;
   }
   info->watcher = std::make_unique<base::FilePathWatcher>();
   info->watcher->Watch(path, false,
-                       base::BindRepeating(&CrostiniSharePath::OnFileChanged,
+                       base::BindRepeating(&GuestOsSharePath::OnFileChanged,
                                            base::Unretained(this)));
 }
 
-void CrostiniSharePath::RegisterSharedPath(const std::string& vm_name,
-                                           const base::FilePath& path) {
+void GuestOsSharePath::RegisterSharedPath(const std::string& vm_name,
+                                          const base::FilePath& path) {
   // Paths may be called to be shared multiple times for the same or different
   // vm.  If path is already registered, add vm_name to list of VMs shared with
   // and return.
@@ -608,12 +607,12 @@
   shared_paths_.emplace(path, SharedPathInfo(vm_name));
   if (!no_file_watchers_for_testing_) {
     sequenced_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&CrostiniSharePath::StartFileWatcher,
+        FROM_HERE, base::BindOnce(&GuestOsSharePath::StartFileWatcher,
                                   base::Unretained(this), path));
   }
 }
 
-void CrostiniSharePath::OnFileChanged(const base::FilePath& path, bool error) {
+void GuestOsSharePath::OnFileChanged(const base::FilePath& path, bool error) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (shared_paths_.find(path) == shared_paths_.end()) {
     return;
@@ -622,13 +621,12 @@
     shared_paths_.erase(path);
     return;
   }
-  base::PostTaskWithTraits(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&CrostiniSharePath::CheckIfPathDeleted,
-                     base::Unretained(this), path));
+  base::PostTaskWithTraits(FROM_HERE, {base::MayBlock()},
+                           base::BindOnce(&GuestOsSharePath::CheckIfPathDeleted,
+                                          base::Unretained(this), path));
 }
 
-void CrostiniSharePath::CheckIfPathDeleted(const base::FilePath& path) {
+void GuestOsSharePath::CheckIfPathDeleted(const base::FilePath& path) {
   if (base::PathExists(path)) {
     return;
   }
@@ -650,11 +648,11 @@
   }
 
   base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
-                           base::BindOnce(&CrostiniSharePath::PathDeleted,
+                           base::BindOnce(&GuestOsSharePath::PathDeleted,
                                           base::Unretained(this), path));
 }
 
-void CrostiniSharePath::PathDeleted(const base::FilePath& path) {
+void GuestOsSharePath::PathDeleted(const base::FilePath& path) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   auto* info = FindSharedPathInfo(path);
   if (!info) {
@@ -670,7 +668,7 @@
   }
 }
 
-void CrostiniSharePath::OnFilesChanged(
+void GuestOsSharePath::OnFilesChanged(
     const std::vector<drivefs::mojom::FileChange>& changes) {
   auto* integration_service =
       drive::DriveIntegrationServiceFactory::FindForProfile(profile_);
@@ -688,7 +686,7 @@
   }
 }
 
-SharedPathInfo* CrostiniSharePath::FindSharedPathInfo(
+SharedPathInfo* GuestOsSharePath::FindSharedPathInfo(
     const base::FilePath& path) {
   auto it = shared_paths_.find(path);
   if (it == shared_paths_.end()) {
@@ -697,4 +695,4 @@
   return &it->second;
 }
 
-}  // namespace crostini
+}  // namespace guest_os
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path.h b/chrome/browser/chromeos/guest_os/guest_os_share_path.h
similarity index 90%
rename from chrome/browser/chromeos/crostini/crostini_share_path.h
rename to chrome/browser/chromeos/guest_os/guest_os_share_path.h
index db9c45a0..ab1686cd 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path.h
+++ b/chrome/browser/chromeos/guest_os/guest_os_share_path.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_H_
-#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_H_
+#ifndef CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_SHARE_PATH_H_
+#define CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_SHARE_PATH_H_
 
 #include <map>
 #include <memory>
@@ -23,7 +23,7 @@
 
 class Profile;
 
-namespace crostini {
+namespace guest_os {
 
 struct SharedPathInfo {
   explicit SharedPathInfo(const std::string& vm_name);
@@ -34,11 +34,11 @@
   std::set<std::string> vm_names;
 };
 
-// Handles sharing and unsharing paths from the Chrome OS host to the crostini
-// VM via seneschal.
-class CrostiniSharePath : public KeyedService,
-                          public file_manager::VolumeManagerObserver,
-                          public drivefs::DriveFsHostObserver {
+// Handles sharing and unsharing paths from the Chrome OS host to guest VMs via
+// seneschal.
+class GuestOsSharePath : public KeyedService,
+                         public file_manager::VolumeManagerObserver,
+                         public drivefs::DriveFsHostObserver {
  public:
   using SharePathCallback =
       base::OnceCallback<void(const base::FilePath&, bool, std::string)>;
@@ -60,9 +60,9 @@
   // after M77.
   static void MigratePersistedPathsToMultiVM(PrefService* profile_prefs);
 
-  static CrostiniSharePath* GetForProfile(Profile* profile);
-  explicit CrostiniSharePath(Profile* profile);
-  ~CrostiniSharePath() override;
+  static GuestOsSharePath* GetForProfile(Profile* profile);
+  explicit GuestOsSharePath(Profile* profile);
+  ~GuestOsSharePath() override;
 
   // Observer receives unshare events.
   void AddObserver(Observer* obs);
@@ -171,9 +171,9 @@
   std::map<base::FilePath, SharedPathInfo> shared_paths_;
   bool no_file_watchers_for_testing_ = false;
 
-  DISALLOW_COPY_AND_ASSIGN(CrostiniSharePath);
+  DISALLOW_COPY_AND_ASSIGN(GuestOsSharePath);
 };  // class
 
-}  // namespace crostini
+}  // namespace guest_os
 
-#endif  // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_H_
+#endif  // CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_SHARE_PATH_H_
diff --git a/chrome/browser/chromeos/guest_os/guest_os_share_path_factory.cc b/chrome/browser/chromeos/guest_os/guest_os_share_path_factory.cc
new file mode 100644
index 0000000..1232a25
--- /dev/null
+++ b/chrome/browser/chromeos/guest_os/guest_os_share_path_factory.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path_factory.h"
+
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+namespace guest_os {
+
+// static
+GuestOsSharePath* GuestOsSharePathFactory::GetForProfile(Profile* profile) {
+  return static_cast<GuestOsSharePath*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+GuestOsSharePathFactory* GuestOsSharePathFactory::GetInstance() {
+  static base::NoDestructor<GuestOsSharePathFactory> factory;
+  return factory.get();
+}
+
+GuestOsSharePathFactory::GuestOsSharePathFactory()
+    : BrowserContextKeyedServiceFactory(
+          "GuestOsSharePath",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+GuestOsSharePathFactory::~GuestOsSharePathFactory() = default;
+
+KeyedService* GuestOsSharePathFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return new GuestOsSharePath(profile);
+}
+
+}  // namespace guest_os
diff --git a/chrome/browser/chromeos/guest_os/guest_os_share_path_factory.h b/chrome/browser/chromeos/guest_os/guest_os_share_path_factory.h
new file mode 100644
index 0000000..a80f38b0
--- /dev/null
+++ b/chrome/browser/chromeos/guest_os/guest_os_share_path_factory.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_SHARE_PATH_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_SHARE_PATH_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+
+namespace guest_os {
+
+class GuestOsSharePath;
+
+class GuestOsSharePathFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static GuestOsSharePath* GetForProfile(Profile* profile);
+  static GuestOsSharePathFactory* GetInstance();
+
+ private:
+  friend class base::NoDestructor<GuestOsSharePathFactory>;
+
+  GuestOsSharePathFactory();
+  ~GuestOsSharePathFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(GuestOsSharePathFactory);
+};
+
+}  // namespace guest_os
+
+#endif  // CHROME_BROWSER_CHROMEOS_GUEST_OS_GUEST_OS_SHARE_PATH_FACTORY_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc b/chrome/browser/chromeos/guest_os/guest_os_share_path_unittest.cc
similarity index 77%
rename from chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
rename to chrome/browser/chromeos/guest_os/guest_os_share_path_unittest.cc
index 34456d5..be2611c8 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
+++ b/chrome/browser/chromeos/guest_os/guest_os_share_path_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 
 #include "base/bind.h"
 #include "base/files/file_path.h"
@@ -14,6 +14,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_pref_names.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
@@ -32,9 +33,9 @@
 #include "storage/browser/fileapi/external_mount_points.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace crostini {
+namespace guest_os {
 
-class CrostiniSharePathTest : public testing::Test {
+class GuestOsSharePathTest : public testing::Test {
  public:
   const bool PERSIST_YES = true;
   const bool PERSIST_NO = false;
@@ -59,7 +60,7 @@
     EXPECT_TRUE(prefs->HasKey(shared_path_.value()));
     EXPECT_EQ(prefs->FindKey(shared_path_.value())->GetList().size(), 1U);
     EXPECT_EQ(prefs->FindKey(shared_path_.value())->GetList()[0].GetString(),
-              kCrostiniDefaultVmName);
+              crostini::kCrostiniDefaultVmName);
     if (expected_persist == Persist::YES) {
       EXPECT_EQ(prefs->size(), 2U);
       EXPECT_TRUE(prefs->HasKey(share_path_.value()));
@@ -177,7 +178,7 @@
                         expected_failure_reason, success, failure_reason);
   }
 
-  CrostiniSharePathTest()
+  GuestOsSharePathTest()
       : test_browser_thread_bundle_(
             content::TestBrowserThreadBundle::REAL_IO_THREAD) {
     chromeos::DBusThreadManager::Initialize();
@@ -187,7 +188,7 @@
         chromeos::DBusThreadManager::Get()->GetSeneschalClient());
   }
 
-  ~CrostiniSharePathTest() override { chromeos::DBusThreadManager::Shutdown(); }
+  ~GuestOsSharePathTest() override { chromeos::DBusThreadManager::Shutdown(); }
 
   void SetUpVolume() {
     // Setup Downloads and path to share, which depend on MyFilesVolume flag,
@@ -207,18 +208,19 @@
                                 prefs::kGuestOSPathsSharedToVms);
     base::DictionaryValue* shared_paths = update.Get();
     base::Value termina(base::Value::Type::LIST);
-    termina.GetList().emplace_back(base::Value(kCrostiniDefaultVmName));
+    termina.GetList().emplace_back(
+        base::Value(crostini::kCrostiniDefaultVmName));
     shared_paths->SetKey(shared_path_.value(), std::move(termina));
     volume_downloads_ = file_manager::Volume::CreateForDownloads(root_);
-    crostini_share_path_->RegisterSharedPath(kCrostiniDefaultVmName,
+    guest_os_share_path_->RegisterSharedPath(crostini::kCrostiniDefaultVmName,
                                              shared_path_);
   }
 
   void SetUp() override {
     run_loop_ = std::make_unique<base::RunLoop>();
     profile_ = std::make_unique<TestingProfile>();
-    crostini_share_path_ = CrostiniSharePath::GetForProfile(profile());
-    crostini_share_path_->set_no_file_watchers_for_testing();
+    guest_os_share_path_ = GuestOsSharePath::GetForProfile(profile());
+    guest_os_share_path_->set_no_file_watchers_for_testing();
 
     // Setup for DriveFS.
     scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
@@ -230,9 +232,8 @@
     drivefs_ =
         base::FilePath("/media/fuse/drivefs-84675c855b63e12f384d45f033826980");
 
-
     // Create 'vm-running' VM instance which is running.
-    CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
+    crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
         "vm-running");
   }
 
@@ -263,22 +264,22 @@
   std::unique_ptr<base::RunLoop>
       run_loop_;  // run_loop_ must be created on the UI thread.
   std::unique_ptr<TestingProfile> profile_;
-  CrostiniSharePath* crostini_share_path_;
+  GuestOsSharePath* guest_os_share_path_;
   base::test::ScopedFeatureList features_;
   std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
   AccountId account_id_;
 
  private:
   content::TestBrowserThreadBundle test_browser_thread_bundle_;
-  DISALLOW_COPY_AND_ASSIGN(CrostiniSharePathTest);
+  DISALLOW_COPY_AND_ASSIGN(GuestOsSharePathTest);
 };
 
-TEST_F(CrostiniSharePathTest, SuccessDownloadsRoot) {
+TEST_F(GuestOsSharePathTest, SuccessDownloadsRoot) {
   features_.InitWithFeatures({}, {chromeos::features::kMyFilesVolume});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", root_, PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DOWNLOADS, "",
@@ -286,14 +287,14 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessMyFilesRoot) {
+TEST_F(GuestOsSharePathTest, SuccessMyFilesRoot) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
   base::FilePath my_files =
       file_manager::util::GetMyFilesFolderForProfile(profile());
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", my_files, PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::MY_FILES, "",
@@ -301,12 +302,12 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessNoPersist) {
+TEST_F(GuestOsSharePathTest, SuccessNoPersist) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", share_path_, PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::MY_FILES,
@@ -314,12 +315,12 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessPersist) {
+TEST_F(GuestOsSharePathTest, SuccessPersist) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", share_path_, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::YES,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::MY_FILES,
@@ -327,12 +328,12 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessPluginVm) {
+TEST_F(GuestOsSharePathTest, SuccessPluginVm) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "PvmDefault", share_path_, PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "PvmDefault", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::MY_FILES,
@@ -340,12 +341,12 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessDriveFsMyDrive) {
+TEST_F(GuestOsSharePathTest, SuccessDriveFsMyDrive) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append("root").Append("my"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_MY_DRIVE,
@@ -353,24 +354,24 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, FailureDriveFsDisabled) {
+TEST_F(GuestOsSharePathTest, FailureDriveFsDisabled) {
   features_.InitWithFeatures({}, {chromeos::features::kDriveFs});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append("root").Append("my"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "my", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessDriveFsMyDriveRoot) {
+TEST_F(GuestOsSharePathTest, SuccessDriveFsMyDriveRoot) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append("root"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_MY_DRIVE,
@@ -378,25 +379,25 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, FailDriveFsRoot) {
+TEST_F(GuestOsSharePathTest, FailDriveFsRoot) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_, PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessDriveFsTeamDrives) {
+TEST_F(GuestOsSharePathTest, SuccessDriveFsTeamDrives) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append("team_drives").Append("team"), PERSIST_NO,
       base::BindOnce(
-          &CrostiniSharePathTest::SharePathCallback, base::Unretained(this),
+          &GuestOsSharePathTest::SharePathCallback, base::Unretained(this),
           "vm-running", Persist::NO, SeneschalClientCalled::YES,
           &vm_tools::seneschal::SharePathRequest::DRIVEFS_TEAM_DRIVES, "team",
           Success::YES, ""));
@@ -404,12 +405,12 @@
 }
 
 // TODO(crbug.com/917920): Enable when DriveFS enforces allowed write paths.
-TEST_F(CrostiniSharePathTest, DISABLED_SuccessDriveFsComputersGrandRoot) {
+TEST_F(GuestOsSharePathTest, DISABLED_SuccessDriveFsComputersGrandRoot) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append("Computers"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
@@ -418,12 +419,12 @@
 }
 
 // TODO(crbug.com/917920): Remove when DriveFS enforces allowed write paths.
-TEST_F(CrostiniSharePathTest, Bug917920DriveFsComputersGrandRoot) {
+TEST_F(GuestOsSharePathTest, Bug917920DriveFsComputersGrandRoot) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append("Computers"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "", Success::NO,
                      "Path is not allowed"));
@@ -431,12 +432,12 @@
 }
 
 // TODO(crbug.com/917920): Enable when DriveFS enforces allowed write paths.
-TEST_F(CrostiniSharePathTest, DISABLED_SuccessDriveFsComputerRoot) {
+TEST_F(GuestOsSharePathTest, DISABLED_SuccessDriveFsComputerRoot) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append("Computers").Append("pc"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
@@ -445,26 +446,26 @@
 }
 
 // TODO(crbug.com/917920): Remove when DriveFS enforces allowed write paths.
-TEST_F(CrostiniSharePathTest, Bug917920DriveFsComputerRoot) {
+TEST_F(GuestOsSharePathTest, Bug917920DriveFsComputerRoot) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append("Computers").Append("pc"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessDriveFsComputersLevel3) {
+TEST_F(GuestOsSharePathTest, SuccessDriveFsComputersLevel3) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running",
       drivefs_.Append("Computers").Append("pc").Append("SyncFolder"),
       PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
@@ -472,24 +473,24 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, FailDriveFsTrash) {
+TEST_F(GuestOsSharePathTest, FailDriveFsTrash) {
   features_.InitWithFeatures({chromeos::features::kDriveFs}, {});
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", drivefs_.Append(".Trash").Append("in-the-trash"),
       PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SuccessRemovable) {
+TEST_F(GuestOsSharePathTest, SuccessRemovable) {
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", base::FilePath("/media/removable/MyUSB"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::REMOVABLE, "MyUSB",
@@ -497,18 +498,18 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, FailRemovableRoot) {
+TEST_F(GuestOsSharePathTest, FailRemovableRoot) {
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", base::FilePath("/media/removable"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SharePathErrorSeneschal) {
+TEST_F(GuestOsSharePathTest, SharePathErrorSeneschal) {
   features_.InitWithFeatures(
       {chromeos::features::kMyFilesVolume, features::kCrostini}, {});
   GetFakeUserManager()->LoginUser(account_id_);
@@ -523,9 +524,9 @@
   share_path_response.set_failure_reason("test failure");
   fake_seneschal_client_->set_share_path_response(share_path_response);
 
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "error-seneschal", share_path_, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "error-seneschal", Persist::YES,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::MY_FILES,
@@ -533,50 +534,50 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SharePathErrorPathNotAbsolute) {
+TEST_F(GuestOsSharePathTest, SharePathErrorPathNotAbsolute) {
   SetUpVolume();
   const base::FilePath path("not/absolute/dir");
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", path, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "", Success::NO,
                      "Path must be absolute"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SharePathErrorReferencesParent) {
+TEST_F(GuestOsSharePathTest, SharePathErrorReferencesParent) {
   SetUpVolume();
   const base::FilePath path("/path/../references/parent");
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", path, PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "", Success::NO,
                      "Path must be absolute"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SharePathErrorNotUnderDownloads) {
+TEST_F(GuestOsSharePathTest, SharePathErrorNotUnderDownloads) {
   SetUpVolume();
   const base::FilePath path("/not/under/downloads");
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-running", path, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-running", Persist::NO,
                      SeneschalClientCalled::NO, nullptr, "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SharePathVmToBeRestarted) {
+TEST_F(GuestOsSharePathTest, SharePathVmToBeRestarted) {
   features_.InitWithFeatures(
       {chromeos::features::kMyFilesVolume, features::kCrostini}, {});
   GetFakeUserManager()->LoginUser(account_id_);
   SetUpVolume();
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "vm-to-be-started", share_path_, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "vm-to-be-started", Persist::YES,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::MY_FILES,
@@ -584,48 +585,48 @@
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SharePathErrorVmCouldNotBeStarted) {
+TEST_F(GuestOsSharePathTest, SharePathErrorVmCouldNotBeStarted) {
   SetUpVolume();
   vm_tools::concierge::StartVmResponse start_vm_response;
   start_vm_response.set_status(vm_tools::concierge::VM_STATUS_FAILURE);
   fake_concierge_client_->set_start_vm_response(start_vm_response);
 
-  crostini_share_path_->SharePath(
+  guest_os_share_path_->SharePath(
       "error-vm-could-not-be-started", share_path_, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::SharePathCallback,
                      base::Unretained(this), "error-vm-could-not-be-started",
                      Persist::YES, SeneschalClientCalled::NO, nullptr, "",
                      Success::NO, "VM could not be started"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, SharePersistedPaths) {
+TEST_F(GuestOsSharePathTest, SharePersistedPaths) {
   SetUpVolume();
   base::FilePath share_path2_ = root_.AppendASCII("path-to-share-2");
   ASSERT_TRUE(base::CreateDirectory(share_path2_));
-  CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
-      kCrostiniDefaultVmName);
+  crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
+      crostini::kCrostiniDefaultVmName);
   base::Value shared_paths(base::Value::Type::DICTIONARY);
   base::Value vms(base::Value::Type::LIST);
-  vms.GetList().emplace_back(base::Value(kCrostiniDefaultVmName));
+  vms.GetList().emplace_back(base::Value(crostini::kCrostiniDefaultVmName));
   shared_paths.SetKey(share_path_.value(), std::move(vms));
   base::Value vms2(base::Value::Type::LIST);
-  vms2.GetList().emplace_back(base::Value(kCrostiniDefaultVmName));
+  vms2.GetList().emplace_back(base::Value(crostini::kCrostiniDefaultVmName));
   shared_paths.SetKey(share_path2_.value(), std::move(vms2));
   profile()->GetPrefs()->Set(prefs::kGuestOSPathsSharedToVms, shared_paths);
-  crostini_share_path_->SharePersistedPaths(
-      kCrostiniDefaultVmName,
-      base::BindOnce(&CrostiniSharePathTest::SharePersistedPathsCallback,
+  guest_os_share_path_->SharePersistedPaths(
+      crostini::kCrostiniDefaultVmName,
+      base::BindOnce(&GuestOsSharePathTest::SharePersistedPathsCallback,
                      base::Unretained(this)));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, RegisterPersistedPaths) {
+TEST_F(GuestOsSharePathTest, RegisterPersistedPaths) {
   base::Value shared_paths(base::Value::Type::DICTIONARY);
   SetUpVolume();
   profile()->GetPrefs()->Set(prefs::kGuestOSPathsSharedToVms, shared_paths);
 
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/a"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/a"));
   const base::DictionaryValue* prefs =
       profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
   EXPECT_EQ(prefs->size(), 1U);
@@ -633,23 +634,23 @@
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v1");
 
   // Adding the same path again for same VM should not cause any changes.
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/a"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/a"));
   prefs = profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
   EXPECT_EQ(prefs->size(), 1U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
 
   // Adding the same path for a new VM adds to the vm list.
-  crostini_share_path_->RegisterPersistedPath("v2", base::FilePath("/a/a/a"));
+  guest_os_share_path_->RegisterPersistedPath("v2", base::FilePath("/a/a/a"));
   EXPECT_EQ(prefs->size(), 1U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 2U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v1");
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[1].GetString(), "v2");
 
   // Add more paths.
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/b"));
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/c"));
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/b/a"));
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/b/a/a"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/b"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a/c"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/b/a"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/b/a/a"));
   EXPECT_EQ(prefs->size(), 5U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 2U);
   EXPECT_EQ(prefs->FindKey("/a/a/b")->GetList().size(), 1U);
@@ -658,7 +659,7 @@
   EXPECT_EQ(prefs->FindKey("/b/a/a")->GetList().size(), 1U);
 
   // Adding /a/a should remove /a/a/a, /a/a/b, /a/a/c.
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a/a"));
   EXPECT_EQ(prefs->size(), 4U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v2");
@@ -668,7 +669,7 @@
   EXPECT_EQ(prefs->FindKey("/a/a")->GetList()[0].GetString(), "v1");
 
   // Adding /a should remove /a/a, /a/b/a.
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/a"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/a"));
   EXPECT_EQ(prefs->size(), 3U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v2");
@@ -677,7 +678,7 @@
   EXPECT_EQ(prefs->FindKey("/a")->GetList()[0].GetString(), "v1");
 
   // Adding / should remove all others.
-  crostini_share_path_->RegisterPersistedPath("v1", base::FilePath("/"));
+  guest_os_share_path_->RegisterPersistedPath("v1", base::FilePath("/"));
   EXPECT_EQ(prefs->size(), 2U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList().size(), 1U);
   EXPECT_EQ(prefs->FindKey("/a/a/a")->GetList()[0].GetString(), "v2");
@@ -685,14 +686,14 @@
   EXPECT_EQ(prefs->FindKey("/")->GetList()[0].GetString(), "v1");
 
   // Add / for v2.
-  crostini_share_path_->RegisterPersistedPath("v2", base::FilePath("/"));
+  guest_os_share_path_->RegisterPersistedPath("v2", base::FilePath("/"));
   EXPECT_EQ(prefs->size(), 1U);
   EXPECT_EQ(prefs->FindKey("/")->GetList().size(), 2U);
   EXPECT_EQ(prefs->FindKey("/")->GetList()[0].GetString(), "v1");
   EXPECT_EQ(prefs->FindKey("/")->GetList()[1].GetString(), "v2");
 }
 
-TEST_F(CrostiniSharePathTest, UnsharePathSuccess) {
+TEST_F(GuestOsSharePathTest, UnsharePathSuccess) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
   DictionaryPrefUpdate update(profile()->GetPrefs(),
@@ -701,27 +702,27 @@
   base::Value vms(base::Value::Type::LIST);
   vms.GetList().emplace_back(base::Value("vm-running"));
   shared_paths->SetKey(shared_path_.value(), std::move(vms));
-  crostini_share_path_->UnsharePath(
+  guest_os_share_path_->UnsharePath(
       "vm-running", shared_path_, true,
-      base::BindOnce(&CrostiniSharePathTest::UnsharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
                      base::Unretained(this), shared_path_, Persist::NO,
                      SeneschalClientCalled::YES, "MyFiles/already-shared",
                      Success::YES, ""));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, UnsharePathRoot) {
+TEST_F(GuestOsSharePathTest, UnsharePathRoot) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  crostini_share_path_->UnsharePath(
+  guest_os_share_path_->UnsharePath(
       "vm-running", root_, true,
-      base::BindOnce(&CrostiniSharePathTest::UnsharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
                      base::Unretained(this), root_, Persist::NO,
                      SeneschalClientCalled::YES, "MyFiles", Success::YES, ""));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, UnsharePathVmNotRunning) {
+TEST_F(GuestOsSharePathTest, UnsharePathVmNotRunning) {
   SetUpVolume();
   DictionaryPrefUpdate update(profile()->GetPrefs(),
                               prefs::kGuestOSPathsSharedToVms);
@@ -729,16 +730,16 @@
   base::Value vms(base::Value::Type::LIST);
   vms.GetList().emplace_back(base::Value("vm-not-running"));
   shared_paths->SetKey(shared_path_.value(), std::move(vms));
-  crostini_share_path_->UnsharePath(
+  guest_os_share_path_->UnsharePath(
       "vm-not-running", shared_path_, true,
-      base::BindOnce(&CrostiniSharePathTest::UnsharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
                      base::Unretained(this), shared_path_, Persist::NO,
                      SeneschalClientCalled::NO, "", Success::YES,
                      "VM not running"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, UnsharePathPluginVmNotRunning) {
+TEST_F(GuestOsSharePathTest, UnsharePathPluginVmNotRunning) {
   SetUpVolume();
   DictionaryPrefUpdate update(profile()->GetPrefs(),
                               prefs::kGuestOSPathsSharedToVms);
@@ -746,28 +747,28 @@
   base::Value vms(base::Value::Type::LIST);
   vms.GetList().emplace_back(base::Value("PvmDefault"));
   shared_paths->SetKey(shared_path_.value(), std::move(vms));
-  crostini_share_path_->UnsharePath(
+  guest_os_share_path_->UnsharePath(
       "PvmDefault", shared_path_, true,
-      base::BindOnce(&CrostiniSharePathTest::UnsharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
                      base::Unretained(this), shared_path_, Persist::NO,
                      SeneschalClientCalled::NO, "", Success::YES,
                      "PluginVm not running"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, UnsharePathInvalidPath) {
+TEST_F(GuestOsSharePathTest, UnsharePathInvalidPath) {
   SetUpVolume();
   base::FilePath invalid("invalid/path");
-  crostini_share_path_->UnsharePath(
+  guest_os_share_path_->UnsharePath(
       "vm-running", invalid, true,
-      base::BindOnce(&CrostiniSharePathTest::UnsharePathCallback,
+      base::BindOnce(&GuestOsSharePathTest::UnsharePathCallback,
                      base::Unretained(this), invalid, Persist::NO,
                      SeneschalClientCalled::NO, "", Success::NO,
                      "Invalid path to unshare"));
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, MigratePersistedPathsToMultiVM) {
+TEST_F(GuestOsSharePathTest, MigratePersistedPathsToMultiVM) {
   SetUpVolume();
   base::ListValue shared_paths = base::ListValue();
   base::FilePath downloads_file = profile()->GetPath().Append("Downloads/file");
@@ -775,7 +776,7 @@
   base::FilePath not_downloads("/not/downloads");
   shared_paths.AppendString(not_downloads.value());
   profile()->GetPrefs()->Set(prefs::kCrostiniSharedPaths, shared_paths);
-  CrostiniSharePath::MigratePersistedPathsToMultiVM(profile()->GetPrefs());
+  GuestOsSharePath::MigratePersistedPathsToMultiVM(profile()->GetPrefs());
   EXPECT_EQ(
       profile()->GetPrefs()->GetList(prefs::kCrostiniSharedPaths)->GetSize(),
       0U);
@@ -790,7 +791,7 @@
             "termina");
 }
 
-TEST_F(CrostiniSharePathTest, GetPersistedSharedPaths) {
+TEST_F(GuestOsSharePathTest, GetPersistedSharedPaths) {
   SetUpVolume();
   // path1:['vm1'], path2:['vm2'], path3:['vm3'], path12:['vm1','vm2']
   base::Value shared_paths(base::Value::Type::DICTIONARY);
@@ -815,140 +816,140 @@
   profile()->GetPrefs()->Set(prefs::kGuestOSPathsSharedToVms, shared_paths);
 
   std::vector<base::FilePath> paths =
-      crostini_share_path_->GetPersistedSharedPaths("vm1");
+      guest_os_share_path_->GetPersistedSharedPaths("vm1");
   std::sort(paths.begin(), paths.end());
   EXPECT_EQ(paths.size(), 2U);
   EXPECT_EQ(paths[0], path1);
   EXPECT_EQ(paths[1], path12);
 
-  paths = crostini_share_path_->GetPersistedSharedPaths("vm2");
+  paths = guest_os_share_path_->GetPersistedSharedPaths("vm2");
   std::sort(paths.begin(), paths.end());
   EXPECT_EQ(paths.size(), 2U);
   EXPECT_EQ(paths[0], path12);
   EXPECT_EQ(paths[1], path2);
 
-  paths = crostini_share_path_->GetPersistedSharedPaths("vm3");
+  paths = guest_os_share_path_->GetPersistedSharedPaths("vm3");
   EXPECT_EQ(paths.size(), 1U);
   EXPECT_EQ(paths[0], path3);
 
-  paths = crostini_share_path_->GetPersistedSharedPaths("vm4");
+  paths = guest_os_share_path_->GetPersistedSharedPaths("vm4");
   EXPECT_EQ(paths.size(), 0U);
 }
 
-TEST_F(CrostiniSharePathTest, ShareOnMountSuccessParentMount) {
+TEST_F(GuestOsSharePathTest, ShareOnMountSuccessParentMount) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
-      kCrostiniDefaultVmName);
-  crostini_share_path_->set_mount_event_seneschal_callback_for_testing(
-      base::BindRepeating(&CrostiniSharePathTest::MountEventSharePathCallback,
+  crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
+      crostini::kCrostiniDefaultVmName);
+  guest_os_share_path_->set_mount_event_seneschal_callback_for_testing(
+      base::BindRepeating(&GuestOsSharePathTest::MountEventSharePathCallback,
                           base::Unretained(this), "share-on-mount",
-                          shared_path_, kCrostiniDefaultVmName, Persist::NO,
-                          SeneschalClientCalled::YES,
+                          shared_path_, crostini::kCrostiniDefaultVmName,
+                          Persist::NO, SeneschalClientCalled::YES,
                           &vm_tools::seneschal::SharePathRequest::MY_FILES,
                           "already-shared", Success::YES, ""));
-  crostini_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
+  guest_os_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
                                         *volume_downloads_);
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, ShareOnMountSuccessSelfMount) {
+TEST_F(GuestOsSharePathTest, ShareOnMountSuccessSelfMount) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
-      kCrostiniDefaultVmName);
+  crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
+      crostini::kCrostiniDefaultVmName);
   auto volume_shared_path =
       file_manager::Volume::CreateForDownloads(shared_path_);
-  crostini_share_path_->set_mount_event_seneschal_callback_for_testing(
-      base::BindRepeating(&CrostiniSharePathTest::MountEventSharePathCallback,
+  guest_os_share_path_->set_mount_event_seneschal_callback_for_testing(
+      base::BindRepeating(&GuestOsSharePathTest::MountEventSharePathCallback,
                           base::Unretained(this), "share-on-mount",
-                          shared_path_, kCrostiniDefaultVmName, Persist::NO,
-                          SeneschalClientCalled::YES,
+                          shared_path_, crostini::kCrostiniDefaultVmName,
+                          Persist::NO, SeneschalClientCalled::YES,
                           &vm_tools::seneschal::SharePathRequest::MY_FILES,
                           "already-shared", Success::YES, ""));
-  crostini_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
+  guest_os_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
                                         *volume_shared_path);
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, ShareOnMountVmNotRunning) {
+TEST_F(GuestOsSharePathTest, ShareOnMountVmNotRunning) {
   SetUpVolume();
 
   // Test mount.
-  crostini_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
+  guest_os_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
                                         *volume_downloads_);
   EXPECT_EQ(fake_seneschal_client_->share_path_called(), false);
 
   // Test unmount.
-  crostini_share_path_->OnVolumeUnmounted(
+  guest_os_share_path_->OnVolumeUnmounted(
       chromeos::MountError::MOUNT_ERROR_NONE, *volume_downloads_);
   EXPECT_EQ(fake_seneschal_client_->share_path_called(), false);
 }
 
-TEST_F(CrostiniSharePathTest, ShareOnMountVolumeUnrelated) {
+TEST_F(GuestOsSharePathTest, ShareOnMountVolumeUnrelated) {
   SetUpVolume();
   auto volume_unrelated_ = file_manager::Volume::CreateForDownloads(
       base::FilePath("/unrelated/path"));
 
   // Test mount.
-  crostini_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
+  guest_os_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
                                         *volume_unrelated_);
   EXPECT_EQ(fake_seneschal_client_->share_path_called(), false);
 
   // Test unmount.
-  crostini_share_path_->OnVolumeUnmounted(
+  guest_os_share_path_->OnVolumeUnmounted(
       chromeos::MountError::MOUNT_ERROR_NONE, *volume_unrelated_);
   EXPECT_EQ(fake_seneschal_client_->share_path_called(), false);
 }
 
-TEST_F(CrostiniSharePathTest, UnshareOnUnmountSuccessParentMount) {
+TEST_F(GuestOsSharePathTest, UnshareOnUnmountSuccessParentMount) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
-      kCrostiniDefaultVmName);
-  crostini_share_path_->set_mount_event_seneschal_callback_for_testing(
-      base::BindRepeating(&CrostiniSharePathTest::MountEventUnsharePathCallback,
+  crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
+      crostini::kCrostiniDefaultVmName);
+  guest_os_share_path_->set_mount_event_seneschal_callback_for_testing(
+      base::BindRepeating(&GuestOsSharePathTest::MountEventUnsharePathCallback,
                           base::Unretained(this), "unshare-on-unmount",
                           shared_path_, Persist::YES,
                           SeneschalClientCalled::YES, "MyFiles/already-shared",
                           Success::YES, ""));
-  crostini_share_path_->OnVolumeUnmounted(
+  guest_os_share_path_->OnVolumeUnmounted(
       chromeos::MountError::MOUNT_ERROR_NONE, *volume_downloads_);
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, UnshareOnUnmountSuccessSelfMount) {
+TEST_F(GuestOsSharePathTest, UnshareOnUnmountSuccessSelfMount) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
-      kCrostiniDefaultVmName);
+  crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
+      crostini::kCrostiniDefaultVmName);
   auto volume_shared_path =
       file_manager::Volume::CreateForDownloads(shared_path_);
-  crostini_share_path_->set_mount_event_seneschal_callback_for_testing(
-      base::BindRepeating(&CrostiniSharePathTest::MountEventUnsharePathCallback,
+  guest_os_share_path_->set_mount_event_seneschal_callback_for_testing(
+      base::BindRepeating(&GuestOsSharePathTest::MountEventUnsharePathCallback,
                           base::Unretained(this), "unshare-on-unmount",
                           shared_path_, Persist::YES,
                           SeneschalClientCalled::YES, "MyFiles/already-shared",
                           Success::YES, ""));
-  crostini_share_path_->OnVolumeUnmounted(
+  guest_os_share_path_->OnVolumeUnmounted(
       chromeos::MountError::MOUNT_ERROR_NONE, *volume_shared_path);
   run_loop()->Run();
 }
 
-TEST_F(CrostiniSharePathTest, UnshareOnDelete) {
+TEST_F(GuestOsSharePathTest, UnshareOnDelete) {
   features_.InitWithFeatures({chromeos::features::kMyFilesVolume}, {});
   SetUpVolume();
-  CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
-      kCrostiniDefaultVmName);
+  crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
+      crostini::kCrostiniDefaultVmName);
   auto volume_shared_path =
       file_manager::Volume::CreateForDownloads(shared_path_);
-  crostini_share_path_->set_mount_event_seneschal_callback_for_testing(
-      base::BindRepeating(&CrostiniSharePathTest::MountEventUnsharePathCallback,
+  guest_os_share_path_->set_mount_event_seneschal_callback_for_testing(
+      base::BindRepeating(&GuestOsSharePathTest::MountEventUnsharePathCallback,
                           base::Unretained(this), "unshare-on-delete",
                           shared_path_, Persist::NO, SeneschalClientCalled::YES,
                           "MyFiles/already-shared", Success::YES, ""));
-  crostini_share_path_->PathDeleted(shared_path_);
+  guest_os_share_path_->PathDeleted(shared_path_);
   run_loop()->Run();
 }
 
-}  // namespace crostini
+}  // namespace guest_os
diff --git a/chrome/browser/chromeos/input_method/ime_service_connector.cc b/chrome/browser/chromeos/input_method/ime_service_connector.cc
index d155cb1..9da44782 100644
--- a/chrome/browser/chromeos/input_method/ime_service_connector.cc
+++ b/chrome/browser/chromeos/input_method/ime_service_connector.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/input_method/ime_service_connector.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/files/file_util.h"
@@ -11,11 +12,36 @@
 #include "chromeos/services/ime/public/mojom/constants.mojom.h"
 #include "content/public/browser/system_connector.h"
 
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+
 namespace chromeos {
 namespace input_method {
 
 namespace {
 
+constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
+    net::DefineNetworkTrafficAnnotation("ime_url_downloader", R"(
+    semantics {
+      sender: "IME Service Downloader"
+      description:
+        "When user selects a new input method in ChromeOS, it may request a"
+        "corresponding language module downloaded if it does not exist."
+      trigger: "User switches to an input method without language module."
+      data:
+        "The language module download URL. No user identifier is sent."
+      destination: GOOGLE_OWNED_SERVICE
+    }
+    policy {
+      cookies_allowed: NO
+      policy_exception_justification:
+        "Not implemented, considered not useful."
+    })");
+
 bool IsImePathInvalid(const base::FilePath& file_path) {
   // Only non-empty, relative path which doesn't reference a parent is allowed.
   return file_path.empty() || file_path.IsAbsolute() ||
@@ -32,6 +58,7 @@
 
 ImeServiceConnector::ImeServiceConnector(Profile* profile)
     : profile_(profile),
+      url_loader_factory_(profile->GetURLLoaderFactory()),
       instance_id_(base::Token::CreateRandom()),
       access_(this) {}
 
@@ -41,17 +68,35 @@
     const GURL& url,
     const base::FilePath& file_path,
     DownloadImeFileToCallback callback) {
-  // TODO(https://crbug.com/837156): Download file by the network service.
+  // For now, we don't allow the client to download multi files at same time.
+  // Downloading request will be aborted and return empty before the current
+  // downloading task exits.
+  // TODO(https://crbug.com/971954): Support multi downloads.
   // Validate url and file_path, return an empty file path if not.
-  if (IsURLInvalid(url) || IsImePathInvalid(file_path)) {
+  if (url_loader_ || IsURLInvalid(url) || IsImePathInvalid(file_path)) {
     base::FilePath empty_path;
     std::move(callback).Run(empty_path);
     return;
   }
 
-  // Final path always starts from profile path.
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = url;
+  // Disable cookies for this request.
+  resource_request->load_flags =
+      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES |
+      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
+
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 traffic_annotation);
+
+  // Download the language module into a preconfigured ime folder of current
+  // user's home which is allowed in IME service's sandbox.
   base::FilePath full_path = profile_->GetPath().Append(file_path);
-  std::move(callback).Run(full_path);
+  url_loader_->DownloadToFile(
+      url_loader_factory_.get(),
+      base::BindOnce(&ImeServiceConnector::OnFileDownloadComplete,
+                     base::Unretained(this), std::move(callback)),
+      full_path);
 }
 
 void ImeServiceConnector::SetupImeService(
@@ -79,5 +124,13 @@
   access_client_.reset();
 }
 
+void ImeServiceConnector::OnFileDownloadComplete(
+    DownloadImeFileToCallback client_callback,
+    base::FilePath path) {
+  std::move(client_callback).Run(path);
+  url_loader_.reset();
+  return;
+}
+
 }  // namespace input_method
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/ime_service_connector.h b/chrome/browser/chromeos/input_method/ime_service_connector.h
index 7ee2529..c68f30c 100644
--- a/chrome/browser/chromeos/input_method/ime_service_connector.h
+++ b/chrome/browser/chromeos/input_method/ime_service_connector.h
@@ -14,9 +14,13 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
-#include "services/service_manager/public/cpp/connector.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace chromeos {
 
 namespace input_method {
@@ -36,11 +40,19 @@
   void SetupImeService(
       mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver);
 
+  void OnFileDownloadComplete(DownloadImeFileToCallback client_callback,
+                              base::FilePath path);
+
  private:
   void OnPlatformAccessConnectionLost();
 
   Profile* profile_;
 
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // The current request in progress, or NULL.
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+
   // There is 1:1 mapping from the instance IDs to IME services running out of
   // process.
   const base::Token instance_id_;
diff --git a/chrome/browser/chromeos/kiosk_next_home/OWNERS b/chrome/browser/chromeos/kiosk_next_home/OWNERS
deleted file mode 100644
index 7fb73f6..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-file://ash/kiosk_next/OWNERS
-
-# COMPONENT: UI>Shell>KioskNext
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
deleted file mode 100644
index 03825da85..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/app_controller_service.h"
-
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/optional.h"
-#include "base/strings/string_util.h"
-#include "chrome/browser/apps/app_service/app_icon_source.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.h"
-#include "chrome/browser/chromeos/kiosk_next_home/intent_config_helper.h"
-#include "chrome/browser/chromeos/kiosk_next_home/metrics_helper.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
-#include "chrome/services/app_service/public/mojom/types.mojom.h"
-#include "components/arc/arc_service_manager.h"
-#include "components/arc/common/app.mojom.h"
-#include "components/arc/session/arc_bridge_service.h"
-#include "content/public/browser/url_data_source.h"
-#include "ui/display/types/display_constants.h"
-#include "ui/events/event_constants.h"
-#include "url/gurl.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-namespace {
-
-// Returns AppInstance from ArcBridgeService if available, or nullptr.
-arc::mojom::AppInstance* GetArcAppInstanceForLaunchIntent() {
-  return arc::ArcServiceManager::Get()
-             ? ARC_GET_INSTANCE_FOR_METHOD(
-                   arc::ArcServiceManager::Get()->arc_bridge_service()->app(),
-                   LaunchIntent)
-             : nullptr;
-}
-
-void RecordLaunchIntentResult(LaunchIntentResult result) {
-  UMA_HISTOGRAM_ENUMERATION("KioskNextHome.Bridge.LaunchIntentResult", result);
-}
-
-}  // namespace
-
-// static
-AppControllerService* AppControllerService::Get(
-    content::BrowserContext* context) {
-  return AppControllerServiceFactory::GetForBrowserContext(context);
-}
-
-AppControllerService::AppControllerService(Profile* profile)
-    : profile_(profile),
-      app_service_proxy_(apps::AppServiceProxyFactory::GetForProfile(profile)),
-      intent_config_helper_(IntentConfigHelper::GetInstance()) {
-  DCHECK(profile);
-  Observe(&app_service_proxy_->AppRegistryCache());
-
-  // ArcServiceManager is always set in production code.
-  arc::ArcServiceManager::Get()->arc_bridge_service()->app()->AddObserver(this);
-
-  // Add the chrome://app-icon URL data source.
-  // TODO(ltenorio): Move this to a more suitable location when we change
-  // the Kiosk Next Home to WebUI.
-  content::URLDataSource::Add(profile,
-                              std::make_unique<apps::AppIconSource>(profile));
-}
-
-AppControllerService::~AppControllerService() {
-  arc::ArcServiceManager::Get()->arc_bridge_service()->app()->RemoveObserver(
-      this);
-}
-
-void AppControllerService::BindRequest(mojom::AppControllerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
-void AppControllerService::GetApps(
-    mojom::AppController::GetAppsCallback callback) {
-  RecordBridgeAction(BridgeAction::kListApps);
-  std::vector<chromeos::kiosk_next_home::mojom::AppPtr> app_list;
-  // Using AppUpdate objects here since that's how the app list is intended to
-  // be consumed. Refer to AppRegistryCache::ForEachApp for more information.
-  app_service_proxy_->AppRegistryCache().ForEachApp(
-      [this, &app_list](const apps::AppUpdate& update) {
-        // Only include apps that are both relevant and installed.
-        if (AppIsRelevantForKioskNextHome(update) &&
-            update.Readiness() != apps::mojom::Readiness::kUninstalledByUser) {
-          app_list.push_back(CreateAppPtr(update));
-        }
-      });
-  std::move(callback).Run(std::move(app_list));
-}
-
-void AppControllerService::SetClient(mojom::AppControllerClientPtr client) {
-  client_ = std::move(client);
-
-  client_->OnArcStatusChanged(arc_status_);
-}
-
-void AppControllerService::LaunchApp(const std::string& app_id) {
-  RecordBridgeAction(BridgeAction::kLaunchApp);
-  app_service_proxy_->Launch(app_id, ui::EventFlags::EF_NONE,
-                             apps::mojom::LaunchSource::kFromKioskNextHome,
-                             display::kDefaultDisplayId);
-}
-
-void AppControllerService::UninstallApp(const std::string& app_id) {
-  RecordBridgeAction(BridgeAction::kUninstallApp);
-  app_service_proxy_->Uninstall(app_id);
-}
-
-void AppControllerService::GetArcAndroidId(
-    mojom::AppController::GetArcAndroidIdCallback callback) {
-  RecordBridgeAction(BridgeAction::kGetAndroidId);
-  arc::GetAndroidId(base::BindOnce(
-      [](mojom::AppController::GetArcAndroidIdCallback callback, bool success,
-         int64_t raw_android_id) {
-        UMA_HISTOGRAM_BOOLEAN("KioskNextHome.Bridge.GetAndroidIdSuccess",
-                              success);
-        // The bridge expects the Android id as a hex string.
-        std::stringstream android_id_stream;
-        android_id_stream << std::hex << raw_android_id;
-        std::move(callback).Run(success, android_id_stream.str());
-      },
-      std::move(callback)));
-}
-
-void AppControllerService::LaunchIntent(const std::string& intent,
-                                        LaunchIntentCallback callback) {
-  RecordBridgeAction(BridgeAction::kLaunchIntent);
-  GURL intent_uri(intent);
-  if (!intent_config_helper_->IsIntentAllowed(intent_uri)) {
-    RecordLaunchIntentResult(LaunchIntentResult::kNotAllowed);
-    std::move(callback).Run(false, "Intent not allowed.");
-    return;
-  }
-
-  arc::mojom::AppInstance* app_instance = GetArcAppInstanceForLaunchIntent();
-  if (!app_instance) {
-    RecordLaunchIntentResult(LaunchIntentResult::kArcUnavailable);
-    std::move(callback).Run(false, "ARC bridge not available.");
-    return;
-  }
-
-  RecordLaunchIntentResult(LaunchIntentResult::kSuccess);
-  app_instance->LaunchIntent(intent_uri.spec(), display::kDefaultDisplayId);
-  std::move(callback).Run(true, base::nullopt);
-}
-
-void AppControllerService::OnAppUpdate(const apps::AppUpdate& update) {
-  // Skip this event if there were no changes to the fields that we are
-  // interested in.
-  if (!update.StateIsNull() && !update.NameChanged() &&
-      !update.ReadinessChanged() && !update.ShowInLauncherChanged()) {
-    return;
-  }
-
-  // Skip this app if it's not relevant.
-  if (!AppIsRelevantForKioskNextHome(update))
-    return;
-
-  if (client_) {
-    RecordBridgeAction(BridgeAction::kNotifiedAppChange);
-    client_->OnAppChanged(CreateAppPtr(update));
-  }
-}
-
-void AppControllerService::OnAppRegistryCacheWillBeDestroyed(
-    apps::AppRegistryCache* cache) {
-  Observe(nullptr);
-}
-
-void AppControllerService::OnConnectionReady() {
-  arc_status_ = mojom::ArcStatus::kReady;
-  if (client_)
-    client_->OnArcStatusChanged(arc_status_);
-}
-
-void AppControllerService::OnConnectionClosed() {
-  arc_status_ = mojom::ArcStatus::kStopped;
-  if (client_)
-    client_->OnArcStatusChanged(arc_status_);
-}
-
-void AppControllerService::SetIntentConfigHelperForTesting(
-    std::unique_ptr<IntentConfigHelper> helper) {
-  intent_config_helper_ = std::move(helper);
-}
-
-mojom::AppPtr AppControllerService::CreateAppPtr(
-    const apps::AppUpdate& update) {
-  auto app = chromeos::kiosk_next_home::mojom::App::New();
-  app->app_id = update.AppId();
-  app->type = update.AppType();
-  app->display_name = update.Name();
-  app->readiness = update.Readiness();
-
-  if (app->type == apps::mojom::AppType::kArc)
-    app->android_package_name = MaybeGetAndroidPackageName(app->app_id);
-
-  return app;
-}
-
-bool AppControllerService::AppIsRelevantForKioskNextHome(
-    const apps::AppUpdate& update) {
-  // The Kiosk Next Home app should never be returned since it's considered an
-  // implementation detail.
-  if (update.AppId() == extension_misc::kKioskNextHomeAppId)
-    return false;
-
-  // We only consider relevant apps that can be shown in the launcher.
-  // This skips hidden apps like Galery, Web store, Welcome app, etc.
-  return update.ShowInLauncher() == apps::mojom::OptionalBool::kTrue;
-}
-
-const std::string& AppControllerService::MaybeGetAndroidPackageName(
-    const std::string& app_id) {
-  // Try to find a cached package name for this app.
-  const auto& package_name_it = android_package_map_.find(app_id);
-  if (package_name_it != android_package_map_.end()) {
-    return package_name_it->second;
-  }
-
-  // If we don't find it, try to get the package name from ARC prefs.
-  std::string package_name = arc::AppIdToArcPackageName(app_id, profile_);
-  if (package_name.empty()) {
-    return base::EmptyString();
-  }
-
-  // Now that we have a valid package name, update our caches.
-  android_package_map_[app_id] = package_name;
-  return android_package_map_[app_id];
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.h b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.h
deleted file mode 100644
index 4ad42f3..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_APP_CONTROLLER_SERVICE_H_
-#define CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_APP_CONTROLLER_SERVICE_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom.h"
-#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
-#include "components/arc/common/app.mojom.h"
-#include "components/arc/session/connection_observer.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-
-namespace content {
-class BrowserContext;
-}
-
-class Profile;
-
-namespace apps {
-class AppServiceProxy;
-class AppUpdate;
-}  // namespace apps
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-class IntentConfigHelper;
-
-// Service implementation for the Kiosk Next AppController.
-// This class is responsible for managing Chrome OS apps and returning useful
-// information about them to the Kiosk Next Home.
-class AppControllerService
-    : public mojom::AppController,
-      public KeyedService,
-      public apps::AppRegistryCache::Observer,
-      public arc::ConnectionObserver<arc::mojom::AppInstance> {
- public:
-  // Returns the AppControllerService singleton attached to this |context|.
-  static AppControllerService* Get(content::BrowserContext* context);
-
-  explicit AppControllerService(Profile* profile);
-  ~AppControllerService() override;
-
-  // Binds this service to an AppController request.
-  void BindRequest(mojom::AppControllerRequest request);
-
-  // mojom::AppController:
-  void GetApps(mojom::AppController::GetAppsCallback callback) override;
-  void SetClient(mojom::AppControllerClientPtr client) override;
-  void LaunchApp(const std::string& app_id) override;
-  void UninstallApp(const std::string& app_id) override;
-  void GetArcAndroidId(
-      mojom::AppController::GetArcAndroidIdCallback callback) override;
-  void LaunchIntent(const std::string& intent,
-                    LaunchIntentCallback callback) override;
-
-  // apps::AppRegistryCache::Observer:
-  void OnAppUpdate(const apps::AppUpdate& update) override;
-  void OnAppRegistryCacheWillBeDestroyed(
-      apps::AppRegistryCache* cache) override;
-
-  // arc::ConnectionObserver:
-  void OnConnectionReady() override;
-  void OnConnectionClosed() override;
-
-  // Allows overriding the intent config helper for tests.
-  void SetIntentConfigHelperForTesting(
-      std::unique_ptr<IntentConfigHelper> helper);
-
- private:
-  // Creates a new Kiosk Next App from a delta app update coming from
-  // AppServiceProxy.
-  mojom::AppPtr CreateAppPtr(const apps::AppUpdate& update);
-
-  bool AppIsRelevantForKioskNextHome(const apps::AppUpdate& update);
-
-  // Tries to get the Android package name for this app from ARC++.
-  // If we can't find the package name or this is not an Android app we return
-  // an empty string.
-  const std::string& MaybeGetAndroidPackageName(const std::string& app_id);
-
-  Profile* profile_;
-  mojo::BindingSet<mojom::AppController> bindings_;
-  mojom::AppControllerClientPtr client_;
-  apps::AppServiceProxy* app_service_proxy_;
-  std::unique_ptr<IntentConfigHelper> intent_config_helper_;
-
-  // Map of app ids to android packages.
-  //
-  // Since app ids and packages are stable, we can store them here
-  // and avoid queries to Android prefs.
-  // This cache also takes care of the case when an Android app is uninstalled
-  // and we need to notify the bridge.
-  // In that case we only receive the notification from the AppService after
-  // ARC++ already lost the information about app, so keeping a cache on our end
-  // is necessary.
-  std::map<std::string, std::string> android_package_map_;
-
-  // The current arc status that is reported to observers.
-  mojom::ArcStatus arc_status_ = mojom::ArcStatus::kStopped;
-
-  DISALLOW_COPY_AND_ASSIGN(AppControllerService);
-};
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_APP_CONTROLLER_SERVICE_H_
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.cc
deleted file mode 100644
index 87d279e..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.h"
-
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/chromeos/kiosk_next_home/app_controller_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-// static
-AppControllerService* AppControllerServiceFactory::GetForBrowserContext(
-    content::BrowserContext* context) {
-  return static_cast<AppControllerService*>(
-      GetInstance()->GetServiceForBrowserContext(context, true));
-}
-
-// static
-AppControllerServiceFactory* AppControllerServiceFactory::GetInstance() {
-  static base::NoDestructor<AppControllerServiceFactory> factory;
-  return factory.get();
-}
-
-AppControllerServiceFactory::AppControllerServiceFactory()
-    : BrowserContextKeyedServiceFactory(
-          "KioskNextAppControllerServiceFactory",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(apps::AppServiceProxyFactory::GetInstance());
-  DependsOn(ArcAppListPrefsFactory::GetInstance());
-}
-
-AppControllerServiceFactory::~AppControllerServiceFactory() = default;
-
-KeyedService* AppControllerServiceFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  return new AppControllerService(Profile::FromBrowserContext(context));
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.h b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.h
deleted file mode 100644
index 9c90186..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_APP_CONTROLLER_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_APP_CONTROLLER_SERVICE_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/no_destructor.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-class AppControllerService;
-
-// Singleton that owns and provides AppControllerService instances.
-class AppControllerServiceFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static AppControllerService* GetForBrowserContext(
-      content::BrowserContext* context);
-
-  static AppControllerServiceFactory* GetInstance();
-
- private:
-  friend class base::NoDestructor<AppControllerServiceFactory>;
-
-  AppControllerServiceFactory();
-  ~AppControllerServiceFactory() override;
-
-  // BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(AppControllerServiceFactory);
-};
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_APP_CONTROLLER_SERVICE_FACTORY_H_
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
deleted file mode 100644
index 89502a75..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
+++ /dev/null
@@ -1,790 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/app_controller_service.h"
-
-#include <limits>
-#include <map>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/optional.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/chromeos/kiosk_next_home/intent_config_helper.h"
-#include "chrome/browser/chromeos/kiosk_next_home/metrics_helper.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/common/extensions/extension_constants.h"
-#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
-#include "chrome/services/app_service/public/cpp/app_update.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/arc/test/fake_app_instance.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/display/types/display_constants.h"
-#include "ui/events/event_constants.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-namespace {
-
-// Fake activity that we use when seeding data to ARC.
-constexpr char kFakeActivity[] = "test.kiosk_next_home.activity";
-
-using apps::mojom::AppType;
-using apps::mojom::OptionalBool;
-using apps::mojom::Readiness;
-
-typedef std::map<std::string, mojom::AppPtr> AppMap;
-
-}  // namespace
-
-class FakeAppControllerClient : public mojom::AppControllerClient {
- public:
-  explicit FakeAppControllerClient(mojom::AppControllerClientRequest request)
-      : binding_(this, std::move(request)) {}
-
-  const std::vector<mojom::AppPtr>& app_updates() { return app_updates_; }
-  const std::vector<mojom::ArcStatus>& arc_status() { return arc_status_; }
-
-  // mojom::AppControllerClient:
-  void OnAppChanged(mojom::AppPtr app) override {
-    app_updates_.push_back(std::move(app));
-  }
-
-  void OnArcStatusChanged(mojom::ArcStatus status) override {
-    arc_status_.push_back(status);
-  }
-
-  // Flushes the internal mojo message pipe. Call this any time you trigger a
-  // new mojo method, so the test waits for the full communication to occur.
-  void Flush() { binding_.FlushForTesting(); }
-
- private:
-  mojo::Binding<mojom::AppControllerClient> binding_;
-  std::vector<mojom::AppPtr> app_updates_;
-  std::vector<mojom::ArcStatus> arc_status_;
-};
-
-// Mock instance for the AppServiceProxy. It only overrides a subset of the
-// methods provided by the proxy since we expect that most tests can be written
-// with their real implementations (i.e. AppRegistryCache()).
-class MockAppServiceProxy : public KeyedService, public apps::AppServiceProxy {
- public:
-  static MockAppServiceProxy* OverrideRealProxyForProfile(Profile* profile) {
-    return static_cast<MockAppServiceProxy*>(
-        apps::AppServiceProxyFactory::GetInstance()->SetTestingFactoryAndUse(
-            profile, base::BindRepeating([](content::BrowserContext* context) {
-              return static_cast<std::unique_ptr<KeyedService>>(
-                  std::make_unique<MockAppServiceProxy>());
-            })));
-  }
-
-  // apps::AppServiceProxy:
-  MOCK_METHOD4(Launch,
-               void(const std::string& app_id,
-                    int32_t event_flags,
-                    apps::mojom::LaunchSource launch_source,
-                    int64_t display_id));
-  MOCK_METHOD1(Uninstall, void(const std::string& app_id));
-};
-
-class MockIntentConfigHelper : public IntentConfigHelper {
- public:
-  // IntentConfigHelper:
-  MOCK_CONST_METHOD1(IsIntentAllowed, bool(const GURL& intent_uri));
-};
-
-class AppControllerServiceTest : public testing::Test {
- protected:
-  void SetUp() override {
-    profile_ = std::make_unique<TestingProfile>();
-
-    arc_test_.SetUp(profile());
-    proxy_ = MockAppServiceProxy::OverrideRealProxyForProfile(profile());
-
-    app_controller_service_ = std::make_unique<AppControllerService>(profile());
-    ResetClient();
-  }
-
-  void TearDown() override {
-    // We need to mimic the destruction order from production code, first the
-    // AppControllerService, then arc.
-    app_controller_service_.reset();
-    arc_test_.TearDown();
-  }
-
-  Profile* profile() { return profile_.get(); }
-
-  MockAppServiceProxy* proxy() { return proxy_; }
-
-  AppControllerService* service() { return app_controller_service_.get(); }
-
-  void ResetClient() {
-    mojom::AppControllerClientPtr client_proxy;
-    client_ = std::make_unique<FakeAppControllerClient>(
-        mojo::MakeRequest(&client_proxy));
-    app_controller_service_->SetClient(std::move(client_proxy));
-    client_->Flush();
-  }
-
-  std::string GetAppIdFromAndroidPackage(const std::string& package) {
-    return ArcAppListPrefs::GetAppId(package, kFakeActivity);
-  }
-
-  void AddAndroidPackageToArc(const std::string& package) {
-    arc::mojom::AppInfo app_info;
-    app_info.package_name = package;
-
-    // We are only interested in the package name that we already set above,
-    // but we need to send a full struct so ARC doesn't drop it.
-    app_info.name = "test_app_name";
-    app_info.activity = kFakeActivity;
-    app_info.sticky = false;
-    app_info.notifications_enabled = false;
-    arc_test_.app_instance()->SendAppAdded(app_info);
-  }
-
-  void SetAndroidId(int64_t android_id) {
-    arc_test_.app_instance()->set_android_id(android_id);
-  }
-
-  void StopArc() {
-    arc_test_.StopArcInstance();
-    client_->Flush();
-  }
-
-  void RestartArc() {
-    arc_test_.RestartArcInstance();
-    client_->Flush();
-  }
-
-  void AddAppDeltaToAppService(apps::mojom::AppPtr delta) {
-    std::vector<apps::mojom::AppPtr> deltas;
-    deltas.push_back(std::move(delta));
-    proxy_->AppRegistryCache().OnApps(std::move(deltas));
-
-    client_->Flush();
-  }
-
-  // Gets all apps from the AppControllerService instance being tested and
-  // returns them in a map keyed by their |app_id|.
-  AppMap GetAppsFromController() {
-    AppMap apps;
-    service()->GetApps(base::BindLambdaForTesting(
-        [&apps](std::vector<mojom::AppPtr> app_list) {
-          for (const auto& app : app_list) {
-            apps[app->app_id] = app.Clone();
-          }
-        }));
-    return apps;
-  }
-
-  // Expects the given apps to be returned by a call to
-  // AppControllerService::GetApps(). This function doesn't take into account
-  // the order of the returned apps.
-  void ExpectApps(const std::vector<mojom::App>& expected_apps) {
-    AppMap returned_apps_map = GetAppsFromController();
-
-    EXPECT_EQ(expected_apps.size(), returned_apps_map.size())
-        << "AppServiceController::GetApps() returned wrong number of apps.";
-
-    for (const auto& expected_app : expected_apps) {
-      auto returned_app_it = returned_apps_map.find(expected_app.app_id);
-      ASSERT_NE(returned_app_it, returned_apps_map.end())
-          << "App with app_id " << expected_app.app_id
-          << " was not returned by the AppControllerService::GetApps() "
-             "call.";
-
-      mojom::AppPtr& returned_app = returned_app_it->second;
-
-      ExpectEqualApps(expected_app, *returned_app);
-    }
-  }
-
-  void ExpectArcAndroidIdResponse(bool success, const std::string& android_id) {
-    bool returned_success;
-    std::string returned_android_id;
-
-    service()->GetArcAndroidId(base::BindLambdaForTesting(
-        [&returned_success, &returned_android_id](
-            bool success, const std::string& android_id) {
-          returned_success = success;
-          returned_android_id = android_id;
-        }));
-
-    EXPECT_EQ(returned_success, success);
-    EXPECT_EQ(returned_android_id, android_id);
-  }
-
-  void SetLaunchIntentAllowed(bool allowed) {
-    auto intent_config_helper = std::make_unique<MockIntentConfigHelper>();
-    EXPECT_CALL(*intent_config_helper, IsIntentAllowed(testing::_))
-        .WillOnce(testing::Return(allowed));
-    service()->SetIntentConfigHelperForTesting(std::move(intent_config_helper));
-  }
-
-  void ExpectLaunchIntentResponse(
-      const std::string& intent_uri,
-      bool success,
-      const base::Optional<std::string>& error_message) {
-    bool returned_success;
-    base::Optional<std::string> returned_error_message;
-
-    service()->LaunchIntent(
-        intent_uri, base::BindLambdaForTesting(
-                        [&returned_success, &returned_error_message](
-                            bool success,
-                            const base::Optional<std::string>& error_message) {
-                          returned_success = success;
-                          returned_error_message = error_message;
-                        }));
-
-    EXPECT_EQ(returned_success, success);
-
-    ASSERT_EQ(returned_error_message.has_value(), error_message.has_value());
-    if (returned_error_message.has_value())
-      EXPECT_EQ(returned_error_message.value(), error_message.value());
-  }
-
-  void ExpectNoLaunchedIntents() {
-    EXPECT_EQ(0U, arc_test_.app_instance()->launch_intents().size())
-        << "At least one ARC intent was lauched, we expected none.";
-  }
-
-  void ExpectIntentLaunched(const std::string& intent_uri) {
-    ASSERT_EQ(arc_test_.app_instance()->launch_intents().size(), 1U)
-        << "We expect exactly one ARC intent to be launched.";
-    EXPECT_EQ(arc_test_.app_instance()->launch_intents()[0], intent_uri);
-  }
-
-  // Expects the given apps were passed to the
-  // AppControllerClient::OnAppChanged method in order.
-  void ExpectAppChangedUpdates(
-      const std::vector<mojom::App>& expected_updates) {
-    ASSERT_EQ(expected_updates.size(), client_->app_updates().size());
-
-    for (std::size_t i = 0; i < expected_updates.size(); ++i) {
-      ExpectEqualApps(expected_updates[i], *(client_->app_updates()[i]));
-    }
-  }
-
-  void ExpectArcStatusUpdates(
-      const std::vector<mojom::ArcStatus>& expected_updates) {
-    ASSERT_EQ(expected_updates.size(), client_->arc_status().size());
-
-    for (std::size_t i = 0; i < expected_updates.size(); ++i) {
-      EXPECT_EQ(expected_updates[i], client_->arc_status()[i]);
-    }
-  }
-
-  void ExpectBridgeActionRecorded(BridgeAction action, int count) {
-    // Since seeding apps may fire bridge actions, check only the bucket of
-    // interest.
-    histogram_tester_.ExpectBucketCount("KioskNextHome.Bridge.Action", action,
-                                        count);
-  }
-
-  void ExpectLaunchIntentResultRecorded(LaunchIntentResult result) {
-    histogram_tester_.ExpectUniqueSample(
-        "KioskNextHome.Bridge.LaunchIntentResult", result, 1);
-  }
-
-  void ExpectGetAndroidIdSuccessRecorded(bool success, int count) {
-    histogram_tester_.ExpectUniqueSample(
-        "KioskNextHome.Bridge.GetAndroidIdSuccess", success, count);
-  }
-
- private:
-  content::TestBrowserThreadBundle test_browser_thread_bundle_;
-  std::unique_ptr<TestingProfile> profile_;
-  ArcAppTest arc_test_;
-  MockAppServiceProxy* proxy_ = nullptr;
-  std::unique_ptr<AppControllerService> app_controller_service_;
-  std::unique_ptr<FakeAppControllerClient> client_;
-  base::HistogramTester histogram_tester_;
-
-  void ExpectEqualApps(const mojom::App& expected_app,
-                       const mojom::App& actual_app) {
-    // Test equality of every single field to make tests failures more
-    // readable.
-    EXPECT_EQ(actual_app.app_id, expected_app.app_id);
-    EXPECT_EQ(actual_app.type, expected_app.type);
-    EXPECT_EQ(actual_app.display_name, expected_app.display_name);
-    EXPECT_EQ(actual_app.readiness, expected_app.readiness);
-    EXPECT_EQ(actual_app.android_package_name,
-              expected_app.android_package_name);
-
-    // Catch all clause of equality. This will only be necessary if we add
-    // more fields that are not expected above.
-    ASSERT_TRUE(expected_app.Equals(actual_app));
-  }
-};
-
-TEST_F(AppControllerServiceTest, AppIsFetchedCorrectly) {
-  std::string app_id = "fake_app_id";
-  std::string display_name = "Fake app name";
-  AppType app_type = AppType::kExtension;
-  Readiness readiness = Readiness::kReady;
-
-  // Seeding data.
-  apps::mojom::App delta;
-  delta.app_id = app_id;
-  delta.name = display_name;
-  delta.app_type = app_type;
-  delta.readiness = readiness;
-  delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(delta.Clone());
-
-  mojom::App expected_app;
-  expected_app.android_package_name = "";
-  expected_app.app_id = app_id;
-  expected_app.display_name = display_name;
-  expected_app.type = app_type;
-  expected_app.readiness = readiness;
-
-  ExpectApps({expected_app});
-  ExpectBridgeActionRecorded(BridgeAction::kListApps, 1);
-}
-
-TEST_F(AppControllerServiceTest, AndroidAppIsFetchedCorrectly) {
-  std::string android_package_name = "fake.app.package";
-  std::string app_id = GetAppIdFromAndroidPackage(android_package_name);
-  std::string display_name = "Fake app name";
-  AppType app_type = AppType::kArc;
-  Readiness readiness = Readiness::kReady;
-
-  // Seeding data.
-  AddAndroidPackageToArc(android_package_name);
-  apps::mojom::App delta;
-  delta.app_id = app_id;
-  delta.name = display_name;
-  delta.app_type = app_type;
-  delta.readiness = readiness;
-  delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(delta.Clone());
-
-  mojom::App expected_app;
-  expected_app.android_package_name = android_package_name;
-  expected_app.app_id = app_id;
-  expected_app.display_name = display_name;
-  expected_app.type = app_type;
-  expected_app.readiness = readiness;
-
-  ExpectApps({expected_app});
-  ExpectBridgeActionRecorded(BridgeAction::kListApps, 1);
-}
-
-TEST_F(AppControllerServiceTest, AndroidAppWithMissingPackageFetchedCorrectly) {
-  std::string android_package_name = "fake.app.package";
-  std::string app_id = GetAppIdFromAndroidPackage(android_package_name);
-  std::string display_name = "Fake app name";
-  AppType app_type = AppType::kArc;
-  Readiness readiness = Readiness::kReady;
-
-  // Seeding data. This time we intentionally don't seed the package to ARC.
-  apps::mojom::App delta;
-  delta.app_id = app_id;
-  delta.name = display_name;
-  delta.app_type = app_type;
-  delta.readiness = readiness;
-  delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(delta.Clone());
-
-  mojom::App expected_app;
-  // Since we don't seed information to ARC, we don't expect to receive a
-  // package here. In this case, expect an empty string (and not a crash :)
-  expected_app.android_package_name = "";
-  expected_app.app_id = app_id;
-  expected_app.display_name = display_name;
-  expected_app.type = app_type;
-  expected_app.readiness = readiness;
-
-  ExpectApps({expected_app});
-}
-
-TEST_F(AppControllerServiceTest, AppReadinessIsUpdated) {
-  std::string app_id = "fake_app_id";
-  AppType app_type = AppType::kExtension;
-  Readiness readiness = Readiness::kUnknown;
-
-  apps::mojom::App delta;
-  delta.app_id = app_id;
-  delta.app_type = app_type;
-  delta.readiness = readiness;
-  delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(delta.Clone());
-  ExpectBridgeActionRecorded(BridgeAction::kNotifiedAppChange, 1);
-
-  mojom::App expected_app;
-  expected_app.app_id = app_id;
-  expected_app.type = app_type;
-  expected_app.readiness = readiness;
-  ExpectApps({expected_app});
-
-  // Now we change the readiness.
-  delta.readiness = Readiness::kReady;
-  AddAppDeltaToAppService(delta.Clone());
-  ExpectBridgeActionRecorded(BridgeAction::kNotifiedAppChange, 2);
-
-  expected_app.readiness = Readiness::kReady;
-  ExpectApps({expected_app});
-  ExpectBridgeActionRecorded(BridgeAction::kListApps, 2);
-}
-
-TEST_F(AppControllerServiceTest, AppDisplayNameIsUpdated) {
-  std::string app_id = "fake_app_id";
-  AppType app_type = AppType::kExtension;
-  std::string display_name = "Initial App Name";
-
-  apps::mojom::App delta;
-  delta.app_id = app_id;
-  delta.app_type = app_type;
-  delta.name = display_name;
-  delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(delta.Clone());
-  ExpectBridgeActionRecorded(BridgeAction::kNotifiedAppChange, 1);
-
-  mojom::App expected_app;
-  expected_app.app_id = app_id;
-  expected_app.type = app_type;
-  expected_app.display_name = display_name;
-  ExpectApps({expected_app});
-
-  // Now we change the name.
-  std::string new_display_name = "New App Name";
-  delta.name = new_display_name;
-  AddAppDeltaToAppService(delta.Clone());
-  ExpectBridgeActionRecorded(BridgeAction::kNotifiedAppChange, 2);
-
-  expected_app.display_name = new_display_name;
-  ExpectApps({expected_app});
-  ExpectBridgeActionRecorded(BridgeAction::kListApps, 2);
-}
-
-TEST_F(AppControllerServiceTest, MultipleAppsAreFetchedCorrectly) {
-  // Seed the first app.
-  apps::mojom::App first_delta;
-  first_delta.app_id = "first_app";
-  first_delta.app_type = AppType::kBuiltIn;
-  first_delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(first_delta.Clone());
-
-  mojom::App first_expected_app;
-  first_expected_app.app_id = "first_app";
-  first_expected_app.type = AppType::kBuiltIn;
-  // Expect only the first app.
-  ExpectApps({first_expected_app});
-
-  // Seed second app.
-  apps::mojom::App second_delta;
-  second_delta.app_id = "second_app";
-  second_delta.app_type = AppType::kBuiltIn;
-  second_delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(second_delta.Clone());
-
-  mojom::App second_expected_app;
-  second_expected_app.app_id = "second_app";
-  second_expected_app.type = AppType::kBuiltIn;
-  // Expect both apps.
-  ExpectApps({first_expected_app, second_expected_app});
-}
-
-TEST_F(AppControllerServiceTest, UninstalledAppsAreFiltered) {
-  // First seed an installed app and expect it to be returned.
-  apps::mojom::App app_delta;
-  app_delta.app_id = "app_id";
-  app_delta.app_type = AppType::kBuiltIn;
-  app_delta.show_in_launcher = OptionalBool::kTrue;
-  app_delta.readiness = Readiness::kReady;
-  AddAppDeltaToAppService(app_delta.Clone());
-
-  mojom::App app;
-  app.app_id = "app_id";
-  app.type = AppType::kBuiltIn;
-  app.readiness = Readiness::kReady;
-  ExpectApps({app});
-
-  // Then change the app's readiness and expect it to be filtered.
-  app_delta.readiness = Readiness::kUninstalledByUser;
-  AddAppDeltaToAppService(app_delta.Clone());
-  ExpectApps({});
-}
-
-TEST_F(AppControllerServiceTest, AppsThatAreNotRelevantAreFiltered) {
-  // Seed an app that's allowed to be returned by the AppControllerService.
-  apps::mojom::App allowed_app_delta;
-  allowed_app_delta.app_id = "allowed_app";
-  allowed_app_delta.app_type = AppType::kBuiltIn;
-  allowed_app_delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(allowed_app_delta.Clone());
-
-  apps::mojom::App first_blocked_app_delta;
-  first_blocked_app_delta.app_id = "first_blocked_app";
-  first_blocked_app_delta.app_type = AppType::kBuiltIn;
-  first_blocked_app_delta.show_in_launcher = OptionalBool::kUnknown;
-  AddAppDeltaToAppService(first_blocked_app_delta.Clone());
-
-  apps::mojom::App second_blocked_app_delta;
-  second_blocked_app_delta.app_id = "second_blocked_app";
-  second_blocked_app_delta.app_type = AppType::kBuiltIn;
-  second_blocked_app_delta.show_in_launcher = OptionalBool::kFalse;
-  AddAppDeltaToAppService(second_blocked_app_delta.Clone());
-
-  apps::mojom::App kiosk_next_app_delta;
-  kiosk_next_app_delta.app_id = extension_misc::kKioskNextHomeAppId;
-  kiosk_next_app_delta.app_type = AppType::kBuiltIn;
-  // Even though the Kiosk Next might be allowed in the launcher, we cannot
-  // return it.
-  kiosk_next_app_delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(kiosk_next_app_delta.Clone());
-
-  mojom::App allowed_app;
-  allowed_app.app_id = "allowed_app";
-  allowed_app.type = AppType::kBuiltIn;
-
-  // Expect only the allowed app, all the other ones were filtered.
-  ExpectApps({allowed_app});
-}
-
-TEST_F(AppControllerServiceTest, GetArcAndroidIdReturnsItWhenItHasIt) {
-  SetAndroidId(123456789L);
-  ExpectArcAndroidIdResponse(true, "75bcd15");  // 75bcd15 is 123456789 in hex.
-
-  // Make sure the returned Android ID doesn't get clipped when it's too large.
-  SetAndroidId(std::numeric_limits<int64_t>::max());
-  ExpectArcAndroidIdResponse(true, "7fffffffffffffff");
-
-  ExpectBridgeActionRecorded(BridgeAction::kGetAndroidId, 2);
-  ExpectGetAndroidIdSuccessRecorded(/*success=*/true, 2);
-}
-
-TEST_F(AppControllerServiceTest, GetArcAndroidIdFailureIsPropagated) {
-  // Stop the ARC instance to simulate a failure.
-  StopArc();
-
-  ExpectArcAndroidIdResponse(false, "0");
-  ExpectBridgeActionRecorded(BridgeAction::kGetAndroidId, 1);
-  ExpectGetAndroidIdSuccessRecorded(/*success=*/false, 1);
-}
-
-TEST_F(AppControllerServiceTest, LaunchIntentLaunchesWhenAllowed) {
-  SetLaunchIntentAllowed(/*allowed=*/true);
-  std::string intent = "https://example.com/path?q=query";
-  ExpectLaunchIntentResponse(intent, true, base::nullopt);
-
-  ExpectIntentLaunched(intent);
-  ExpectBridgeActionRecorded(BridgeAction::kLaunchIntent, 1);
-  ExpectLaunchIntentResultRecorded(LaunchIntentResult::kSuccess);
-}
-
-TEST_F(AppControllerServiceTest, LaunchIntentFailsWhenNotAllowed) {
-  SetLaunchIntentAllowed(/*allowed=*/false);
-  std::string intent = "https://example.com/path?q=query";
-  ExpectLaunchIntentResponse(
-      intent, false, base::Optional<std::string>("Intent not allowed."));
-
-  ExpectNoLaunchedIntents();
-  ExpectBridgeActionRecorded(BridgeAction::kLaunchIntent, 1);
-  ExpectLaunchIntentResultRecorded(LaunchIntentResult::kNotAllowed);
-}
-
-TEST_F(AppControllerServiceTest, LaunchIntentFailsWhenArcIsDisabled) {
-  SetLaunchIntentAllowed(/*allowed=*/true);
-  StopArc();
-  std::string intent = "https://example.com/path?q=query";
-  ExpectLaunchIntentResponse(
-      intent, false, base::Optional<std::string>("ARC bridge not available."));
-
-  ExpectNoLaunchedIntents();
-  ExpectBridgeActionRecorded(BridgeAction::kLaunchIntent, 1);
-  ExpectLaunchIntentResultRecorded(LaunchIntentResult::kArcUnavailable);
-}
-
-TEST_F(AppControllerServiceTest, ClientIsNotifiedOfChangesToAndroidApp) {
-  std::string android_package_name = "fake.app.package.2.0";
-  std::string app_id = GetAppIdFromAndroidPackage(android_package_name);
-  AppType app_type = AppType::kArc;
-
-  // Install the Android app.
-  AddAndroidPackageToArc(android_package_name);
-  apps::mojom::App delta;
-  delta.app_id = app_id;
-  delta.app_type = app_type;
-  delta.show_in_launcher = OptionalBool::kTrue;
-  delta.name = "Fake App Name - First Version";
-  AddAppDeltaToAppService(delta.Clone());
-
-  mojom::App first_app_state;
-  first_app_state.android_package_name = android_package_name;
-  first_app_state.app_id = app_id;
-  first_app_state.display_name = "Fake App Name - First Version";
-  first_app_state.type = app_type;
-  ExpectAppChangedUpdates({first_app_state});
-
-  delta.name = "Fake App Name - Second Version";
-  AddAppDeltaToAppService(delta.Clone());
-  mojom::App second_app_state = first_app_state;
-  second_app_state.display_name = "Fake App Name - Second Version";
-  ExpectAppChangedUpdates({first_app_state, second_app_state});
-
-  // Now we stop ARC and check if we still have all the information we had about
-  // the app. We should have cached all that we needed from the previous
-  // updates.
-  StopArc();
-  delta.name = "Fake App Name - Third Version";
-  AddAppDeltaToAppService(delta.Clone());
-  mojom::App third_app_state = second_app_state;
-  third_app_state.display_name = "Fake App Name - Third Version";
-  ExpectAppChangedUpdates({first_app_state, second_app_state, third_app_state});
-}
-
-TEST_F(AppControllerServiceTest, ClientIsNotifiedOfReadinessChanges) {
-  apps::mojom::App app_delta;
-  app_delta.app_id = "app";
-  app_delta.app_type = AppType::kBuiltIn;
-  app_delta.show_in_launcher = OptionalBool::kTrue;
-  app_delta.readiness = Readiness::kReady;
-  AddAppDeltaToAppService(app_delta.Clone());
-
-  mojom::App first_app_state;
-  first_app_state.app_id = "app";
-  first_app_state.type = AppType::kBuiltIn;
-  first_app_state.readiness = Readiness::kReady;
-  ExpectAppChangedUpdates({first_app_state});
-
-  app_delta.readiness = Readiness::kUninstalledByUser;
-  AddAppDeltaToAppService(app_delta.Clone());
-
-  mojom::App second_app_state;
-  second_app_state.app_id = "app";
-  second_app_state.type = AppType::kBuiltIn;
-  second_app_state.readiness = Readiness::kUninstalledByUser;
-  ExpectAppChangedUpdates({first_app_state, second_app_state});
-}
-
-TEST_F(AppControllerServiceTest, ClientIsNotifiedOfNameChanges) {
-  apps::mojom::App app_delta;
-  app_delta.app_id = "app";
-  app_delta.app_type = AppType::kBuiltIn;
-  app_delta.show_in_launcher = OptionalBool::kTrue;
-  app_delta.name = "First App Name";
-  AddAppDeltaToAppService(app_delta.Clone());
-
-  mojom::App first_app_state;
-  first_app_state.app_id = "app";
-  first_app_state.type = AppType::kBuiltIn;
-  first_app_state.display_name = "First App Name";
-  ExpectAppChangedUpdates({first_app_state});
-
-  app_delta.name = "Second App Name";
-  AddAppDeltaToAppService(app_delta.Clone());
-
-  mojom::App second_app_state;
-  second_app_state.app_id = "app";
-  second_app_state.type = AppType::kBuiltIn;
-  second_app_state.display_name = "Second App Name";
-  ExpectAppChangedUpdates({first_app_state, second_app_state});
-}
-
-TEST_F(AppControllerServiceTest, ClientIsNotNotifiedOfChangesToHiddenApp) {
-  // Install an app that should not be shown in the launcher.
-  apps::mojom::App app_delta;
-  app_delta.app_id = "app";
-  app_delta.app_type = AppType::kBuiltIn;
-  app_delta.show_in_launcher = OptionalBool::kFalse;
-  AddAppDeltaToAppService(app_delta.Clone());
-  ExpectAppChangedUpdates({});
-
-  // Even if we keep changing the app, we will not notify the client.
-  app_delta.name = "Fake App Name";
-  AddAppDeltaToAppService(app_delta.Clone());
-  ExpectAppChangedUpdates({});
-  app_delta.readiness = Readiness::kReady;
-  AddAppDeltaToAppService(app_delta.Clone());
-  ExpectAppChangedUpdates({});
-  app_delta.show_in_launcher = OptionalBool::kUnknown;
-  AddAppDeltaToAppService(app_delta.Clone());
-  ExpectAppChangedUpdates({});
-
-  // Now if we change |show_in_launcher| to true, we notify the client with
-  // the latest app state.
-  app_delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(app_delta.Clone());
-
-  mojom::App app_state;
-  app_state.app_id = "app";
-  app_state.type = AppType::kBuiltIn;
-  app_state.display_name = "Fake App Name";
-  app_state.readiness = Readiness::kReady;
-  ExpectAppChangedUpdates({app_state});
-}
-
-TEST_F(AppControllerServiceTest, ClientIsNotNotifiedOfSuperfluousChanges) {
-  apps::mojom::App app_delta;
-  app_delta.app_id = "app";
-  app_delta.app_type = AppType::kBuiltIn;
-  app_delta.show_in_launcher = OptionalBool::kTrue;
-  AddAppDeltaToAppService(app_delta.Clone());
-
-  mojom::App first_app_state;
-  first_app_state.app_id = "app";
-  first_app_state.type = AppType::kBuiltIn;
-  ExpectAppChangedUpdates({first_app_state});
-  ExpectBridgeActionRecorded(BridgeAction::kNotifiedAppChange, 1);
-
-  // We don't care about this field, so changes to it shouldn't be
-  // propagated to the client.
-  app_delta.additional_search_terms = {"random_term"};
-  AddAppDeltaToAppService(app_delta.Clone());
-  ExpectAppChangedUpdates({first_app_state});
-  ExpectBridgeActionRecorded(BridgeAction::kNotifiedAppChange, 1);
-}
-
-TEST_F(AppControllerServiceTest, ClientIsNotifiedOfArcStatusWhenArcStops) {
-  ExpectArcStatusUpdates({mojom::ArcStatus::kReady});
-
-  StopArc();
-
-  ExpectArcStatusUpdates(
-      {mojom::ArcStatus::kReady, mojom::ArcStatus::kStopped});
-}
-
-TEST_F(AppControllerServiceTest, ClientIsNotifiedOfArcStatusWhenArcStarts) {
-  // Since our fake arc instance starts ready, we need to stop it first and
-  // reset the client.
-  StopArc();
-  ResetClient();
-  ExpectArcStatusUpdates({mojom::ArcStatus::kStopped});
-
-  RestartArc();
-  ExpectArcStatusUpdates(
-      {mojom::ArcStatus::kStopped, mojom::ArcStatus::kReady});
-}
-
-TEST_F(AppControllerServiceTest, LaunchAppCallsAppServiceCorrectly) {
-  EXPECT_CALL(*proxy(), Launch("fake_app_id", ui::EventFlags::EF_NONE,
-                               apps::mojom::LaunchSource::kFromKioskNextHome,
-                               display::kDefaultDisplayId));
-
-  service()->LaunchApp("fake_app_id");
-  ExpectBridgeActionRecorded(BridgeAction::kLaunchApp, 1);
-}
-
-TEST_F(AppControllerServiceTest, UninstallAppCallsAppServiceCorrectly) {
-  EXPECT_CALL(*proxy(), Uninstall("fake_app_id"));
-
-  service()->UninstallApp("fake_app_id");
-  ExpectBridgeActionRecorded(BridgeAction::kUninstallApp, 1);
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.cc b/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.cc
deleted file mode 100644
index 3f7cfe7..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.h"
-
-#include <string>
-#include <utility>
-
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chromeos/kiosk_next_home/metrics_helper.h"
-#include "components/user_manager/user.h"
-#include "components/user_manager/user_manager.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-IdentityControllerImpl::IdentityControllerImpl(
-    mojom::IdentityControllerRequest request)
-    : binding_(this, std::move(request)) {}
-
-IdentityControllerImpl::~IdentityControllerImpl() = default;
-
-void IdentityControllerImpl::GetUserInfo(
-    mojom::IdentityController::GetUserInfoCallback callback) {
-  RecordBridgeAction(BridgeAction::kGetUserInfo);
-  auto user_info = mojom::UserInfo::New();
-  user_manager::User* user = user_manager::UserManager::Get()->GetActiveUser();
-  user_info->given_name = base::UTF16ToUTF8(user->GetGivenName());
-  user_info->display_name = base::UTF16ToUTF8(user->GetDisplayName());
-  std::move(callback).Run(std::move(user_info));
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.h b/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.h
deleted file mode 100644
index 4d9ebacb..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_IDENTITY_CONTROLLER_IMPL_H_
-#define CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_IDENTITY_CONTROLLER_IMPL_H_
-
-#include "base/macros.h"
-#include "chrome/browser/chromeos/kiosk_next_home/mojom/identity_controller.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-// IdentityController implementation to expose identity-related capabilities to
-// Kiosk Next Home.
-class IdentityControllerImpl : public mojom::IdentityController {
- public:
-  explicit IdentityControllerImpl(mojom::IdentityControllerRequest request);
-  ~IdentityControllerImpl() override;
-
-  // mojom::IdentityController:
-  void GetUserInfo(
-      mojom::IdentityController::GetUserInfoCallback callback) override;
-
- private:
-  mojo::Binding<mojom::IdentityController> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(IdentityControllerImpl);
-};
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_IDENTITY_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl_unittest.cc b/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl_unittest.cc
deleted file mode 100644
index eb98135..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/identity_controller_impl_unittest.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/chromeos/kiosk_next_home/metrics_helper.h"
-#include "components/user_manager/fake_user_manager.h"
-#include "components/user_manager/scoped_user_manager.h"
-#include "components/user_manager/user_manager.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-namespace {
-
-const char* kUserDisplayName = "User Display Name";
-const char* kUserGivenName = "User Given Name";
-
-}  // namespace
-
-class IdentityControllerImplTest : public testing::Test {
- protected:
-  void SetUp() override {
-    auto* fake_user_manager = new user_manager::FakeUserManager();
-    user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
-        base::WrapUnique(fake_user_manager));
-    AccountId account_id =
-        AccountId::FromUserEmailGaiaId("test@test.com", "123456");
-    // Create new fake user and set its account data.
-    fake_user_manager->AddUser(account_id);
-    user_manager::UserManager::UserAccountData account_data(
-        base::ASCIIToUTF16(kUserDisplayName),
-        base::ASCIIToUTF16(kUserGivenName), std::string() /* locale */);
-    fake_user_manager->UpdateUserAccountData(account_id, account_data);
-    identity_controller_impl_ = std::make_unique<IdentityControllerImpl>(
-        mojo::MakeRequest(&identity_controller_));
-  }
-
-  mojom::IdentityController* identity_controller() {
-    return identity_controller_.get();
-  }
-
-  base::HistogramTester histogram_tester_;
-
- private:
-  base::test::ScopedTaskEnvironment task_environemnt_;
-  std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
-  std::unique_ptr<IdentityControllerImpl> identity_controller_impl_;
-  mojom::IdentityControllerPtr identity_controller_;
-};
-
-TEST_F(IdentityControllerImplTest, GetUserInfo) {
-  base::RunLoop run_loop;
-  mojom::UserInfoPtr returned_user_info;
-  identity_controller()->GetUserInfo(base::BindLambdaForTesting(
-      [&run_loop, &returned_user_info](mojom::UserInfoPtr user_info) {
-        returned_user_info = std::move(user_info);
-        run_loop.Quit();
-      }));
-  run_loop.Run();
-  EXPECT_EQ(returned_user_info->display_name, kUserDisplayName);
-  EXPECT_EQ(returned_user_info->given_name, kUserGivenName);
-  histogram_tester_.ExpectUniqueSample("KioskNextHome.Bridge.Action",
-                                       BridgeAction::kGetUserInfo, 1);
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.cc b/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.cc
deleted file mode 100644
index 674990e..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.cc
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/intent_config_helper.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/json/json_value_converter.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "base/task/post_task.h"
-#include "base/task/task_traits.h"
-#include "content/public/browser/system_connector.h"
-#include "services/data_decoder/public/cpp/safe_json_parser.h"
-#include "url/url_constants.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-namespace {
-
-struct CustomScheme {
-  std::string scheme;
-  std::string path;
-
-  static void RegisterJSONConverter(
-      base::JSONValueConverter<CustomScheme>* converter) {
-    converter->RegisterStringField("scheme", &CustomScheme::scheme);
-    converter->RegisterStringField("path", &CustomScheme::path);
-  }
-};
-
-struct IntentConfig {
-  std::vector<std::unique_ptr<std::string>> allowed_hosts;
-  std::vector<std::unique_ptr<CustomScheme>> allowed_custom_schemes;
-
-  static void RegisterJSONConverter(
-      base::JSONValueConverter<IntentConfig>* converter) {
-    converter->RegisterRepeatedString("allowed_hosts",
-                                      &IntentConfig::allowed_hosts);
-    converter->RegisterRepeatedMessage("allowed_custom_schemes",
-                                       &IntentConfig::allowed_custom_schemes);
-  }
-};
-
-class ReadJsonConfigResourceDelegate : public IntentConfigHelper::Delegate {
- public:
-  ReadJsonConfigResourceDelegate() = default;
-  ~ReadJsonConfigResourceDelegate() override = default;
-
-  // IntentConfigHelper::Delegate:
-  std::string GetJsonConfig() const override {
-    return std::string();
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(ReadJsonConfigResourceDelegate);
-};
-
-}  // namespace
-
-// static
-std::unique_ptr<IntentConfigHelper> IntentConfigHelper::GetInstance() {
-  return std::make_unique<IntentConfigHelper>(
-      std::make_unique<ReadJsonConfigResourceDelegate>());
-}
-
-IntentConfigHelper::IntentConfigHelper(std::unique_ptr<Delegate> delegate)
-    : ready_(false) {
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-      base::BindOnce(
-          [](std::unique_ptr<Delegate> read_config_delegate) {
-            return read_config_delegate->GetJsonConfig();
-          },
-          std::move(delegate)),
-      base::BindOnce(&IntentConfigHelper::ParseConfig,
-                     weak_factory_.GetWeakPtr()));
-}
-
-IntentConfigHelper::IntentConfigHelper() = default;
-
-IntentConfigHelper::~IntentConfigHelper() = default;
-
-bool IntentConfigHelper::IsIntentAllowed(const GURL& intent_uri) const {
-  if (!ready_ || !intent_uri.is_valid())
-    return false;
-
-  if (intent_uri.IsStandard()) {
-    if (intent_uri.SchemeIs(url::kHttpsScheme))
-      return base::Contains(allowed_hosts_, intent_uri.host());
-    // Other standard schemes besides https are not allowed.
-    return false;
-  }
-
-  auto allowed_custom_scheme =
-      allowed_custom_schemes_.find(intent_uri.scheme());
-  if (allowed_custom_scheme == allowed_custom_schemes_.end())
-    return false;
-  // Scheme is allowed, so intent will be allowed if path matches the config.
-  std::string path = allowed_custom_scheme->second;
-  return path == intent_uri.path();
-}
-
-void IntentConfigHelper::ParseConfig(const std::string& json_config) {
-  auto* connector = content::GetSystemConnector();
-  // Service manager may not be initialized in tests.
-  if (!connector)
-    return;
-
-  // Parse JSON in utility process via Data Decoder Service.
-  data_decoder::SafeJsonParser::Parse(
-      connector, json_config,
-      base::BindOnce(&IntentConfigHelper::ParseConfigDone,
-                     weak_factory_.GetWeakPtr()),
-      base::BindOnce(&IntentConfigHelper::ParseConfigFailed,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void IntentConfigHelper::ParseConfigDone(base::Value config_value) {
-  base::JSONValueConverter<IntentConfig> converter;
-  IntentConfig parsed_config;
-  if (converter.Convert(config_value, &parsed_config)) {
-    for (const auto& allowed_host_ptr : parsed_config.allowed_hosts)
-      allowed_hosts_.emplace(std::move(*allowed_host_ptr));
-
-    for (const auto& custom_scheme_ptr : parsed_config.allowed_custom_schemes) {
-      allowed_custom_schemes_[std::move(custom_scheme_ptr->scheme)] =
-          std::move(custom_scheme_ptr->path);
-    }
-
-    ready_ = true;
-  }
-}
-
-void IntentConfigHelper::ParseConfigFailed(const std::string& error) {
-  DLOG(ERROR) << "Failed to parse intent JSON config: " << error;
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.h b/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.h
deleted file mode 100644
index 48874e2..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/intent_config_helper.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_INTENT_CONFIG_HELPER_H_
-#define CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_INTENT_CONFIG_HELPER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/containers/flat_map.h"
-#include "base/containers/flat_set.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/values.h"
-#include "url/gurl.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-// Helper that parses JSON config and checks whether intents are allowed or not
-// according to it.
-class IntentConfigHelper {
- public:
-  // Interface that provides the JSON configuration content.
-  class Delegate {
-   public:
-    virtual ~Delegate() = default;
-
-    virtual std::string GetJsonConfig() const = 0;
-  };
-
-  // Returns instance built with real Delegate implementation that reads JSON
-  // config from a resource file.
-  static std::unique_ptr<IntentConfigHelper> GetInstance();
-
-  explicit IntentConfigHelper(std::unique_ptr<Delegate> delegate);
-
-  virtual ~IntentConfigHelper();
-
-  // Returns true if intent is allowed according to the JSON config file.
-  // Returns false before the parsing is complete or if config is invalid.
-  virtual bool IsIntentAllowed(const GURL& intent_uri) const;
-
- protected:
-  // For testing only.
-  IntentConfigHelper();
-
- private:
-  // Attempts to parse |json_config| via Data Decoder Service.
-  void ParseConfig(const std::string& json_config);
-
-  // Receives parsed JSON config when ready.
-  void ParseConfigDone(base::Value config);
-
-  // Called if config could not be parsed.
-  void ParseConfigFailed(const std::string& error);
-
-  // Until the JSON config has been parsed and ready_ set to true, all calls to
-  // IsIntentAllowed will return false.
-  bool ready_ = false;
-
-  // Set of hosts allowed to be launched.
-  base::flat_set<std::string> allowed_hosts_;
-
-  // Maps allowed custom schemes to required paths.
-  base::flat_map<std::string, std::string> allowed_custom_schemes_;
-
-  base::WeakPtrFactory<IntentConfigHelper> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(IntentConfigHelper);
-};
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_INTENT_CONFIG_HELPER_H_
diff --git a/chrome/browser/chromeos/kiosk_next_home/intent_config_helper_unittest.cc b/chrome/browser/chromeos/kiosk_next_home/intent_config_helper_unittest.cc
deleted file mode 100644
index 712cfc8..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/intent_config_helper_unittest.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/intent_config_helper.h"
-
-#include <memory>
-
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "content/public/test/test_service_manager_context.h"
-#include "services/data_decoder/public/cpp/testing_json_parser.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-namespace {
-
-const char* kTestJsonConfig = R"(
-{
-  "allowed_hosts": ["example.com", "test.test.com"],
-  "allowed_custom_schemes": [
-    {
-      "scheme": "example",
-      "path": "//path"
-    },
-    {
-      "scheme": "test",
-      "path": "//something"
-    }
-  ]
-})";
-
-const char* kHostsOnlyTestJsonConfig = R"(
-{ "allowed_hosts": ["example.com", "test.test.com"] })";
-
-// Quotation mark intentionally missing below.
-const char* kTestInvalidJsonConfig = R"(
-{ "allowed_hosts": ["example.com", "test.test.com] })";
-
-}  // namespace
-
-class FakeConfigDelegate : public IntentConfigHelper::Delegate {
- public:
-  explicit FakeConfigDelegate(const char* json_config)
-      : json_config_(json_config) {}
-
-  // IntentConfigHelper::Delegate:
-  std::string GetJsonConfig() const override { return json_config_; }
-
- private:
-  std::string json_config_;
-};
-
-class IntentConfigHelperTest : public testing::Test {
- protected:
-  std::unique_ptr<IntentConfigHelper> intent_config_helper(
-      const char* json_config) {
-    auto intent_config_helper = std::make_unique<IntentConfigHelper>(
-        std::make_unique<FakeConfigDelegate>(json_config));
-    // Ensure config is read and parsed.
-    test_thread_bundle_.RunUntilIdle();
-    return intent_config_helper;
-  }
-
- private:
-  content::TestBrowserThreadBundle test_thread_bundle_;
-  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
-  content::TestServiceManagerContext service_manager_context_;
-};
-
-TEST_F(IntentConfigHelperTest, IntentNotAllowedWithInvalidUri) {
-  auto intent_helper = intent_config_helper(kTestJsonConfig);
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("invaliduri")));
-}
-
-TEST_F(IntentConfigHelperTest, IntentAllowedForAllowedHosts) {
-  auto intent_helper = intent_config_helper(kTestJsonConfig);
-  EXPECT_TRUE(intent_helper->IsIntentAllowed(GURL("https://example.com/")));
-  EXPECT_TRUE(intent_helper->IsIntentAllowed(
-      GURL("https://example.com/path?query=a#ref")));
-  EXPECT_TRUE(intent_helper->IsIntentAllowed(GURL("https://test.test.com/")));
-}
-
-TEST_F(IntentConfigHelperTest, IntentNotAllowedForHostsNotInConfig) {
-  auto intent_helper = intent_config_helper(kTestJsonConfig);
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("https://test.com")));
-  EXPECT_FALSE(
-      intent_helper->IsIntentAllowed(GURL("https://test.example.com")));
-}
-
-TEST_F(IntentConfigHelperTest, IntentAllowedForAllowedHostsOnly) {
-  auto intent_helper = intent_config_helper(kHostsOnlyTestJsonConfig);
-  EXPECT_TRUE(intent_helper->IsIntentAllowed(GURL("https://example.com/")));
-  EXPECT_TRUE(intent_helper->IsIntentAllowed(
-      GURL("https://example.com/path?query=a#ref")));
-  EXPECT_TRUE(intent_helper->IsIntentAllowed(GURL("https://test.test.com/")));
-  // No custom-scheme intents are allowed.
-  EXPECT_FALSE(
-      intent_helper->IsIntentAllowed(GURL("example.com://path?id=test")));
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("test://something?q=test")));
-}
-
-TEST_F(IntentConfigHelperTest, HttpIntentNotAllowed) {
-  auto intent_helper = intent_config_helper(kTestJsonConfig);
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("http://example.com")));
-}
-
-TEST_F(IntentConfigHelperTest, IntentAllowedForAllowedCustomSchemes) {
-  auto intent_helper = intent_config_helper(kTestJsonConfig);
-  EXPECT_TRUE(intent_helper->IsIntentAllowed(GURL("example://path?id=test")));
-  EXPECT_TRUE(intent_helper->IsIntentAllowed(GURL("test://something?q=test")));
-}
-
-TEST_F(IntentConfigHelperTest, IntentNotAllowedForCustomSchemesNotInConfig) {
-  auto intent_helper = intent_config_helper(kTestJsonConfig);
-  EXPECT_FALSE(
-      intent_helper->IsIntentAllowed(GURL("example://different_path?id=test")));
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("scheme://path")));
-}
-
-TEST_F(IntentConfigHelperTest, IntentNotAllowedWhenConfigIsInvalid) {
-  auto intent_helper = intent_config_helper(kTestInvalidJsonConfig);
-  // With an invalid config, no intents are allowed.
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("https://example.com/")));
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("example://path?id=test")));
-}
-
-TEST_F(IntentConfigHelperTest, IntentNotAllowedWhenConfigIsEmpty) {
-  auto intent_helper = intent_config_helper("{}");
-  // With an empty config, no intents are allowed.
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("https://example.com/")));
-  EXPECT_FALSE(intent_helper->IsIntentAllowed(GURL("example://path?id=test")));
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_browsertest.cc b/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_browsertest.cc
deleted file mode 100644
index 6b8ba664..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_browsertest.cc
+++ /dev/null
@@ -1,373 +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 <memory>
-
-#include "apps/launcher.h"
-#include "apps/test/app_window_waiter.h"
-#include "ash/public/cpp/ash_features.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/test_timeouts.h"
-#include "chrome/browser/chromeos/kiosk_next/kiosk_next_browser_factory.h"
-#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h"
-#include "chrome/browser/chromeos/login/session/user_session_manager.h"
-#include "chrome/browser/chromeos/login/session/user_session_manager_test_api.h"
-#include "chrome/browser/chromeos/login/test/embedded_test_server_mixin.h"
-#include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
-#include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/extensions/component_loader.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chromeos/login/auth/user_context.h"
-#include "components/account_id/account_id.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/test_utils.h"
-#include "extensions/browser/app_window/app_window.h"
-#include "extensions/browser/app_window/app_window_registry.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/common/constants.h"
-#include "net/dns/mock_host_resolver.h"
-#include "url/gurl.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-namespace {
-
-const char* kTestUserGivenName = "Test User";
-const char* kTestUserDisplayName = "Test User Full Name";
-const char* kTestUser = "test-user@gmail.com";
-const char* kTestUserGaiaId = "1234567890";
-
-// Helper class to wait until WebContents finishes loading.
-class WebContentsLoadFinishedWaiter : public content::WebContentsObserver {
- public:
-  explicit WebContentsLoadFinishedWaiter(content::WebContents* web_contents)
-      : content::WebContentsObserver(web_contents) {}
-  ~WebContentsLoadFinishedWaiter() override = default;
-
-  void Wait() {
-    if (!web_contents()->IsLoading())
-      return;
-
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
-  }
-
- private:
-  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
-                     const GURL& url) override {
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
-  std::unique_ptr<base::RunLoop> run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebContentsLoadFinishedWaiter);
-};
-
-const Browser* GetBrowserForWebsite(const GURL& url) {
-  BrowserList* list = BrowserList::GetInstance();
-  for (const Browser* browser : *list) {
-    TabStripModel* tab_strip_model = browser->tab_strip_model();
-    for (int tab_index = 0; tab_index < tab_strip_model->count(); tab_index++) {
-      content::WebContents* web_contents =
-          tab_strip_model->GetWebContentsAt(tab_index);
-      if (web_contents && (web_contents->GetURL() == url))
-        return browser;
-    }
-  }
-  return nullptr;
-}
-
-}  // namespace
-
-// Integration test for Kiosk Next Home app, exercising the Chrome <-> Mojo <->
-// JavaScript bridge interaction. This ensures the services backing Kiosk Next
-// Home are accessible and that the generated Mojo JS code is working.
-class KioskNextHomeBrowserTest : public MixinBasedInProcessBrowserTest {
- public:
-  KioskNextHomeBrowserTest() { set_exit_when_last_browser_closes(false); }
-
-  void SetUp() override {
-    feature_list_.InitAndEnableFeature(ash::features::kKioskNextShell);
-    MixinBasedInProcessBrowserTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-
-    // Enable all component extensions.
-    extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
-
-    test::UserSessionManagerTestApi session_manager_test_api(
-        UserSessionManager::GetInstance());
-    session_manager_test_api.SetShouldObtainTokenHandleInTests(false);
-    login_manager_mixin_.LoginAndWaitForActiveSession(GetUserContext());
-
-    MixinBasedInProcessBrowserTest::SetUpOnMainThread();
-
-    LaunchKioskNextHome();
-  }
-
- protected:
-  virtual UserContext GetUserContext() const {
-    return LoginManagerMixin::CreateDefaultUserContext(test_user_);
-  }
-
-  void RunJS(const std::string& js_script) {
-    EXPECT_TRUE(content::ExecuteScript(app_render_frame_host_, js_script));
-  }
-
-  // Runs JavaScript code in the app's render frame host. To extract the string
-  // result, the code must call 'domAutomationController.send(result)' with the
-  // desired result, otherwise it will hang until timeout.
-  std::string RunJSAndGetStringResult(const std::string& js_script) {
-    std::string result;
-    EXPECT_TRUE(content::ExecuteScriptAndExtractString(app_render_frame_host_,
-                                                       js_script, &result));
-    return result;
-  }
-
-  // Similar to RunJSAndGetStringResult, but for a double result.
-  double RunJSAndGetDoubleResult(const std::string& js_script) {
-    double result;
-    EXPECT_TRUE(content::ExecuteScriptAndExtractDouble(app_render_frame_host_,
-                                                       js_script, &result));
-    return result;
-  }
-
-  LoginManagerMixin::TestUserInfo test_user_{
-      AccountId::FromUserEmailGaiaId(kTestUser, kTestUserGaiaId)};
-
- private:
-  void LaunchKioskNextHome() {
-    Profile* profile = ProfileManager::GetActiveUserProfile();
-    // Get reference to Kiosk Next Home app.
-    const extensions::Extension* app =
-        extensions::ExtensionRegistry::Get(profile)->GetInstalledExtension(
-            extension_misc::kKioskNextHomeAppId);
-    ASSERT_TRUE(app);
-
-    // Launch app and wait for its window.
-    apps::LaunchPlatformApp(profile, app,
-                            extensions::AppLaunchSource::kSourceChromeInternal);
-    apps::AppWindowWaiter app_waiter(
-        extensions::AppWindowRegistry::Get(profile),
-        extension_misc::kKioskNextHomeAppId);
-    extensions::AppWindow* window =
-        app_waiter.WaitForShownWithTimeout(TestTimeouts::action_timeout());
-    ASSERT_TRUE(window);
-
-    // Wait for web contents to fully load.
-    WebContentsLoadFinishedWaiter web_contents_waiter(
-        window->app_window_contents_for_test()->GetWebContents());
-    web_contents_waiter.Wait();
-    app_render_frame_host_ = window->app_window_contents_for_test()
-                                 ->GetWebContents()
-                                 ->GetMainFrame();
-    ASSERT_TRUE(app_render_frame_host_);
-  }
-
-  base::test::ScopedFeatureList feature_list_;
-  LoginManagerMixin login_manager_mixin_{&mixin_host_, {test_user_}};
-  EmbeddedTestServerSetupMixin embedded_test_server_{&mixin_host_,
-                                                     embedded_test_server()};
-  content::RenderFrameHost* app_render_frame_host_;
-
-  DISALLOW_COPY_AND_ASSIGN(KioskNextHomeBrowserTest);
-};
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeBrowserTest, LaunchIntent) {
-  // Intents are not allowed by default.
-  std::string error_message = RunJSAndGetStringResult(
-      R"(kioskNextHome.getChromeOsBridge().launchIntent('test.intent')
-           .catch(error => domAutomationController.send(error)))");
-  EXPECT_EQ("Intent not allowed.", error_message);
-}
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeBrowserTest, GetApps) {
-  // Perform checks on apps on JS side and verify a summary of possible errors,
-  // which is simpler than piping apps and representing them in C++ code.
-  std::string error_message = RunJSAndGetStringResult(
-      R"(kioskNextHome.getChromeOsBridge().getApps().then(apps => {
-           const errorMessages = [];
-           if (!apps) {
-             errorMessages.push('No apps returned.');
-           }
-           apps.forEach(app => {
-             if (!app.appId) {
-               errorMessages.push('Missing app id.');
-             }
-             if (!app.displayName) {
-               errorMessages.push('Missing app display name.');
-             }
-             if (!app.thumbnailImage ||
-                 !app.thumbnailImage.startsWith('chrome://app-icon')) {
-               errorMessages.push('Invalid or missing app thumbnail: ' +
-                                  app.thumbnailImage);
-             }
-             if (app.readiness == kioskNextHome.AppReadiness.UNKNOWN) {
-               errorMessages.push(
-                 'Got unknown readiness for app id ' + app.appId);
-             }
-             if (app.type == kioskNextHome.AppType.UNKNOWN) {
-               errorMessages.push(
-                 'Got unknown type for app id ' + app.appId);
-             }
-           });
-           domAutomationController.send(errorMessages.join('\n'));
-         }))");
-  EXPECT_TRUE(error_message.empty())
-      << "Found errors in GetApps response: " << std::endl
-      << error_message;
-
-  // Get a semicolon separated list of app ids.
-  std::string app_ids = RunJSAndGetStringResult(
-      R"(kioskNextHome.getChromeOsBridge().getApps().then(apps => {
-           const appIds = [];
-           apps.forEach(app => appIds.push(app.appId));
-           domAutomationController.send(appIds.join(';'));
-         }))");
-  // Check that Files Manager's id is present.
-  EXPECT_TRUE(app_ids.find(extension_misc::kFilesManagerAppId) !=
-              std::string::npos);
-  // Check that Kiosk Next Home's own id is *not* present.
-  EXPECT_TRUE(app_ids.find(extension_misc::kKioskNextHomeAppId) ==
-              std::string::npos);
-}
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeBrowserTest, LaunchApp) {
-  RunJS(content::JsReplace("kioskNextHome.getChromeOsBridge().launchApp($1)",
-                           extension_misc::kFilesManagerAppId));
-  apps::AppWindowWaiter files_manager_waiter(
-      extensions::AppWindowRegistry::Get(
-          ProfileManager::GetActiveUserProfile()),
-      extension_misc::kFilesManagerAppId);
-  EXPECT_TRUE(files_manager_waiter.WaitForShownWithTimeout(
-      TestTimeouts::action_timeout()));
-}
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeBrowserTest, LaunchKioskNextBrowser) {
-  const std::string kUrl = "https://www.url1.com";
-  RunJS(content::JsReplace(
-      "kioskNextHome.getChromeOsBridge().launchKioskNextWebsite({url : $1})",
-      kUrl));
-  Browser* browser =
-      KioskNextBrowserList::Get().GetBrowserForWebsite(GURL(kUrl));
-  EXPECT_NE(browser, nullptr);
-}
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeBrowserTest, LaunchWebsite) {
-  const std::string kUrl = "https://www.url1.com";
-  RunJS(content::JsReplace(
-      "kioskNextHome.getChromeOsBridge().launchWebsite({url : $1})", kUrl));
-  const Browser* browser =
-      KioskNextBrowserList::Get().GetBrowserForWebsite(GURL(kUrl));
-  EXPECT_EQ(browser, nullptr);
-  EXPECT_NE(GetBrowserForWebsite(GURL(kUrl)), nullptr);
-}
-// Fixture variant that sets up additional user information for
-// GetUser(Given|Display)Name methods.
-class KioskNextHomeUserInfoBrowserTest : public KioskNextHomeBrowserTest {
- public:
-  KioskNextHomeUserInfoBrowserTest() = default;
-
-  void SetUpOnMainThread() override {
-    user_manager::UserManager::Get()->UpdateUserAccountData(
-        test_user_.account_id, user_manager::UserManager::UserAccountData(
-                                   base::ASCIIToUTF16(kTestUserDisplayName),
-                                   base::ASCIIToUTF16(kTestUserGivenName),
-                                   std::string() /* locale */));
-
-    KioskNextHomeBrowserTest::SetUpOnMainThread();
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(KioskNextHomeUserInfoBrowserTest);
-};
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeUserInfoBrowserTest, GetUserGivenName) {
-  std::string given_name = RunJSAndGetStringResult(
-      R"(kioskNextHome.getChromeOsBridge().getUserGivenName()
-           .then(name => domAutomationController.send(name)))");
-  EXPECT_EQ(kTestUserGivenName, given_name);
-}
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeUserInfoBrowserTest, GetUserDisplayName) {
-  std::string display_name = RunJSAndGetStringResult(
-      R"(kioskNextHome.getChromeOsBridge().getUserDisplayName()
-           .then(name => domAutomationController.send(name)))");
-  EXPECT_EQ(kTestUserDisplayName, display_name);
-}
-
-// Fixture variant that sets up a fake refresh token needed to issue access
-// tokens and access account info via IdentityAccessor.
-class KioskNextHomeRefreshTokenBrowserTest : public KioskNextHomeBrowserTest {
- public:
-  KioskNextHomeRefreshTokenBrowserTest() = default;
-
-  void SetUpOnMainThread() override {
-    fake_gaia_.SetupFakeGaiaForLogin(kTestUser, kTestUserGaiaId,
-                                     FakeGaiaMixin::kFakeRefreshToken);
-
-    KioskNextHomeBrowserTest::SetUpOnMainThread();
-  }
-
-  UserContext GetUserContext() const override {
-    UserContext user_context = KioskNextHomeBrowserTest::GetUserContext();
-    user_context.SetRefreshToken(FakeGaiaMixin::kFakeRefreshToken);
-    return user_context;
-  }
-
- private:
-  FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()};
-
-  DISALLOW_COPY_AND_ASSIGN(KioskNextHomeRefreshTokenBrowserTest);
-};
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeRefreshTokenBrowserTest, GetAccessToken) {
-  std::string access_token = RunJSAndGetStringResult(
-      R"(kioskNextHome.getChromeOsBridge().getAccessToken(['fake_scope'])
-           .then(token => domAutomationController.send(token)))");
-  EXPECT_EQ(access_token, FakeGaiaMixin::kFakeAllScopeAccessToken);
-}
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeRefreshTokenBrowserTest,
-                       FetchAccessTokenExpiration) {
-  // Access token fetchers take a 10% margin from token expirations before
-  // sending them to avoid returning an expired token.
-  int expected_expiration = FakeGaiaMixin::kFakeAccessTokenExpiration * 9 / 10;
-
-  double raw_expiration_time = RunJSAndGetDoubleResult(
-      R"(kioskNextHome.getChromeOsBridge().fetchAccessToken(['fake_scope'])
-           .then(tokenInfo => {
-                   domAutomationController.send(tokenInfo.expirationTime);
-                 }))");
-  base::Time expiration_time = base::Time::FromJsTime(raw_expiration_time);
-  base::TimeDelta actual_expiration = expiration_time - base::Time::Now();
-
-  // We give a 5 second margin of error for the token expiration. In this test
-  // we are mostly interested if the expiration reaches the Kiosk Next Home, not
-  // in per second precision.
-  EXPECT_NEAR(actual_expiration.InSeconds(), expected_expiration, 5);
-}
-
-IN_PROC_BROWSER_TEST_F(KioskNextHomeRefreshTokenBrowserTest, GetAccountId) {
-  std::string id = RunJSAndGetStringResult(
-      R"(kioskNextHome.getChromeOsBridge().getAccountId()
-           .then(id => domAutomationController.send(id)))");
-  EXPECT_EQ(kTestUserGaiaId, id);
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_interface_broker_impl.cc b/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_interface_broker_impl.cc
deleted file mode 100644
index 6722ac5..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_interface_broker_impl.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_interface_broker_impl.h"
-
-#include <utility>
-
-#include "chrome/browser/chromeos/kiosk_next_home/app_controller_service.h"
-#include "chrome/browser/chromeos/kiosk_next_home/identity_controller_impl.h"
-#include "chrome/browser/chromeos/kiosk_next_home/website_controller_impl.h"
-#include "chrome/browser/profiles/profile.h"
-#include "content/public/browser/browser_context.h"
-#include "services/identity/public/mojom/constants.mojom.h"
-#include "services/identity/public/mojom/identity_accessor.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-KioskNextHomeInterfaceBrokerImpl::KioskNextHomeInterfaceBrokerImpl(
-    content::BrowserContext* context)
-    : context_(context),
-      connector_(content::BrowserContext::GetConnectorFor(context)->Clone()) {}
-
-KioskNextHomeInterfaceBrokerImpl::~KioskNextHomeInterfaceBrokerImpl() = default;
-
-void KioskNextHomeInterfaceBrokerImpl::GetIdentityAccessor(
-    ::identity::mojom::IdentityAccessorRequest request) {
-  connector_->BindInterface(::identity::mojom::kServiceName,
-                            std::move(request));
-}
-
-void KioskNextHomeInterfaceBrokerImpl::GetIdentityController(
-    mojom::IdentityControllerRequest request) {
-  identity_controller_ =
-      std::make_unique<IdentityControllerImpl>(std::move(request));
-}
-
-void KioskNextHomeInterfaceBrokerImpl::GetAppController(
-    mojom::AppControllerRequest request) {
-  AppControllerService::Get(context_)->BindRequest(std::move(request));
-}
-
-void KioskNextHomeInterfaceBrokerImpl::GetWebsiteController(
-    mojom::WebsiteControllerRequest request) {
-  website_controller_ =
-      std::make_unique<WebsiteControllerImpl>(std::move(request));
-}
-
-void KioskNextHomeInterfaceBrokerImpl::BindRequest(
-    mojom::KioskNextHomeInterfaceBrokerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_interface_broker_impl.h b/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_interface_broker_impl.h
deleted file mode 100644
index d77de29..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_interface_broker_impl.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_KIOSK_NEXT_HOME_INTERFACE_BROKER_IMPL_H_
-#define CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_KIOSK_NEXT_HOME_INTERFACE_BROKER_IMPL_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/supports_user_data.h"
-#include "chrome/browser/chromeos/kiosk_next_home/mojom/kiosk_next_home_interface_broker.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/identity/public/mojom/identity_accessor.mojom.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace service_manager {
-class Connector;
-}
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-class IdentityControllerImpl;
-class WebsiteControllerImpl;
-
-// Implementation of interface responsible for brokering other interfaces needed
-// to support Kiosk Next Home functionality.
-class KioskNextHomeInterfaceBrokerImpl
-    : public mojom::KioskNextHomeInterfaceBroker,
-      public base::SupportsUserData::Data {
- public:
-  explicit KioskNextHomeInterfaceBrokerImpl(content::BrowserContext* context);
-  ~KioskNextHomeInterfaceBrokerImpl() override;
-
-  // Binds client requests to this implementation.
-  void BindRequest(mojom::KioskNextHomeInterfaceBrokerRequest request);
-
-  // mojom::KioskNextHomeInterfaceBroker:
-  void GetIdentityAccessor(
-      ::identity::mojom::IdentityAccessorRequest request) override;
-  void GetIdentityController(mojom::IdentityControllerRequest request) override;
-  void GetAppController(mojom::AppControllerRequest request) override;
-  void GetWebsiteController(mojom::WebsiteControllerRequest request) override;
-
- private:
-  content::BrowserContext* context_;
-  mojo::BindingSet<mojom::KioskNextHomeInterfaceBroker> bindings_;
-  std::unique_ptr<IdentityControllerImpl> identity_controller_;
-  std::unique_ptr<WebsiteControllerImpl> website_controller_;
-
-  // Clone of BrowserContext's Connector, which allows binding to other
-  // services.
-  std::unique_ptr<service_manager::Connector> connector_;
-
-  DISALLOW_COPY_AND_ASSIGN(KioskNextHomeInterfaceBrokerImpl);
-};
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_KIOSK_NEXT_HOME_INTERFACE_BROKER_IMPL_H_
diff --git a/chrome/browser/chromeos/kiosk_next_home/metrics_helper.cc b/chrome/browser/chromeos/kiosk_next_home/metrics_helper.cc
deleted file mode 100644
index 73bd3b98..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/metrics_helper.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/metrics_helper.h"
-
-#include "base/metrics/histogram_macros.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-void RecordBridgeAction(BridgeAction action) {
-  UMA_HISTOGRAM_ENUMERATION("KioskNextHome.Bridge.Action", action);
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/metrics_helper.h b/chrome/browser/chromeos/kiosk_next_home/metrics_helper.h
deleted file mode 100644
index d42fe61d..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/metrics_helper.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_METRICS_HELPER_H_
-#define CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_METRICS_HELPER_H_
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class BridgeAction {
-  kListApps = 0,
-  kLaunchApp = 1,
-  kUninstallApp = 2,
-  kNotifiedAppChange = 3,
-  kGetAndroidId = 4,
-  kLaunchIntent = 5,
-  kGetUserInfo = 6,
-  kMaxValue = kGetUserInfo,
-};
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class LaunchIntentResult {
-  kSuccess = 0,
-  kNotAllowed = 1,
-  kArcUnavailable = 2,
-  kMaxValue = kArcUnavailable,
-};
-
-// Records KioskNextHome bridge method calls.
-void RecordBridgeAction(BridgeAction action);
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_METRICS_HELPER_H_
diff --git a/chrome/browser/chromeos/kiosk_next_home/mojom/BUILD.gn b/chrome/browser/chromeos/kiosk_next_home/mojom/BUILD.gn
deleted file mode 100644
index 41b102b2..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/mojom/BUILD.gn
+++ /dev/null
@@ -1,20 +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("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojom") {
-  sources = [
-    "app_controller.mojom",
-    "identity_controller.mojom",
-    "kiosk_next_home_interface_broker.mojom",
-    "website_controller.mojom",
-  ]
-
-  public_deps = [
-    "//chrome/services/app_service/public/mojom:types",
-    "//services/identity/public/mojom",
-    "//url/mojom:url_mojom_gurl",
-  ]
-}
diff --git a/chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom b/chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom
deleted file mode 100644
index 40e8974..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom
+++ /dev/null
@@ -1,73 +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.
-
-module chromeos.kiosk_next_home.mojom;
-
-import "chrome/services/app_service/public/mojom/types.mojom";
-
-// Represents an app on Chrome OS. This struct has all the information
-// necessary to display the app in the Kiosk Next Home app list/grid, including
-// readiness, thumbnail image, display name, etc.
-struct App {
-  // Stable and unique identifier for this app.
-  string app_id;
-
-  // The type of this app (e.g. ARC, Crostini, extension, etc).
-  apps.mojom.AppType type;
-
-  // Name used when displaying the app in an app list or grid.
-  string display_name;
-
-  // Android package name for this app.
-  // This field will only be present if this is an ARC app.
-  string android_package_name;
-
-  // The current state of readiness for this app.
-  // It can tell if the app is suspended, uninstalled, etc.
-  apps.mojom.Readiness readiness;
-};
-
-enum ArcStatus {
-  // Arc is still booting up or encountered an error.
-  kStopped,
-  // Arc is connected and ready to be used. Android ID is available.
-  kReady,
-};
-
-// Interface used by AppController clients to receive app related events.
-interface AppControllerClient {
-  // Called when any field from the given app changes.
-  OnAppChanged(App app);
-
-  // Called when the Arc status changes. Called at first when the client is
-  // set.
-  OnArcStatusChanged(ArcStatus status);
-};
-
-// Interface for managing Chrome OS apps from the Kiosk Next Home.
-interface AppController {
-  // Returns all the local apps installed on this Chrome OS device.
-  // It reads from all the app sources, including ARC++ (Android),
-  // Crostini (Linux), extensions and PWAs.
-  // Note that as soon as this method returns, some of the apps might have
-  // changed. To always get the most updated information, attach an app
-  // observer through the AddObserver method.
-  GetApps() => (array<App> apps);
-
-  // Sets the client that will be notified when an app changes.
-  SetClient(AppControllerClient client);
-
-  // Launches the app with the given app_id.
-  LaunchApp(string app_id);
-
-  // Shows a prompt to uninstall the app with the given app_id.
-  UninstallApp(string app_id);
-
-  // Gets the Android ID for the ARC container.
-  GetArcAndroidId() => (bool success, string android_id);
-
-  // Launches allowed ARC intents.
-  // Intents are checked via chromeos::kiosk_next_home::IntentConfigHelper.
-  LaunchIntent(string intent) => (bool launched, string? error_message);
-};
diff --git a/chrome/browser/chromeos/kiosk_next_home/mojom/identity_controller.mojom b/chrome/browser/chromeos/kiosk_next_home/mojom/identity_controller.mojom
deleted file mode 100644
index 251b5209..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/mojom/identity_controller.mojom
+++ /dev/null
@@ -1,15 +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.
-
-module chromeos.kiosk_next_home.mojom;
-
-struct UserInfo {
-  string given_name;
-  string display_name;
-};
-
-// Exposes identity-related capabilities to Kiosk Next Home.
-interface IdentityController {
-  GetUserInfo() => (UserInfo user_info);
-};
diff --git a/chrome/browser/chromeos/kiosk_next_home/mojom/kiosk_next_home_interface_broker.mojom b/chrome/browser/chromeos/kiosk_next_home/mojom/kiosk_next_home_interface_broker.mojom
deleted file mode 100644
index da76632..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/mojom/kiosk_next_home_interface_broker.mojom
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module chromeos.kiosk_next_home.mojom;
-
-import "chrome/browser/chromeos/kiosk_next_home/mojom/app_controller.mojom";
-import "chrome/browser/chromeos/kiosk_next_home/mojom/website_controller.mojom";
-import "chrome/browser/chromeos/kiosk_next_home/mojom/identity_controller.mojom";
-import "services/identity/public/mojom/identity_accessor.mojom";
-
-// Single entry point for any interface requests issued by the Kiosk Next
-// Home app and brokered by the browser for services needed to support
-// its functionality.
-interface KioskNextHomeInterfaceBroker {
-  // Binds IdentityAccessor service request.
-  // TODO(brunoad): migrate needed methods to IdentityController and remove.
-  GetIdentityAccessor(identity.mojom.IdentityAccessor& request);
-
-  // Binds the IdentityController service request.
-  GetIdentityController(IdentityController& request);
-
-  // Binds the AppController service request.
-  GetAppController(AppController& request);
-
-  // Binds the WebsiteController service request.
-  GetWebsiteController(WebsiteController& request);
-};
diff --git a/chrome/browser/chromeos/kiosk_next_home/mojom/website_controller.mojom b/chrome/browser/chromeos/kiosk_next_home/mojom/website_controller.mojom
deleted file mode 100644
index 8f4abdd..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/mojom/website_controller.mojom
+++ /dev/null
@@ -1,16 +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.
-
-module chromeos.kiosk_next_home.mojom;
-
-import "url/mojom/url.mojom";
-
-// Creates Browser instances and navigates them to the requested URL.
-interface WebsiteController {
-  // Launches KioskNextBrowser with the given URL.
-  LaunchKioskNextWebsite(url.mojom.Url url);
-
-  // Launches tabbed browser with the given URL.
-  LaunchWebsite(url.mojom.Url url);
-};
diff --git a/chrome/browser/chromeos/kiosk_next_home/website_controller_impl.cc b/chrome/browser/chromeos/kiosk_next_home/website_controller_impl.cc
deleted file mode 100644
index bb07cd00..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/website_controller_impl.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/kiosk_next_home/website_controller_impl.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "chrome/browser/chromeos/kiosk_next/kiosk_next_browser_factory.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/browser/ui/browser_navigator_params.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "components/user_manager/user_manager.h"
-#include "ui/base/page_transition_types.h"
-#include "ui/base/window_open_disposition.h"
-#include "url/gurl.h"
-
-namespace chromeos {
-namespace kiosk_next_home {
-namespace {
-
-Profile* GetActiveUserProfile() {
-  auto* user = user_manager::UserManager::Get()->GetActiveUser();
-  Profile* profile = ProfileHelper::Get()->GetProfileByUser(user);
-  DCHECK(profile);
-  return profile;
-}
-
-}  // namespace
-
-WebsiteControllerImpl::WebsiteControllerImpl(
-    mojom::WebsiteControllerRequest request)
-    : binding_(this, std::move(request)) {}
-
-WebsiteControllerImpl::~WebsiteControllerImpl() = default;
-
-void WebsiteControllerImpl::LaunchKioskNextWebsite(const GURL& url) {
-  KioskNextBrowserFactory::Get()->CreateForWebsite(url);
-}
-
-void WebsiteControllerImpl::LaunchWebsite(const GURL& url) {
-  Browser::CreateParams create_params(GetActiveUserProfile(),
-                                      true /* user_gesture */);
-  Browser* browser = Browser::Create(create_params);
-
-  NavigateParams navigate_params(browser, url,
-                                 ui::PageTransition::PAGE_TRANSITION_LINK);
-  navigate_params.disposition = WindowOpenDisposition::CURRENT_TAB;
-  Navigate(&navigate_params);
-
-  browser->window()->Show();
-}
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/kiosk_next_home/website_controller_impl.h b/chrome/browser/chromeos/kiosk_next_home/website_controller_impl.h
deleted file mode 100644
index a5af5e4..0000000
--- a/chrome/browser/chromeos/kiosk_next_home/website_controller_impl.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_WEBSITE_CONTROLLER_IMPL_H_
-#define CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_WEBSITE_CONTROLLER_IMPL_H_
-
-#include "base/macros.h"
-#include "chrome/browser/chromeos/kiosk_next_home/mojom/website_controller.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-class GURL;
-
-namespace chromeos {
-namespace kiosk_next_home {
-
-class WebsiteControllerImpl : public mojom::WebsiteController {
- public:
-  explicit WebsiteControllerImpl(mojom::WebsiteControllerRequest request);
-  ~WebsiteControllerImpl() override;
-
-  // Launches the website using KioskNextBrowserFactory.
-  void LaunchKioskNextWebsite(const GURL& url) override;
-
-  // Launches the url in normal tabbed browser window.
-  void LaunchWebsite(const GURL& url) override;
-
- private:
-  mojo::Binding<mojom::WebsiteController> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebsiteControllerImpl);
-};
-
-}  // namespace kiosk_next_home
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_KIOSK_NEXT_HOME_WEBSITE_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/chromeos/login/auth/auth_prewarmer.cc b/chrome/browser/chromeos/login/auth/auth_prewarmer.cc
index 6be81f7..3c64117 100644
--- a/chrome/browser/chromeos/login/auth/auth_prewarmer.cc
+++ b/chrome/browser/chromeos/login/auth/auth_prewarmer.cc
@@ -6,7 +6,6 @@
 
 #include <stddef.h>
 
-#include "base/optional.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/chromeos/login/helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -18,6 +17,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/base/load_flags.h"
+#include "net/base/network_isolation_key.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "url/gurl.h"
 
@@ -67,7 +67,8 @@
   if (network_context) {
     // Do nothing if NetworkContext isn't available.
     network_context->PreconnectSockets(kConnectionsNeeded, url, kLoadFlags,
-                                       kShouldUsePrivacyMode, base::nullopt);
+                                       kShouldUsePrivacyMode,
+                                       net::NetworkIsolationKey());
   }
   if (!completion_callback_.is_null()) {
     base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
diff --git a/chrome/browser/chromeos/login/easy_unlock/chrome_proximity_auth_client.cc b/chrome/browser/chromeos/login/easy_unlock/chrome_proximity_auth_client.cc
index 333e9f78..d11fb81f 100644
--- a/chrome/browser/chromeos/login/easy_unlock/chrome_proximity_auth_client.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/chrome_proximity_auth_client.cc
@@ -16,13 +16,11 @@
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_window.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/version_info.h"
-#include "services/identity/public/cpp/identity_manager.h"
 
 using proximity_auth::ScreenlockState;
 
@@ -33,15 +31,6 @@
 
 ChromeProximityAuthClient::~ChromeProximityAuthClient() {}
 
-std::string ChromeProximityAuthClient::GetAuthenticatedUsername() const {
-  const identity::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfileIfExists(profile_);
-  // |profile_| has to be a signed-in profile with IdentityManager already
-  // created. Otherwise, just crash to collect stack.
-  DCHECK(identity_manager);
-  return identity_manager->GetPrimaryAccountInfo().email;
-}
-
 void ChromeProximityAuthClient::UpdateScreenlockState(ScreenlockState state) {
   EasyUnlockService* service = EasyUnlockService::Get(profile_);
   if (service)
diff --git a/chrome/browser/chromeos/login/easy_unlock/chrome_proximity_auth_client.h b/chrome/browser/chromeos/login/easy_unlock/chrome_proximity_auth_client.h
index 7ccf242f..6fa59de 100644
--- a/chrome/browser/chromeos/login/easy_unlock/chrome_proximity_auth_client.h
+++ b/chrome/browser/chromeos/login/easy_unlock/chrome_proximity_auth_client.h
@@ -20,7 +20,6 @@
   ~ChromeProximityAuthClient() override;
 
   // proximity_auth::ProximityAuthClient:
-  std::string GetAuthenticatedUsername() const override;
   void UpdateScreenlockState(proximity_auth::ScreenlockState state) override;
   void FinalizeUnlock(bool success) override;
   void FinalizeSignin(const std::string& secret) override;
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
index e8d5640..223f80a 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/gcm/gcm_profile_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -56,7 +55,6 @@
 #include "extensions/browser/event_router.h"
 #include "extensions/common/constants.h"
 #include "google_apis/gaia/gaia_auth_util.h"
-#include "services/identity/public/cpp/identity_manager.h"
 
 namespace chromeos {
 
@@ -288,18 +286,10 @@
 }
 
 AccountId EasyUnlockServiceRegular::GetAccountId() const {
-  identity::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(profile());
-  // |profile| has to be a signed-in profile with IdentityManager already
-  // created. Otherwise, just crash to collect stack.
-  DCHECK(identity_manager);
-  const CoreAccountInfo account_info =
-      identity_manager->GetPrimaryAccountInfo();
-
-  // A regular signed-in (i.e., non-login) profile should always have an email.
-  DCHECK(!account_info.email.empty());
-
-  return AccountIdFromAccountInfo(account_info);
+  const user_manager::User* const primary_user =
+      user_manager::UserManager::Get()->GetPrimaryUser();
+  DCHECK(primary_user);
+  return primary_user->GetAccountId();
 }
 
 void EasyUnlockServiceRegular::SetHardlockAfterKeyOperation(
@@ -363,15 +353,8 @@
 
   pref_manager_.reset(new proximity_auth::ProximityAuthProfilePrefManager(
       profile()->GetPrefs(), multidevice_setup_client_));
-
-  // TODO(crbug.com/857494): Thousands of unrelated browser_tests provide a
-  // profile with an empty email to this service, causing crashes further on.
-  // Investigate what this service is doing so differently (i.e., incorrectly)
-  // from others that causes such issues with so many browser_tests.
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) {
-    pref_manager_->StartSyncingToLocalState(g_browser_process->local_state(),
-                                            GetAccountId());
-  }
+  pref_manager_->StartSyncingToLocalState(g_browser_process->local_state(),
+                                          GetAccountId());
 
   LoadRemoteDevices();
 
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_unittest_chromeos.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_unittest_chromeos.cc
index 2e84101..62b5910f 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_unittest_chromeos.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_unittest_chromeos.cc
@@ -48,9 +48,7 @@
 
 namespace {
 
-// Emails for fake users used in tests.
-const char kTestUserPrimary[] = "primary_user@nowhere.com";
-const char kTestUserSecondary[] = "secondary_user@nowhere.com";
+const char kTestUserEmail[] = "user@nowhere.com";
 
 class MockEasyUnlockNotificationController
     : public EasyUnlockNotificationController {
@@ -116,7 +114,7 @@
     TestingBrowserProcess::GetGlobal()->SetLocalState(&local_pref_service_);
     RegisterLocalState(local_pref_service_.registry());
 
-    profile_ = SetUpProfile(kTestUserPrimary, &profile_gaia_id_);
+    profile_ = SetUpProfile(kTestUserEmail, &profile_gaia_id_);
   }
 
   void TearDown() override {
@@ -147,9 +145,7 @@
                   base::BindRepeating(&CreateEasyUnlockServiceForTest)}});
 
     // Note: This can simply be a local variable as the test does not need to
-    // interact with IdentityTestEnvironment outside of this method. If that
-    // ever changes, there will need to be distinct instance variables for the
-    // environments associated with |profile_| and |secondary_profile_|.
+    // interact with IdentityTestEnvironment outside of this method.
     IdentityTestEnvironmentProfileAdaptor identity_test_env_adaptor(
         profile.get());
     CoreAccountInfo account_info =
@@ -168,18 +164,11 @@
     return profile;
   }
 
-  void SetUpSecondaryProfile() {
-    secondary_profile_ =
-        SetUpProfile(kTestUserSecondary, &secondary_profile_gaia_id_);
-  }
-
   // Must outlive TestingProfiles.
   content::TestBrowserThreadBundle thread_bundle_;
 
   std::unique_ptr<TestingProfile> profile_;
   std::string profile_gaia_id_;
-  std::unique_ptr<TestingProfile> secondary_profile_;
-  std::string secondary_profile_gaia_id_;
   MockUserManager* mock_user_manager_;
 
   user_manager::ScopedUserManager scoped_user_manager_;
@@ -211,13 +200,8 @@
 }
 
 TEST_F(EasyUnlockServiceTest, GetAccountId) {
-  EXPECT_EQ(AccountId::FromUserEmailGaiaId(kTestUserPrimary, profile_gaia_id_),
+  EXPECT_EQ(AccountId::FromUserEmailGaiaId(kTestUserEmail, profile_gaia_id_),
             EasyUnlockService::Get(profile_.get())->GetAccountId());
-
-  SetUpSecondaryProfile();
-  EXPECT_EQ(AccountId::FromUserEmailGaiaId(kTestUserSecondary,
-                                           secondary_profile_gaia_id_),
-            EasyUnlockService::Get(secondary_profile_.get())->GetAccountId());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
index 08d5c38c..c00f5fa 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
-#include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
@@ -37,6 +36,7 @@
 #include "chromeos/system/fake_statistics_provider.h"
 #include "chromeos/tpm/install_attributes.h"
 #include "components/policy/core/common/policy_switches.h"
+#include "components/policy/test_support/local_policy_test_server.h"
 #include "components/strings/grit/components_strings.h"
 
 namespace chromeos {
diff --git a/chrome/browser/chromeos/login/login_client_cert_usage_observer.cc b/chrome/browser/chromeos/login/login_client_cert_usage_observer.cc
new file mode 100644
index 0000000..8debad0
--- /dev/null
+++ b/chrome/browser/chromeos/login/login_client_cert_usage_observer.cc
@@ -0,0 +1,109 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/login_client_cert_usage_observer.h"
+
+#include <cstdint>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/x509_util.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+
+namespace chromeos {
+
+namespace {
+
+CertificateProviderService* GetCertificateProviderService() {
+  Profile* signin_profile = ProfileHelper::GetSigninProfile();
+  return CertificateProviderServiceFactory::GetForBrowserContext(
+      signin_profile);
+}
+
+bool ObtainSignatureAlgorithms(
+    const net::X509Certificate& cert,
+    std::vector<ChallengeResponseKey::SignatureAlgorithm>*
+        signature_algorithms) {
+  auto* certificate_provider_service = GetCertificateProviderService();
+  base::StringPiece spki;
+  if (!net::asn1::ExtractSPKIFromDERCert(
+          net::x509_util::CryptoBufferAsStringPiece(cert.cert_buffer()),
+          &spki)) {
+    return false;
+  }
+  std::vector<uint16_t> ssl_algorithms;
+  if (!certificate_provider_service->GetSupportedAlgorithmsBySpki(
+          spki.as_string(), &ssl_algorithms)) {
+    return false;
+  }
+  signature_algorithms->clear();
+  for (auto ssl_algorithm : ssl_algorithms) {
+    switch (ssl_algorithm) {
+      case SSL_SIGN_RSA_PKCS1_SHA1:
+        signature_algorithms->push_back(
+            ChallengeResponseKey::SignatureAlgorithm::kRsassaPkcs1V15Sha1);
+        break;
+      case SSL_SIGN_RSA_PKCS1_SHA256:
+        signature_algorithms->push_back(
+            ChallengeResponseKey::SignatureAlgorithm::kRsassaPkcs1V15Sha256);
+        break;
+      case SSL_SIGN_RSA_PKCS1_SHA384:
+        signature_algorithms->push_back(
+            ChallengeResponseKey::SignatureAlgorithm::kRsassaPkcs1V15Sha384);
+        break;
+      case SSL_SIGN_RSA_PKCS1_SHA512:
+        signature_algorithms->push_back(
+            ChallengeResponseKey::SignatureAlgorithm::kRsassaPkcs1V15Sha512);
+        break;
+    }
+  }
+  return !signature_algorithms->empty();
+}
+
+}  // namespace
+
+LoginClientCertUsageObserver::LoginClientCertUsageObserver() {
+  GetCertificateProviderService()->AddObserver(this);
+}
+
+LoginClientCertUsageObserver::~LoginClientCertUsageObserver() {
+  GetCertificateProviderService()->RemoveObserver(this);
+}
+
+bool LoginClientCertUsageObserver::ClientCertsWereUsed() const {
+  return used_cert_count_ > 0;
+}
+
+bool LoginClientCertUsageObserver::GetOnlyUsedClientCert(
+    scoped_refptr<net::X509Certificate>* cert,
+    std::vector<ChallengeResponseKey::SignatureAlgorithm>* signature_algorithms)
+    const {
+  if (!used_cert_count_)
+    return false;
+  if (used_cert_count_ > 1) {
+    LOG(ERROR)
+        << "Failed to choose the client certificate for offline user "
+           "authentication, since more than one client certificate was used";
+    return false;
+  }
+  DCHECK(used_cert_);
+  if (!ObtainSignatureAlgorithms(*used_cert_, signature_algorithms))
+    return false;
+  *cert = used_cert_;
+  return true;
+}
+
+void LoginClientCertUsageObserver::OnSignCompleted(
+    const scoped_refptr<net::X509Certificate>& certificate) {
+  if (!used_cert_ || !used_cert_->EqualsExcludingChain(certificate.get()))
+    ++used_cert_count_;
+  used_cert_ = certificate;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/login_client_cert_usage_observer.h b/chrome/browser/chromeos/login/login_client_cert_usage_observer.h
new file mode 100644
index 0000000..d766d9e
--- /dev/null
+++ b/chrome/browser/chromeos/login/login_client_cert_usage_observer.h
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_CLIENT_CERT_USAGE_OBSERVER_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_CLIENT_CERT_USAGE_OBSERVER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
+#include "chromeos/login/auth/challenge_response_key.h"
+#include "net/cert/x509_certificate.h"
+
+namespace chromeos {
+
+// Observes and remembers the extension-provided client certificates that were
+// used to successfully sign data on the login screen.
+class LoginClientCertUsageObserver final
+    : public CertificateProviderService::Observer {
+ public:
+  LoginClientCertUsageObserver();
+  ~LoginClientCertUsageObserver() override;
+
+  // Returns whether at least one certificate was used.
+  bool ClientCertsWereUsed() const;
+  // Returns whether exactly one unique certificate was used, and, if so,
+  // writes this certificate to |cert| and appends the signature algorithms
+  // supported by its provider into |signature_algorithms|.
+  bool GetOnlyUsedClientCert(
+      scoped_refptr<net::X509Certificate>* cert,
+      std::vector<ChallengeResponseKey::SignatureAlgorithm>*
+          signature_algorithms) const;
+
+ private:
+  // CertificateProviderService::Observer:
+  void OnSignCompleted(
+      const scoped_refptr<net::X509Certificate>& certificate) override;
+
+  // How many times the client certificate, used on the login screen, has
+  // changed.
+  int used_cert_count_ = 0;
+  // One of the client certificates that was used on the login screen.
+  scoped_refptr<net::X509Certificate> used_cert_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoginClientCertUsageObserver);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_CLIENT_CERT_USAGE_OBSERVER_H_
diff --git a/chrome/browser/chromeos/login/test/active_directory_login_mixin.h b/chrome/browser/chromeos/login/test/active_directory_login_mixin.h
index 2d6acf5..7b2b2f2 100644
--- a/chrome/browser/chromeos/login/test/active_directory_login_mixin.h
+++ b/chrome/browser/chromeos/login/test/active_directory_login_mixin.h
@@ -12,9 +12,9 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h"
 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
-#include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/local_policy_test_server.h"
 #include "content/public/test/browser_test_utils.h"
 
 namespace chromeos {
diff --git a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
index 666739e..5f3c458 100644
--- a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
+++ b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
@@ -12,10 +12,10 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h"
 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
-#include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/cloud_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/local_policy_test_server.h"
 
 namespace chromeos {
 
diff --git a/chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_dialog_delegate_unittest.cc b/chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_dialog_delegate_unittest.cc
index 7d6f6627..e858548 100644
--- a/chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_dialog_delegate_unittest.cc
+++ b/chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_dialog_delegate_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h"
 #include "chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_window.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
diff --git a/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc b/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc
index e4d20da..efe396ca 100644
--- a/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc
+++ b/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc
@@ -60,10 +60,10 @@
 
   ~ToolbarRowView() override {}
 
-  void Init(views::View* back,
-            views::View* forward,
-            views::View* reload,
-            views::View* location_bar) {
+  void Init(std::unique_ptr<views::View> back,
+            std::unique_ptr<views::View> forward,
+            std::unique_ptr<views::View> reload,
+            std::unique_ptr<views::View> location_bar) {
     GridLayout* layout = SetLayoutManager(std::make_unique<GridLayout>());
 
     const int related_horizontal_spacing =
@@ -88,10 +88,10 @@
     column_set->AddPaddingColumn(0, related_horizontal_spacing);
 
     layout->StartRow(0, 0);
-    layout->AddView(back);
-    layout->AddView(forward);
-    layout->AddView(reload);
-    layout->AddView(location_bar);
+    layout->AddView(std::move(back));
+    layout->AddView(std::move(forward));
+    layout->AddView(std::move(reload));
+    layout->AddView(std::move(location_bar));
   }
 
  private:
@@ -139,7 +139,7 @@
 }
 
 void SimpleWebViewDialog::StartLoad(const GURL& url) {
-  if (!web_view_container_.get())
+  if (!web_view_container_)
     web_view_container_.reset(new views::WebView(profile_));
   web_view_ = web_view_container_.get();
   web_view_->set_owned_by_client();
@@ -165,39 +165,48 @@
   SetBackground(views::CreateSolidBackground(kDialogColor));
 
   // Back/Forward buttons.
-  back_ = new views::ImageButton(this);
-  back_->set_triggerable_event_flags(ui::EF_LEFT_MOUSE_BUTTON |
-                                     ui::EF_MIDDLE_MOUSE_BUTTON);
-  back_->set_tag(IDC_BACK);
-  back_->SetImageHorizontalAlignment(views::ImageButton::ALIGN_RIGHT);
-  back_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
-  back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
-  back_->SetID(VIEW_ID_BACK_BUTTON);
+  auto back = std::make_unique<views::ImageButton>(this);
+  back->set_triggerable_event_flags(ui::EF_LEFT_MOUSE_BUTTON |
+                                    ui::EF_MIDDLE_MOUSE_BUTTON);
+  back->set_tag(IDC_BACK);
+  back->SetImageHorizontalAlignment(views::ImageButton::ALIGN_RIGHT);
+  back->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
+  back->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
+  back->SetID(VIEW_ID_BACK_BUTTON);
+  back_ = back.get();
 
-  forward_ = new views::ImageButton(this);
-  forward_->set_triggerable_event_flags(ui::EF_LEFT_MOUSE_BUTTON |
-                                        ui::EF_MIDDLE_MOUSE_BUTTON);
-  forward_->set_tag(IDC_FORWARD);
-  forward_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
-  forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
-  forward_->SetID(VIEW_ID_FORWARD_BUTTON);
+  auto forward = std::make_unique<views::ImageButton>(this);
+  forward->set_triggerable_event_flags(ui::EF_LEFT_MOUSE_BUTTON |
+                                       ui::EF_MIDDLE_MOUSE_BUTTON);
+  forward->set_tag(IDC_FORWARD);
+  forward->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
+  forward->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
+  forward->SetID(VIEW_ID_FORWARD_BUTTON);
+  forward_ = forward.get();
 
   // Location bar.
-  location_bar_ = new LocationBarView(nullptr, profile_, command_updater_.get(),
-                                      this, true);
+  auto location_bar = std::make_unique<LocationBarView>(
+      nullptr, profile_, command_updater_.get(), this, true);
+  location_bar_ = location_bar.get();
 
   // Reload button.
-  reload_ = new ReloadButton(command_updater_.get());
-  reload_->set_triggerable_event_flags(ui::EF_LEFT_MOUSE_BUTTON |
-                                       ui::EF_MIDDLE_MOUSE_BUTTON);
-  reload_->set_tag(IDC_RELOAD);
-  reload_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_RELOAD));
-  reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
-  reload_->SetID(VIEW_ID_RELOAD_BUTTON);
+  auto reload = std::make_unique<ReloadButton>(command_updater_.get());
+  reload->set_triggerable_event_flags(ui::EF_LEFT_MOUSE_BUTTON |
+                                      ui::EF_MIDDLE_MOUSE_BUTTON);
+  reload->set_tag(IDC_RELOAD);
+  reload->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_RELOAD));
+  reload->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
+  reload->SetID(VIEW_ID_RELOAD_BUTTON);
+  reload_ = reload.get();
 
   // Use separate view to setup custom background.
-  ToolbarRowView* toolbar_row = new ToolbarRowView;
-  toolbar_row->Init(back_, forward_, reload_, location_bar_);
+  auto toolbar_row = std::make_unique<ToolbarRowView>();
+  toolbar_row->Init(std::move(back), std::move(forward), std::move(reload),
+                    std::move(location_bar));
+  // Add the views as child views before the grid layout is installed. This
+  // ensures ownership is more clear.
+  ToolbarRowView* toolbar_row_ptr = AddChildView(std::move(toolbar_row));
+  AddChildView(web_view_);
 
   // Layout.
   GridLayout* layout = SetLayoutManager(std::make_unique<GridLayout>());
@@ -214,12 +223,12 @@
 
   // Setup layout rows.
   layout->StartRow(0, 0);
-  layout->AddView(toolbar_row);
+  layout->AddExistingView(toolbar_row_ptr);
 
   layout->AddPaddingRow(0, kInnerMargin);
 
   layout->StartRow(1, 1);
-  layout->AddView(web_view_container_.get());
+  layout->AddExistingView(web_view_);
   layout->AddPaddingRow(0, kInnerMargin);
 
   LoadImages();
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
index 21cbe371..8632e8b 100644
--- a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
@@ -138,7 +138,7 @@
  public:
   std::unique_ptr<net::test_server::BasicHttpResponse> HandleRequest(
       const net::test_server::HttpRequest& request) {
-    if (request.relative_url != "/avatar.jpg")
+    if (request.relative_url.find("/avatar.jpg") == std::string::npos)
       return nullptr;
 
     // Check whether the token string is the same.
diff --git a/chrome/browser/chromeos/network_change_manager_client_browsertest.cc b/chrome/browser/chromeos/network_change_manager_client_browsertest.cc
index d43fe43..85cbfdd 100644
--- a/chrome/browser/chromeos/network_change_manager_client_browsertest.cc
+++ b/chrome/browser/chromeos/network_change_manager_client_browsertest.cc
@@ -5,12 +5,13 @@
 #include "base/run_loop.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/shill/shill_device_client.h"
 #include "chromeos/dbus/shill/shill_service_client.h"
 #include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/system_connector.h"
+#include "content/public/common/service_names.mojom.h"
 #include "net/base/network_change_notifier.h"
-#include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
+#include "services/network/public/mojom/network_service_test.mojom.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
 namespace chromeos {
@@ -101,6 +102,12 @@
     service_client_ =
         DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
     service_client_->ClearServices();
+
+    // Make sure everyone thinks we have an ethernet connection.
+    NetObserver().WaitForConnectionType(
+        net::NetworkChangeNotifier::CONNECTION_ETHERNET);
+    NetworkServiceObserver().WaitForConnectionType(
+        network::mojom::ConnectionType::CONNECTION_ETHERNET);
   }
 
   ShillServiceClient::TestInterface* service_client() {
@@ -113,9 +120,8 @@
 
 // Tests that network changes from shill are received by both the
 // NetworkChangeNotifier and NetworkConnectionTracker.
-// TODO(crbug.com/934583): Fix the flakiness.
 IN_PROC_BROWSER_TEST_F(NetworkChangeManagerClientBrowserTest,
-                       DISABLED_ReceiveNotifications) {
+                       ReceiveNotifications) {
   NetObserver net_observer;
   NetworkServiceObserver network_service_observer;
 
@@ -139,39 +145,26 @@
 
 // Tests that the NetworkChangeManagerClient reconnects to the network service
 // after it gets disconnected.
-// TODO(crbug.com/934583): Fix the flakiness.
 IN_PROC_BROWSER_TEST_F(NetworkChangeManagerClientBrowserTest,
-                       DISABLED_ReconnectToNetworkService) {
-  {
-    // Make sure everyone thinks we have an ethernet connection.
-    NetObserver net_observer;
-    NetworkServiceObserver network_service_observer;
-    net_observer.WaitForConnectionType(
-        net::NetworkChangeNotifier::CONNECTION_ETHERNET);
-    network_service_observer.WaitForConnectionType(
-        network::mojom::ConnectionType::CONNECTION_ETHERNET);
-  }
-
-  NetObserver net_observer;
+                       ReconnectToNetworkService) {
   NetworkServiceObserver network_service_observer;
 
-  SimulateNetworkServiceCrash();
+  // Manually call SimulateCrash instead of
+  // BrowserTestBase::SimulateNetworkServiceCrash to avoid the cleanup and
+  // reconnection work it does for you.
+  network::mojom::NetworkServiceTestPtr network_service_test;
+  content::GetSystemConnector()->BindInterface(
+      content::mojom::kNetworkServiceName, &network_service_test);
+  network_service_test->SimulateCrash();
+
   service_client()->AddService("wifi", "wifi", "wifi", shill::kTypeWifi,
                                shill::kStateOnline, true);
 
-  net_observer.WaitForConnectionType(
+  NetObserver().WaitForConnectionType(
       net::NetworkChangeNotifier::CONNECTION_WIFI);
-  // NetworkChangeNotifier will send a CONNECTION_NONE notification before
-  // the CONNECTION_WIFI one.
-  EXPECT_EQ(2, net_observer.change_count_);
-  EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_WIFI,
-            net_observer.last_connection_type_);
-
   network_service_observer.WaitForConnectionType(
       network::mojom::ConnectionType::CONNECTION_WIFI);
   EXPECT_EQ(2, network_service_observer.change_count_);
-  EXPECT_EQ(network::mojom::ConnectionType::CONNECTION_WIFI,
-            network_service_observer.last_connection_type_);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
index f1a3a2f..0c6319f 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
@@ -7,7 +7,7 @@
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "base/bind_helpers.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_files.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -315,14 +315,14 @@
   EnsureDefaultSharedDirExists(
       profile_, base::BindOnce(&PluginVmManager::OnDefaultSharedDirExists,
                                weak_ptr_factory_.GetWeakPtr()));
-  crostini::CrostiniSharePath::GetForProfile(profile_)->SharePersistedPaths(
+  guest_os::GuestOsSharePath::GetForProfile(profile_)->SharePersistedPaths(
       kPluginVmName, base::DoNothing());
 }
 
 void PluginVmManager::OnDefaultSharedDirExists(const base::FilePath& dir,
                                                bool exists) {
   if (exists) {
-    crostini::CrostiniSharePath::GetForProfile(profile_)->SharePath(
+    guest_os::GuestOsSharePath::GetForProfile(profile_)->SharePath(
         kPluginVmName, dir, false,
         base::BindOnce([](const base::FilePath& dir, bool success,
                           std::string failure_reason) {
diff --git a/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc b/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
index c4b18b3..542fa4d 100644
--- a/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
@@ -78,7 +78,8 @@
         }
       )");
   return std::unique_ptr<UploadJob>(new UploadJobImpl(
-      upload_url, robot_account_id, device_oauth2_token_service,
+      upload_url, robot_account_id,
+      device_oauth2_token_service->GetAccessTokenManager(),
       g_browser_process->shared_url_loader_factory(), delegate,
       base::WrapUnique(new UploadJobImpl::RandomMimeBoundaryGenerator),
       traffic_annotation, base::ThreadTaskRunnerHandle::Get()));
diff --git a/chrome/browser/chromeos/policy/signin_profile_extensions_policy_browsertest.cc b/chrome/browser/chromeos/policy/signin_profile_extensions_policy_browsertest.cc
index affb188..ebd8b0a 100644
--- a/chrome/browser/chromeos/policy/signin_profile_extensions_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/signin_profile_extensions_policy_browsertest.cc
@@ -285,10 +285,13 @@
 // Tests that a background page is created for the installed sign-in profile
 // app.
 IN_PROC_BROWSER_TEST_F(SigninProfileExtensionsPolicyTest, BackgroundPage) {
+  EXPECT_FALSE(
+      chromeos::ProfileHelper::SigninProfileHasLoginScreenExtensions());
   ExtensionBackgroundPageReadyObserver page_observer(kNotWhitelistedAppId);
   AddExtensionForForceInstallation(kNotWhitelistedAppId,
                                    kNotWhitelistedUpdateManifestPath);
   page_observer.Wait();
+  EXPECT_TRUE(chromeos::ProfileHelper::SigninProfileHasLoginScreenExtensions());
 }
 
 // Tests installation of multiple sign-in profile apps.
diff --git a/chrome/browser/chromeos/policy/system_log_uploader.cc b/chrome/browser/chromeos/policy/system_log_uploader.cc
index f07a181..544c44c 100644
--- a/chrome/browser/chromeos/policy/system_log_uploader.cc
+++ b/chrome/browser/chromeos/policy/system_log_uploader.cc
@@ -171,7 +171,8 @@
         }
       )");
   return std::make_unique<UploadJobImpl>(
-      upload_url, robot_account_id, device_oauth2_token_service,
+      upload_url, robot_account_id,
+      device_oauth2_token_service->GetAccessTokenManager(),
       g_browser_process->shared_url_loader_factory(), delegate,
       std::make_unique<UploadJobImpl::RandomMimeBoundaryGenerator>(),
       traffic_annotation, task_runner_);
diff --git a/chrome/browser/chromeos/policy/upload_job_impl.cc b/chrome/browser/chromeos/policy/upload_job_impl.cc
index ef836e97..f7f107f 100644
--- a/chrome/browser/chromeos/policy/upload_job_impl.cc
+++ b/chrome/browser/chromeos/policy/upload_job_impl.cc
@@ -17,7 +17,6 @@
 #include "base/syslog_logging.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/google_service_auth_error.h"
-#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/base/mime_util.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -153,7 +152,7 @@
 UploadJobImpl::UploadJobImpl(
     const GURL& upload_url,
     const std::string& account_id,
-    OAuth2TokenService* token_service,
+    OAuth2AccessTokenManager* access_token_manager,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     Delegate* delegate,
     std::unique_ptr<MimeBoundaryGenerator> boundary_generator,
@@ -162,7 +161,7 @@
     : OAuth2AccessTokenManager::Consumer("cros_upload_job"),
       upload_url_(upload_url),
       account_id_(account_id),
-      token_service_(token_service),
+      access_token_manager_(access_token_manager),
       url_loader_factory_(std::move(url_loader_factory)),
       delegate_(delegate),
       boundary_generator_(std::move(boundary_generator)),
@@ -171,7 +170,7 @@
       retry_(0),
       task_runner_(task_runner),
       weak_factory_(this) {
-  DCHECK(token_service_);
+  DCHECK(access_token_manager_);
   DCHECK(url_loader_factory_);
   DCHECK(delegate_);
   SYSLOG(INFO) << "Upload job created.";
@@ -233,7 +232,7 @@
   OAuth2AccessTokenManager::ScopeSet scope_set;
   scope_set.insert(GaiaConstants::kDeviceManagementServiceOAuth);
   access_token_request_ =
-      token_service_->StartRequest(account_id_, scope_set, this);
+      access_token_manager_->StartRequest(account_id_, scope_set, this);
 }
 
 bool UploadJobImpl::SetUpMultipart() {
@@ -385,8 +384,8 @@
       // Request new token and retry.
       OAuth2AccessTokenManager::ScopeSet scope_set;
       scope_set.insert(GaiaConstants::kDeviceManagementServiceOAuth);
-      token_service_->InvalidateAccessToken(account_id_, scope_set,
-                                            access_token_);
+      access_token_manager_->InvalidateAccessToken(account_id_, scope_set,
+                                                   access_token_);
       access_token_.clear();
       task_runner_->PostDelayedTask(
           FROM_HERE,
diff --git a/chrome/browser/chromeos/policy/upload_job_impl.h b/chrome/browser/chromeos/policy/upload_job_impl.h
index 9aa2487..b3d08ce 100644
--- a/chrome/browser/chromeos/policy/upload_job_impl.h
+++ b/chrome/browser/chromeos/policy/upload_job_impl.h
@@ -31,8 +31,6 @@
 class SimpleURLLoader;
 }  // namespace network
 
-class OAuth2TokenService;
-
 namespace policy {
 
 // This implementation of UploadJob uses the OAuth2AccessTokenManager to acquire
@@ -67,7 +65,7 @@
   UploadJobImpl(
       const GURL& upload_url,
       const std::string& account_id,
-      OAuth2TokenService* token_service,
+      OAuth2AccessTokenManager* access_token_manager,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       Delegate* delegate,
       std::unique_ptr<MimeBoundaryGenerator> boundary_generator,
@@ -141,8 +139,8 @@
   // The account ID that will be used for the access token fetch.
   const std::string account_id_;
 
-  // The token service used to retrieve the access token.
-  OAuth2TokenService* const token_service_;
+  // The token manager used to retrieve the access token.
+  OAuth2AccessTokenManager* const access_token_manager_;
 
   // This is used to initialize the network::SimpleURLLoader object.
   const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
diff --git a/chrome/browser/chromeos/policy/upload_job_unittest.cc b/chrome/browser/chromeos/policy/upload_job_unittest.cc
index 9ff3973..a93efc6 100644
--- a/chrome/browser/chromeos/policy/upload_job_unittest.cc
+++ b/chrome/browser/chromeos/policy/upload_job_unittest.cc
@@ -63,6 +63,12 @@
   DISALLOW_COPY_AND_ASSIGN(RepeatingMimeBoundaryGenerator);
 };
 
+// TODO(https://crbug.com/967598): Change this to be a mock
+// OAuth2AccessTokenManager class (marking the necessary methods of
+// OAuth2AccessTokenManager virtual) as part of changing
+// OAuth2AccessTokenManager::{StartRequestForClientWithContext(),
+// InvalidateAccessToken()} to directly call the methods being overridden here
+// rather than calling through to OAuth2TokenService.
 class MockOAuth2TokenService : public FakeOAuth2TokenService {
  public:
   MockOAuth2TokenService();
@@ -200,8 +206,9 @@
       std::unique_ptr<UploadJobImpl::MimeBoundaryGenerator>
           mime_boundary_generator) {
     std::unique_ptr<UploadJob> upload_job(new UploadJobImpl(
-        GetServerURL(), kRobotAccountId, &oauth2_service_, url_loader_factory_,
-        this, std::move(mime_boundary_generator), TRAFFIC_ANNOTATION_FOR_TESTS,
+        GetServerURL(), kRobotAccountId,
+        oauth2_service_.GetAccessTokenManager(), url_loader_factory_, this,
+        std::move(mime_boundary_generator), TRAFFIC_ANNOTATION_FOR_TESTS,
         base::ThreadTaskRunnerHandle::Get()));
 
     std::map<std::string, std::string> header_entries;
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector.cc b/chrome/browser/chromeos/printing/usb_printer_detector.cc
index 5c3a75d4..0e5de535 100644
--- a/chrome/browser/chromeos/printing/usb_printer_detector.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_detector.cc
@@ -27,13 +27,11 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon_client.h"
 #include "chromeos/printing/ppd_provider.h"
-#include "chromeos/printing/usb_printer_id.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/system_connector.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "services/device/public/mojom/constants.mojom.h"
-#include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 
@@ -130,24 +128,9 @@
         GuessEffectiveMakeAndModel(device_info));
     entry.ppd_search_data.discovery_type =
         PrinterSearchData::PrinterDiscoveryType::kUsb;
+    // TODO(https://crbug.com/895037): Add in command set from IEEE1284
 
-    // Query printer for an IEEE Device ID.
-    device::mojom::UsbDevicePtr device_ptr;
-    device_manager_->GetDevice(device_info.guid, mojo::MakeRequest(&device_ptr),
-                               nullptr /* device_client */);
-    GetDeviceId(std::move(device_ptr),
-                base::BindOnce(&UsbPrinterDetectorImpl::OnGetDeviceId,
-                               weak_factory_.GetWeakPtr(), std::move(entry),
-                               device_info.guid));
-  }
-
-  void OnGetDeviceId(DetectedPrinter entry,
-                     std::string guid,
-                     UsbPrinterId printer_id) {
-    entry.ppd_search_data.printer_id = std::move(printer_id);
-
-    // Add detected printer.
-    printers_[guid] = entry;
+    printers_[device_info.guid] = entry;
     if (on_printers_found_callback_) {
       on_printers_found_callback_.Run(GetPrinters());
     }
diff --git a/chrome/browser/chromeos/printing/usb_printer_util.cc b/chrome/browser/chromeos/printing/usb_printer_util.cc
index e486a02481..fe57a93 100644
--- a/chrome/browser/chromeos/printing/usb_printer_util.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_util.cc
@@ -7,13 +7,10 @@
 #include <ctype.h>
 #include <stdint.h>
 
-#include <algorithm>
 #include <string>
-#include <utility>
 #include <vector>
 
 #include "base/big_endian.h"
-#include "base/bind_helpers.h"
 #include "base/hash/md5.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
@@ -22,9 +19,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/printing/printer_configuration.h"
-#include "chromeos/printing/usb_printer_id.h"
 #include "services/device/public/cpp/usb/usb_utils.h"
-#include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -46,69 +41,6 @@
 // (http://www.usb.org/developers/docs/devclass_docs/IPP.zip).
 constexpr uint8_t kPrinterIppusbProtocol = 4;
 
-// Configuration for a GET_DEVICE_ID Printer Class-Specific Request.
-const int kGetDeviceIdRequest = 0;
-const int kDefaultInterface = 0;
-const int kDefaultConfiguration = 0;
-
-// Callback for device.mojom.UsbDevice.ControlTransferIn.
-// Expects |data| to hold a newly queried Device ID.
-void OnControlTransfer(device::mojom::UsbDevicePtr device_ptr,
-                       GetDeviceIdCallback cb,
-                       device::mojom::UsbTransferStatus status,
-                       const std::vector<uint8_t>& data) {
-  if (status != device::mojom::UsbTransferStatus::COMPLETED || data.empty()) {
-    return std::move(cb).Run({});
-  }
-
-  // Cleanup device_ptr.
-  device_ptr->ReleaseInterface(kDefaultInterface, base::DoNothing());
-  device_ptr->Close(base::DoNothing());
-
-  return std::move(cb).Run(UsbPrinterId(data));
-}
-
-// Callback for device.mojom.UsbDevice.ClaimInterface.
-// If interface was claimed successfully, attempts to query printer for a
-// Device ID.
-void OnClaimInterface(device::mojom::UsbDevicePtr device_ptr,
-                      GetDeviceIdCallback cb,
-                      bool success) {
-  if (!success) {
-    return std::move(cb).Run({});
-  }
-
-  auto params = device::mojom::UsbControlTransferParams::New();
-  params->type = device::mojom::UsbControlTransferType::CLASS;
-  params->recipient = device::mojom::UsbControlTransferRecipient::INTERFACE;
-  params->request = kGetDeviceIdRequest;
-  params->value = kDefaultConfiguration;  // default config index
-  params->index = kDefaultInterface;      // default interface index
-
-  // Query for IEEE1284 string.
-  auto* device = device_ptr.get();
-  device->ControlTransferIn(
-      std::move(params), 255 /* max size */, 2000 /* 2 second timeout */,
-      base::BindOnce(OnControlTransfer, std::move(device_ptr), std::move(cb)));
-}
-
-// Callback for device.mojom.UsbDevice.Open.
-// If device was opened successfully, attempts to claim printer's default
-// interface.
-void OnDeviceOpen(device::mojom::UsbDevicePtr device_ptr,
-                  GetDeviceIdCallback cb,
-                  device::mojom::UsbOpenDeviceError error) {
-  if (error != device::mojom::UsbOpenDeviceError::OK || !device_ptr) {
-    return std::move(cb).Run({});
-  }
-
-  // Claim interface.
-  auto* device = device_ptr.get();
-  device->ClaimInterface(
-      kDefaultInterface,
-      base::BindOnce(OnClaimInterface, std::move(device_ptr), std::move(cb)));
-}
-
 // Escape URI strings the same way cups does it, so we end up with a URI cups
 // recognizes.  Cups hex-encodes '%', ' ', and anything not in the standard
 // ASCII range.  CUPS lets everything else through unchanged.
@@ -176,7 +108,7 @@
 // possible for that device.  So we basically toss every bit of stable
 // information from the device into an MD5 hash, and then hexify the hash value
 // as a suffix to "usb-" as the final printer id.
-std::string CreateUsbPrinterId(const UsbDeviceInfo& device_info) {
+std::string UsbPrinterId(const UsbDeviceInfo& device_info) {
   // Paranoid checks; in the unlikely event someone messes with the USB device
   // definition, our (supposedly stable) hashes will change.
   static_assert(sizeof(device_info.class_code) == 1, "Class size changed");
@@ -326,17 +258,9 @@
 
   printer->set_description(printer->display_name());
   printer->set_uri(UsbPrinterUri(device_info));
-  printer->set_id(CreateUsbPrinterId(device_info));
+  printer->set_id(UsbPrinterId(device_info));
   printer->set_supports_ippusb(UsbDeviceSupportsIppusb(device_info));
   return printer;
 }
 
-void GetDeviceId(device::mojom::UsbDevicePtr device_ptr,
-                 GetDeviceIdCallback cb) {
-  // Open device.
-  auto* device = device_ptr.get();
-  device->Open(
-      base::BindOnce(OnDeviceOpen, std::move(device_ptr), std::move(cb)));
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/usb_printer_util.h b/chrome/browser/chromeos/printing/usb_printer_util.h
index f05d8ce..76066b5 100644
--- a/chrome/browser/chromeos/printing/usb_printer_util.h
+++ b/chrome/browser/chromeos/printing/usb_printer_util.h
@@ -14,7 +14,6 @@
 namespace chromeos {
 
 class Printer;
-class UsbPrinterId;
 
 base::string16 GetManufacturerName(
     const device::mojom::UsbDeviceInfo& device_info);
@@ -34,12 +33,6 @@
 std::unique_ptr<Printer> UsbDeviceToPrinter(
     const device::mojom::UsbDeviceInfo& device_info);
 
-// Expects |device_ptr| to be linked to a Printer-class USB Device. Queries the
-// printer for its IEEE 1284 Standard Device ID.
-using GetDeviceIdCallback = base::OnceCallback<void(UsbPrinterId)>;
-void GetDeviceId(device::mojom::UsbDevicePtr device_ptr,
-                 GetDeviceIdCallback cb);
-
 }  // namespace chromeos
 
 #endif  // CHROME_BROWSER_CHROMEOS_PRINTING_USB_PRINTER_UTIL_H__
diff --git a/chrome/browser/chromeos/profiles/profile_helper.cc b/chrome/browser/chromeos/profiles/profile_helper.cc
index 425210cd..9da9dc2 100644
--- a/chrome/browser/chromeos/profiles/profile_helper.cc
+++ b/chrome/browser/chromeos/profiles/profile_helper.cc
@@ -27,10 +27,12 @@
 #include "chromeos/constants/chromeos_constants.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/account_id/account_id.h"
+#include "components/prefs/pref_service.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browsing_data_remover.h"
+#include "extensions/browser/pref_names.h"
 #include "extensions/common/constants.h"
 
 namespace chromeos {
@@ -183,6 +185,25 @@
 }
 
 // static
+bool ProfileHelper::IsSigninProfileInitialized() {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  return profile_manager &&
+         profile_manager->GetProfileByPath(GetSigninProfileDir());
+}
+
+// static
+bool ProfileHelper::SigninProfileHasLoginScreenExtensions() {
+  DCHECK(IsSigninProfileInitialized());
+  const Profile* profile = GetSigninProfile();
+  const PrefService* prefs = profile->GetPrefs();
+  DCHECK(prefs->GetInitializationStatus() ==
+         PrefService::INITIALIZATION_STATUS_SUCCESS);
+  const base::DictionaryValue* pref_value =
+      prefs->GetDictionary(extensions::pref_names::kLoginScreenExtensions);
+  return !pref_value->DictEmpty();
+}
+
+// static
 bool ProfileHelper::IsLockScreenAppProfile(const Profile* profile) {
   return profile &&
          profile->GetPath().BaseName().value() == kLockScreenAppProfile;
diff --git a/chrome/browser/chromeos/profiles/profile_helper.h b/chrome/browser/chromeos/profiles/profile_helper.h
index 95593d9..b8b31bf 100644
--- a/chrome/browser/chromeos/profiles/profile_helper.h
+++ b/chrome/browser/chromeos/profiles/profile_helper.h
@@ -83,6 +83,14 @@
   // signin Profile.
   static bool IsSigninProfile(const Profile* profile);
 
+  // Returns true if the signin profile has been initialized.
+  static bool IsSigninProfileInitialized();
+
+  // Returns true if the signin profile has force-installed extensions set by
+  // policy. This DCHECKs that the profile is created, its PrefService is
+  // initialized and the associated pref exists.
+  static bool SigninProfileHasLoginScreenExtensions();
+
   // Returns the path used for the lock screen apps profile - profile used
   // for launching platform apps that can display windows on top of the lock
   // screen.
diff --git a/chrome/browser/chromeos/settings/device_identity_provider.cc b/chrome/browser/chromeos/settings/device_identity_provider.cc
index e91f11a..9dcf3e0 100644
--- a/chrome/browser/chromeos/settings/device_identity_provider.cc
+++ b/chrome/browser/chromeos/settings/device_identity_provider.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/settings/device_identity_provider.h"
 
+#include "base/bind_helpers.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
 
 namespace chromeos {
@@ -88,14 +89,22 @@
     chromeos::DeviceOAuth2TokenService* token_service)
     : token_service_(token_service) {
   // TODO(blundell): Can |token_service_| ever actually be non-null?
-  if (token_service_)
-    token_service_->AddObserver(this);
+  if (token_service_) {
+    token_service->SetRefreshTokenAvailableCallback(
+        base::BindRepeating(&DeviceIdentityProvider::OnRefreshTokenAvailable,
+                            base::Unretained(this)));
+    token_service->SetRefreshTokenRevokedCallback(
+        base::BindRepeating(&DeviceIdentityProvider::OnRefreshTokenRevoked,
+                            base::Unretained(this)));
+  }
 }
 
 DeviceIdentityProvider::~DeviceIdentityProvider() {
   // TODO(blundell): Can |token_service_| ever actually be non-null?
-  if (token_service_)
-    token_service_->RemoveObserver(this);
+  if (token_service_) {
+    token_service_->SetRefreshTokenAvailableCallback(base::NullCallback());
+    token_service_->SetRefreshTokenRevokedCallback(base::NullCallback());
+  }
 }
 
 CoreAccountId DeviceIdentityProvider::GetActiveAccountId() {
diff --git a/chrome/browser/chromeos/settings/device_identity_provider.h b/chrome/browser/chromeos/settings/device_identity_provider.h
index 0d94dc4..56be412 100644
--- a/chrome/browser/chromeos/settings/device_identity_provider.h
+++ b/chrome/browser/chromeos/settings/device_identity_provider.h
@@ -13,8 +13,7 @@
 class DeviceOAuth2TokenService;
 
 // Identity provider implementation backed by DeviceOAuth2TokenService.
-class DeviceIdentityProvider : public invalidation::IdentityProvider,
-                               public OAuth2TokenServiceObserver {
+class DeviceIdentityProvider : public invalidation::IdentityProvider {
  public:
   explicit DeviceIdentityProvider(
       chromeos::DeviceOAuth2TokenService* token_service);
@@ -32,9 +31,8 @@
                              const std::string& access_token) override;
   void SetActiveAccountId(const CoreAccountId& account_id) override;
 
-  // OAuth2TokenServiceObserver:
-  void OnRefreshTokenAvailable(const CoreAccountId& account_id) override;
-  void OnRefreshTokenRevoked(const CoreAccountId& account_id) override;
+  void OnRefreshTokenAvailable(const CoreAccountId& account_id);
+  void OnRefreshTokenRevoked(const CoreAccountId& account_id);
 
  private:
   chromeos::DeviceOAuth2TokenService* token_service_;
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
index b77dfe38..2ffa891 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
@@ -78,6 +78,30 @@
   GetDeviceDelegate()->set_robot_account_id_for_testing(account_id);
 }
 
+void DeviceOAuth2TokenService::SetRefreshTokenAvailableCallback(
+    RefreshTokenAvailableCallback callback) {
+  on_refresh_token_available_callback_ = std::move(callback);
+}
+
+void DeviceOAuth2TokenService::SetRefreshTokenRevokedCallback(
+    RefreshTokenRevokedCallback callback) {
+  on_refresh_token_revoked_callback_ = std::move(callback);
+}
+
+void DeviceOAuth2TokenService::OnRefreshTokenAvailable(
+    const CoreAccountId& account_id) {
+  OAuth2TokenService::OnRefreshTokenAvailable(account_id);
+  if (on_refresh_token_available_callback_)
+    on_refresh_token_available_callback_.Run(account_id);
+}
+
+void DeviceOAuth2TokenService::OnRefreshTokenRevoked(
+    const CoreAccountId& account_id) {
+  OAuth2TokenService::OnRefreshTokenRevoked(account_id);
+  if (on_refresh_token_revoked_callback_)
+    on_refresh_token_revoked_callback_.Run(account_id);
+}
+
 void DeviceOAuth2TokenService::FetchOAuth2Token(
     OAuth2AccessTokenManager::RequestImpl* request,
     const CoreAccountId& account_id,
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.h b/chrome/browser/chromeos/settings/device_oauth2_token_service.h
index 12b6737..2dc57f21 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.h
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.h
@@ -31,6 +31,11 @@
     : public OAuth2TokenService,
       public DeviceOAuth2TokenServiceDelegate::ValidationStatusDelegate {
  public:
+  typedef base::RepeatingCallback<void(const CoreAccountId& /* account_id */)>
+      RefreshTokenAvailableCallback;
+  typedef base::RepeatingCallback<void(const CoreAccountId& /* account_id */)>
+      RefreshTokenRevokedCallback;
+
   typedef base::Callback<void(bool)> StatusCallback;
 
   // Persist the given refresh token on the device. Overwrites any previous
@@ -50,6 +55,19 @@
   // RefreshTokenIsAvailable().
   void set_robot_account_id_for_testing(const CoreAccountId& account_id);
 
+  // If set, this callback will be invoked when a new refresh token is
+  // available.
+  void SetRefreshTokenAvailableCallback(RefreshTokenAvailableCallback callback);
+
+  // If set, this callback will be invoked when a refresh token is revoked.
+  void SetRefreshTokenRevokedCallback(RefreshTokenRevokedCallback callback);
+
+  // OAuth2TokenServiceObserver:
+  // NOTE: OAuth2TokenService already adds itself as an observer, so this class
+  // doesn't actually need to add/remove itself as an O2TSObserver.
+  void OnRefreshTokenAvailable(const CoreAccountId& account_id) override;
+  void OnRefreshTokenRevoked(const CoreAccountId& account_id) override;
+
  protected:
   // Implementation of OAuth2TokenService.
   void FetchOAuth2Token(
@@ -89,6 +107,10 @@
   // validating the token.
   std::vector<PendingRequest*> pending_requests_;
 
+  // Callbacks to invoke, if set, for refresh token-related events.
+  RefreshTokenAvailableCallback on_refresh_token_available_callback_;
+  RefreshTokenRevokedCallback on_refresh_token_revoked_callback_;
+
   DISALLOW_COPY_AND_ASSIGN(DeviceOAuth2TokenService);
 };
 
diff --git a/chrome/browser/chromeos/usb/OWNERS b/chrome/browser/chromeos/usb/OWNERS
index 9d9bf0c4..dc48383c 100644
--- a/chrome/browser/chromeos/usb/OWNERS
+++ b/chrome/browser/chromeos/usb/OWNERS
@@ -1 +1 @@
-file://chrome/browser/chromeos/crostini/OWNERS
+file://chrome/browser/chromeos/guest_os/OWNERS
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.cc b/chrome/browser/chromeos/usb/cros_usb_detector.cc
index d1bc954..97a98d1 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.cc
+++ b/chrome/browser/chromeos/usb/cros_usb_detector.cc
@@ -392,7 +392,7 @@
 void CrosUsbDetector::AttachUsbDeviceToVm(
     const std::string& vm_name,
     const std::string& guid,
-    crostini::CrostiniManager::CrostiniResultCallback callback) {
+    base::OnceCallback<void(bool success)> callback) {
   auto it = available_device_info_.find(guid);
   if (it == available_device_info_.end()) {
     return;
@@ -420,12 +420,12 @@
 void CrosUsbDetector::DetachUsbDeviceFromVm(
     const std::string& vm_name,
     const std::string& guid,
-    crostini::CrostiniManager::CrostiniResultCallback callback) {
+    base::OnceCallback<void(bool success)> callback) {
   const auto& it = available_device_info_.find(guid);
   if (it == available_device_info_.end()) {
     // If there wasn't an existing attachment, then removal is a no-op and
     // always succeeds
-    std::move(callback).Run(crostini::CrostiniResult::SUCCESS);
+    std::move(callback).Run(/*success=*/true);
     return;
   }
   const auto& device_info = it->second;
@@ -439,7 +439,7 @@
   }
 
   if (!guest_port) {
-    std::move(callback).Run(crostini::CrostiniResult::SUCCESS);
+    std::move(callback).Run(/*success=*/true);
     return;
   }
   manager()->DetachUsbDevice(
@@ -459,11 +459,11 @@
 void CrosUsbDetector::OnAttachUsbDeviceOpened(
     const std::string& vm_name,
     device::mojom::UsbDeviceInfoPtr device_info,
-    crostini::CrostiniManager::CrostiniResultCallback callback,
+    base::OnceCallback<void(bool success)> callback,
     base::File file) {
   if (!file.IsValid()) {
     LOG(ERROR) << "Permission broker refused access to USB device";
-    std::move(callback).Run(crostini::CrostiniResult::PERMISSION_BROKER_ERROR);
+    std::move(callback).Run(/*success=*/false);
     return;
   }
 
@@ -480,10 +480,10 @@
 void CrosUsbDetector::OnUsbDeviceAttachFinished(
     const std::string& vm_name,
     const std::string& guid,
-    crostini::CrostiniManager::CrostiniResultCallback callback,
-    uint8_t guest_port,
-    crostini::CrostiniResult result) {
-  if (result == crostini::CrostiniResult::SUCCESS) {
+    base::OnceCallback<void(bool success)> callback,
+    bool success,
+    uint8_t guest_port) {
+  if (success) {
     for (auto& device : shared_usb_devices_) {
       if (device.guid == guid) {
         device.guest_port = guest_port;
@@ -491,15 +491,15 @@
     }
   }
   SignalSharedUsbDeviceObservers();
-  std::move(callback).Run(result);
+  std::move(callback).Run(success);
 }
 
 void CrosUsbDetector::OnUsbDeviceDetachFinished(
     const std::string& vm_name,
     const std::string& guid,
-    crostini::CrostiniManager::CrostiniResultCallback callback,
+    base::OnceCallback<void(bool success)> callback,
     uint8_t guest_port,
-    crostini::CrostiniResult result) {
+    bool success) {
   for (auto& device : shared_usb_devices_) {
     if (device.guid == guid) {
       device.guest_port = base::nullopt;
@@ -508,7 +508,7 @@
     }
   }
   SignalSharedUsbDeviceObservers();
-  std::move(callback).Run(result);
+  std::move(callback).Run(success);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.h b/chrome/browser/chromeos/usb/cros_usb_detector.h
index a44702d..5b0621b 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.h
+++ b/chrome/browser/chromeos/usb/cros_usb_detector.h
@@ -88,14 +88,12 @@
   void OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device) override;
   void OnDeviceRemoved(device::mojom::UsbDeviceInfoPtr device) override;
 
-  void AttachUsbDeviceToVm(
-      const std::string& vm_name,
-      const std::string& guid,
-      crostini::CrostiniManager::CrostiniResultCallback callback);
-  void DetachUsbDeviceFromVm(
-      const std::string& vm_name,
-      const std::string& guid,
-      crostini::CrostiniManager::CrostiniResultCallback callback);
+  void AttachUsbDeviceToVm(const std::string& vm_name,
+                           const std::string& guid,
+                           base::OnceCallback<void(bool success)> callback);
+  void DetachUsbDeviceFromVm(const std::string& vm_name,
+                             const std::string& guid,
+                             base::OnceCallback<void(bool success)> callback);
 
   void AddSharedUsbDeviceObserver(SharedUsbDeviceObserver* observer);
   void RemoveSharedUsbDeviceObserver(SharedUsbDeviceObserver* observer);
@@ -120,25 +118,24 @@
       std::vector<device::mojom::UsbDeviceInfoPtr> devices);
 
   // Callback for AttachUsbDeviceToVm after opening a file handler.
-  void OnAttachUsbDeviceOpened(
-      const std::string& vm_name,
-      device::mojom::UsbDeviceInfoPtr device,
-      crostini::CrostiniManager::CrostiniResultCallback callback,
-      base::File file);
+  void OnAttachUsbDeviceOpened(const std::string& vm_name,
+                               device::mojom::UsbDeviceInfoPtr device,
+                               base::OnceCallback<void(bool success)> callback,
+                               base::File file);
 
   // Callbacks for when the USB device state has been updated.
   void OnUsbDeviceAttachFinished(
       const std::string& vm_name,
       const std::string& guid,
-      crostini::CrostiniManager::CrostiniResultCallback callback,
-      uint8_t guest_port,
-      crostini::CrostiniResult result);
+      base::OnceCallback<void(bool success)> callback,
+      bool success,
+      uint8_t guest_port);
   void OnUsbDeviceDetachFinished(
       const std::string& vm_name,
       const std::string& guid,
-      crostini::CrostiniManager::CrostiniResultCallback callback,
+      base::OnceCallback<void(bool success)> callback,
       uint8_t guest_port,
-      crostini::CrostiniResult result);
+      bool success);
 
   // Returns true when a device should show a notification when attached.
   bool ShouldShowNotification(const device::mojom::UsbDeviceInfo& device_info);
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
index e567ee4..61c0bba7 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
@@ -335,6 +335,12 @@
     case WilcoDtcSupportdEvent::kDockError:
       notification_controller_->ShowDockErrorNotification();
       return;
+    case WilcoDtcSupportdEvent::kDockDisplay:
+      notification_controller_->ShowDockDisplayNotification();
+      return;
+    case WilcoDtcSupportdEvent::kDockThunderbolt:
+      notification_controller_->ShowDockThunderboltNotification();
+      return;
   }
   LOG(ERROR) << "Unrecognized event " << event << " event";
 }
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.cc
index 68bf0083..69712d7 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.cc
@@ -25,12 +25,15 @@
 
 namespace {
 
+const char kNotifierWilco[] = "ash.wilco";
+
 const char kWilcoDtcSupportdNotificationIdBatteryAuth[] = "BatteryAuth";
 const char kWilcoDtcSupportdNotificationIdNonWilcoCharger[] = "NonWilcoCharger";
 const char kWilcoDtcSupportdNotificationIdIncompatibleDock[] =
     "IncompatibleDock";
 const char kWilcoDtcSupportdNotificationIdDockHardwareError[] = "DockError";
-const char kNotifierWilco[] = "ash.wilco";
+const char kWilcoDtcSupportdNotificationIdDockDisplay[] = "DockDisplay";
+const char kWilcoDtcSupportdNotificationIdDockThunderbolt[] = "DockThunderbolt";
 
 class WilcoDtcSupportdNotificationDelegate
     : public message_center::NotificationDelegate {
@@ -91,7 +94,7 @@
   DisplayNotification(kWilcoDtcSupportdNotificationIdNonWilcoCharger,
                       IDS_WILCO_NOTIFICATION_NON_WILCO_CHARGER_TITLE,
                       IDS_WILCO_NOTIFICATION_NON_WILCO_CHARGER_MESSAGE,
-                      message_center::SYSTEM_PRIORITY,
+                      message_center::DEFAULT_PRIORITY,
                       ash::kNotificationBatteryIcon,
                       message_center::SystemNotificationWarningLevel::WARNING,
                       HelpAppLauncher::HelpTopic::HELP_WILCO);
@@ -123,6 +126,31 @@
   return kWilcoDtcSupportdNotificationIdDockHardwareError;
 }
 
+std::string
+WilcoDtcSupportdNotificationController::ShowDockDisplayNotification() const {
+  DisplayNotification(kWilcoDtcSupportdNotificationIdDockDisplay,
+                      IDS_WILCO_NOTIFICATION_DOCK_DISPLAY_TITLE,
+                      IDS_WILCO_NOTIFICATION_DOCK_DISPLAY_MESSAGE,
+                      message_center::DEFAULT_PRIORITY,
+                      ash::kNotificationSettingsIcon,
+                      message_center::SystemNotificationWarningLevel::NORMAL,
+                      HelpAppLauncher::HelpTopic::HELP_WILCO);
+  return kWilcoDtcSupportdNotificationIdDockDisplay;
+}
+
+std::string
+WilcoDtcSupportdNotificationController::ShowDockThunderboltNotification()
+    const {
+  DisplayNotification(kWilcoDtcSupportdNotificationIdDockThunderbolt,
+                      IDS_WILCO_NOTIFICATION_DOCK_THUNDERBOLT_TITLE,
+                      IDS_WILCO_NOTIFICATION_DOCK_THUNDERBOLT_MESSAGE,
+                      message_center::DEFAULT_PRIORITY,
+                      ash::kNotificationSettingsIcon,
+                      message_center::SystemNotificationWarningLevel::NORMAL,
+                      HelpAppLauncher::HelpTopic::HELP_WILCO);
+  return kWilcoDtcSupportdNotificationIdDockThunderbolt;
+}
+
 void WilcoDtcSupportdNotificationController::DisplayNotification(
     const std::string& notification_id,
     const int title_id,
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.h b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.h
index 127ba7dd..80e6eced 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.h
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.h
@@ -36,6 +36,12 @@
   virtual std::string ShowIncompatibleDockNotification() const;
   // Displays notification when the attached dock presents hardware failures.
   virtual std::string ShowDockErrorNotification() const;
+  // Displays notification when HDMI and USB Type-C are used for displays at the
+  // same time with the attached dock.
+  virtual std::string ShowDockDisplayNotification() const;
+  // Displays notification when the attached dock has unsupported Thunderbolt
+  // capabilities.
+  virtual std::string ShowDockThunderboltNotification() const;
 
  private:
   void DisplayNotification(
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
index a080e19e..8101892 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
@@ -33,7 +33,7 @@
     {&WilcoDtcSupportdNotificationController::ShowNonWilcoChargerNotification,
      IDS_WILCO_NOTIFICATION_NON_WILCO_CHARGER_TITLE,
      IDS_WILCO_NOTIFICATION_NON_WILCO_CHARGER_MESSAGE,
-     message_center::SYSTEM_PRIORITY},
+     message_center::DEFAULT_PRIORITY},
     {&WilcoDtcSupportdNotificationController::ShowIncompatibleDockNotification,
      IDS_WILCO_NOTIFICATION_INCOMPATIBLE_DOCK_TITLE,
      IDS_WILCO_NOTIFICATION_INCOMPATIBLE_DOCK_MESSAGE,
@@ -41,6 +41,14 @@
     {&WilcoDtcSupportdNotificationController::ShowDockErrorNotification,
      IDS_WILCO_NOTIFICATION_DOCK_ERROR_TITLE,
      IDS_WILCO_NOTIFICATION_DOCK_ERROR_MESSAGE,
+     message_center::DEFAULT_PRIORITY},
+    {&WilcoDtcSupportdNotificationController::ShowDockDisplayNotification,
+     IDS_WILCO_NOTIFICATION_DOCK_DISPLAY_TITLE,
+     IDS_WILCO_NOTIFICATION_DOCK_DISPLAY_MESSAGE,
+     message_center::DEFAULT_PRIORITY},
+    {&WilcoDtcSupportdNotificationController::ShowDockThunderboltNotification,
+     IDS_WILCO_NOTIFICATION_DOCK_THUNDERBOLT_TITLE,
+     IDS_WILCO_NOTIFICATION_DOCK_THUNDERBOLT_MESSAGE,
      message_center::DEFAULT_PRIORITY}};
 
 class WilcoDtcSupportdNotificationControllerTest
diff --git a/chrome/browser/component_updater/on_device_head_suggest_component_installer.cc b/chrome/browser/component_updater/on_device_head_suggest_component_installer.cc
new file mode 100644
index 0000000..359d791
--- /dev/null
+++ b/chrome/browser/component_updater/on_device_head_suggest_component_installer.cc
@@ -0,0 +1,131 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/component_updater/on_device_head_suggest_component_installer.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/task/post_task.h"
+#include "chrome/browser/browser_process.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_paths.h"
+#include "components/component_updater/component_updater_service.h"
+
+namespace component_updater {
+
+namespace {
+// CRX hash. The extension id is: mokoelfideihaddlikjdmdbecmlibfaj.
+const uint8_t kOnDeviceHeadSuggestPublicKeySHA256[32] = {
+    0xce, 0xae, 0x4b, 0x58, 0x34, 0x87, 0x03, 0x3b, 0x8a, 0x93, 0xc3,
+    0x14, 0x2c, 0xb8, 0x15, 0x09, 0x6e, 0x41, 0x6b, 0xc0, 0xb4, 0x0d,
+    0x96, 0x2b, 0xd6, 0x7e, 0xd9, 0x97, 0xc6, 0x2a, 0xb0, 0x9f};
+
+void UpdateModelDirectory(const base::FilePath& file_path) {
+  base::PathService::Override(DIR_ON_DEVICE_HEAD_SUGGEST, file_path);
+}
+
+// Normalizes and returns current application locale, i.e capitalizes all
+// letters and removes all hyphens and underscores in the locale string,
+// e.g. "en-US" -> "ENUS".
+std::string GetNormalizedLocale() {
+  std::string locale = g_browser_process->GetApplicationLocale();
+  for (const auto c : "-_")
+    locale.erase(std::remove(locale.begin(), locale.end(), c), locale.end());
+
+  std::transform(locale.begin(), locale.end(), locale.begin(),
+                 [](char c) -> char { return base::ToUpperASCII(c); });
+  return locale;
+}
+
+}  // namespace
+
+OnDeviceHeadSuggestInstallerPolicy::OnDeviceHeadSuggestInstallerPolicy()
+    : accept_locale_(GetNormalizedLocale()) {}
+
+OnDeviceHeadSuggestInstallerPolicy::~OnDeviceHeadSuggestInstallerPolicy() =
+    default;
+
+bool OnDeviceHeadSuggestInstallerPolicy::VerifyInstallation(
+    const base::DictionaryValue& manifest,
+    const base::FilePath& install_dir) const {
+  const std::string* name = manifest.FindStringPath("name");
+  const std::string* locale = manifest.FindStringPath("locale");
+
+  if (!name || !locale)
+    return false;
+
+  if (!base::StartsWith(*name, "OnDeviceHeadSuggest",
+                        base::CompareCase::SENSITIVE) ||
+      *locale != accept_locale_)
+    return false;
+
+  return base::PathExists(install_dir);
+}
+
+bool OnDeviceHeadSuggestInstallerPolicy::
+    SupportsGroupPolicyEnabledComponentUpdates() const {
+  return false;
+}
+
+bool OnDeviceHeadSuggestInstallerPolicy::RequiresNetworkEncryption() const {
+  return true;
+}
+
+update_client::CrxInstaller::Result
+OnDeviceHeadSuggestInstallerPolicy::OnCustomInstall(
+    const base::DictionaryValue& manifest,
+    const base::FilePath& install_dir) {
+  return update_client::CrxInstaller::Result(0);  // Nothing custom here.
+}
+
+void OnDeviceHeadSuggestInstallerPolicy::OnCustomUninstall() {}
+
+void OnDeviceHeadSuggestInstallerPolicy::ComponentReady(
+    const base::Version& version,
+    const base::FilePath& install_dir,
+    std::unique_ptr<base::DictionaryValue> manifest) {
+  base::PostTaskWithTraits(FROM_HERE,
+                           {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+                           base::BindOnce(&UpdateModelDirectory, install_dir));
+}
+
+base::FilePath OnDeviceHeadSuggestInstallerPolicy::GetRelativeInstallDir()
+    const {
+  return base::FilePath(FILE_PATH_LITERAL("OnDeviceHeadSuggestModel"));
+}
+
+void OnDeviceHeadSuggestInstallerPolicy::GetHash(
+    std::vector<uint8_t>* hash) const {
+  hash->assign(std::begin(kOnDeviceHeadSuggestPublicKeySHA256),
+               std::end(kOnDeviceHeadSuggestPublicKeySHA256));
+}
+
+std::string OnDeviceHeadSuggestInstallerPolicy::GetName() const {
+  return "OnDeviceHeadSuggest";
+}
+
+update_client::InstallerAttributes
+OnDeviceHeadSuggestInstallerPolicy::GetInstallerAttributes() const {
+  return {{"accept_locale", accept_locale_}};
+}
+
+std::vector<std::string> OnDeviceHeadSuggestInstallerPolicy::GetMimeTypes()
+    const {
+  return std::vector<std::string>();
+}
+
+void RegisterOnDeviceHeadSuggestComponent(ComponentUpdateService* cus) {
+  auto installer = base::MakeRefCounted<ComponentInstaller>(
+      std::make_unique<OnDeviceHeadSuggestInstallerPolicy>());
+  installer->Register(cus, base::OnceClosure());
+}
+
+}  // namespace component_updater
diff --git a/chrome/browser/component_updater/on_device_head_suggest_component_installer.h b/chrome/browser/component_updater/on_device_head_suggest_component_installer.h
new file mode 100644
index 0000000..438e287
--- /dev/null
+++ b/chrome/browser/component_updater/on_device_head_suggest_component_installer.h
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_ON_DEVICE_HEAD_SUGGEST_COMPONENT_INSTALLER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_ON_DEVICE_HEAD_SUGGEST_COMPONENT_INSTALLER_H_
+
+#include <vector>
+
+#include "components/component_updater/component_installer.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace component_updater {
+
+class ComponentUpdateService;
+
+// The installer policy implementation which is required by component updater in
+// order to update the on device model used by Omnibox suggest provider
+// OnDeviceHeadProvider.
+class OnDeviceHeadSuggestInstallerPolicy : public ComponentInstallerPolicy {
+ public:
+  OnDeviceHeadSuggestInstallerPolicy();
+  ~OnDeviceHeadSuggestInstallerPolicy() override;
+
+ private:
+  // ComponentInstallerPolicy implementation.
+  bool VerifyInstallation(const base::DictionaryValue& manifest,
+                          const base::FilePath& install_dir) const override;
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
+  bool RequiresNetworkEncryption() const override;
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::DictionaryValue& manifest,
+      const base::FilePath& install_dir) override;
+  void OnCustomUninstall() override;
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      std::unique_ptr<base::DictionaryValue> manifest) override;
+  base::FilePath GetRelativeInstallDir() const override;
+  void GetHash(std::vector<uint8_t>* hash) const override;
+  std::string GetName() const override;
+  update_client::InstallerAttributes GetInstallerAttributes() const override;
+  std::vector<std::string> GetMimeTypes() const override;
+
+  // The application (normalized) locale when this policy is created. Models
+  // which do not match this locale will be rejected.
+  std::string accept_locale_;
+
+  DISALLOW_COPY_AND_ASSIGN(OnDeviceHeadSuggestInstallerPolicy);
+};
+
+// Registers a OnDeviceHeadSuggest component with |cus|.
+void RegisterOnDeviceHeadSuggestComponent(ComponentUpdateService* cus);
+
+}  // namespace component_updater
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_ON_DEVICE_HEAD_SUGGEST_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc
index 2aae4e12..e5b8e85 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc
@@ -51,7 +51,7 @@
     net::ProxyList proxies;
     proxies.SetFromPacString(kProxyPac);
     test_context_->data_reduction_proxy_service()->SetConfiguredProxiesOnUI(
-        proxies);
+        proxies, {});
     test_context_->test_network_quality_tracker()
         ->ReportEffectiveConnectionTypeForTesting(
             net::EFFECTIVE_CONNECTION_TYPE_4G);
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index 62385a4..718537f 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -584,10 +584,15 @@
     const std::string& mime_type,
     const std::string& request_origin,
     int64_t content_length,
+    bool is_transient,
     content::WebContents* web_contents) {
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (offline_pages::OfflinePageUtils::CanDownloadAsOfflinePage(url,
+  // For background service downloads we don't want offline pages backend to
+  // intercept the download. |is_transient| flag is used to determine whether
+  // the download corresponds to background service.
+  if (!is_transient &&
+      offline_pages::OfflinePageUtils::CanDownloadAsOfflinePage(url,
                                                                 mime_type)) {
     offline_pages::OfflinePageUtils::ScheduleDownload(
         web_contents, offline_pages::kDownloadNamespace, url,
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h
index 54d230f..e203c50 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.h
+++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -98,6 +98,7 @@
       const std::string& mime_type,
       const std::string& request_origin,
       int64_t content_length,
+      bool is_transient,
       content::WebContents* web_contents) override;
   void GetSaveDir(content::BrowserContext* browser_context,
                   base::FilePath* website_save_dir,
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
index 1e2ad3e..21671708 100644
--- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -58,6 +58,7 @@
 #endif
 
 #if defined(OS_ANDROID)
+#include "chrome/browser/android/feature_utilities.h"
 #include "chrome/browser/download/download_prompt_status.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "components/infobars/core/infobar.h"
@@ -846,6 +847,20 @@
       kInsecureDownloadHistogramInitiatorInsecureTargetInsecure, histograms);
 }
 
+#if defined(OS_ANDROID)
+TEST_F(ChromeDownloadManagerDelegateTest, InterceptDownloadByOfflinePages) {
+  const GURL kUrl("http://example.com/foo");
+  std::string mime_type = "text/html";
+  bool should_intercept = delegate()->InterceptDownloadIfApplicable(
+      kUrl, "", "", mime_type, "", 10, false /*is_transient*/, nullptr);
+  EXPECT_TRUE(should_intercept);
+
+  should_intercept = delegate()->InterceptDownloadIfApplicable(
+      kUrl, "", "", mime_type, "", 10, true /*is_transient*/, nullptr);
+  EXPECT_FALSE(should_intercept);
+}
+#endif
+
 TEST_F(ChromeDownloadManagerDelegateTest, BlockedAsActiveContent_HttpChain) {
   // Tests blocking unsafe active content downloads when a step in the referrer
   // chain is HTTP, using the default mime-type matching policy.
@@ -1570,6 +1585,11 @@
 
 TEST_F(ChromeDownloadManagerDelegateTest,
        RequestConfirmation_Android_WithLocationChangeEnabled) {
+#if defined(OS_ANDROID)
+  // We do not prompt for location in this case.
+  if (chrome::android::IsNoTouchModeEnabled())
+    return;
+#endif
   DeleteContents();
   SetContents(CreateTestWebContents());
 
diff --git a/chrome/browser/download/download_manager_utils.cc b/chrome/browser/download/download_manager_utils.cc
index 2378e43f..e6f31da0 100644
--- a/chrome/browser/download/download_manager_utils.cc
+++ b/chrome/browser/download/download_manager_utils.cc
@@ -17,6 +17,7 @@
 #include "components/download/public/common/simple_download_manager_coordinator.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_request_utils.h"
+#include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/system_connector.h"
 
 #if defined(OS_ANDROID)
@@ -50,20 +51,55 @@
   DCHECK(manager);
 }
 
-void CreateInProgressDownloadManager(SimpleFactoryKey* key) {
+}  // namespace
+
+// static
+download::InProgressDownloadManager*
+DownloadManagerUtils::RetrieveInProgressDownloadManager(Profile* profile) {
+  SimpleFactoryKey* key = profile->GetProfileKey();
+  GetInProgressDownloadManager(key);
+  auto& map = GetInProgressManagerMap();
+  return map[key].release();
+}
+
+// static
+void DownloadManagerUtils::InitializeSimpleDownloadManager(
+    SimpleFactoryKey* key) {
+#if defined(OS_ANDROID)
+  if (!g_browser_process) {
+    GetInProgressDownloadManager(key);
+    return;
+  }
+#endif
+  if (base::FeatureList::IsEnabled(
+          download::features::
+              kUseInProgressDownloadManagerForDownloadService)) {
+    GetInProgressDownloadManager(key);
+  } else {
+    FullBrowserTransitionManager::Get()->RegisterCallbackOnProfileCreation(
+        key, base::BindOnce(&GetDownloadManagerOnProfileCreation));
+  }
+}
+
+// static
+download::InProgressDownloadManager*
+DownloadManagerUtils::GetInProgressDownloadManager(SimpleFactoryKey* key) {
   auto& map = GetInProgressManagerMap();
   auto it = map.find(key);
   // Create the InProgressDownloadManager if it hasn't been created yet.
   if (it == map.end()) {
+    service_manager::Connector* connector = content::GetSystemConnector();
     auto in_progress_manager =
         std::make_unique<download::InProgressDownloadManager>(
             nullptr, key->IsOffTheRecord() ? base::FilePath() : key->GetPath(),
             base::BindRepeating(&IgnoreOriginSecurityCheck),
             base::BindRepeating(&content::DownloadRequestUtils::IsURLSafe),
-            content::GetSystemConnector());
+            connector);
+    content::GetNetworkServiceFromConnector(connector);
     download::SimpleDownloadManagerCoordinator* coordinator =
         SimpleDownloadManagerCoordinatorFactory::GetForKey(key);
-    coordinator->SetSimpleDownloadManager(in_progress_manager.get(), false);
+    coordinator->SetSimpleDownloadManager(in_progress_manager.get(),
+                                          key->IsOffTheRecord());
     scoped_refptr<network::SharedURLLoaderFactory> factory =
         SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory();
     in_progress_manager->set_url_loader_factory_getter(
@@ -71,41 +107,5 @@
             factory->Clone()));
     map[key] = std::move(in_progress_manager);
   }
-}
-
-}  // namespace
-
-download::InProgressDownloadManager*
-DownloadManagerUtils::RetrieveInProgressDownloadManager(Profile* profile) {
-  // TODO(qinmin): use the profile to retrieve SimpleFactoryKey and
-  // create SimpleDownloadManagerCoordinator.
-#if defined(OS_ANDROID)
-  download::InProgressDownloadManager* manager =
-      DownloadManagerService::GetInstance()->RetriveInProgressDownloadManager(
-          profile);
-  if (manager)
-    return manager;
-#endif
-  SimpleFactoryKey* key = profile->GetProfileKey();
-  CreateInProgressDownloadManager(key);
-  auto& map = GetInProgressManagerMap();
-  return map[key].release();
-}
-
-void DownloadManagerUtils::InitializeSimpleDownloadManager(
-    SimpleFactoryKey* key) {
-#if defined(OS_ANDROID)
-  if (!g_browser_process) {
-    DownloadManagerService::GetInstance()->CreateInProgressDownloadManager();
-    return;
-  }
-#endif
-  if (base::FeatureList::IsEnabled(
-          download::features::
-              kUseInProgressDownloadManagerForDownloadService)) {
-    CreateInProgressDownloadManager(key);
-  } else {
-    FullBrowserTransitionManager::Get()->RegisterCallbackOnProfileCreation(
-        key, base::BindOnce(&GetDownloadManagerOnProfileCreation));
-  }
+  return map[key].get();
 }
diff --git a/chrome/browser/download/download_manager_utils.h b/chrome/browser/download/download_manager_utils.h
index 68623c7c..d9294070 100644
--- a/chrome/browser/download/download_manager_utils.h
+++ b/chrome/browser/download/download_manager_utils.h
@@ -24,6 +24,11 @@
   // possible.
   static void InitializeSimpleDownloadManager(SimpleFactoryKey* key);
 
+  // Creates an InProgressDownloadManager for a particular |key| if it doesn't
+  // exist and return the pointer.
+  static download::InProgressDownloadManager* GetInProgressDownloadManager(
+      SimpleFactoryKey* key);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(DownloadManagerUtils);
 };
diff --git a/chrome/browser/download/notification/download_item_notification.cc b/chrome/browser/download/notification/download_item_notification.cc
index a8f4a7a..c83a877 100644
--- a/chrome/browser/download/notification/download_item_notification.cc
+++ b/chrome/browser/download/notification/download_item_notification.cc
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "base/bind.h"
 #include "base/files/file_util.h"
+#include "base/i18n/rtl.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
@@ -787,10 +788,14 @@
       }
     case download::DownloadItem::COMPLETE:
       // If the file has been removed: Removed
-      if (item_->GetFileExternallyRemoved())
+      if (item_->GetFileExternallyRemoved()) {
         return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED);
-      else
-        return item_->GetFileNameToReportUser().LossyDisplayName();
+      } else {
+        base::string16 file_name =
+            item_->GetFileNameToReportUser().LossyDisplayName();
+        base::i18n::AdjustStringForLocaleDirection(&file_name);
+        return file_name;
+      }
     case download::DownloadItem::CANCELLED:
       // "Cancelled"
       return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED);
diff --git a/chrome/browser/engagement/important_sites_util.cc b/chrome/browser/engagement/important_sites_util.cc
index cf0876f..0ab87f3 100644
--- a/chrome/browser/engagement/important_sites_util.cc
+++ b/chrome/browser/engagement/important_sites_util.cc
@@ -382,6 +382,7 @@
 std::vector<ImportantDomainInfo>
 ImportantSitesUtil::GetImportantRegisterableDomains(Profile* profile,
                                                     size_t max_results) {
+  SCOPED_UMA_HISTOGRAM_TIMER("Storage.ImportantSites.GenerationTime");
   std::map<std::string, ImportantDomainInfo> important_info;
   std::map<GURL, double> engagement_map;
 
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index b6a0422..bba3399 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -12,10 +12,6 @@
 import("//pdf/features.gni")
 import("//rlz/buildflags/buildflags.gni")
 
-if (is_chromeos) {
-  import("//chrome/browser/chromeos/kiosk_next_home/kiosk_next.gni")
-}
-
 assert(enable_extensions)
 
 jumbo_static_library("extensions") {
@@ -977,6 +973,7 @@
       "//chromeos/dbus/cryptohome",
       "//chromeos/dbus/power",
       "//chromeos/disks",
+      "//chromeos/login/auth",
       "//chromeos/login/login_state",
       "//chromeos/network",
       "//chromeos/services/ime/public/mojom",
@@ -1003,10 +1000,6 @@
       "//ui/ozone",
       "//ui/views",
     ]
-    if (enable_kiosk_next) {
-      defines += [ "KIOSK_NEXT" ]
-      deps += [ "//chrome/browser/chromeos/kiosk_next_home/mojom" ]
-    }
     if (enable_nacl) {
       deps += [ "//chrome/browser/resources/chromeos/zip_archiver" ]
     }
diff --git a/chrome/browser/extensions/api/cast_streaming/performance_test.cc b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
index d06ede3..13f5fbcb 100644
--- a/chrome/browser/extensions/api/cast_streaming/performance_test.cc
+++ b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
@@ -659,7 +659,8 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_P(CastV2PerformanceTest, Performance) {
+// TODO(https://crbug.com/974427) Disabled due to flakiness.
+IN_PROC_BROWSER_TEST_P(CastV2PerformanceTest, DISABLED_Performance) {
   net::IPEndPoint receiver_end_point = media::cast::test::GetFreeLocalPort();
   VLOG(1) << "Got local UDP port for testing: "
           << receiver_end_point.ToString();
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index 2a3b0f1..851424d 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -434,12 +434,14 @@
   ASSERT_TRUE(app_browser->is_app());
 }
 
-// Disabled: crbug.com/230165, crbug.com/915339
-#if defined(OS_WIN) || defined(OS_MACOSX)
+// Disabled: crbug.com/230165, crbug.com/915339, crbug.com/979399
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_CHROMEOS)
 #define MAYBE_LaunchTabApp DISABLED_LaunchTabApp
 #else
 #define MAYBE_LaunchTabApp LaunchTabApp
 #endif
+
 IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_LaunchTabApp) {
   extensions::ExtensionService* service =
       extensions::ExtensionSystem::Get(browser()->profile())
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
index 524ae75..f07d7d2 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
@@ -35,6 +35,9 @@
 #include "chrome/browser/chromeos/login/quick_unlock/auth_token.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chromeos/login/auth/password_visibility_utils.h"
+#include "components/user_manager/user.h"
 #endif
 
 namespace {
@@ -217,6 +220,13 @@
 #elif defined(OS_MACOSX)
   return password_manager_util_mac::AuthenticateUser(purpose);
 #elif defined(OS_CHROMEOS)
+  const bool user_cannot_manually_enter_password =
+      !chromeos::password_visibility::AccountHasUserFacingPassword(
+          chromeos::ProfileHelper::Get()
+              ->GetUserByProfile(profile_)
+              ->GetAccountId());
+  if (user_cannot_manually_enter_password)
+    return true;
   chromeos::quick_unlock::QuickUnlockStorage* quick_unlock_storage =
       chromeos::quick_unlock::QuickUnlockFactory::GetForProfile(profile_);
   const chromeos::quick_unlock::AuthToken* auth_token =
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 484ab0f..c600c7a 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -44,6 +44,7 @@
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/ash_pref_names.h"  // nogncheck
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_pref_names.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"
@@ -401,15 +402,15 @@
   (*s_whitelist)[::prefs::kTextToSpeechVolume] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
 
-  // Crostini
+  // Guest OS
   (*s_whitelist)[crostini::prefs::kCrostiniEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
-  (*s_whitelist)[crostini::prefs::kGuestOSPathsSharedToVms] =
-      settings_api::PrefType::PREF_TYPE_DICTIONARY;
   (*s_whitelist)[crostini::prefs::kCrostiniSharedUsbDevices] =
       settings_api::PrefType::PREF_TYPE_LIST;
   (*s_whitelist)[crostini::prefs::kCrostiniContainers] =
       settings_api::PrefType::PREF_TYPE_LIST;
+  (*s_whitelist)[guest_os::prefs::kGuestOSPathsSharedToVms] =
+      settings_api::PrefType::PREF_TYPE_DICTIONARY;
 
   // Android Apps.
   (*s_whitelist)[arc::prefs::kArcEnabled] =
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 3ab11da..8004a6c 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -219,15 +219,9 @@
     *destination = std::make_unique<T>(*source);
 }
 
-void ReportRequestedWindowState(windows::WindowState state) {
-  UMA_HISTOGRAM_ENUMERATION("TabsApi.RequestedWindowState", state,
-                            windows::WINDOW_STATE_LAST + 1);
-}
-
 ui::WindowShowState ConvertToWindowShowState(windows::WindowState state) {
   switch (state) {
     case windows::WINDOW_STATE_NORMAL:
-    case windows::WINDOW_STATE_DOCKED:
       return ui::SHOW_STATE_NORMAL;
     case windows::WINDOW_STATE_MINIMIZED:
       return ui::SHOW_STATE_MINIMIZED;
@@ -261,7 +255,6 @@
       // If maximised/fullscreen, default focused state should be focused.
       return !(create_data->focused && !*create_data->focused) && !has_bound;
     case windows::WINDOW_STATE_NORMAL:
-    case windows::WINDOW_STATE_DOCKED:
     case windows::WINDOW_STATE_NONE:
       return true;
   }
@@ -522,10 +515,6 @@
   std::string extension_id;
 
   if (create_data) {
-    // Report UMA stats to decide when to remove the deprecated "docked" windows
-    // state (crbug.com/703733).
-    ReportRequestedWindowState(create_data->state);
-
     // Figure out window type before figuring out bounds so that default
     // bounds can be set according to the window type.
     switch (create_data->type) {
@@ -672,10 +661,6 @@
     return RespondNow(Error(error));
   }
 
-  // Report UMA stats to decide when to remove the deprecated "docked" windows
-  // state (crbug.com/703733).
-  ReportRequestedWindowState(params->update_info.state);
-
   // Don't allow locked fullscreen operations on a window without the proper
   // permission (also don't allow any operations on a locked window if the
   // extension doesn't have the permission).
diff --git a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
index c4b6229..fe7875b 100644
--- a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
+++ b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
@@ -18,7 +18,6 @@
 #include "ash/keyboard/ui/resources/keyboard_resource_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/file_manager/file_manager_string_util.h"
-#include "chrome/grit/component_extension_resources.h"
 #include "extensions/common/constants.h"
 #include "third_party/ink/grit/ink_resources.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -39,11 +38,6 @@
     {"web_store/webstore_icon_16.png", IDR_WEBSTORE_ICON_16},
 #endif
 
-#if defined(KIOSK_NEXT)
-    {"chromeos/kiosk_next_home/kiosk_next_home.mojom.js",
-     IDR_KIOSK_NEXT_HOME_MOJOM_JS},
-#endif
-
 #if defined(OS_CHROMEOS)
     {"chrome_app/chrome_app_icon_32.png", IDR_CHROME_APP_ICON_32},
     {"chrome_app/chrome_app_icon_192.png", IDR_CHROME_APP_ICON_192},
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
index 0ad6ab1..6ac8a69 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -36,7 +36,6 @@
 #include "chrome/common/url_constants.h"
 #include "components/dom_distiller/core/url_constants.h"
 #include "components/guest_view/browser/guest_view_message_filter.h"
-#include "components/rappor/public/rappor_utils.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browser_url_handler.h"
@@ -771,50 +770,6 @@
 }
 
 // static
-void ChromeContentBrowserClientExtensionsPart::
-    LogInitiatorSchemeBypassingDocumentBlocking(
-        const url::Origin& initiator_origin,
-        int render_process_id,
-        content::ResourceType resource_type) {
-  // Return early if the RenderProcessHost can't be found.  This can happen if
-  // the process goes away for some reason during the IO -> UI thread hop
-  // required for calling LogInitiatorSchemeBypassingDocumentBlocking.
-  content::RenderProcessHost* process_host =
-      content::RenderProcessHost::FromID(render_process_id);
-  if (!process_host)
-    return;
-  content::BrowserContext* browser_context = process_host->GetBrowserContext();
-
-  // Until there is Site Isolation in GuestViews, CORB cannot offer protection
-  // against compromised renderers.  Therefore, let's ignore GuestViews when
-  // gathering the list of extensions that issue CORB-eligible requests.
-  if (process_host->IsForGuestsOnly())
-    return;
-
-  // Assert that |initiator_origin| corresponds to an extension and extract the
-  // |extension_id|.
-  DCHECK_EQ(kExtensionScheme, initiator_origin.scheme());
-  const std::string& extension_id = initiator_origin.host();
-  ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context);
-  DCHECK(registry);
-  DCHECK(registry->enabled_extensions().GetByID(extension_id));
-
-  // Don't log anything if the request was initiated by an extension process
-  // (we're only interested in requests initiated by content scripts).
-  ProcessMap* process_map = ProcessMap::Get(browser_context);
-  if (process_map->Contains(extension_id, render_process_id))
-    return;
-
-  // Log that CORB would have blocked in a meaningful way a request that was
-  // initiated by a content script.
-  UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Allowed.ContentScript",
-                            resource_type);
-  rappor::SampleString(rappor::GetDefaultService(),
-                       "Extensions.CrossOriginFetchFromContentScript2",
-                       rappor::UMA_RAPPOR_TYPE, extension_id);
-}
-
-// static
 network::mojom::URLLoaderFactoryPtrInfo
 ChromeContentBrowserClientExtensionsPart::
     CreateURLLoaderFactoryForNetworkRequests(
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
index 9a35eab..a81f69a 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
@@ -93,11 +93,6 @@
   static std::unique_ptr<content::VpnServiceProxy> GetVpnServiceProxy(
       content::BrowserContext* browser_context);
 
-  static void LogInitiatorSchemeBypassingDocumentBlocking(
-      const url::Origin& initiator_origin,
-      int render_process_id,
-      content::ResourceType resource_type);
-
   static network::mojom::URLLoaderFactoryPtrInfo
   CreateURLLoaderFactoryForNetworkRequests(
       content::RenderProcessHost* process,
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index 89e6fa7..37972b5 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_content_browser_client.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
 #include "chrome/browser/extensions/api/chrome_extensions_api_client.h"
 #include "chrome/browser/extensions/api/content_settings/content_settings_service.h"
@@ -361,8 +362,13 @@
 ExtensionCache* ChromeExtensionsBrowserClient::GetExtensionCache() {
   if (!extension_cache_.get()) {
 #if defined(OS_CHROMEOS)
+    base::TaskPriority task_priority =
+        chromeos::ProfileHelper::IsSigninProfileInitialized() &&
+                chromeos::ProfileHelper::SigninProfileHasLoginScreenExtensions()
+            ? base::TaskPriority::USER_VISIBLE
+            : base::TaskPriority::BEST_EFFORT;
     extension_cache_.reset(new ExtensionCacheImpl(
-        std::make_unique<ChromeOSExtensionCacheDelegate>()));
+        std::make_unique<ChromeOSExtensionCacheDelegate>(), task_priority));
 #else
     extension_cache_.reset(new NullExtensionCache());
 #endif
diff --git a/chrome/browser/extensions/chrome_extensions_interface_registration.cc b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
index be64883..666e402a 100644
--- a/chrome/browser/extensions/chrome_extensions_interface_registration.cc
+++ b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
@@ -22,7 +22,6 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/ash_features.h"
 #include "chromeos/services/ime/public/mojom/constants.mojom.h"
 #include "chromeos/services/ime/public/mojom/input_engine.mojom.h"
 #include "chromeos/services/media_perception/public/mojom/media_perception.mojom.h"
@@ -41,11 +40,6 @@
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #endif
 
-#if defined(KIOSK_NEXT)
-#include "chrome/browser/chromeos/kiosk_next_home/kiosk_next_home_interface_broker_impl.h"
-#include "chrome/browser/chromeos/kiosk_next_home/mojom/kiosk_next_home_interface_broker.mojom.h"  // nogncheck
-#endif  // defined(KIOSK_NEXT)
-
 namespace extensions {
 namespace {
 #if defined(OS_CHROMEOS)
@@ -60,28 +54,6 @@
 }
 #endif  // defined(GOOGLE_CHROME_BUILD)
 
-#if defined(KIOSK_NEXT)
-const char kKioskNextHomeInterfaceBrokerImplKey[] = "cros_kiosk_next_home_impl";
-
-void BindKioskNextHomeInterfaceBrokerRequest(
-    content::BrowserContext* context,
-    chromeos::kiosk_next_home::mojom::KioskNextHomeInterfaceBrokerRequest
-        request,
-    content::RenderFrameHost* source) {
-  auto* impl =
-      static_cast<chromeos::kiosk_next_home::KioskNextHomeInterfaceBrokerImpl*>(
-          context->GetUserData(kKioskNextHomeInterfaceBrokerImplKey));
-  if (!impl) {
-    auto new_impl = std::make_unique<
-        chromeos::kiosk_next_home::KioskNextHomeInterfaceBrokerImpl>(context);
-    impl = new_impl.get();
-    context->SetUserData(kKioskNextHomeInterfaceBrokerImplKey,
-                         std::move(new_impl));
-  }
-  impl->BindRequest(std::move(request));
-}
-#endif  // defined(KIOSK_NEXT)
-
 // Translates the renderer-side source ID to video device id.
 void TranslateVideoDeviceId(
     const std::string& salt,
@@ -156,14 +128,6 @@
   }
 #endif  // defined(GOOGLE_CHROME_BUILD)
 
-#if defined(KIOSK_NEXT)
-  if (base::FeatureList::IsEnabled(ash::features::kKioskNextShell) &&
-      extension->id() == extension_misc::kKioskNextHomeAppId) {
-    registry->AddInterface(
-        base::BindRepeating(&BindKioskNextHomeInterfaceBrokerRequest, context));
-  }
-#endif  // defined(KIOSK_NEXT)
-
   if (extension->permissions_data()->HasAPIPermission(
           APIPermission::kMediaPerceptionPrivate)) {
     extensions::ExtensionsAPIClient* client =
diff --git a/chrome/browser/extensions/clipboard_extension_helper_chromeos.cc b/chrome/browser/extensions/clipboard_extension_helper_chromeos.cc
index f78db23..db4b805 100644
--- a/chrome/browser/extensions/clipboard_extension_helper_chromeos.cc
+++ b/chrome/browser/extensions/clipboard_extension_helper_chromeos.cc
@@ -10,7 +10,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "chrome/browser/image_decoder.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
diff --git a/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc b/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc
index d3c77a1..d32f28f 100644
--- a/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc
+++ b/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc
@@ -99,9 +99,6 @@
     case IDR_HELP_MANIFEST:
     case IDR_QUICKOFFICE_MANIFEST:
 #endif  // defined(GOOGLE_CHROME_BUILD)
-#if defined(KIOSK_NEXT)
-    case IDR_KIOSK_NEXT_HOME_MANIFEST:
-#endif  // defined(KIOSK_NEXT)
 #endif  // defined(OS_CHROMEOS)
       return true;
   }
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index d72a6d6..69519cc 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -541,8 +541,6 @@
     AddZipArchiverExtension();
 #endif  // BUILDFLAG(ENABLE_NACL)
 
-    AddKioskNextExtension();
-
 #if defined(GOOGLE_CHROME_BUILD)
     std::string id = Add(IDR_QUICKOFFICE_MANIFEST,
                          base::FilePath(FILE_PATH_LITERAL(
@@ -658,19 +656,6 @@
                           extension_misc::kEspeakSpeechSynthesisExtensionId));
 }
 
-void ComponentLoader::AddKioskNextExtension() {
-#if defined(KIOSK_NEXT)
-  if (base::FeatureList::IsEnabled(ash::features::kKioskNextShell)) {
-    // Always install KioskNextHome for testing if the feature is enabled.
-    if (enable_background_extensions_during_testing ||
-        profile_->GetPrefs()->GetBoolean(ash::prefs::kKioskNextShellEnabled)) {
-      Add(IDR_KIOSK_NEXT_HOME_MANIFEST,
-          base::FilePath(FILE_PATH_LITERAL("chromeos/kiosk_next_home")));
-    }
-  }
-#endif  // defined(KIOSK_NEXT)
-}
-
 void ComponentLoader::EnableFileSystemInGuestMode(const std::string& id) {
   if (!IsNormalSession()) {
     // TODO(dpolukhin): Hack to enable HTML5 temporary file system for
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 33b54e1..f46f29a 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -71,6 +71,7 @@
 #include "chrome/common/url_constants.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/crx_file/id_util.h"
+#include "components/favicon_base/favicon_url_parser.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
@@ -903,8 +904,9 @@
   // to make sure that the FaviconSource is registered with the
   // ChromeURLDataManager.
   if (permissions_data->HasHostPermission(GURL(chrome::kChromeUIFaviconURL))) {
-    content::URLDataSource::Add(profile_,
-                                std::make_unique<FaviconSource>(profile_));
+    content::URLDataSource::Add(
+        profile_, std::make_unique<FaviconSource>(
+                      profile_, chrome::FaviconUrlFormat::kFaviconLegacy));
   }
 
   // Same for chrome://theme/ resources.
diff --git a/chrome/browser/extensions/extension_unload_browsertest.cc b/chrome/browser/extensions/extension_unload_browsertest.cc
index 687a51d..5306e1d6 100644
--- a/chrome/browser/extensions/extension_unload_browsertest.cc
+++ b/chrome/browser/extensions/extension_unload_browsertest.cc
@@ -3,19 +3,71 @@
 // found in the LICENSE file.
 
 #include "base/feature_list.h"
+#include "base/run_loop.h"
+#include "base/scoped_observer.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/disable_reason.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+#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 "services/network/public/cpp/features.h"
 #include "ui/base/window_open_disposition.h"
+#include "url/origin.h"
+#include "url/url_constants.h"
 
 namespace extensions {
 
+namespace {
+
+// A helper class to wait for a particular tab count. Requires the tab strip
+// to outlive this object.
+class TestTabStripModelObserver : public TabStripModelObserver {
+ public:
+  explicit TestTabStripModelObserver(TabStripModel* model)
+      : model_(model), desired_count_(0), scoped_observer_(this) {
+    scoped_observer_.Add(model);
+  }
+  ~TestTabStripModelObserver() override = default;
+
+  void WaitForTabCount(int count) {
+    if (model_->count() == count)
+      return;
+    desired_count_ = count;
+    run_loop_.Run();
+  }
+
+ private:
+  // TabStripModelObserver:
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override {
+    if (model_->count() == desired_count_)
+      run_loop_.Quit();
+  }
+
+  TabStripModel* model_;
+  int desired_count_;
+  base::RunLoop run_loop_;
+  ScopedObserver<TabStripModel, TabStripModelObserver> scoped_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestTabStripModelObserver);
+};
+
+}  // namespace
+
 class ExtensionUnloadBrowserTest : public ExtensionBrowserTest {
  public:
   void SetUpOnMainThread() override {
@@ -92,6 +144,106 @@
                   ->IsRenderFrameLive());
 }
 
+// Tests that windows with opaque origins opened by the extension are closed
+// when the extension is unloaded. Regression test for https://crbug.com/894477.
+IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, OpenedOpaqueWindows) {
+  TestExtensionDir test_dir;
+  constexpr char kManifest[] =
+      R"({
+           "name": "Test",
+           "manifest_version": 2,
+           "version": "0.1",
+           "background": {
+             "scripts": ["background.js"]
+           }
+         })";
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
+                     "window.open('about:blank');");
+
+  const GURL about_blank(url::kAboutBlankURL);
+  content::TestNavigationObserver about_blank_observer(about_blank);
+  about_blank_observer.StartWatchingNewWebContents();
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+  about_blank_observer.WaitForNavigationFinished();
+
+  EXPECT_EQ(2, browser()->tab_strip_model()->count());
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_EQ(about_blank, web_contents->GetLastCommittedURL());
+  url::Origin frame_origin =
+      web_contents->GetMainFrame()->GetLastCommittedOrigin();
+  url::SchemeHostPort precursor_tuple =
+      frame_origin.GetTupleOrPrecursorTupleIfOpaque();
+  EXPECT_EQ(kExtensionScheme, precursor_tuple.scheme());
+  EXPECT_EQ(extension->id(), precursor_tuple.host());
+
+  TestTabStripModelObserver test_tab_strip_model_observer(
+      browser()->tab_strip_model());
+  extension_service()->DisableExtension(extension->id(),
+                                        disable_reason::DISABLE_USER_ACTION);
+  test_tab_strip_model_observer.WaitForTabCount(1);
+
+  EXPECT_EQ(1, browser()->tab_strip_model()->count());
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, CrashedTabs) {
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(
+      R"({
+           "name": "test extension",
+           "manifest_version": 2,
+           "version": "0.1"
+         })");
+  test_dir.WriteFile(FILE_PATH_LITERAL("page.html"),
+                     "<!doctype html><html><body>Hello world</body></html>");
+  scoped_refptr<const Extension> extension(
+      LoadExtension(test_dir.UnpackedPath()));
+  ASSERT_TRUE(extension);
+  const GURL page_url = extension->GetResourceURL("page.html");
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+  EXPECT_EQ(2, browser()->tab_strip_model()->count());
+
+  content::WebContents* active_tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_EQ(page_url, active_tab->GetLastCommittedURL());
+
+  {
+    content::ScopedAllowRendererCrashes allow_renderer_crashes(
+        active_tab->GetMainFrame()->GetProcess());
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL("chrome://crash"), WindowOpenDisposition::CURRENT_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  }
+
+  // There should still be two open tabs, but the active one is crashed.
+  EXPECT_EQ(2, browser()->tab_strip_model()->count());
+  EXPECT_TRUE(active_tab->IsCrashed());
+
+  // Even though the tab is crashed, it should still have the last committed
+  // URL of the extension page.
+  EXPECT_EQ(page_url, active_tab->GetLastCommittedURL());
+
+  // Unloading the extension should close the crashed tab, since its origin was
+  // still the extension's origin.
+  TestTabStripModelObserver test_tab_strip_model_observer(
+      browser()->tab_strip_model());
+  extension_service()->DisableExtension(extension->id(),
+                                        disable_reason::DISABLE_USER_ACTION);
+  test_tab_strip_model_observer.WaitForTabCount(1);
+
+  EXPECT_EQ(1, browser()->tab_strip_model()->count());
+  EXPECT_NE(extension->url().GetOrigin(), browser()
+                                              ->tab_strip_model()
+                                              ->GetActiveWebContents()
+                                              ->GetLastCommittedURL()
+                                              .GetOrigin());
+}
+
 // TODO(devlin): Investigate what to do for embedded iframes.
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_cache_impl.cc b/chrome/browser/extensions/updater/extension_cache_impl.cc
index e20c1a9..b00fe0d 100644
--- a/chrome/browser/extensions/updater/extension_cache_impl.cc
+++ b/chrome/browser/extensions/updater/extension_cache_impl.cc
@@ -27,13 +27,14 @@
 namespace extensions {
 
 ExtensionCacheImpl::ExtensionCacheImpl(
-    std::unique_ptr<ChromeOSExtensionCacheDelegate> delegate)
+    std::unique_ptr<ChromeOSExtensionCacheDelegate> delegate,
+    base::TaskPriority task_priority)
     : cache_(new LocalExtensionCache(
           delegate->GetCacheDir(),
           delegate->GetMaximumCacheSize(),
           delegate->GetMaximumCacheAge(),
           base::CreateSequencedTaskRunnerWithTraits(
-              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+              {base::MayBlock(), task_priority,
                base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}))),
       weak_ptr_factory_(this) {
   notification_registrar_.Add(
diff --git a/chrome/browser/extensions/updater/extension_cache_impl.h b/chrome/browser/extensions/updater/extension_cache_impl.h
index 9ec7718..f83d1b1c 100644
--- a/chrome/browser/extensions/updater/extension_cache_impl.h
+++ b/chrome/browser/extensions/updater/extension_cache_impl.h
@@ -14,6 +14,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/task/task_traits.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/updater/extension_cache.h"
@@ -33,7 +34,8 @@
                            public content::NotificationObserver {
  public:
   explicit ExtensionCacheImpl(
-      std::unique_ptr<ChromeOSExtensionCacheDelegate> delegate);
+      std::unique_ptr<ChromeOSExtensionCacheDelegate> delegate,
+      base::TaskPriority task_priority = base::TaskPriority::BEST_EFFORT);
   ~ExtensionCacheImpl() override;
 
   // Implementation of ExtensionCache.
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
index 3bd2516..827f49d3 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
@@ -36,7 +36,6 @@
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
-#include "chrome/browser/ui/ash/kiosk_next_shell_client.h"
 #include "chromeos/dbus/util/version_loader.h"
 #include "chromeos/system/statistics_provider.h"
 #include "components/user_manager/user_manager.h"
@@ -68,7 +67,6 @@
 constexpr char kMonitorInfoKey[] = "monitor_info";
 constexpr char kAccountTypeKey[] = "account_type";
 constexpr char kDemoModeConfigKey[] = "demo_mode_config";
-constexpr char kKioskNextKey[] = "kiosk_next";
 #else
 constexpr char kOsVersionTag[] = "OS VERSION";
 #endif
@@ -247,11 +245,6 @@
   response->emplace(kDemoModeConfigKey,
                     chromeos::DemoSession::DemoConfigToString(
                         chromeos::DemoSession::GetDemoConfig()));
-  response->emplace(
-      kKioskNextKey,
-      KioskNextShellClient::Get() && KioskNextShellClient::Get()->has_launched()
-          ? "enabled"
-          : "disabled");
   PopulateLocalStateSettings(response.get());
 
   // Chain asynchronous fetchers: PopulateMonitorInfoAsync, PopulateEntriesAsync
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index ca24614..2195379 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -801,9 +801,14 @@
     "expiry_milestone": 78
   },
   {
+    "name": "enable-assistant-media-session-integration",
+    "owners": [ "croissant-eng" ],
+    "expiry_milestone": 78
+  },
+  {
     "name": "enable-assistant-app-support",
     "owners": [ "croissant-eng" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 78
   },
   {
     "name": "enable-assistant-dsp",
@@ -2555,6 +2560,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "omnibox-wrap-popup-position",
+    "owners": [ "krb", "chrome-omnibox-team@google.com" ],
+    "expiry_milestone": 81
+  },
+  {
     "name": "omnibox-zero-suggestions-on-ntp",
     "owners": [ "chrome-android-omnibox-team@google.com" ],
     "expiry_milestone": 81
@@ -2894,7 +2904,7 @@
   },
   {
     "name": "terminal-system-app",
-    "owners": [ "joelhockey", "benwells", "//chrome/browser/chromeos/crostini/OWNERS" ],
+    "owners": [ "joelhockey", "benwells", "//chrome/browser/chromeos/guest_os/OWNERS" ],
     "expiry_milestone": 82
   },
   {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 91f8b86..4c896dd 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2714,6 +2714,10 @@
 const char kOmniboxTabSwitchSuggestionsDescription[] =
     "Enable suggestions for switching to open tabs within the Omnibox.";
 
+const char kOmniboxWrapPopupPositionName[] = "Omnibox wrap pop-up position";
+const char kOmniboxWrapPopupPositionDescription[] =
+    "Enable wrapping the Omnibox pop-up position between top and bottom.";
+
 const char kProactiveTabFreezeAndDiscardName[] =
     "Proactive Tab Freeze and Discard";
 const char kProactiveTabFreezeAndDiscardDescription[] =
@@ -3064,6 +3068,11 @@
     "Combine Launcher search with the power of Assistant to provide the most "
     "useful answer for each query. Requires Assistant to be enabled.";
 
+const char kEnableAssistantMediaSessionIntegrationName[] =
+    "Assistant Media Session integration";
+const char kEnableAssistantMediaSessionIntegrationDescription[] =
+    "Enable Assistant Media Session Integration.";
+
 const char kEnableAssistantVoiceMatchName[] = "Enable Assistant Voice Match";
 const char kEnableAssistantVoiceMatchDescription[] =
     "Enable the Assistant Voice Match feature";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 2fd0fda..b88a12d 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1595,6 +1595,9 @@
 extern const char kOmniboxTabSwitchSuggestionsName[];
 extern const char kOmniboxTabSwitchSuggestionsDescription[];
 
+extern const char kOmniboxWrapPopupPositionName[];
+extern const char kOmniboxWrapPopupPositionDescription[];
+
 extern const char kProactiveTabFreezeAndDiscardName[];
 extern const char kProactiveTabFreezeAndDiscardDescription[];
 
@@ -1827,6 +1830,9 @@
 extern const char kEnableAssistantLauncherIntegrationName[];
 extern const char kEnableAssistantLauncherIntegrationDescription[];
 
+extern const char kEnableAssistantMediaSessionIntegrationName[];
+extern const char kEnableAssistantMediaSessionIntegrationDescription[];
+
 extern const char kEnableAssistantVoiceMatchName[];
 extern const char kEnableAssistantVoiceMatchDescription[];
 
diff --git a/chrome/browser/gcm/gcm_profile_service_factory.cc b/chrome/browser/gcm/gcm_profile_service_factory.cc
index afd9081a..f54a70c 100644
--- a/chrome/browser/gcm/gcm_profile_service_factory.cc
+++ b/chrome/browser/gcm/gcm_profile_service_factory.cc
@@ -19,13 +19,13 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/storage_partition.h"
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/gcm/gcm_product_util.h"
 #include "chrome/common/channel_info.h"
 #include "components/gcm_driver/gcm_client_factory.h"
 #include "content/public/browser/browser_context.h"
-#include "content/public/browser/storage_partition.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #endif
 
@@ -132,8 +132,10 @@
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));
   std::unique_ptr<GCMProfileService> service = nullptr;
 #if defined(OS_ANDROID)
-  service = base::WrapUnique(
-      new GCMProfileService(profile->GetPath(), blocking_task_runner));
+  service = std::make_unique<GCMProfileService>(
+      profile->GetPath(), blocking_task_runner,
+      content::BrowserContext::GetDefaultStoragePartition(profile)
+          ->GetURLLoaderFactoryForBrowserProcess());
 #else
   service = std::make_unique<GCMProfileService>(
       profile->GetPrefs(), profile->GetPath(),
diff --git a/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc b/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
index 21407c16..bf4cad2 100644
--- a/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
+++ b/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
@@ -84,6 +84,8 @@
   CHECK(load_time_data);
 
   PopulateStringsForSharedHTML(load_time_data);
+  security_interstitials::common_string_util::PopulateDarkModeDisplaySetting(
+      load_time_data);
 
   load_time_data->SetString(
       "tabTitle",
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
index 5a284b1b..dedff24 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -217,6 +217,15 @@
 
 ThrottleCheckResult LookalikeUrlNavigationThrottle::HandleThrottleRequest(
     const GURL& url) {
+  // Ignore if running unit tests. Some tests use
+  // TestMockTimeTaskRunner::ScopedContext and call CreateTestWebContents()
+  // which navigates and waits for throttles to complete using a RunLoop.
+  // However, TestMockTimeTaskRunner::ScopedContext disallows RunLoop so those
+  // tests crash. We should only do this with a real profile anyways.
+  if (profile_->AsTestingProfile()) {
+    return content::NavigationThrottle::PROCEED;
+  }
+
   content::NavigationHandle* handle = navigation_handle();
 
   // Ignore subframe and same document navigations.
diff --git a/chrome/browser/media/output_protection_proxy.cc b/chrome/browser/media/output_protection_proxy.cc
index d364c6c..f495ccc6 100644
--- a/chrome/browser/media/output_protection_proxy.cc
+++ b/chrome/browser/media/output_protection_proxy.cc
@@ -11,12 +11,24 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/display/types/display_constants.h"
 
+namespace {
+
+gfx::NativeView GetRenderFrameView(int render_process_id, int render_frame_id) {
+  auto* host =
+      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+  return host ? host->GetNativeView() : gfx::kNullNativeView;
+}
+
+}  // namespace
+
 OutputProtectionProxy::OutputProtectionProxy(int render_process_id,
                                              int render_frame_id)
     : render_process_id_(render_process_id),
       render_frame_id_(render_frame_id),
 #if defined(OS_CHROMEOS)
-      output_protection_delegate_(render_process_id, render_frame_id),
+      output_protection_delegate_(
+          // On OS_CHROMEOS, NativeView and NativeWindow are both aura::Window*.
+          GetRenderFrameView(render_process_id, render_frame_id)),
 #endif  // defined(OS_CHROMEOS)
       weak_ptr_factory_(this) {
 }
@@ -60,11 +72,8 @@
   DVLOG(1) << __func__ << ": " << success << ", " << link_mask;
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  content::RenderFrameHost* rfh =
-      content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
-  // TODO(xjz): Investigate whether this check (and the checks in
-  // OutputProtectionDelegate) should be removed.
-  if (!rfh) {
+  // TODO(xjz): Investigate whether this check should be removed.
+  if (!GetRenderFrameView(render_process_id_, render_frame_id_)) {
     LOG(WARNING) << "RenderFrameHost is not alive.";
     callback.Run(false, 0, 0);
     return;
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc b/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
index fdf8fdb..bf93726 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
@@ -22,24 +22,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// TODO(crbug.com/969812): Fix memory leaks in tests and re-enable on LSAN.
-#ifdef LEAK_SANITIZER
-#define MAYBE_CreateRoute DISABLED_CreateRoute
-#define MAYBE_CreateRouteFailsInvalidSource \
-  DISABLED_CreateRouteFailsInvalidSource
-#define MAYBE_StartObservingMediaSinks DISABLED_StartObservingMediaSinks
-#define MAYBE_TerminateRoute DISABLED_TerminateRoute
-#define MAYBE_BroadcastRequest DISABLED_BroadcastRequest
-#define MAYBE_CreateRouteFailsInvalidSink DISABLED_CreateRouteFailsInvalidSink
-#else
-#define MAYBE_CreateRoute CreateRoute
-#define MAYBE_CreateRouteFailsInvalidSource CreateRouteFailsInvalidSource
-#define MAYBE_StartObservingMediaSinks StartObservingMediaSinks
-#define MAYBE_TerminateRoute TerminateRoute
-#define MAYBE_BroadcastRequest BroadcastRequest
-#define MAYBE_CreateRouteFailsInvalidSink CreateRouteFailsInvalidSink
-#endif
-
 using ::testing::_;
 
 namespace media_router {
@@ -68,9 +50,10 @@
     router_binding_ = std::make_unique<mojo::Binding<mojom::MediaRouter>>(
         &mock_router_, mojo::MakeRequest(&router_ptr));
 
-    CastSessionTracker::SetInstanceForTest(
+    session_tracker_ = std::unique_ptr<CastSessionTracker>(
         new CastSessionTracker(&media_sink_service_, &message_handler_,
                                socket_service_.task_runner()));
+    CastSessionTracker::SetInstanceForTest(session_tracker_.get());
 
     EXPECT_CALL(mock_router_, OnSinkAvailabilityUpdated(_, _));
     provider_ = std::make_unique<CastMediaRouteProvider>(
@@ -85,6 +68,7 @@
   void TearDown() override {
     provider_.reset();
     CastSessionTracker::SetInstanceForTest(nullptr);
+    session_tracker_.reset();
   }
 
   void ExpectCreateRouteSuccessAndSetRoute(
@@ -129,6 +113,7 @@
   cast_channel::MockCastSocketService socket_service_;
   cast_channel::MockCastMessageHandler message_handler_;
 
+  std::unique_ptr<CastSessionTracker> session_tracker_;
   TestMediaSinkService media_sink_service_;
   MockCastAppDiscoveryService app_discovery_service_;
   std::unique_ptr<CastMediaRouteProvider> provider_;
@@ -140,7 +125,7 @@
   DISALLOW_COPY_AND_ASSIGN(CastMediaRouteProviderTest);
 };
 
-TEST_F(CastMediaRouteProviderTest, MAYBE_StartObservingMediaSinks) {
+TEST_F(CastMediaRouteProviderTest, StartObservingMediaSinks) {
   MediaSource::Id non_cast_source("not-a-cast-source:foo");
   EXPECT_CALL(app_discovery_service_, DoStartObservingMediaSinks(_)).Times(0);
   provider_->StartObservingMediaSinks(non_cast_source);
@@ -153,7 +138,7 @@
   EXPECT_TRUE(app_discovery_service_.callbacks().empty());
 }
 
-TEST_F(CastMediaRouteProviderTest, MAYBE_BroadcastRequest) {
+TEST_F(CastMediaRouteProviderTest, BroadcastRequest) {
   media_sink_service_.AddOrUpdateSink(CreateCastSink(1));
   media_sink_service_.AddOrUpdateSink(CreateCastSink(2));
   MediaSource::Id source_id(
@@ -171,7 +156,7 @@
   EXPECT_TRUE(app_discovery_service_.callbacks().empty());
 }
 
-TEST_F(CastMediaRouteProviderTest, MAYBE_CreateRouteFailsInvalidSink) {
+TEST_F(CastMediaRouteProviderTest, CreateRouteFailsInvalidSink) {
   // Sink does not exist.
   provider_->CreateRoute(
       kCastSource, "sinkId", kPresentationId, origin_, kTabId, kRouteTimeout,
@@ -181,7 +166,7 @@
                      RouteRequestResult::ResultCode::SINK_NOT_FOUND));
 }
 
-TEST_F(CastMediaRouteProviderTest, MAYBE_CreateRouteFailsInvalidSource) {
+TEST_F(CastMediaRouteProviderTest, CreateRouteFailsInvalidSource) {
   MediaSinkInternal sink = CreateCastSink(1);
   media_sink_service_.AddOrUpdateSink(sink);
 
@@ -193,7 +178,7 @@
                      RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER));
 }
 
-TEST_F(CastMediaRouteProviderTest, MAYBE_CreateRoute) {
+TEST_F(CastMediaRouteProviderTest, CreateRoute) {
   MediaSinkInternal sink = CreateCastSink(1);
   media_sink_service_.AddOrUpdateSink(sink);
 
@@ -206,7 +191,7 @@
           base::Unretained(this)));
 }
 
-TEST_F(CastMediaRouteProviderTest, MAYBE_TerminateRoute) {
+TEST_F(CastMediaRouteProviderTest, TerminateRoute) {
   MediaSinkInternal sink = CreateCastSink(1);
   media_sink_service_.AddOrUpdateSink(sink);
 
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 f60c66f8..49155227 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -223,8 +223,6 @@
   UMA_HISTOGRAM_ENUMERATION("Windows.Kernel32Version",
                             os_info.Kernel32Version(),
                             base::win::Version::WIN_LAST);
-  UMA_HISTOGRAM_BOOLEAN("Windows.InCompatibilityMode",
-                        os_info.version() != os_info.Kernel32Version());
 
   UMA_HISTOGRAM_BOOLEAN("Windows.HasHighResolutionTimeTicks",
                         base::TimeTicks::IsHighResolution());
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index 2dffb7c..729c4a25 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/metrics/tab_footprint_aggregator.h"
 #include "chrome/browser/performance_manager/graph/frame_node_impl.h"
 #include "chrome/browser/performance_manager/graph/graph_impl.h"
+#include "chrome/browser/performance_manager/graph/graph_impl_operations.h"
 #include "chrome/browser/performance_manager/graph/page_node_impl.h"
 #include "chrome/browser/performance_manager/graph/process_node_impl.h"
 #include "chrome/browser/performance_manager/performance_manager.h"
@@ -794,7 +795,7 @@
       }
       if (native_library_resident_not_ordered_kb != metrics.kInvalid) {
         base::UmaHistogramCustomCounts(
-            "Memory.NativeLibrary.ResidentNotOrdereredCodeMemoryFootprint",
+            "Memory.NativeLibrary.ResidentNotOrderedCodeMemoryFootprint",
             native_library_resident_not_ordered_kb, 1000, 100000, 100);
       }
     }
@@ -860,7 +861,8 @@
     process_info.launch_time = process_node->launch_time();
 
     base::flat_set<performance_manager::PageNodeImpl*> page_nodes =
-        process_node->GetAssociatedPageNodes();
+        performance_manager::GraphImplOperations::GetAssociatedPageNodes(
+            process_node);
     for (performance_manager::PageNodeImpl* page_node : page_nodes) {
       if (page_node->ukm_source_id() == ukm::kInvalidSourceId)
         continue;
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
index 36f84c8..8b981cd2 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
@@ -854,7 +854,7 @@
   histograms.ExpectTotalCount(
       "Memory.NativeLibrary.NotResidentOrderedCodeMemoryFootprint", 0);
   histograms.ExpectTotalCount(
-      "Memory.NativeLibrary.ResidentNotOrdereredCodeMemoryFootprint", 0);
+      "Memory.NativeLibrary.ResidentNotOrderedCodeMemoryFootprint", 0);
 
   // Simulate some metrics emission.
   scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter =
@@ -894,7 +894,7 @@
       "Memory.NativeLibrary.NotResidentOrderedCodeMemoryFootprint",
       kNativeLibraryNotResidentOrderedCodeFootprint, 1);
   histograms.ExpectUniqueSample(
-      "Memory.NativeLibrary.ResidentNotOrdereredCodeMemoryFootprint",
+      "Memory.NativeLibrary.ResidentNotOrderedCodeMemoryFootprint",
       kNativeLibraryResidentNotOrderedCodeFootprint, 1);
 }
 
diff --git a/chrome/browser/notifications/scheduler/display_agent_android.cc b/chrome/browser/notifications/scheduler/display_agent_android.cc
index 566ec98..66a5a4d4 100644
--- a/chrome/browser/notifications/scheduler/display_agent_android.cc
+++ b/chrome/browser/notifications/scheduler/display_agent_android.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/notifications/scheduler/public/user_action_handler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
-#include "ui/gfx/android/java_bitmap.h"
 
+using base::android::ConvertUTF16ToJavaString;
 using base::android::ConvertUTF8ToJavaString;
 
 namespace {
@@ -64,16 +64,15 @@
   // TODO(xingliu): Refactor and hook to NotificationDisplayService.
   DCHECK(notification_data);
   JNIEnv* env = base::android::AttachCurrentThread();
-  DCHECK(!notification_data->icon.empty());
-  DCHECK(!notification_data->id.empty());
   DCHECK(!notification_data->title.empty());
   DCHECK(!notification_data->message.empty());
 
+  // TODO(xingliu): Populate the icon to Java.
   auto java_notification_data = Java_DisplayAgent_Constructor(
       env, ConvertUTF8ToJavaString(env, notification_data->id),
-      ConvertUTF8ToJavaString(env, notification_data->title),
-      ConvertUTF8ToJavaString(env, notification_data->message),
-      gfx::ConvertToJavaBitmap(&notification_data->icon));
+      ConvertUTF16ToJavaString(env, notification_data->title),
+      ConvertUTF16ToJavaString(env, notification_data->message),
+      nullptr /*icon*/);
 
   Java_DisplayAgent_showNotification(env, java_notification_data);
 }
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
index f8ebff8..92adbf6 100644
--- a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
+++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
@@ -94,6 +94,22 @@
   }
 }
 
+void ImpressionHistoryTrackerImpl::GetImpressionDetail(
+    SchedulerClientType type,
+    ImpressionDetail::ImpressionDetailCallback callback) {
+  DCHECK(initialized_);
+  auto it = client_states_.find(type);
+  if (it == client_states_.end())
+    return;
+
+  auto* state = it->second.get();
+  int num_notification_shown_today =
+      notifications::NotificationsShownToday(state);
+  ImpressionDetail detail(state->current_max_daily_show,
+                          num_notification_shown_today);
+  std::move(callback).Run(std::move(detail));
+}
+
 void ImpressionHistoryTrackerImpl::OnClick(const std::string& notification_id) {
   OnClickInternal(notification_id, true /*update_db*/);
 }
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
index a89104f2..ecf3ff28 100644
--- a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
+++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
@@ -19,6 +19,7 @@
 #include "chrome/browser/notifications/scheduler/internal/collection_store.h"
 #include "chrome/browser/notifications/scheduler/internal/impression_types.h"
 #include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/public/impression_detail.h"
 #include "chrome/browser/notifications/scheduler/public/user_action_handler.h"
 
 namespace notifications {
@@ -59,6 +60,11 @@
       std::map<SchedulerClientType, const ClientState*>* client_states)
       const = 0;
 
+  // Queries the impression detail of a given |SchedulerClientType|.
+  virtual void GetImpressionDetail(
+      SchedulerClientType type,
+      ImpressionDetail::ImpressionDetailCallback callback) = 0;
+
   virtual ~ImpressionHistoryTracker() = default;
 
  protected:
@@ -86,6 +92,9 @@
   void AnalyzeImpressionHistory() override;
   void GetClientStates(std::map<SchedulerClientType, const ClientState*>*
                            client_states) const override;
+  void GetImpressionDetail(
+      SchedulerClientType type,
+      ImpressionDetail::ImpressionDetailCallback callback) override;
   void OnClick(const std::string& notification_id) override;
   void OnActionClick(const std::string& notification_id,
                      ActionButtonType button_type) override;
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc
index 022dc63..56b3b778 100644
--- a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc
+++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc
@@ -35,6 +35,29 @@
                                    std::move(params)));
 }
 
+void InitAwareNotificationScheduler::DeleteAllNotifications(
+    SchedulerClientType type) {
+  if (IsReady()) {
+    impl_->DeleteAllNotifications(type);
+    return;
+  }
+  MaybeCacheClosure(
+      base::BindOnce(&InitAwareNotificationScheduler::DeleteAllNotifications,
+                     weak_ptr_factory_.GetWeakPtr(), type));
+}
+
+void InitAwareNotificationScheduler::GetImpressionDetail(
+    SchedulerClientType type,
+    ImpressionDetail::ImpressionDetailCallback callback) {
+  if (IsReady()) {
+    impl_->GetImpressionDetail(type, std::move(callback));
+    return;
+  }
+  MaybeCacheClosure(base::BindOnce(
+      &InitAwareNotificationScheduler::GetImpressionDetail,
+      weak_ptr_factory_.GetWeakPtr(), type, std::move(callback)));
+}
+
 void InitAwareNotificationScheduler::OnStartTask(
     TaskFinishedCallback callback) {
   if (IsReady()) {
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h
index 5e8e047..5d71758 100644
--- a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h
+++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h
@@ -34,6 +34,10 @@
   void Init(InitCallback init_callback) override;
   void Schedule(
       std::unique_ptr<NotificationParams> notification_params) override;
+  void DeleteAllNotifications(SchedulerClientType type) override;
+  void GetImpressionDetail(
+      SchedulerClientType type,
+      ImpressionDetail::ImpressionDetailCallback callback) override;
   void OnStartTask(TaskFinishedCallback callback) override;
   void OnStopTask() override;
   void OnClick(const std::string& notification_id) override;
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc
index 0d97a766..b42c591 100644
--- a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc
@@ -27,6 +27,10 @@
 
   MOCK_METHOD1(Init, void(InitCallback));
   MOCK_METHOD1(Schedule, void(std::unique_ptr<NotificationParams>));
+  MOCK_METHOD1(DeleteAllNotifications, void(SchedulerClientType type));
+  MOCK_METHOD2(GetImpressionDetail,
+               void(SchedulerClientType type,
+                    ImpressionDetail::ImpressionDetailCallback callback));
   MOCK_METHOD1(OnStartTask, void(TaskFinishedCallback));
   MOCK_METHOD0(OnStopTask, void());
   MOCK_METHOD1(OnClick, void(const std::string&));
diff --git a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc
index f114c8e..ca5fb759 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc
+++ b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc
@@ -23,6 +23,17 @@
   scheduler_->Schedule(std::move(notification_params));
 }
 
+void NotificationScheduleServiceImpl::DeleteNotifications(
+    SchedulerClientType type) {
+  scheduler_->DeleteAllNotifications(type);
+}
+
+void NotificationScheduleServiceImpl::GetImpressionDetail(
+    SchedulerClientType type,
+    ImpressionDetail::ImpressionDetailCallback callback) {
+  scheduler_->GetImpressionDetail(type, std::move(callback));
+}
+
 NotificationBackgroundTaskScheduler::Handler*
 NotificationScheduleServiceImpl::GetBackgroundTaskSchedulerHandler() {
   return this;
diff --git a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h
index 210e4f9..612c433 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h
+++ b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h
@@ -30,6 +30,10 @@
   // NotificationScheduleService implementation.
   void Schedule(
       std::unique_ptr<NotificationParams> notification_params) override;
+  void DeleteNotifications(SchedulerClientType type) override;
+  void GetImpressionDetail(
+      SchedulerClientType,
+      ImpressionDetail::ImpressionDetailCallback callback) override;
   NotificationBackgroundTaskScheduler::Handler*
   GetBackgroundTaskSchedulerHandler() override;
   UserActionHandler* GetUserActionHandler() override;
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
index 3c15e9ac..7d1cee05 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
@@ -37,7 +37,11 @@
 class InitHelper {
  public:
   using InitCallback = base::OnceCallback<void(bool)>;
-  InitHelper() : weak_ptr_factory_(this) {}
+  InitHelper()
+      : context_(nullptr),
+        notification_manager_delegate_(nullptr),
+        impression_tracker_delegate_(nullptr),
+        weak_ptr_factory_(this) {}
 
   ~InitHelper() = default;
 
@@ -49,53 +53,51 @@
       ScheduledNotificationManager::Delegate* notification_manager_delegate,
       ImpressionHistoryTracker::Delegate* impression_tracker_delegate,
       InitCallback callback) {
+    // TODO(xingliu): Initialize the databases in parallel, we currently
+    // initialize one by one to work around a shared db issue. See
+    // https://crbug.com/978680.
+    context_ = context;
+    notification_manager_delegate_ = notification_manager_delegate;
+    impression_tracker_delegate_ = impression_tracker_delegate;
     callback_ = std::move(callback);
+
     context->icon_store()->Init(base::BindOnce(
         &InitHelper::OnIconStoreInitialized, weak_ptr_factory_.GetWeakPtr()));
-    context->impression_tracker()->Init(
-        impression_tracker_delegate,
-        base::BindOnce(&InitHelper::OnImpressionTrackerInitialized,
-                       weak_ptr_factory_.GetWeakPtr()));
-    context->notification_manager()->Init(
-        notification_manager_delegate,
-        base::BindOnce(&InitHelper::OnNotificationManagerInitialized,
-                       weak_ptr_factory_.GetWeakPtr()));
   }
 
  private:
   void OnIconStoreInitialized(bool success) {
-    icon_store_initialzed_ = success;
-    MaybeFinishInitialization();
+    if (!success) {
+      std::move(callback_).Run(false /*success*/);
+      return;
+    }
+
+    context_->impression_tracker()->Init(
+        impression_tracker_delegate_,
+        base::BindOnce(&InitHelper::OnImpressionTrackerInitialized,
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 
   void OnImpressionTrackerInitialized(bool success) {
-    impression_tracker_initialzed_ = success;
-    MaybeFinishInitialization();
+    if (!success) {
+      std::move(callback_).Run(false /*success*/);
+      return;
+    }
+
+    context_->notification_manager()->Init(
+        notification_manager_delegate_,
+        base::BindOnce(&InitHelper::OnNotificationManagerInitialized,
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 
   void OnNotificationManagerInitialized(bool success) {
-    notification_manager_initialized_ = success;
-    MaybeFinishInitialization();
-  }
-
-  void MaybeFinishInitialization() {
-    bool all_finished = icon_store_initialzed_.has_value() &&
-                        impression_tracker_initialzed_.has_value() &&
-                        notification_manager_initialized_.has_value();
-    // Notify the initialization result when all subcomponents are initialized.
-    if (!all_finished)
-      return;
-
-    bool success = icon_store_initialzed_.value() &&
-                   impression_tracker_initialzed_.value() &&
-                   notification_manager_initialized_.value();
     std::move(callback_).Run(success);
   }
 
+  NotificationSchedulerContext* context_;
+  ScheduledNotificationManager::Delegate* notification_manager_delegate_;
+  ImpressionHistoryTracker::Delegate* impression_tracker_delegate_;
   InitCallback callback_;
-  base::Optional<bool> icon_store_initialzed_;
-  base::Optional<bool> impression_tracker_initialzed_;
-  base::Optional<bool> notification_manager_initialized_;
 
   base::WeakPtrFactory<InitHelper> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(InitHelper);
@@ -128,6 +130,18 @@
       std::unique_ptr<NotificationParams> notification_params) override {
     context_->notification_manager()->ScheduleNotification(
         std::move(notification_params));
+    ScheduleBackgroundTask();
+  }
+
+  void DeleteAllNotifications(SchedulerClientType type) override {
+    context_->notification_manager()->DeleteNotifications(type);
+  }
+
+  void GetImpressionDetail(
+      SchedulerClientType type,
+      ImpressionDetail::ImpressionDetailCallback callback) override {
+    context_->impression_tracker()->GetImpressionDetail(type,
+                                                        std::move(callback));
   }
 
   void OnInitialized(std::unique_ptr<InitHelper>,
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.h b/chrome/browser/notifications/scheduler/internal/notification_scheduler.h
index 3a5015d..4ffe00c 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_scheduler.h
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.h
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/public/impression_detail.h"
 #include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
 #include "chrome/browser/notifications/scheduler/public/user_action_handler.h"
 
@@ -38,6 +39,14 @@
   virtual void Schedule(
       std::unique_ptr<NotificationParams> notification_params) = 0;
 
+  // Queries impression detail for a given |SchedulerClientType|.
+  virtual void GetImpressionDetail(
+      SchedulerClientType type,
+      ImpressionDetail::ImpressionDetailCallback callback) = 0;
+
+  // Deletes all notifications of a given |SchedulerClientType|.
+  virtual void DeleteAllNotifications(SchedulerClientType type) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(NotificationScheduler);
 };
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc
index 338f9ff1..2694a781 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc
@@ -31,6 +31,7 @@
     std::unique_ptr<DisplayDecider> display_decider,
     std::unique_ptr<SchedulerConfig> config)
     : client_registrar_(std::move(client_registrar)),
+      icon_store_(std::move(icon_store)),
       impression_tracker_(std::move(impression_tracker)),
       notification_manager_(std::move(notification_manager)),
       display_agent_(std::move(display_agent)),
diff --git a/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc b/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc
index 83f4517..d14a4fa2 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/notifications/scheduler/internal/notification_store.h"
 
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_task_environment.h"
 #include "chrome/browser/notifications/scheduler/internal/proto_conversion.h"
 #include "chrome/browser/notifications/scheduler/test/test_utils.h"
@@ -149,7 +150,7 @@
   VerifyDataInDb(std::move(expected));
 
   // Update and verified the new data.
-  entry.notification_data.title = "test_title";
+  entry.notification_data.title = base::UTF8ToUTF16("test_title");
   expected = std::make_unique<DbEntries>();
   expected->emplace_back(entry);
   store()->Update(kGuid, entry,
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
index 4d0df4e..92f9d29 100644
--- a/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
+++ b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
 
 namespace notifications {
 
@@ -174,8 +175,8 @@
 void NotificationDataToProto(NotificationData* notification_data,
                              proto::NotificationData* proto) {
   proto->set_id(notification_data->id);
-  proto->set_title(notification_data->title);
-  proto->set_message(notification_data->message);
+  proto->set_title(base::UTF16ToUTF8(notification_data->title));
+  proto->set_message(base::UTF16ToUTF8(notification_data->message));
   proto->set_url(notification_data->url);
 }
 
@@ -183,8 +184,8 @@
 void NotificationDataFromProto(proto::NotificationData* proto,
                                NotificationData* notification_data) {
   notification_data->id = proto->id();
-  notification_data->title = proto->title();
-  notification_data->message = proto->message();
+  notification_data->title = base::UTF8ToUTF16(proto->title());
+  notification_data->message = base::UTF8ToUTF16(proto->message());
   notification_data->url = proto->url();
 }
 
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
index 8bda062..1379d40 100644
--- a/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/notifications/scheduler/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -170,8 +171,8 @@
 
   // Test notification data.
   entry.notification_data.id = kGuid;
-  entry.notification_data.title = "title";
-  entry.notification_data.message = "message";
+  entry.notification_data.title = base::UTF8ToUTF16("title");
+  entry.notification_data.message = base::UTF8ToUTF16("message");
   entry.icon_uuid = "icon_uuid";
   entry.notification_data.url = "url";
   TestNotificationEntryConversion(&entry);
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
index 6e411c63..5026dfd 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/guid.h"
 #include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_task_environment.h"
 #include "chrome/browser/notifications/scheduler/internal/collection_store.h"
 #include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
@@ -136,7 +137,7 @@
 TEST_F(ScheduledNotificationManagerTest, ScheduleNotification) {
   InitWithData(std::vector<NotificationEntry>());
   NotificationData notification_data;
-  notification_data.title = kTitle;
+  notification_data.title = base::UTF8ToUTF16(kTitle);
   ScheduleParams schedule_params;
   schedule_params.priority = ScheduleParams::Priority::kHigh;
   auto params = std::make_unique<NotificationParams>(
@@ -157,7 +158,7 @@
   EXPECT_NE(entry->create_time, base::Time());
 
   // TODO(xingliu): change these to compare with operator==.
-  EXPECT_EQ(entry->notification_data.title, kTitle);
+  EXPECT_EQ(base::UTF16ToUTF8(entry->notification_data.title), kTitle);
   EXPECT_EQ(entry->schedule_params.priority, ScheduleParams::Priority::kHigh);
 }
 
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc b/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
index ccd175d..c3c734c 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
@@ -59,6 +59,35 @@
   }
 }
 
+int NotificationsShownToday(ClientState* state) {
+  int count = 0;
+  auto impressions = state->impressions;
+  base::Time now(base::Time::Now());
+  base::Time beginning_of_today, beginning_of_tomorrow;
+  bool success = ToLocalHour(0, now, 0, &beginning_of_today);
+  beginning_of_tomorrow += base::TimeDelta::FromDays(1);
+  DCHECK(success);
+  if (impressions.rbegin()->create_time < beginning_of_today) {
+    count = 0;
+  } else if (impressions.begin()->create_time > beginning_of_tomorrow) {
+    count = impressions.size();
+  } else {
+    auto right = lower_bound(impressions.rbegin(), impressions.rend(),
+                             beginning_of_today,
+                             [](const Impression& lhs, const base::Time& rhs) {
+                               return lhs.create_time < rhs;
+                             });
+    auto left = upper_bound(impressions.rbegin(), impressions.rend(),
+                            beginning_of_tomorrow,
+                            [](const base::Time& lhs, const Impression& rhs) {
+                              return lhs < rhs.create_time;
+                            });
+    count = right - left + 1;
+  }
+  DCHECK_LE(count, state->current_max_daily_show);
+  return count;
+}
+
 std::unique_ptr<ClientState> CreateNewClientState(
     SchedulerClientType type,
     const SchedulerConfig& config) {
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils.h b/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
index cdd12447..05e944d 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
@@ -34,6 +34,9 @@
     int* shown_total,
     SchedulerClientType* last_shown_type);
 
+// Counts the number of notifications shown today of a given |state|.
+int NotificationsShownToday(ClientState* state);
+
 // Creates client state data for new registered client.
 std::unique_ptr<ClientState> CreateNewClientState(
     SchedulerClientType type,
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.cc b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.cc
index 11f2f8c..5dc0243 100644
--- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.cc
+++ b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.cc
@@ -60,3 +60,8 @@
       base::saturated_cast<jlong>(window_start.InMilliseconds()),
       base::saturated_cast<jlong>(window_end.InMilliseconds()));
 }
+
+void NotificationBackgroundTaskSchedulerAndroid::Cancel() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_NotificationSchedulerTask_cancel(env);
+}
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h
index 1586117c..d7cfde95 100644
--- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h
+++ b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h
@@ -25,6 +25,7 @@
   void Schedule(notifications::SchedulerTaskTime scheduler_task_time,
                 base::TimeDelta window_start,
                 base::TimeDelta window_end) override;
+  void Cancel() override;
 
   DISALLOW_COPY_AND_ASSIGN(NotificationBackgroundTaskSchedulerAndroid);
 };
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc b/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc
index f3d4e43..c61cbc9 100644
--- a/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc
+++ b/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc
@@ -21,6 +21,7 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/notifications/scheduler/display_agent_android.h"
+#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h"
 #endif
 
 namespace {
@@ -29,7 +30,6 @@
   auto client_registrar =
       std::make_unique<notifications::NotificationSchedulerClientRegistrar>();
   // TODO(xingliu): Register clients here.
-
   return client_registrar;
 }
 
@@ -66,12 +66,14 @@
   base::FilePath storage_dir =
       profile->GetPath().Append(chrome::kNotificationSchedulerStorageDirname);
   auto client_registrar = RegisterClients();
-  auto background_task_scheduler =
-      std::make_unique<NotificationBackgroundTaskSchedulerImpl>();
 #if defined(OS_ANDROID)
   auto display_agent = std::make_unique<DisplayAgentAndroid>();
+  auto background_task_scheduler =
+      std::make_unique<NotificationBackgroundTaskSchedulerAndroid>();
 #else
   auto display_agent = notifications::DisplayAgent::Create();
+  auto background_task_scheduler =
+      std::make_unique<NotificationBackgroundTaskSchedulerImpl>();
 #endif
   auto* db_provider = leveldb_proto::ProtoDatabaseProviderFactory::GetForKey(
       profile->GetProfileKey());
diff --git a/chrome/browser/notifications/scheduler/public/BUILD.gn b/chrome/browser/notifications/scheduler/public/BUILD.gn
index 7140be0..b5a1c01 100644
--- a/chrome/browser/notifications/scheduler/public/BUILD.gn
+++ b/chrome/browser/notifications/scheduler/public/BUILD.gn
@@ -12,6 +12,8 @@
   sources = [
     "display_agent.cc",
     "display_agent.h",
+    "impression_detail.cc",
+    "impression_detail.h",
     "notification_background_task_scheduler.h",
     "notification_data.cc",
     "notification_data.h",
diff --git a/chrome/browser/notifications/scheduler/public/impression_detail.cc b/chrome/browser/notifications/scheduler/public/impression_detail.cc
new file mode 100644
index 0000000..16c2f35
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/impression_detail.cc
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/notifications/scheduler/public/impression_detail.h"
+
+namespace notifications {
+
+ImpressionDetail::ImpressionDetail()
+    : current_max_daily_show(0), num_shown_today(0) {}
+
+ImpressionDetail::ImpressionDetail(int current_max_daily_show,
+                                   int num_shown_today)
+    : current_max_daily_show(current_max_daily_show),
+      num_shown_today(num_shown_today) {}
+
+ImpressionDetail::ImpressionDetail(const ImpressionDetail& other) = default;
+
+ImpressionDetail::~ImpressionDetail() = default;
+
+bool ImpressionDetail::operator==(const ImpressionDetail& other) const {
+  return current_max_daily_show == other.current_max_daily_show &&
+         num_shown_today == other.num_shown_today;
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/impression_detail.h b/chrome/browser/notifications/scheduler/public/impression_detail.h
new file mode 100644
index 0000000..f3773ae
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/impression_detail.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_IMPRESSION_DETAIL_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_IMPRESSION_DETAIL_H_
+
+#include "base/callback.h"
+
+namespace notifications {
+
+struct ImpressionDetail {
+  using ImpressionDetailCallback = base::OnceCallback<void(ImpressionDetail)>;
+
+  ImpressionDetail();
+  ImpressionDetail(int current_max_daily_show, int num_shown_today);
+  ImpressionDetail(const ImpressionDetail& other);
+  ~ImpressionDetail();
+  bool operator==(const ImpressionDetail& other) const;
+
+  // The maximum number of notifications shown to the user for this type.
+  // May change if the user interacts with the notification per day.
+  int current_max_daily_show;
+
+  // The current number of notifications shown to the user for this type today.
+  int num_shown_today;
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_IMPRESSION_DETAIL_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.h b/chrome/browser/notifications/scheduler/public/notification_data.h
index e4d93cc..255ee3b4 100644
--- a/chrome/browser/notifications/scheduler/public/notification_data.h
+++ b/chrome/browser/notifications/scheduler/public/notification_data.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/strings/string16.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace notifications {
@@ -14,7 +15,8 @@
 // Contains data used to display a scheduled notification. All fields will be
 // persisted to disk as protobuffer NotificationData. The clients of
 // notification scheduler can optionally use the texts or icon in this struct,
-// or use hard coded assets id.
+// or retrieving the hard coded assets and rewrites the data before notification
+// is shown.
 struct NotificationData {
   NotificationData();
   NotificationData(const NotificationData& other);
@@ -27,10 +29,10 @@
   std::string id;
 
   // The title of the notification.
-  std::string title;
+  base::string16 title;
 
   // The body text of the notification.
-  std::string message;
+  base::string16 message;
 
   // The bitmap for the small icon.
   SkBitmap icon;
diff --git a/chrome/browser/notifications/scheduler/public/notification_schedule_service.h b/chrome/browser/notifications/scheduler/public/notification_schedule_service.h
index abaed3b..17e7ef0 100644
--- a/chrome/browser/notifications/scheduler/public/notification_schedule_service.h
+++ b/chrome/browser/notifications/scheduler/public/notification_schedule_service.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/public/impression_detail.h"
 #include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
 #include "components/keyed_service/core/keyed_service.h"
 
@@ -26,6 +27,14 @@
   virtual void Schedule(
       std::unique_ptr<NotificationParams> notification_params) = 0;
 
+  // Deletes notifications of a given |SchedulerClientType|.
+  virtual void DeleteNotifications(SchedulerClientType type) = 0;
+
+  // Queries impression details for a given |SchedulerClientType|.
+  virtual void GetImpressionDetail(
+      SchedulerClientType type,
+      ImpressionDetail::ImpressionDetailCallback callback) = 0;
+
   // Returns NotificationBackgroundTaskScheduler Handler.
   virtual NotificationBackgroundTaskScheduler::Handler*
   GetBackgroundTaskSchedulerHandler() = 0;
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc
index 6a3d704..07c8431 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.cc
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc
@@ -24,7 +24,6 @@
 #include "base/task/post_task.h"
 #include "base/time/time.h"
 #include "chrome/android/chrome_jni_headers/OfflinePageBridge_jni.h"
-#include "chrome/android/chrome_jni_headers/SavePageRequest_jni.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/offline_pages/offline_page_mhtml_archiver.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
@@ -32,11 +31,9 @@
 #include "chrome/browser/offline_pages/offline_page_utils.h"
 #include "chrome/browser/offline_pages/prefetch/prefetched_pages_notifier.h"
 #include "chrome/browser/offline_pages/recent_tab_helper.h"
-#include "chrome/browser/offline_pages/request_coordinator_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "components/offline_pages/core/archive_validator.h"
-#include "components/offline_pages/core/background/request_coordinator.h"
 #include "components/offline_pages/core/background/request_queue_results.h"
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/client_policy_controller.h"
@@ -47,7 +44,6 @@
 #include "components/offline_pages/core/offline_page_types.h"
 #include "components/offline_pages/core/page_criteria.h"
 #include "components/offline_pages/core/request_header/offline_page_header.h"
-#include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/filename_util.h"
 
@@ -213,67 +209,6 @@
   RunLoadUrlParamsCallbackAndroid(j_callback_obj, launch_url, offline_header);
 }
 
-ScopedJavaLocalRef<jobjectArray> JNI_SavePageRequest_CreateJavaSavePageRequests(
-    JNIEnv* env,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  return OfflinePageBridge::CreateJavaSavePageRequests(env,
-                                                       std::move(requests));
-}
-
-void OnGetAllRequestsDone(
-    const ScopedJavaGlobalRef<jobject>& j_callback_obj,
-    std::vector<std::unique_ptr<SavePageRequest>> all_requests) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-
-  ScopedJavaLocalRef<jobjectArray> j_result_obj =
-      JNI_SavePageRequest_CreateJavaSavePageRequests(env,
-                                                     std::move(all_requests));
-  base::android::RunObjectCallbackAndroid(j_callback_obj, j_result_obj);
-}
-
-UpdateRequestResult ToUpdateRequestResult(ItemActionStatus status) {
-  switch (status) {
-    case ItemActionStatus::SUCCESS:
-      return UpdateRequestResult::SUCCESS;
-    case ItemActionStatus::NOT_FOUND:
-      return UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
-    case ItemActionStatus::STORE_ERROR:
-      return UpdateRequestResult::STORE_FAILURE;
-    case ItemActionStatus::ALREADY_EXISTS:
-    default:
-      NOTREACHED();
-  }
-  return UpdateRequestResult::STORE_FAILURE;
-}
-
-void OnRemoveRequestsDone(const ScopedJavaGlobalRef<jobject>& j_callback_obj,
-                          const MultipleItemStatuses& removed_request_results) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-
-  std::vector<int> update_request_results;
-  std::vector<int64_t> update_request_ids;
-
-  for (std::pair<int64_t, ItemActionStatus> remove_result :
-       removed_request_results) {
-    update_request_ids.emplace_back(std::get<0>(remove_result));
-    update_request_results.emplace_back(
-        static_cast<int>(ToUpdateRequestResult(std::get<1>(remove_result))));
-  }
-
-  ScopedJavaLocalRef<jlongArray> j_result_ids =
-      base::android::ToJavaLongArray(env, update_request_ids);
-  ScopedJavaLocalRef<jintArray> j_result_codes =
-      base::android::ToJavaIntArray(env, update_request_results);
-
-  Java_RequestsRemovedCallback_onResult(env, j_callback_obj, j_result_ids,
-                                        j_result_codes);
-}
-
-void SavePageLaterCallback(const ScopedJavaGlobalRef<jobject>& j_callback_obj,
-                           AddRequestResult value) {
-  base::android::RunIntCallbackAndroid(j_callback_obj, static_cast<int>(value));
-}
-
 void PublishPageDone(
     const ScopedJavaGlobalRef<jobject>& j_published_callback_obj,
     const base::FilePath& file_path,
@@ -299,18 +234,19 @@
 }
 
 static ScopedJavaLocalRef<jobject>
-JNI_OfflinePageBridge_GetOfflinePageBridgeForProfile(
+JNI_OfflinePageBridge_GetOfflinePageBridgeForProfileKey(
     JNIEnv* env,
-    const JavaParamRef<jobject>& j_profile) {
-  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
+    const JavaParamRef<jobject>& j_profile_key) {
+  ProfileKey* profile_key =
+      ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key);
 
   // Return null if there is no reasonable context for the provided Java
   // profile.
-  if (profile == nullptr)
+  if (profile_key == nullptr)
     return ScopedJavaLocalRef<jobject>();
 
   OfflinePageModel* offline_page_model =
-      OfflinePageModelFactory::GetForBrowserContext(profile);
+      OfflinePageModelFactory::GetForKey(profile_key);
 
   // Return null if we cannot get an offline page model for provided profile.
   if (offline_page_model == nullptr)
@@ -319,7 +255,7 @@
   OfflinePageBridge* bridge = static_cast<OfflinePageBridge*>(
       offline_page_model->GetUserData(kOfflinePageBridgeKey));
   if (!bridge) {
-    bridge = new OfflinePageBridge(env, profile, offline_page_model);
+    bridge = new OfflinePageBridge(env, profile_key, offline_page_model);
     offline_page_model->SetUserData(kOfflinePageBridgeKey,
                                     base::WrapUnique(bridge));
   }
@@ -335,38 +271,6 @@
 }
 
 // static
-ScopedJavaLocalRef<jobjectArray> OfflinePageBridge::CreateJavaSavePageRequests(
-    JNIEnv* env,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  ScopedJavaLocalRef<jclass> save_page_request_clazz = base::android::GetClass(
-      env, "org/chromium/chrome/browser/offlinepages/SavePageRequest");
-  jobjectArray joa = env->NewObjectArray(
-      requests.size(), save_page_request_clazz.obj(), nullptr);
-  base::android::CheckException(env);
-
-  for (size_t i = 0; i < requests.size(); ++i) {
-    SavePageRequest request = *(requests[i]);
-    ScopedJavaLocalRef<jstring> name_space =
-        ConvertUTF8ToJavaString(env, request.client_id().name_space);
-    ScopedJavaLocalRef<jstring> id =
-        ConvertUTF8ToJavaString(env, request.client_id().id);
-    ScopedJavaLocalRef<jstring> url =
-        ConvertUTF8ToJavaString(env, request.url().spec());
-    ScopedJavaLocalRef<jstring> origin =
-        ConvertUTF8ToJavaString(env, request.request_origin());
-
-    ScopedJavaLocalRef<jobject> j_save_page_request =
-        Java_SavePageRequest_create(
-            env, static_cast<int>(request.request_state()),
-            request.request_id(), url, name_space, id, origin,
-            static_cast<int>(request.auto_fetch_notification_state()));
-    env->SetObjectArrayElement(joa, i, j_save_page_request.obj());
-  }
-
-  return ScopedJavaLocalRef<jobjectArray>(env, joa);
-}
-
-// static
 void OfflinePageBridge::AddOfflinePageItemsToJavaList(
     JNIEnv* env,
     const JavaRef<jobject>& j_result_obj,
@@ -399,9 +303,9 @@
 }
 
 OfflinePageBridge::OfflinePageBridge(JNIEnv* env,
-                                     content::BrowserContext* browser_context,
+                                     SimpleFactoryKey* key,
                                      OfflinePageModel* offline_page_model)
-    : browser_context_(browser_context),
+    : key_(key),
       offline_page_model_(offline_page_model),
       weak_ptr_factory_(this) {
   ScopedJavaLocalRef<jobject> j_offline_page_bridge =
@@ -612,8 +516,8 @@
   j_callback_ref.Reset(env, j_callback_obj);
 
   OfflinePageUtils::SelectPagesForURL(
-      browser_context_, GURL(ConvertJavaStringToUTF8(env, j_online_url)),
-      tab_id, base::BindOnce(&SelectPageCallback, j_callback_ref));
+      key_, GURL(ConvertJavaStringToUTF8(env, j_online_url)), tab_id,
+      base::BindOnce(&SelectPageCallback, j_callback_ref));
 }
 
 void OfflinePageBridge::SavePage(JNIEnv* env,
@@ -650,39 +554,6 @@
       base::Bind(&SavePageCallback, j_callback_ref, save_page_params.url));
 }
 
-void OfflinePageBridge::SavePageLater(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jobject>& j_callback_obj,
-    const JavaParamRef<jstring>& j_url,
-    const JavaParamRef<jstring>& j_namespace,
-    const JavaParamRef<jstring>& j_client_id,
-    const JavaParamRef<jstring>& j_origin,
-    jboolean user_requested) {
-  DCHECK(j_callback_obj);
-  ScopedJavaGlobalRef<jobject> j_callback_ref;
-  j_callback_ref.Reset(env, j_callback_obj);
-
-  offline_pages::ClientId client_id;
-  client_id.name_space = ConvertJavaStringToUTF8(env, j_namespace);
-  client_id.id = ConvertJavaStringToUTF8(env, j_client_id);
-
-  RequestCoordinator* coordinator =
-      offline_pages::RequestCoordinatorFactory::GetInstance()->
-          GetForBrowserContext(browser_context_);
-
-  RequestCoordinator::SavePageLaterParams params;
-  params.url = GURL(ConvertJavaStringToUTF8(env, j_url));
-  params.client_id = client_id;
-  params.user_requested = static_cast<bool>(user_requested);
-  params.availability =
-      RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER;
-  params.request_origin = ConvertJavaStringToUTF8(env, j_origin);
-
-  coordinator->SavePageLater(
-      params, base::BindOnce(&SavePageLaterCallback, j_callback_ref));
-}
-
 void OfflinePageBridge::PublishInternalPageByOfflineId(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
@@ -692,7 +563,7 @@
   j_published_callback_ref.Reset(env, j_published_callback);
 
   OfflinePageModel* offline_page_model =
-      OfflinePageModelFactory::GetForBrowserContext(browser_context_);
+      OfflinePageModelFactory::GetForKey(key_);
   DCHECK(offline_page_model);
 
   offline_page_model->GetPageByOfflineId(
@@ -711,7 +582,7 @@
   j_published_callback_ref.Reset(env, j_published_callback);
 
   OfflinePageModel* offline_page_model =
-      OfflinePageModelFactory::GetForBrowserContext(browser_context_);
+      OfflinePageModelFactory::GetForKey(key_);
   DCHECK(offline_page_model);
   PageCriteria criteria;
   criteria.guid = ConvertJavaStringToUTF8(env, j_guid);
@@ -748,7 +619,7 @@
     return;
   }
   OfflinePageModel* offline_page_model =
-      OfflinePageModelFactory::GetForBrowserContext(browser_context_);
+      OfflinePageModelFactory::GetForKey(key_);
   DCHECK(offline_page_model);
 
   // If it has already been published, bail out.
@@ -814,52 +685,6 @@
       web_contents);
 }
 
-void OfflinePageBridge::GetRequestsInQueue(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jobject>& j_callback_obj) {
-  ScopedJavaGlobalRef<jobject> j_callback_ref(j_callback_obj);
-
-  RequestCoordinator* coordinator =
-      offline_pages::RequestCoordinatorFactory::GetInstance()
-          ->GetForBrowserContext(browser_context_);
-
-  if (!coordinator) {
-    // Callback with null to signal that results are unavailable.
-    const JavaParamRef<jobject> empty_result(nullptr);
-    base::android::RunObjectCallbackAndroid(j_callback_obj, empty_result);
-    return;
-  }
-
-  coordinator->GetAllRequests(
-      base::BindOnce(&OnGetAllRequestsDone, j_callback_ref));
-}
-
-void OfflinePageBridge::RemoveRequestsFromQueue(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jlongArray>& j_request_ids_array,
-    const JavaParamRef<jobject>& j_callback_obj) {
-  std::vector<int64_t> request_ids;
-  base::android::JavaLongArrayToInt64Vector(env, j_request_ids_array,
-                                            &request_ids);
-  ScopedJavaGlobalRef<jobject> j_callback_ref(j_callback_obj);
-
-  RequestCoordinator* coordinator =
-      offline_pages::RequestCoordinatorFactory::GetInstance()
-          ->GetForBrowserContext(browser_context_);
-
-  if (!coordinator) {
-    // Callback with null to signal that results are unavailable.
-    const JavaParamRef<jobject> empty_result(nullptr);
-    base::android::RunObjectCallbackAndroid(j_callback_obj, empty_result);
-    return;
-  }
-
-  coordinator->RemoveRequests(
-      request_ids, base::BindOnce(&OnRemoveRequestsDone, j_callback_ref));
-}
-
 void OfflinePageBridge::WillCloseTab(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.h b/chrome/browser/offline_pages/android/offline_page_bridge.h
index 17ce918..629fea4 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.h
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.h
@@ -22,8 +22,9 @@
 #include "components/offline_pages/core/offline_page_item.h"
 #include "components/offline_pages/core/offline_page_model.h"
 
+class SimpleFactoryKey;
+
 namespace content {
-class BrowserContext;
 class WebContents;
 }
 
@@ -44,11 +45,6 @@
       JNIEnv* env,
       const OfflinePageItem& offline_page);
 
-  static base::android::ScopedJavaLocalRef<jobjectArray>
-  CreateJavaSavePageRequests(
-      JNIEnv* env,
-      std::vector<std::unique_ptr<SavePageRequest>> requests);
-
   static void AddOfflinePageItemsToJavaList(
       JNIEnv* env,
       const base::android::JavaRef<jobject>& j_result_obj,
@@ -58,7 +54,7 @@
       const content::WebContents* web_contents);
 
   OfflinePageBridge(JNIEnv* env,
-                    content::BrowserContext* browser_context,
+                    SimpleFactoryKey* key,
                     OfflinePageModel* offline_page_model);
   ~OfflinePageBridge() override;
 
@@ -137,15 +133,6 @@
                 const base::android::JavaParamRef<jstring>& j_client_id,
                 const base::android::JavaParamRef<jstring>& j_origin);
 
-  void SavePageLater(JNIEnv* env,
-                     const base::android::JavaParamRef<jobject>& obj,
-                     const base::android::JavaParamRef<jobject>& j_callback_obj,
-                     const base::android::JavaParamRef<jstring>& url,
-                     const base::android::JavaParamRef<jstring>& j_namespace,
-                     const base::android::JavaParamRef<jstring>& j_client_id,
-                     const base::android::JavaParamRef<jstring>& j_origin,
-                     jboolean user_requested);
-
   void PublishInternalPageByOfflineId(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
@@ -173,17 +160,6 @@
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jobject>& j_web_contents);
 
-  void GetRequestsInQueue(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jobject>& j_callback_obj);
-
-  void RemoveRequestsFromQueue(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jlongArray>& j_request_ids_array,
-      const base::android::JavaParamRef<jobject>& j_callback_obj);
-
   void WillCloseTab(JNIEnv* env,
                     const base::android::JavaParamRef<jobject>& obj,
                     const base::android::JavaParamRef<jobject>& j_web_contents);
@@ -282,7 +258,7 @@
 
   base::android::ScopedJavaGlobalRef<jobject> java_ref_;
   // Not owned.
-  content::BrowserContext* browser_context_;
+  SimpleFactoryKey* key_;
   // Not owned.
   OfflinePageModel* offline_page_model_;
 
diff --git a/chrome/browser/offline_pages/android/offline_test_util_jni.cc b/chrome/browser/offline_pages/android/offline_test_util_jni.cc
index 3ff4dc2e..dafb977e 100644
--- a/chrome/browser/offline_pages/android/offline_test_util_jni.cc
+++ b/chrome/browser/offline_pages/android/offline_test_util_jni.cc
@@ -15,6 +15,7 @@
 #include "chrome/android/test_support_jni_headers/OfflineTestUtil_jni.h"
 #include "chrome/browser/android/profile_key_util.h"
 #include "chrome/browser/offline_pages/android/offline_page_bridge.h"
+#include "chrome/browser/offline_pages/android/request_coordinator_bridge.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h"
 #include "chrome/browser/offline_pages/request_coordinator_factory.h"
@@ -53,9 +54,8 @@
     std::vector<std::unique_ptr<SavePageRequest>> all_requests) {
   JNIEnv* env = base::android::AttachCurrentThread();
   base::android::RunObjectCallbackAndroid(
-      j_callback_obj,
-      offline_pages::android::OfflinePageBridge::CreateJavaSavePageRequests(
-          env, std::move(all_requests)));
+      j_callback_obj, offline_pages::android::CreateJavaSavePageRequests(
+                          env, std::move(all_requests)));
 }
 
 void OnGetAllPagesDone(
diff --git a/chrome/browser/offline_pages/android/request_coordinator_bridge.cc b/chrome/browser/offline_pages/android/request_coordinator_bridge.cc
new file mode 100644
index 0000000..bbe6699
--- /dev/null
+++ b/chrome/browser/offline_pages/android/request_coordinator_bridge.cc
@@ -0,0 +1,205 @@
+// Copyright 2019 The Chromium 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/offline_pages/android/request_coordinator_bridge.h"
+
+#include <utility>
+
+#include "base/android/callback_android.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "chrome/android/chrome_jni_headers/RequestCoordinatorBridge_jni.h"
+#include "chrome/android/chrome_jni_headers/SavePageRequest_jni.h"
+#include "chrome/browser/offline_pages/request_coordinator_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaParamRef;
+using base::android::JavaRef;
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace offline_pages {
+namespace android {
+
+namespace {
+
+void OnGetAllRequestsDone(
+    const ScopedJavaGlobalRef<jobject>& j_callback_obj,
+    std::vector<std::unique_ptr<SavePageRequest>> all_requests) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  ScopedJavaLocalRef<jobjectArray> j_result_obj =
+      CreateJavaSavePageRequests(env, std::move(all_requests));
+  base::android::RunObjectCallbackAndroid(j_callback_obj, j_result_obj);
+}
+
+UpdateRequestResult ToUpdateRequestResult(ItemActionStatus status) {
+  switch (status) {
+    case ItemActionStatus::SUCCESS:
+      return UpdateRequestResult::SUCCESS;
+    case ItemActionStatus::NOT_FOUND:
+      return UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
+    case ItemActionStatus::STORE_ERROR:
+      return UpdateRequestResult::STORE_FAILURE;
+    case ItemActionStatus::ALREADY_EXISTS:
+      NOTREACHED();
+  }
+  return UpdateRequestResult::STORE_FAILURE;
+}
+
+void OnRemoveRequestsDone(const ScopedJavaGlobalRef<jobject>& j_callback_obj,
+                          const MultipleItemStatuses& removed_request_results) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  std::vector<int> update_request_results;
+  std::vector<int64_t> update_request_ids;
+
+  for (const std::pair<int64_t, ItemActionStatus>& remove_result :
+       removed_request_results) {
+    update_request_ids.emplace_back(std::get<0>(remove_result));
+    update_request_results.emplace_back(
+        static_cast<int>(ToUpdateRequestResult(std::get<1>(remove_result))));
+  }
+
+  ScopedJavaLocalRef<jlongArray> j_result_ids =
+      base::android::ToJavaLongArray(env, update_request_ids);
+  ScopedJavaLocalRef<jintArray> j_result_codes =
+      base::android::ToJavaIntArray(env, update_request_results);
+
+  Java_RequestsRemovedCallback_onResult(env, j_callback_obj, j_result_ids,
+                                        j_result_codes);
+}
+
+void SavePageLaterCallback(const ScopedJavaGlobalRef<jobject>& j_callback_obj,
+                           AddRequestResult value) {
+  base::android::RunIntCallbackAndroid(j_callback_obj, static_cast<int>(value));
+}
+
+RequestCoordinator* GetRequestCoordinator(
+    const JavaParamRef<jobject>& j_profile) {
+  content::BrowserContext* context =
+      ProfileAndroid::FromProfileAndroid(j_profile);
+  return offline_pages::RequestCoordinatorFactory::GetInstance()
+      ->GetForBrowserContext(context);
+}
+
+}  // namespace
+
+ScopedJavaLocalRef<jobjectArray> CreateJavaSavePageRequests(
+    JNIEnv* env,
+    const std::vector<std::unique_ptr<SavePageRequest>>& requests) {
+  ScopedJavaLocalRef<jclass> save_page_request_clazz = base::android::GetClass(
+      env, "org/chromium/chrome/browser/offlinepages/SavePageRequest");
+  jobjectArray joa = env->NewObjectArray(
+      requests.size(), save_page_request_clazz.obj(), nullptr);
+  base::android::CheckException(env);
+
+  for (size_t i = 0; i < requests.size(); ++i) {
+    const SavePageRequest& request = *(requests[i]);
+    ScopedJavaLocalRef<jstring> name_space =
+        ConvertUTF8ToJavaString(env, request.client_id().name_space);
+    ScopedJavaLocalRef<jstring> id =
+        ConvertUTF8ToJavaString(env, request.client_id().id);
+    ScopedJavaLocalRef<jstring> url =
+        ConvertUTF8ToJavaString(env, request.url().spec());
+    ScopedJavaLocalRef<jstring> origin =
+        ConvertUTF8ToJavaString(env, request.request_origin());
+
+    ScopedJavaLocalRef<jobject> j_save_page_request =
+        Java_SavePageRequest_create(
+            env, static_cast<int>(request.request_state()),
+            request.request_id(), url, name_space, id, origin,
+            static_cast<int>(request.auto_fetch_notification_state()));
+    env->SetObjectArrayElement(joa, i, j_save_page_request.obj());
+  }
+
+  return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
+JNI_EXPORT void JNI_RequestCoordinatorBridge_SavePageLater(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_profile,
+    const JavaParamRef<jobject>& j_callback_obj,
+    const JavaParamRef<jstring>& j_url,
+    const JavaParamRef<jstring>& j_namespace,
+    const JavaParamRef<jstring>& j_client_id,
+    const JavaParamRef<jstring>& j_origin,
+    jboolean user_requested) {
+  DCHECK(j_callback_obj);
+  ScopedJavaGlobalRef<jobject> j_callback_ref;
+  j_callback_ref.Reset(env, j_callback_obj);
+
+  offline_pages::ClientId client_id;
+  client_id.name_space = ConvertJavaStringToUTF8(env, j_namespace);
+  client_id.id = ConvertJavaStringToUTF8(env, j_client_id);
+
+  RequestCoordinator* coordinator = GetRequestCoordinator(j_profile);
+
+  if (!coordinator) {
+    // Callback with null to signal that results are unavailable.
+    base::android::RunObjectCallbackAndroid(j_callback_obj, JavaRef<jobject>());
+    return;
+  }
+
+  RequestCoordinator::SavePageLaterParams params;
+  params.url = GURL(ConvertJavaStringToUTF8(env, j_url));
+  params.client_id = client_id;
+  params.user_requested = static_cast<bool>(user_requested);
+  params.availability =
+      RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER;
+  params.request_origin = ConvertJavaStringToUTF8(env, j_origin);
+
+  coordinator->SavePageLater(
+      params, base::BindOnce(&SavePageLaterCallback, j_callback_ref));
+}
+
+JNI_EXPORT void JNI_RequestCoordinatorBridge_GetRequestsInQueue(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_profile,
+    const JavaParamRef<jobject>& j_callback_obj) {
+  ScopedJavaGlobalRef<jobject> j_callback_ref(j_callback_obj);
+
+  RequestCoordinator* coordinator = GetRequestCoordinator(j_profile);
+
+  if (!coordinator) {
+    // Callback with null to signal that results are unavailable.
+    const JavaParamRef<jobject> empty_result(nullptr);
+    base::android::RunObjectCallbackAndroid(j_callback_obj, empty_result);
+    return;
+  }
+
+  coordinator->GetAllRequests(
+      base::BindOnce(&OnGetAllRequestsDone, j_callback_ref));
+}
+
+JNI_EXPORT void JNI_RequestCoordinatorBridge_RemoveRequestsFromQueue(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_profile,
+    const JavaParamRef<jlongArray>& j_request_ids_array,
+    const JavaParamRef<jobject>& j_callback_obj) {
+  std::vector<int64_t> request_ids;
+  base::android::JavaLongArrayToInt64Vector(env, j_request_ids_array,
+                                            &request_ids);
+  ScopedJavaGlobalRef<jobject> j_callback_ref(j_callback_obj);
+
+  RequestCoordinator* coordinator = GetRequestCoordinator(j_profile);
+
+  if (!coordinator) {
+    // Callback with null to signal that results are unavailable.
+    const JavaParamRef<jobject> empty_result(nullptr);
+    base::android::RunObjectCallbackAndroid(j_callback_obj, empty_result);
+    return;
+  }
+
+  coordinator->RemoveRequests(
+      request_ids, base::BindOnce(&OnRemoveRequestsDone, j_callback_ref));
+}
+
+}  // namespace android
+}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/android/request_coordinator_bridge.h b/chrome/browser/offline_pages/android/request_coordinator_bridge.h
new file mode 100644
index 0000000..1d1ce31
--- /dev/null
+++ b/chrome/browser/offline_pages/android/request_coordinator_bridge.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_ANDROID_REQUEST_COORDINATOR_BRIDGE_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_REQUEST_COORDINATOR_BRIDGE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+
+namespace offline_pages {
+
+class SavePageRequest;
+
+namespace android {
+
+base::android::ScopedJavaLocalRef<jobjectArray> CreateJavaSavePageRequests(
+    JNIEnv* env,
+    const std::vector<std::unique_ptr<SavePageRequest>>& requests);
+
+}  // namespace android
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_REQUEST_COORDINATOR_BRIDGE_H_
diff --git a/chrome/browser/offline_pages/offline_page_request_handler.cc b/chrome/browser/offline_pages/offline_page_request_handler.cc
index 3b0eee4..cc609999 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler.cc
@@ -32,6 +32,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/file_stream.h"
 #include "net/base/filename_util.h"
@@ -77,6 +78,12 @@
 // Consistent with the buffer size used in url request data reading.
 const size_t kMaxBufferSizeForValidation = 4096;
 
+content::BrowserThread::ID GetJobThreadID() {
+  return base::FeatureList::IsEnabled(features::kNavigationLoaderOnUI)
+             ? content::BrowserThread::UI
+             : content::BrowserThread::IO;
+}
+
 void GetFileSize(const base::FilePath& file_path, int64_t* file_size) {
   bool succeeded = base::GetFileSize(file_path, file_size);
   if (!succeeded) {
@@ -285,10 +292,10 @@
                       : nullptr;
 }
 
-void NotifyAvailableOfflinePagesOnIO(
+void NotifyAvailableOfflinePagesOnJobThread(
     base::WeakPtr<OfflinePageRequestHandler> job,
     const std::vector<OfflinePageRequestHandler::Candidate>& candidates) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(GetJobThreadID());
 
   if (job)
     job->OnOfflinePagesAvailable(candidates);
@@ -300,11 +307,16 @@
     const std::vector<OfflinePageRequestHandler::Candidate>& candidates) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // Delegates to IO thread since OfflinePageRequestHandler should only be
-  // accessed from IO thread.
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&NotifyAvailableOfflinePagesOnIO, job, candidates));
+  if (base::FeatureList::IsEnabled(features::kNavigationLoaderOnUI)) {
+    NotifyAvailableOfflinePagesOnJobThread(job, candidates);
+  } else {
+    // Delegates to IO thread since OfflinePageRequestHandler should only be
+    // accessed from IO thread.
+    base::PostTaskWithTraits(
+        FROM_HERE, {content::BrowserThread::IO},
+        base::BindOnce(&NotifyAvailableOfflinePagesOnJobThread, job,
+                       candidates));
+  }
 }
 
 // Failed to find an offline page.
@@ -401,14 +413,17 @@
     return;
   }
 
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  SimpleFactoryKey* key = profile->GetProfileKey();
+
   // If an int64 offline ID is present in the offline header, try to load that
   // particular version.
   if (!offline_header.id.empty()) {
     int64_t offline_id;
     if (base::StringToInt64(offline_header.id, &offline_id)) {
       OfflinePageModel* offline_page_model =
-          OfflinePageModelFactory::GetForBrowserContext(
-              web_contents->GetBrowserContext());
+          OfflinePageModelFactory::GetForKey(key);
       if (!offline_page_model) {
         FailedToFindOfflinePage(RequestResult::OFFLINE_PAGE_NOT_FOUND,
                                 network_state, job);
@@ -422,7 +437,7 @@
   }
 
   OfflinePageUtils::SelectPagesForURL(
-      web_contents->GetBrowserContext(), url, tab_id,
+      key, url, tab_id,
       base::BindOnce(&SelectPagesForURLDone, url, offline_header, network_state,
                      job, web_contents_getter));
 }
@@ -504,6 +519,7 @@
       candidate_index_(0),
       has_range_header_(false),
       weak_ptr_factory_(this) {
+  DCHECK_CURRENTLY_ON(GetJobThreadID());
   std::string offline_header_value;
   extra_request_headers.GetHeader(kOfflinePageHeader, &offline_header_value);
   // Note that |offline_header| will be empty if parsing from the header value
@@ -518,7 +534,7 @@
 
 OfflinePageRequestHandler::NetworkState
 OfflinePageRequestHandler::GetNetworkState() const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(GetJobThreadID());
 
   if (offline_header_.reason == OfflinePageHeader::Reason::NET_ERROR)
     return OfflinePageRequestHandler::NetworkState::FLAKY_NETWORK;
@@ -561,12 +577,19 @@
     return;
   }
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&GetPagesToServeURL, url_, offline_header_, network_state_,
-                     delegate_->GetWebContentsGetter(),
-                     delegate_->GetTabIdGetter(),
-                     weak_ptr_factory_.GetWeakPtr()));
+  if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
+    GetPagesToServeURL(url_, offline_header_, network_state_,
+                       delegate_->GetWebContentsGetter(),
+                       delegate_->GetTabIdGetter(),
+                       weak_ptr_factory_.GetWeakPtr());
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {content::BrowserThread::UI},
+        base::BindOnce(&GetPagesToServeURL, url_, offline_header_,
+                       network_state_, delegate_->GetWebContentsGetter(),
+                       delegate_->GetTabIdGetter(),
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
 }
 
 void OfflinePageRequestHandler::Kill() {
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 a690241..da092c2 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/offline_pages/offline_page_request_job.h"
-
 #include <memory>
 #include <string>
 #include <utility>
@@ -24,7 +22,6 @@
 #include "base/time/default_clock.h"
 #include "build/build_config.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
-#include "chrome/browser/offline_pages/offline_page_request_interceptor.h"
 #include "chrome/browser/offline_pages/offline_page_tab_helper.h"
 #include "chrome/browser/offline_pages/offline_page_url_loader.h"
 #include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
diff --git a/chrome/browser/offline_pages/offline_page_request_interceptor.cc b/chrome/browser/offline_pages/offline_page_request_interceptor.cc
deleted file mode 100644
index 1f66c09..0000000
--- a/chrome/browser/offline_pages/offline_page_request_interceptor.cc
+++ /dev/null
@@ -1,25 +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/offline_pages/offline_page_request_interceptor.h"
-
-#include "base/supports_user_data.h"
-#include "chrome/browser/offline_pages/offline_page_request_job.h"
-
-namespace offline_pages {
-
-OfflinePageRequestInterceptor::OfflinePageRequestInterceptor() = default;
-
-OfflinePageRequestInterceptor::~OfflinePageRequestInterceptor() = default;
-
-net::URLRequestJob* OfflinePageRequestInterceptor::MaybeInterceptRequest(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate) const {
-  // OfflinePageRequestJob::Create may return a nullptr if the interception
-  // is not needed for some sort of requests, like non-main resource request,
-  // non-http request and more.
-  return OfflinePageRequestJob::Create(request, network_delegate);
-}
-
-}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/offline_page_request_interceptor.h b/chrome/browser/offline_pages/offline_page_request_interceptor.h
deleted file mode 100644
index d91b2d0..0000000
--- a/chrome/browser/offline_pages/offline_page_request_interceptor.h
+++ /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.
-
-#ifndef CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_REQUEST_INTERCEPTOR_H_
-#define CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_REQUEST_INTERCEPTOR_H_
-
-#include "base/macros.h"
-#include "net/url_request/url_request_interceptor.h"
-
-namespace net {
-class NetworkDelegate;
-class URLRequest;
-class URLRequestJob;
-}
-
-namespace offline_pages {
-
-// An interceptor to hijack requests and potentially service them based on
-// their offline information. Created one per profile.
-class OfflinePageRequestInterceptor : public net::URLRequestInterceptor {
- public:
-  // Embedder must guarantee that |previews_decider| outlives |this|.
-  OfflinePageRequestInterceptor();
-  ~OfflinePageRequestInterceptor() override;
-
- private:
-  // Overrides from net::URLRequestInterceptor:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageRequestInterceptor);
-};
-
-}  // namespace offline_pages
-
-#endif // CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_REQUEST_INTERCEPTOR_H_
diff --git a/chrome/browser/offline_pages/offline_page_request_job.cc b/chrome/browser/offline_pages/offline_page_request_job.cc
deleted file mode 100644
index 4c3f168..0000000
--- a/chrome/browser/offline_pages/offline_page_request_job.cc
+++ /dev/null
@@ -1,235 +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 <string>
-#include <utility>
-
-#include "chrome/browser/offline_pages/offline_page_request_job.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/strings/stringprintf.h"
-#include "base/supports_user_data.h"
-#include "base/time/time.h"
-#include "chrome/browser/offline_pages/offline_page_utils.h"
-#include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
-#include "components/offline_pages/core/offline_clock.h"
-#include "content/public/browser/resource_request_info.h"
-#include "content/public/common/resource_type.h"
-#include "net/url_request/url_request.h"
-
-namespace offline_pages {
-
-namespace {
-
-const char kUserDataKey[] = "offline_page_key";
-
-// Contains the info to handle offline page request.
-class OfflinePageRequestInfo : public base::SupportsUserData::Data {
- public:
-  OfflinePageRequestInfo() : use_default_(false) {}
-  ~OfflinePageRequestInfo() override {}
-
-  static OfflinePageRequestInfo* GetFromRequest(
-      const net::URLRequest* request) {
-    return static_cast<OfflinePageRequestInfo*>(
-        request->GetUserData(&kUserDataKey));
-  }
-
-  bool use_default() const { return use_default_; }
-  void set_use_default(bool use_default) { use_default_ = use_default; }
-
- private:
-  // True if the next time this request is started, the request should be
-  // serviced from the default handler.
-  bool use_default_;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageRequestInfo);
-};
-
-}  // namespace
-
-// static
-OfflinePageRequestJob* OfflinePageRequestJob::Create(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate) {
-  content::ResourceRequestInfo* resource_request_info =
-      content::ResourceRequestInfo::ForRequest(request);
-  if (!resource_request_info)
-    return nullptr;
-
-  // Ignore the requests not for the main resource.
-  if (resource_request_info->GetResourceType() !=
-      content::ResourceType::kMainFrame) {
-    return nullptr;
-  }
-
-  // Ignore non-http/https requests.
-  if (!request->url().SchemeIsHTTPOrHTTPS())
-    return nullptr;
-
-  // Ignore requests other than GET.
-  if (request->method() != "GET")
-    return nullptr;
-
-  OfflinePageRequestInfo* info =
-      OfflinePageRequestInfo::GetFromRequest(request);
-  if (info) {
-    // Fall back to default which is set when an offline page cannot be served,
-    // either page not found or online version desired.
-    if (info->use_default())
-      return nullptr;
-  } else {
-    request->SetUserData(&kUserDataKey,
-                         std::make_unique<OfflinePageRequestInfo>());
-  }
-
-  return new OfflinePageRequestJob(request, network_delegate);
-}
-
-OfflinePageRequestJob::OfflinePageRequestJob(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate)
-    : net::URLRequestJob(request, network_delegate) {}
-
-OfflinePageRequestJob::~OfflinePageRequestJob() {}
-
-void OfflinePageRequestJob::SetWebContentsGetterForTesting(
-    OfflinePageRequestHandler::Delegate::WebContentsGetter
-        web_contents_getter) {
-  web_contents_getter_ = web_contents_getter;
-}
-
-void OfflinePageRequestJob::SetTabIdGetterForTesting(
-    OfflinePageRequestHandler::Delegate::TabIdGetter tab_id_getter) {
-  tab_id_getter_ = tab_id_getter;
-}
-
-void OfflinePageRequestJob::Start() {
-  request_handler_ = std::make_unique<OfflinePageRequestHandler>(
-      request()->url(), request()->extra_request_headers(), this);
-  request_handler_->Start();
-}
-
-void OfflinePageRequestJob::Kill() {
-  request_handler_->Kill();
-  net::URLRequestJob::Kill();
-}
-
-int OfflinePageRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
-  return request_handler_->ReadRawData(dest, dest_size);
-}
-
-void OfflinePageRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
-  scoped_refptr<net::HttpResponseHeaders> redirect_headers =
-      request_handler_->GetRedirectHeaders();
-  if (!redirect_headers) {
-    net::URLRequestJob::GetResponseInfo(info);
-    return;
-  }
-
-  info->headers = redirect_headers;
-  info->request_time = OfflineTimeNow();
-  info->response_time = info->request_time;
-}
-
-void OfflinePageRequestJob::GetLoadTimingInfo(
-    net::LoadTimingInfo* load_timing_info) const {
-  // Set send_start and send_end to receive_redirect_headers_end_ to be
-  // consistent with network cache behavior.
-  load_timing_info->send_start = base::TimeTicks::Now();
-  load_timing_info->send_end = load_timing_info->send_start;
-  load_timing_info->receive_headers_end = load_timing_info->send_start;
-}
-
-bool OfflinePageRequestJob::CopyFragmentOnRedirect(const GURL& location) const {
-  return false;
-}
-
-bool OfflinePageRequestJob::GetMimeType(std::string* mime_type) const {
-  if (request_handler_->IsServingOfflinePage() &&
-      request()->status().is_success()) {
-    *mime_type = "multipart/related";
-    return true;
-  }
-  return false;
-}
-
-void OfflinePageRequestJob::SetExtraRequestHeaders(
-    const net::HttpRequestHeaders& headers) {
-}
-
-void OfflinePageRequestJob::FallbackToDefault() {
-  OfflinePageRequestInfo* info =
-      OfflinePageRequestInfo::GetFromRequest(request());
-  DCHECK(info);
-  info->set_use_default(true);
-
-  net::URLRequestJob::NotifyRestartRequired();
-}
-
-void OfflinePageRequestJob::NotifyStartError(int error) {
-  net::URLRequestJob::NotifyStartError(
-      net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
-}
-
-void OfflinePageRequestJob::NotifyHeadersComplete(int64_t file_size) {
-  set_expected_content_size(file_size);
-  net::URLRequestJob::NotifyHeadersComplete();
-}
-
-void OfflinePageRequestJob::NotifyReadRawDataComplete(int bytes_read) {
-  net::URLRequestJob::ReadRawDataComplete(bytes_read);
-}
-
-void OfflinePageRequestJob::SetOfflinePageNavigationUIData(
-    bool is_offline_page) {
-  // This method should be called before the response data is received.
-  DCHECK(!has_response_started());
-
-  content::ResourceRequestInfo* info =
-      content::ResourceRequestInfo::ForRequest(request());
-  ChromeNavigationUIData* navigation_data =
-      static_cast<ChromeNavigationUIData*>(info->GetNavigationUIData());
-  if (navigation_data) {
-    std::unique_ptr<OfflinePageNavigationUIData> offline_page_data =
-        std::make_unique<OfflinePageNavigationUIData>(is_offline_page);
-    navigation_data->SetOfflinePageNavigationUIData(
-        std::move(offline_page_data));
-  }
-}
-
-bool OfflinePageRequestJob::ShouldAllowPreview() const {
-  content::ResourceRequestInfo* info =
-      content::ResourceRequestInfo::ForRequest(request());
-
-  bool preview_allowed =
-      info && (info->GetPreviewsState() & content::OFFLINE_PAGE_ON);
-  return preview_allowed;
-}
-
-int OfflinePageRequestJob::GetPageTransition() const {
-  content::ResourceRequestInfo* info =
-      content::ResourceRequestInfo::ForRequest(request());
-  return info ? static_cast<int>(info->GetPageTransition()) : 0;
-}
-
-OfflinePageRequestHandler::Delegate::WebContentsGetter
-OfflinePageRequestJob::GetWebContentsGetter() const {
-  if (!web_contents_getter_.is_null())
-    return web_contents_getter_;
-  content::ResourceRequestInfo* info =
-      content::ResourceRequestInfo::ForRequest(request());
-  return info ? info->GetWebContentsGetterForRequest()
-              : OfflinePageRequestHandler::Delegate::WebContentsGetter();
-}
-
-OfflinePageRequestHandler::Delegate::TabIdGetter
-OfflinePageRequestJob::GetTabIdGetter() const {
-  if (!tab_id_getter_.is_null())
-    return tab_id_getter_;
-  return base::Bind(&OfflinePageUtils::GetTabId);
-}
-
-}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/offline_page_request_job.h b/chrome/browser/offline_pages/offline_page_request_job.h
deleted file mode 100644
index dcb46ba..0000000
--- a/chrome/browser/offline_pages/offline_page_request_job.h
+++ /dev/null
@@ -1,74 +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_OFFLINE_PAGES_OFFLINE_PAGE_REQUEST_JOB_H_
-#define CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_REQUEST_JOB_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/scoped_refptr.h"
-#include "chrome/browser/offline_pages/offline_page_request_handler.h"
-#include "net/url_request/url_request_job.h"
-
-namespace offline_pages {
-
-class OfflinePageRequestHandler;
-
-// A request job that serves offline contents without network service enabled.
-class OfflinePageRequestJob : public net::URLRequestJob,
-                              public OfflinePageRequestHandler::Delegate {
- public:
-  // Creates and returns a job to serve the offline page. Nullptr is returned if
-  // offline page cannot or should not be served. Embedder must gaurantee that
-  // |previews_decider| outlives the returned instance.
-  static OfflinePageRequestJob* Create(net::URLRequest* request,
-                                       net::NetworkDelegate* network_delegate);
-
-  ~OfflinePageRequestJob() override;
-
-  void SetWebContentsGetterForTesting(
-      OfflinePageRequestHandler::Delegate::WebContentsGetter
-          web_contents_getter);
-  void SetTabIdGetterForTesting(
-      OfflinePageRequestHandler::Delegate::TabIdGetter tab_id_getter);
-
- private:
-  OfflinePageRequestJob(net::URLRequest* request,
-                        net::NetworkDelegate* network_delegate);
-
-  // net::URLRequestJob overrides:
-  void Start() override;
-  void Kill() override;
-  int ReadRawData(net::IOBuffer* dest, int dest_size) override;
-  void GetResponseInfo(net::HttpResponseInfo* info) override;
-  void GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override;
-  bool CopyFragmentOnRedirect(const GURL& location) const override;
-  bool GetMimeType(std::string* mime_type) const override;
-  void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers) override;
-
-  // OfflinePageRequestHandler::Delegate overrides:
-  void FallbackToDefault() override;
-  void NotifyStartError(int error) override;
-  void NotifyHeadersComplete(int64_t file_size) override;
-  void NotifyReadRawDataComplete(int bytes_read) override;
-  void SetOfflinePageNavigationUIData(bool is_offline_page) override;
-  bool ShouldAllowPreview() const override;
-  int GetPageTransition() const override;
-  OfflinePageRequestHandler::Delegate::WebContentsGetter GetWebContentsGetter()
-      const override;
-  OfflinePageRequestHandler::Delegate::TabIdGetter GetTabIdGetter()
-      const override;
-
-  std::unique_ptr<OfflinePageRequestHandler> request_handler_;
-
-  OfflinePageRequestHandler::Delegate::WebContentsGetter web_contents_getter_;
-  OfflinePageRequestHandler::Delegate::TabIdGetter tab_id_getter_;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageRequestJob);
-};
-
-}  // namespace offline_pages
-
-#endif  // CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_REQUEST_JOB_H_
diff --git a/chrome/browser/offline_pages/offline_page_tab_helper.cc b/chrome/browser/offline_pages/offline_page_tab_helper.cc
index 77d12cb..9cd8e2a7 100644
--- a/chrome/browser/offline_pages/offline_page_tab_helper.cc
+++ b/chrome/browser/offline_pages/offline_page_tab_helper.cc
@@ -322,12 +322,14 @@
     return;
   }
 
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   PageCriteria criteria;
   criteria.url = navigation_handle->GetURL();
   criteria.pages_for_tab_id = tab_id;
   criteria.maximum_matches = 1;
   OfflinePageUtils::SelectPagesWithCriteria(
-      web_contents()->GetBrowserContext(), criteria,
+      profile->GetProfileKey(), criteria,
       base::BindOnce(&OfflinePageTabHelper::SelectPagesForURLDone,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/offline_pages/offline_page_url_loader.cc b/chrome/browser/offline_pages/offline_page_url_loader.cc
index 78041c67..029bcb010 100644
--- a/chrome/browser/offline_pages/offline_page_url_loader.cc
+++ b/chrome/browser/offline_pages/offline_page_url_loader.cc
@@ -96,8 +96,6 @@
       is_offline_preview_allowed_(tentative_resource_request.previews_state &
                                   content::OFFLINE_PAGE_ON),
       weak_ptr_factory_(this) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
   // TODO(crbug.com/876527): Figure out how offline page interception should
   // interact with URLLoaderThrottles. It might be incorrect to use
   // |tentative_resource_request.headers| here, since throttles can rewrite
diff --git a/chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.cc b/chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.cc
index f1e4e851..f9d8860 100644
--- a/chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.cc
+++ b/chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.cc
@@ -22,10 +22,9 @@
 
 void OfflinePageURLLoaderRequestInterceptor::MaybeCreateLoader(
     const network::ResourceRequest& tentative_resource_request,
+    content::BrowserContext* browser_context,
     content::ResourceContext* resource_context,
     content::URLLoaderRequestInterceptor::LoaderCallback callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
   url_loader_ = OfflinePageURLLoader::Create(
       navigation_ui_data_, frame_tree_node_id_, tentative_resource_request,
       base::BindOnce(&OfflinePageURLLoaderRequestInterceptor::OnRequestHandled,
diff --git a/chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.h b/chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.h
index 6908469..1651d0d 100644
--- a/chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.h
+++ b/chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.h
@@ -25,6 +25,7 @@
 
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      content::BrowserContext* browser_context,
       content::ResourceContext* resource_context,
       content::URLLoaderRequestInterceptor::LoaderCallback callback) override;
 
diff --git a/chrome/browser/offline_pages/offline_page_utils.cc b/chrome/browser/offline_pages/offline_page_utils.cc
index e66cf96..b8d02b5 100644
--- a/chrome/browser/offline_pages/offline_page_utils.cc
+++ b/chrome/browser/offline_pages/offline_page_utils.cc
@@ -175,23 +175,23 @@
 
 // static
 void OfflinePageUtils::SelectPagesForURL(
-    content::BrowserContext* browser_context,
+    SimpleFactoryKey* key,
     const GURL& url,
     int tab_id,
     base::OnceCallback<void(const std::vector<OfflinePageItem>&)> callback) {
   PageCriteria criteria;
   criteria.url = url;
   criteria.pages_for_tab_id = tab_id;
-  SelectPagesWithCriteria(browser_context, criteria, std::move(callback));
+  SelectPagesWithCriteria(key, criteria, std::move(callback));
 }
 
 // static
 void OfflinePageUtils::SelectPagesWithCriteria(
-    content::BrowserContext* browser_context,
+    SimpleFactoryKey* key,
     const PageCriteria& criteria,
     base::OnceCallback<void(const std::vector<OfflinePageItem>&)> callback) {
   OfflinePageModel* offline_page_model =
-      OfflinePageModelFactory::GetForBrowserContext(browser_context);
+      OfflinePageModelFactory::GetForKey(key);
   if (!offline_page_model) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
diff --git a/chrome/browser/offline_pages/offline_page_utils.h b/chrome/browser/offline_pages/offline_page_utils.h
index 5b340e0..e6855abde 100644
--- a/chrome/browser/offline_pages/offline_page_utils.h
+++ b/chrome/browser/offline_pages/offline_page_utils.h
@@ -15,6 +15,8 @@
 #include "components/offline_pages/core/offline_page_types.h"
 #include "url/gurl.h"
 
+class SimpleFactoryKey;
+
 namespace base {
 class Time;
 }
@@ -68,13 +70,13 @@
   // the search. The returned list is sorted by descending creation date so that
   // the most recent offline page will be the first element of the list.
   static void SelectPagesForURL(
-      content::BrowserContext* browser_context,
+      SimpleFactoryKey* key,
       const GURL& url,
       int tab_id,
       base::OnceCallback<void(const std::vector<OfflinePageItem>&)> callback);
 
   static void SelectPagesWithCriteria(
-      content::BrowserContext* browser_context,
+      SimpleFactoryKey* key,
       const PageCriteria& criteria,
       base::OnceCallback<void(const std::vector<OfflinePageItem>&)> callback);
 
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 7a292a8..d96e836 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -73,6 +73,7 @@
 #if defined(OS_ANDROID)
 #include "chrome/browser/autofill/manual_filling_controller_impl.h"
 #include "chrome/browser/autofill/mock_address_accessory_controller.h"
+#include "chrome/browser/autofill/mock_credit_card_accessory_controller.h"
 #include "chrome/browser/autofill/mock_manual_filling_view.h"
 #include "chrome/browser/password_manager/password_accessory_controller_impl.h"
 #include "chrome/browser/password_manager/password_generation_controller.h"
@@ -761,6 +762,7 @@
   autofill::TestAutofillClient test_autofill_client_;
   NiceMock<MockPasswordAccessoryController> mock_pwd_controller_;
   NiceMock<MockAddressAccessoryController> mock_address_controller_;
+  NiceMock<MockCreditCardAccessoryController> mock_cc_controller_;
 };
 
 std::unique_ptr<password_manager::ContentPasswordManagerDriver>
@@ -774,7 +776,7 @@
     content::WebContents* web_contents) {
   ManualFillingControllerImpl::CreateForWebContentsForTesting(
       web_contents, mock_pwd_controller_.AsWeakPtr(),
-      mock_address_controller_.AsWeakPtr(),
+      mock_address_controller_.AsWeakPtr(), mock_cc_controller_.AsWeakPtr(),
       std::make_unique<NiceMock<MockManualFillingView>>());
 }
 
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.cc b/chrome/browser/password_manager/password_accessory_controller_impl.cc
index a4c9961..7d2b83ef 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/autofill/core/browser/ui/accessory_sheet_enums.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_util.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/autofill/core/common/password_generation_util.h"
@@ -182,6 +183,10 @@
   suggestions->clear();
   for (const auto& pair : best_matches) {
     const PasswordForm* form = pair.second;
+    if (!url::IsSameOriginWith(origin.GetURL(), form->origin) &&
+        !base::FeatureList::IsEnabled(
+            autofill::features::kAutofillKeyboardAccessory))
+      continue;  // Skip matches for PSL origins in V1 which has no UI for that.
     suggestions->emplace_back(form->password_value, GetDisplayUsername(*form),
                               /*selectable=*/!form->username_value.empty());
   }
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc b/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
index 561514e..9166387 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
@@ -85,6 +85,7 @@
   PasswordForm form;
   form.username_value = ASCIIToUTF16(username);
   form.password_value = ASCIIToUTF16(password);
+  form.origin = GURL(kExampleSite);
   std::unique_ptr<const PasswordForm> form_ptr(
       new PasswordForm(std::move(form)));
   auto username_form_pair =
diff --git a/chrome/browser/password_manager/touch_to_fill_controller.cc b/chrome/browser/password_manager/touch_to_fill_controller.cc
index 1be8f39..6b82f05 100644
--- a/chrome/browser/password_manager/touch_to_fill_controller.cc
+++ b/chrome/browser/password_manager/touch_to_fill_controller.cc
@@ -63,8 +63,7 @@
                         /*is_selectable=*/false);
   }
 
-  GetManualFillingController()->ShowTouchToFillSheet(
-      std::move(builder).Build());
+  GetManualFillingController()->RefreshSuggestions(std::move(builder).Build());
 }
 
 void TouchToFillController::OnFillingTriggered(
diff --git a/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc b/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
index 07552771..0763b53 100644
--- a/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
+++ b/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
@@ -64,7 +64,7 @@
   bob.label = bob_realm;
 
   EXPECT_CALL(manual_filling_controller(),
-              ShowTouchToFillSheet(
+              RefreshSuggestions(
                   autofill::AccessorySheetData::Builder(
                       autofill::AccessoryTabType::TOUCH_TO_FILL,
                       base::ASCIIToUTF16("Touch to Fill"))
diff --git a/chrome/browser/payments/chrome_payment_request_delegate.cc b/chrome/browser/payments/chrome_payment_request_delegate.cc
index 44468e6..3b3f246 100644
--- a/chrome/browser/payments/chrome_payment_request_delegate.cc
+++ b/chrome/browser/payments/chrome_payment_request_delegate.cc
@@ -197,4 +197,8 @@
       web_contents_);
 }
 
+bool ChromePaymentRequestDelegate::SkipUiForBasicCard() const {
+  return false;  // Only tests do this.
+}
+
 }  // namespace payments
diff --git a/chrome/browser/payments/chrome_payment_request_delegate.h b/chrome/browser/payments/chrome_payment_request_delegate.h
index d40ba84e..35d0187 100644
--- a/chrome/browser/payments/chrome_payment_request_delegate.h
+++ b/chrome/browser/payments/chrome_payment_request_delegate.h
@@ -54,6 +54,7 @@
       PaymentHandlerOpenWindowCallback callback) override;
   bool IsInteractive() const override;
   std::string GetInvalidSslCertificateErrorMessage() override;
+  bool SkipUiForBasicCard() const override;
 
  protected:
   // Reference to the dialog so that we can satisfy calls to CloseDialog(). This
diff --git a/chrome/browser/payments/service_worker_payment_app_factory_browsertest.cc b/chrome/browser/payments/service_worker_payment_app_factory_browsertest.cc
index ccf5c2f..cd6be44 100644
--- a/chrome/browser/payments/service_worker_payment_app_factory_browsertest.cc
+++ b/chrome/browser/payments/service_worker_payment_app_factory_browsertest.cc
@@ -237,8 +237,15 @@
   void ExpectInstallablePaymentAppInScope(const std::string& scope) {
     ASSERT_FALSE(installable_apps().empty());
     WebAppInstallationInfo* app = nullptr;
+
+    const GURL expected_scope(scope);
+    url::Replacements<char> clear_port;
+    clear_port.ClearPort();
+
     for (const auto& it : installable_apps()) {
-      if (it.second->sw_scope == scope) {
+      // |sw_scope| may contain the test server port. Ignore it in comparison.
+      if (GURL(it.second->sw_scope).ReplaceComponents(clear_port) ==
+          expected_scope) {
         app = it.second.get();
         break;
       }
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.cc b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.cc
index ead87197..fa43d8b 100644
--- a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.cc
+++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.cc
@@ -111,66 +111,66 @@
 FrozenFrameAggregator::FrozenFrameAggregator() = default;
 FrozenFrameAggregator::~FrozenFrameAggregator() = default;
 
-void FrozenFrameAggregator::OnRegistered() {
+void FrozenFrameAggregator::OnFrameNodeAdded(const FrameNode* frame_node) {
+  auto* frame_impl = FrameNodeImpl::FromNode(frame_node);
+  DCHECK(!IsFrozen(frame_impl));  // A newly created node can never be frozen.
+  AddOrRemoveFrame(frame_impl, 1);
+}
+
+void FrozenFrameAggregator::OnBeforeFrameNodeRemoved(
+    const FrameNode* frame_node) {
+  AddOrRemoveFrame(FrameNodeImpl::FromNode(frame_node), -1);
+}
+
+void FrozenFrameAggregator::OnIsCurrentChanged(const FrameNode* frame_node) {
+  auto* frame_impl = FrameNodeImpl::FromNode(frame_node);
+  int32_t current_frame_delta = frame_impl->is_current() ? 1 : -1;
+  int32_t frozen_frame_delta = IsFrozen(frame_impl) ? current_frame_delta : 0;
+  UpdateFrameCounts(frame_impl, current_frame_delta, frozen_frame_delta);
+}
+
+void FrozenFrameAggregator::OnFrameLifecycleStateChanged(
+    const FrameNode* frame_node) {
+  auto* frame_impl = FrameNodeImpl::FromNode(frame_node);
+  if (!frame_impl->is_current())
+    return;
+  int32_t frozen_frame_delta = IsFrozen(frame_impl) ? 1 : -1;
+  UpdateFrameCounts(frame_impl, 0, frozen_frame_delta);
+}
+
+void FrozenFrameAggregator::OnPassedToGraph(Graph* graph) {
+  RegisterObservers(graph);
+}
+
+void FrozenFrameAggregator::OnTakenFromGraph(Graph* graph) {
+  UnregisterObservers(graph);
+}
+
+void FrozenFrameAggregator::OnPageNodeAdded(const PageNode* page_node) {
+  auto* page_impl = PageNodeImpl::FromNode(page_node);
+  DCHECK_EQ(LifecycleState::kRunning, page_impl->lifecycle_state());
+  FrozenDataImpl::GetOrCreate(page_impl);
+}
+
+void FrozenFrameAggregator::OnProcessNodeAdded(
+    const ProcessNode* process_node) {
+  FrozenDataImpl::GetOrCreate(ProcessNodeImpl::FromNode(process_node));
+}
+
+void FrozenFrameAggregator::RegisterObservers(Graph* graph) {
   // This observer presumes that it's been added before any nodes exist in the
   // graph.
-  DCHECK(graph()->nodes().empty());
+  // TODO(chrisha): Add graph introspection functions to Graph.
+  DCHECK(GraphImpl::FromGraph(graph)->nodes().empty());
+  graph->AddFrameNodeObserver(this);
+  graph->AddPageNodeObserver(this);
+  graph->AddProcessNodeObserver(this);
 }
 
-bool FrozenFrameAggregator::ShouldObserve(const NodeBase* node) {
-  // Use the ShouldObserve hook to ensure page and process node attached data
-  // is initialized. There's no need to observe these nodes beyond that.
-  switch (node->type()) {
-    case FrameNodeImpl::Type():
-      return true;
-
-    case PageNodeImpl::Type(): {
-      auto* page_node = PageNodeImpl::FromNodeBase(node);
-      // Expect a page to always start in the running state.
-      DCHECK_EQ(LifecycleState::kRunning, page_node->lifecycle_state());
-      FrozenDataImpl::GetOrCreate(page_node);
-      return false;
-    }
-
-    case ProcessNodeImpl::Type(): {
-      FrozenDataImpl::GetOrCreate(ProcessNodeImpl::FromNodeBase(node));
-      return false;
-    }
-
-    default:
-      return false;
-  }
-  NOTREACHED();
-}
-
-void FrozenFrameAggregator::OnNodeAdded(NodeBase* node) {
-  // We only observe frame nodes.
-  DCHECK_EQ(FrameNodeImpl::Type(), node->type());
-
-  auto* frame_node = FrameNodeImpl::FromNodeBase(node);
-  DCHECK(!IsFrozen(frame_node));  // A newly created node can never be frozen.
-  AddOrRemoveFrame(frame_node, 1);
-}
-
-void FrozenFrameAggregator::OnBeforeNodeRemoved(NodeBase* node) {
-  if (node->type() != FrameNodeImpl::Type())
-    return;
-
-  auto* frame_node = FrameNodeImpl::FromNodeBase(node);
-  AddOrRemoveFrame(frame_node, -1);
-}
-
-void FrozenFrameAggregator::OnIsCurrentChanged(FrameNodeImpl* frame_node) {
-  int32_t current_frame_delta = frame_node->is_current() ? 1 : -1;
-  int32_t frozen_frame_delta = IsFrozen(frame_node) ? current_frame_delta : 0;
-  UpdateFrameCounts(frame_node, current_frame_delta, frozen_frame_delta);
-}
-
-void FrozenFrameAggregator::OnLifecycleStateChanged(FrameNodeImpl* frame_node) {
-  if (!frame_node->is_current())
-    return;
-  int32_t frozen_frame_delta = IsFrozen(frame_node) ? 1 : -1;
-  UpdateFrameCounts(frame_node, 0, frozen_frame_delta);
+void FrozenFrameAggregator::UnregisterObservers(Graph* graph) {
+  graph->RemoveFrameNodeObserver(this);
+  graph->RemovePageNodeObserver(this);
+  graph->RemoveProcessNodeObserver(this);
 }
 
 void FrozenFrameAggregator::AddOrRemoveFrame(FrameNodeImpl* frame_node,
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h
index 07dbac0..10644bc4 100644
--- a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h
+++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h
@@ -5,18 +5,25 @@
 #ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_DECORATORS_FROZEN_FRAME_AGGREGATOR_H_
 #define CHROME_BROWSER_PERFORMANCE_MANAGER_DECORATORS_FROZEN_FRAME_AGGREGATOR_H_
 
-#include "chrome/browser/performance_manager/observers/graph_observer.h"
+#include "chrome/browser/performance_manager/public/graph/frame_node.h"
+#include "chrome/browser/performance_manager/public/graph/graph.h"
+#include "chrome/browser/performance_manager/public/graph/page_node.h"
+#include "chrome/browser/performance_manager/public/graph/process_node.h"
 
 namespace performance_manager {
 
-class NodeBase;
 class FrameNodeImpl;
 class PageNodeImpl;
 class ProcessNodeImpl;
 
 // The FrozenFrameAggregator is responsible for tracking frame frozen states,
-// and aggregating this property to the page and process nodes.
-class FrozenFrameAggregator : public GraphImplObserverDefaultImpl {
+// and aggregating this property to the page and process nodes. As a GraphOwned
+// object it takes care of registering itself as an observer when added to the
+// graph.
+class FrozenFrameAggregator : public FrameNode::ObserverDefaultImpl,
+                              public GraphOwnedDefaultImpl,
+                              public PageNode::ObserverDefaultImpl,
+                              public ProcessNode::ObserverDefaultImpl {
  public:
   struct Data;
 
@@ -25,17 +32,31 @@
   FrozenFrameAggregator();
   ~FrozenFrameAggregator() override;
 
-  // GraphObserver implementation:
-  void OnRegistered() override;
-  bool ShouldObserve(const NodeBase* node) override;
-  void OnNodeAdded(NodeBase* node) override;
-  void OnBeforeNodeRemoved(NodeBase* node) override;
-  void OnIsCurrentChanged(FrameNodeImpl* frame_node) override;
-  void OnLifecycleStateChanged(FrameNodeImpl* page_node) override;
+  // FrameNodeObserver implementation:
+  void OnFrameNodeAdded(const FrameNode* frame_node) override;
+  void OnBeforeFrameNodeRemoved(const FrameNode* frame_node) override;
+  void OnIsCurrentChanged(const FrameNode* frame_node) override;
+  void OnFrameLifecycleStateChanged(const FrameNode* frame_node) override;
+
+  // GraphOwned implementation:
+  void OnPassedToGraph(Graph* graph) override;
+  void OnTakenFromGraph(Graph* graph) override;
+
+  // PageNodeObserver implementation:
+  void OnPageNodeAdded(const PageNode* page_node) override;
+
+  // ProcessNodeObserver implementation:
+  void OnProcessNodeAdded(const ProcessNode* process_node) override;
 
  protected:
   friend class FrozenFrameAggregatorTest;
 
+  // (Un)registers the various node observer flavors of this object with the
+  // graph. These are invoked by OnPassedIntoGraph and OnTakenFromGraph, but
+  // hoisted to their own functions for testing.
+  void RegisterObservers(Graph* graph);
+  void UnregisterObservers(Graph* graph);
+
   // Used to update counts when adding or removing a |frame_node|. A |delta| of
   // -1 indicates a removal, while +1 indicates adding.
   void AddOrRemoveFrame(FrameNodeImpl* frame_node, int32_t delta);
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc
index 7f182e9..697aa46 100644
--- a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc
+++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc
@@ -43,14 +43,12 @@
   ~FrozenFrameAggregatorTest() override = default;
 
   void SetUp() override {
-    ffa_ = std::make_unique<FrozenFrameAggregator>();
-    graph()->RegisterObserver(ffa_.get());
+    ffa_ = new FrozenFrameAggregator();
+    graph()->PassToGraph(base::WrapUnique(ffa_));
     process_node_ = CreateNode<ProcessNodeImpl>();
     page_node_ = CreateNode<PageNodeImpl>();
   }
 
-  void TearDown() override { graph()->UnregisterObserver(ffa_.get()); }
-
   template <typename NodeType>
   void ExpectData(NodeType* node,
                   uint32_t current_frame_count,
@@ -85,7 +83,7 @@
                                      parent_frame_node, frame_tree_node_id);
   }
 
-  std::unique_ptr<FrozenFrameAggregator> ffa_;
+  FrozenFrameAggregator* ffa_;
   TestNodeWrapper<ProcessNodeImpl> process_node_;
   TestNodeWrapper<PageNodeImpl> page_node_;
 
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc
index c2b24fb..c3cdc66c3 100644
--- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc
+++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc
@@ -212,7 +212,7 @@
 
 void PageAlmostIdleDecorator::UpdateLoadIdleStateProcess(
     ProcessNodeImpl* process_node) {
-  for (auto* frame_node : process_node->GetFrameNodes())
+  for (auto* frame_node : process_node->frame_nodes())
     UpdateLoadIdleStateFrame(frame_node);
 }
 
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl.h b/chrome/browser/performance_manager/graph/frame_node_impl.h
index 57d10f8..aee3e55 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl.h
+++ b/chrome/browser/performance_manager/graph/frame_node_impl.h
@@ -195,7 +195,7 @@
   ObservedProperty::NotifiesOnlyOnChanges<
       LifecycleState,
       &GraphImplObserver::OnLifecycleStateChanged,
-      &FrameNodeObserver::OnLifecycleStateChanged>
+      &FrameNodeObserver::OnFrameLifecycleStateChanged>
       lifecycle_state_{LifecycleState::kRunning};
 
   // This is a one way switch. Once marked an ad-frame, always an ad-frame.
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
index 65008e6..62974b9 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
@@ -125,7 +125,7 @@
   MOCK_METHOD1(OnBeforeFrameNodeRemoved, void(const FrameNode*));
   MOCK_METHOD1(OnIsCurrentChanged, void(const FrameNode*));
   MOCK_METHOD1(OnNetworkAlmostIdleChanged, void(const FrameNode*));
-  MOCK_METHOD1(OnLifecycleStateChanged, void(const FrameNode*));
+  MOCK_METHOD1(OnFrameLifecycleStateChanged, void(const FrameNode*));
   MOCK_METHOD1(OnNonPersistentNotificationCreated, void(const FrameNode*));
   MOCK_METHOD1(OnURLChanged, void(const FrameNode*));
 
@@ -177,9 +177,9 @@
   frame_node->SetNetworkAlmostIdle();
   EXPECT_EQ(raw_frame_node, obs.TakeNotifiedFrameNode());
 
-  // Invoke "SetLifecycleState" and expect an "OnLifecycleStateChanged"
+  // Invoke "SetLifecycleState" and expect an "OnFrameLifecycleStateChanged"
   // callback.
-  EXPECT_CALL(obs, OnLifecycleStateChanged(_))
+  EXPECT_CALL(obs, OnFrameLifecycleStateChanged(_))
       .WillOnce(Invoke(&obs, &MockObserver::SetNotifiedFrameNode));
   frame_node->SetLifecycleState(
       resource_coordinator::mojom::LifecycleState::kFrozen);
diff --git a/chrome/browser/performance_manager/graph/graph.cc b/chrome/browser/performance_manager/graph/graph.cc
index 27a6cf9e..ce08169 100644
--- a/chrome/browser/performance_manager/graph/graph.cc
+++ b/chrome/browser/performance_manager/graph/graph.cc
@@ -12,4 +12,10 @@
 GraphObserver::GraphObserver() = default;
 GraphObserver::~GraphObserver() = default;
 
+GraphOwned::GraphOwned() = default;
+GraphOwned::~GraphOwned() = default;
+
+GraphOwnedDefaultImpl::GraphOwnedDefaultImpl() = default;
+GraphOwnedDefaultImpl::~GraphOwnedDefaultImpl() = default;
+
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_impl.cc b/chrome/browser/performance_manager/graph/graph_impl.cc
index 8c397fdb..858951e4 100644
--- a/chrome/browser/performance_manager/graph/graph_impl.cc
+++ b/chrome/browser/performance_manager/graph/graph_impl.cc
@@ -61,6 +61,8 @@
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
+// TODO(chrisha|siggi): Move this to a TearDown, which is invoked by the
+// performance manager before the graph destructor.
 GraphImpl::~GraphImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -68,6 +70,18 @@
   for (auto* observer : graph_observers_)
     observer->OnBeforeGraphDestroyed(this);
 
+  // Clean up graph owned objects. This causes their TakeFromGraph callbacks to
+  // be invoked, and ideally they clean up any observers they may have, etc.
+  while (!graph_owned_.empty())
+    auto object = TakeFromGraph(graph_owned_.begin()->first);
+
+  // At this point, all typed observers should be empty.
+  DCHECK(graph_observers_.empty());
+  DCHECK(frame_node_observers_.empty());
+  DCHECK(page_node_observers_.empty());
+  DCHECK(process_node_observers_.empty());
+  DCHECK(system_node_observers_.empty());
+
   // All observers should have been removed before the graph is deleted.
   // TODO(chrisha): This will disappear, as new observers are allowed to stay
   // attached at graph death.
@@ -131,6 +145,26 @@
   RemoveObserverImpl(&system_node_observers_, observer);
 }
 
+void GraphImpl::PassToGraph(std::unique_ptr<GraphOwned> graph_owned) {
+  auto* raw = graph_owned.get();
+  DCHECK(!base::Contains(graph_owned_, raw));
+  graph_owned_.insert(std::make_pair(raw, std::move(graph_owned)));
+  raw->OnPassedToGraph(this);
+}
+
+std::unique_ptr<GraphOwned> GraphImpl::TakeFromGraph(GraphOwned* graph_owned) {
+  std::unique_ptr<GraphOwned> object;
+  auto it = graph_owned_.find(graph_owned);
+  if (it != graph_owned_.end()) {
+    DCHECK_EQ(graph_owned, it->first);
+    DCHECK_EQ(graph_owned, it->second.get());
+    object = std::move(it->second);
+    graph_owned_.erase(it);
+    object->OnTakenFromGraph(this);
+  }
+  return object;
+}
+
 void GraphImpl::RegisterObserver(GraphImplObserver* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   observer->SetGraph(this);
diff --git a/chrome/browser/performance_manager/graph/graph_impl.h b/chrome/browser/performance_manager/graph/graph_impl.h
index 780ef83..b7bf449 100644
--- a/chrome/browser/performance_manager/graph/graph_impl.h
+++ b/chrome/browser/performance_manager/graph/graph_impl.h
@@ -13,6 +13,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/process/process_handle.h"
 #include "base/sequence_checker.h"
@@ -57,6 +58,8 @@
   void RemovePageNodeObserver(PageNodeObserver* observer) override;
   void RemoveProcessNodeObserver(ProcessNodeObserver* observer) override;
   void RemoveSystemNodeObserver(SystemNodeObserver* observer) override;
+  void PassToGraph(std::unique_ptr<GraphOwned> graph_owned) override;
+  std::unique_ptr<GraphOwned> TakeFromGraph(GraphOwned* graph_owned) override;
   uintptr_t GetImplType() const override;
   const void* GetImpl() const override;
 
@@ -106,6 +109,9 @@
   // Allows explicitly invoking SystemNode destruction for testing.
   void ReleaseSystemNodeForTesting() { ReleaseSystemNode(); }
 
+  // Returns the number of objects in the |graph_owned_| map, for testing.
+  size_t GraphOwnedCountForTesting() const { return graph_owned_.size(); }
+
  protected:
   friend class NodeBase;
 
@@ -149,6 +155,10 @@
   std::vector<ProcessNodeObserver*> process_node_observers_;
   std::vector<SystemNodeObserver*> system_node_observers_;
 
+  // Graph-owned objects. For now we only expect O(10) clients, hence the
+  // flat_map.
+  base::flat_map<GraphOwned*, std::unique_ptr<GraphOwned>> graph_owned_;
+
   // User data storage for the graph.
   friend class NodeAttachedDataMapHelper;
   using NodeAttachedDataKey = std::pair<const Node*, const void*>;
diff --git a/chrome/browser/performance_manager/graph/graph_impl_operations.cc b/chrome/browser/performance_manager/graph/graph_impl_operations.cc
new file mode 100644
index 0000000..7332ef48
--- /dev/null
+++ b/chrome/browser/performance_manager/graph/graph_impl_operations.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 "chrome/browser/performance_manager/graph/graph_impl_operations.h"
+
+#include "chrome/browser/performance_manager/graph/process_node_impl.h"
+
+namespace performance_manager {
+
+// static
+base::flat_set<PageNodeImpl*> GraphImplOperations::GetAssociatedPageNodes(
+    const ProcessNodeImpl* process) {
+  base::flat_set<PageNodeImpl*> page_nodes;
+  for (auto* frame_node : process->frame_nodes())
+    page_nodes.insert(frame_node->page_node());
+  return page_nodes;
+}
+
+// static
+base::flat_set<ProcessNodeImpl*> GraphImplOperations::GetAssociatedProcessNodes(
+    const PageNodeImpl* page) {
+  base::flat_set<ProcessNodeImpl*> process_nodes;
+  VisitFrameTreePreOrder(page,
+                         [&process_nodes](FrameNodeImpl* frame_node) -> bool {
+                           if (auto* process_node = frame_node->process_node())
+                             process_nodes.insert(process_node);
+                           return true;
+                         });
+  return process_nodes;
+}
+
+// static
+std::vector<FrameNodeImpl*> GraphImplOperations::GetFrameNodes(
+    const PageNodeImpl* page) {
+  std::vector<FrameNodeImpl*> frame_nodes;
+  frame_nodes.reserve(20);  // This is in the 99.9th %ile of frame tree sizes.
+
+  for (auto* main_frame_node : page->main_frame_nodes())
+    frame_nodes.push_back(main_frame_node);
+
+  for (size_t i = 0; i < frame_nodes.size(); ++i) {
+    auto* parent_frame_node = frame_nodes[i];
+    for (auto* frame_node : parent_frame_node->child_frame_nodes())
+      frame_nodes.push_back(frame_node);
+  }
+
+  return frame_nodes;
+}
+
+// static
+bool GraphImplOperations::HasFrame(const PageNodeImpl* page,
+                                   FrameNodeImpl* frame) {
+  bool has_frame = false;
+  VisitFrameTreePreOrder(page, [&has_frame, frame](FrameNodeImpl* f) -> bool {
+    if (f == frame) {
+      has_frame = true;
+      return false;
+    }
+    return true;
+  });
+  return has_frame;
+}
+
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_impl_operations.h b/chrome/browser/performance_manager/graph/graph_impl_operations.h
new file mode 100644
index 0000000..ae68ee1
--- /dev/null
+++ b/chrome/browser/performance_manager/graph/graph_impl_operations.h
@@ -0,0 +1,99 @@
+// Copyright 2019 The Chromium 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_PERFORMANCE_MANAGER_GRAPH_GRAPH_IMPL_OPERATIONS_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_GRAPH_GRAPH_IMPL_OPERATIONS_H_
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_set.h"
+#include "chrome/browser/performance_manager/graph/frame_node_impl.h"
+#include "chrome/browser/performance_manager/graph/page_node_impl.h"
+
+namespace performance_manager {
+
+class ProcessNodeImpl;
+
+// A collection of utilities for performing common queries and traversals on a
+// graph.
+struct GraphImplOperations {
+  // Returns the collection of page nodes that are associated with the given
+  // |process|. A page is associated with a process if the page's frame tree
+  // contains 1 or more frames hosted in the given |process|.
+  static base::flat_set<PageNodeImpl*> GetAssociatedPageNodes(
+      const ProcessNodeImpl* process);
+
+  // Returns the collection of process nodes associated with the given |page|.
+  // A |process| is associated with a page if the page's frame tree contains 1
+  // or more frames hosted in that |process|.
+  static base::flat_set<ProcessNodeImpl*> GetAssociatedProcessNodes(
+      const PageNodeImpl* page);
+
+  // Returns the collection of frame nodes associated with a page. This is
+  // returned in level order, with main frames first (level 0), main frame
+  // children next (level 1), all the way down to the deepest leaf frames.
+  static std::vector<FrameNodeImpl*> GetFrameNodes(const PageNodeImpl* page);
+
+  // Traverse the frame tree of a |page| in the given order, invoking the
+  // provided |callable| for each frame node in the tree. The |callable| has to
+  // provide a "bool operator()(FrameNodeImpl*)". If the visitor returns false
+  // then the iteration is halted.
+  template <typename Callable>
+  static void VisitFrameTreePreOrder(const PageNodeImpl* page,
+                                     Callable callable);
+  template <typename Callable>
+  static void VisitFrameTreePostOrder(const PageNodeImpl* page,
+                                      Callable callable);
+
+  // Returns true if the given |frame| is in the frame tree associated with the
+  // given |page|.
+  static bool HasFrame(const PageNodeImpl* page, FrameNodeImpl* frame);
+};
+
+// Implementation details for VisitFrameTree*.
+namespace internal {
+
+template <typename Callable>
+bool VisitFrameAndChildren(FrameNodeImpl* frame,
+                           Callable callable,
+                           bool pre_order) {
+  if (pre_order && !callable(frame))
+    return false;
+  for (auto* child : frame->child_frame_nodes()) {
+    if (!VisitFrameAndChildren(child, callable, pre_order))
+      return false;
+  }
+  if (!pre_order && !callable(frame))
+    return false;
+  return true;
+}
+
+template <typename Callable>
+void VisitFrameTree(const PageNodeImpl* page,
+                    Callable callable,
+                    bool pre_order) {
+  for (auto* main_frame_node : page->main_frame_nodes()) {
+    if (!VisitFrameAndChildren(main_frame_node, callable, pre_order))
+      return;
+  }
+}
+
+}  // namespace internal
+
+// static
+template <typename Callable>
+void GraphImplOperations::VisitFrameTreePreOrder(const PageNodeImpl* page,
+                                                 Callable callable) {
+  internal::VisitFrameTree(page, callable, true);
+}
+
+// static
+template <typename Callable>
+void GraphImplOperations::VisitFrameTreePostOrder(const PageNodeImpl* page,
+                                                  Callable callable) {
+  internal::VisitFrameTree(page, callable, false);
+}
+
+}  // namespace performance_manager
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_GRAPH_GRAPH_IMPL_OPERATIONS_H_
diff --git a/chrome/browser/performance_manager/graph/graph_impl_operations_unittest.cc b/chrome/browser/performance_manager/graph/graph_impl_operations_unittest.cc
new file mode 100644
index 0000000..92bcdf0c
--- /dev/null
+++ b/chrome/browser/performance_manager/graph/graph_impl_operations_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright 2019 The Chromium 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/performance_manager/graph/graph_impl_operations.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "chrome/browser/performance_manager/graph/graph_test_harness.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_manager {
+
+namespace {
+
+class GraphImplOperationsTest : public GraphTestHarness {
+ public:
+  // Sets up two parallel frame trees that span multiple processes each.
+  void SetUp() override {
+    process1_ = CreateNode<ProcessNodeImpl>();
+    process2_ = CreateNode<ProcessNodeImpl>();
+    page1_ = CreateNode<PageNodeImpl>();
+    page2_ = CreateNode<PageNodeImpl>();
+    mainframe1_ =
+        CreateNode<FrameNodeImpl>(process1_.get(), page1_.get(), nullptr, 0);
+    mainframe2_ =
+        CreateNode<FrameNodeImpl>(process2_.get(), page2_.get(), nullptr, 1);
+    childframe1a_ = CreateNode<FrameNodeImpl>(process2_.get(), page1_.get(),
+                                              mainframe1_.get(), 2);
+    childframe1b_ = CreateNode<FrameNodeImpl>(process2_.get(), page1_.get(),
+                                              mainframe1_.get(), 3);
+    childframe2a_ = CreateNode<FrameNodeImpl>(process1_.get(), page2_.get(),
+                                              mainframe2_.get(), 4);
+    childframe2b_ = CreateNode<FrameNodeImpl>(process1_.get(), page2_.get(),
+                                              mainframe2_.get(), 5);
+  }
+
+  TestNodeWrapper<ProcessNodeImpl> process1_;
+  TestNodeWrapper<ProcessNodeImpl> process2_;
+  TestNodeWrapper<PageNodeImpl> page1_;
+  TestNodeWrapper<PageNodeImpl> page2_;
+
+  // Root nodes. |mainframeX_| is in |processX_|.
+  TestNodeWrapper<FrameNodeImpl> mainframe1_;
+  TestNodeWrapper<FrameNodeImpl> mainframe2_;
+
+  // Children of |mainframe1_|, but in |process2_|.
+  TestNodeWrapper<FrameNodeImpl> childframe1a_;
+  TestNodeWrapper<FrameNodeImpl> childframe1b_;
+
+  // Children of |mainframe2_|, but in |process1_|.
+  TestNodeWrapper<FrameNodeImpl> childframe2a_;
+  TestNodeWrapper<FrameNodeImpl> childframe2b_;
+};
+
+}  // namespace
+
+TEST_F(GraphImplOperationsTest, GetAssociatedPageNodes) {
+  auto page_nodes =
+      GraphImplOperations::GetAssociatedPageNodes(process1_.get());
+  EXPECT_EQ(2u, page_nodes.size());
+  EXPECT_THAT(page_nodes,
+              testing::UnorderedElementsAre(page1_.get(), page2_.get()));
+}
+
+TEST_F(GraphImplOperationsTest, GetAssociatedProcessNodes) {
+  auto process_nodes =
+      GraphImplOperations::GetAssociatedProcessNodes(page1_.get());
+  EXPECT_EQ(2u, process_nodes.size());
+  EXPECT_THAT(process_nodes,
+              testing::UnorderedElementsAre(process1_.get(), process2_.get()));
+}
+
+TEST_F(GraphImplOperationsTest, GetFrameNodes) {
+  // Add a grandchild frame.
+  auto grandchild = CreateNode<FrameNodeImpl>(process1_.get(), page1_.get(),
+                                              childframe1a_.get(), 6);
+
+  auto frame_nodes = GraphImplOperations::GetFrameNodes(page1_.get());
+  EXPECT_THAT(frame_nodes, testing::UnorderedElementsAre(
+                               mainframe1_.get(), childframe1a_.get(),
+                               childframe1b_.get(), grandchild.get()));
+  // In a level order the main-frame is first, and the grandchild is last. The
+  // two children can come in any order.
+  EXPECT_EQ(mainframe1_.get(), frame_nodes[0]);
+  EXPECT_EQ(grandchild.get(), frame_nodes[3]);
+}
+
+TEST_F(GraphImplOperationsTest, VisitFrameTree) {
+  auto frame_nodes = GraphImplOperations::GetFrameNodes(page1_.get());
+
+  std::vector<FrameNodeImpl*> visited;
+  GraphImplOperations::VisitFrameTreePreOrder(
+      page1_.get(), [&visited](FrameNodeImpl* frame_node) -> bool {
+        visited.push_back(frame_node);
+        return true;
+      });
+  EXPECT_THAT(visited,
+              testing::UnorderedElementsAre(
+                  mainframe1_.get(), childframe1a_.get(), childframe1b_.get()));
+  // In pre-order the main frame is first.
+  EXPECT_EQ(mainframe1_.get(), visited[0]);
+
+  visited.clear();
+  GraphImplOperations::VisitFrameTreePostOrder(
+      page1_.get(), [&visited](FrameNodeImpl* frame_node) -> bool {
+        visited.push_back(frame_node);
+        return true;
+      });
+  EXPECT_THAT(visited,
+              testing::UnorderedElementsAre(
+                  mainframe1_.get(), childframe1a_.get(), childframe1b_.get()));
+  // In post-order the main frame is last.
+  EXPECT_EQ(mainframe1_.get(), visited[2]);
+}
+
+TEST_F(GraphImplOperationsTest, HasFrame) {
+  EXPECT_TRUE(GraphImplOperations::HasFrame(page1_.get(), childframe1a_.get()));
+  EXPECT_FALSE(
+      GraphImplOperations::HasFrame(page1_.get(), childframe2a_.get()));
+}
+
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_impl_unittest.cc b/chrome/browser/performance_manager/graph/graph_impl_unittest.cc
index dc62b1a..3e7d222 100644
--- a/chrome/browser/performance_manager/graph/graph_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/graph_impl_unittest.cc
@@ -138,7 +138,7 @@
   LenientMockObserver() {}
   ~LenientMockObserver() override {}
 
-  MOCK_METHOD1(OnBeforeGraphDestroyed, void(const Graph*));
+  MOCK_METHOD1(OnBeforeGraphDestroyed, void(Graph*));
 };
 
 using MockObserver = ::testing::StrictMock<LenientMockObserver>;
@@ -150,15 +150,79 @@
 
 TEST(GraphImplTest, ObserverWorks) {
   std::unique_ptr<GraphImpl> graph = base::WrapUnique(new GraphImpl());
-  const Graph* raw_graph = graph.get();
+  Graph* raw_graph = graph.get();
 
   MockObserver obs;
   graph->AddGraphObserver(&obs);
   graph->RemoveGraphObserver(&obs);
   graph->AddGraphObserver(&obs);
 
-  EXPECT_CALL(obs, OnBeforeGraphDestroyed(raw_graph));
+  // Expect the graph teardown callback to be invoked. We have to unregister our
+  // observer in order to maintain graph invariants.
+  EXPECT_CALL(obs, OnBeforeGraphDestroyed(raw_graph))
+      .WillOnce(testing::Invoke(
+          [&obs](Graph* graph) { graph->RemoveGraphObserver(&obs); }));
   graph.reset();
 }
 
+namespace {
+
+class Foo : public GraphOwned {
+ public:
+  explicit Foo(int* destructor_count) : destructor_count_(destructor_count) {}
+
+  ~Foo() override { (*destructor_count_)++; }
+
+  // GraphOwned implementation:
+  void OnPassedToGraph(Graph* graph) override { passed_to_called_ = true; }
+  void OnTakenFromGraph(Graph* graph) override { taken_from_called_ = true; }
+
+  bool passed_to_called() const { return passed_to_called_; }
+  bool taken_from_called() const { return taken_from_called_; }
+
+ private:
+  bool passed_to_called_ = false;
+  bool taken_from_called_ = false;
+  int* destructor_count_ = nullptr;
+};
+
+}  // namespace
+
+TEST(GraphImplTest, GraphOwned) {
+  int destructor_count = 0;
+
+  std::unique_ptr<Foo> foo1 = base::WrapUnique(new Foo(&destructor_count));
+  std::unique_ptr<Foo> foo2 = base::WrapUnique(new Foo(&destructor_count));
+  auto* raw1 = foo1.get();
+  auto* raw2 = foo2.get();
+
+  // Pass both objects to the graph.
+  std::unique_ptr<GraphImpl> graph = base::WrapUnique(new GraphImpl());
+  EXPECT_EQ(0u, graph->GraphOwnedCountForTesting());
+  EXPECT_FALSE(raw1->passed_to_called());
+  graph->PassToGraph(std::move(foo1));
+  EXPECT_TRUE(raw1->passed_to_called());
+  EXPECT_EQ(1u, graph->GraphOwnedCountForTesting());
+  EXPECT_FALSE(raw2->passed_to_called());
+  graph->PassToGraph(std::move(foo2));
+  EXPECT_TRUE(raw2->passed_to_called());
+  EXPECT_EQ(2u, graph->GraphOwnedCountForTesting());
+
+  // Take one back.
+  EXPECT_FALSE(raw1->taken_from_called());
+  foo1 = graph->TakeFromGraphAs<Foo>(raw1);
+  EXPECT_TRUE(raw1->taken_from_called());
+  EXPECT_EQ(1u, graph->GraphOwnedCountForTesting());
+
+  // Destroy that object and expect its destructor to have been invoked.
+  EXPECT_EQ(0, destructor_count);
+  foo1.reset();
+  EXPECT_EQ(1, destructor_count);
+
+  // Now destroy the graph and expect the other object to have been torn down
+  // too.
+  graph.reset();
+  EXPECT_EQ(2, destructor_count);
+}
+
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_operations.cc b/chrome/browser/performance_manager/graph/graph_operations.cc
new file mode 100644
index 0000000..9092dac
--- /dev/null
+++ b/chrome/browser/performance_manager/graph/graph_operations.cc
@@ -0,0 +1,85 @@
+// Copyright 2019 The Chromium 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/performance_manager/public/graph/graph_operations.h"
+
+#include "chrome/browser/performance_manager/graph/frame_node_impl.h"
+#include "chrome/browser/performance_manager/graph/graph_impl_operations.h"
+#include "chrome/browser/performance_manager/graph/page_node_impl.h"
+#include "chrome/browser/performance_manager/graph/process_node_impl.h"
+
+namespace performance_manager {
+
+namespace {
+
+template <typename ImplContainerType, typename PublicContainerType>
+PublicContainerType ConvertContainer(const ImplContainerType& impls) {
+  PublicContainerType result;
+  for (auto* impl : impls) {
+    // Use the hinting insert, which all containers support. This will result
+    // in the same ordering for vectors, and for containers that are sorted it
+    // will actually provide the optimal hint. For hashed containers this
+    // parameter will be ignored so is effectively a nop.
+    result.insert(result.end(), impl);
+  }
+  return result;
+}
+
+}  // namespace
+
+// static
+base::flat_set<const PageNode*> GraphOperations::GetAssociatedPageNodes(
+    const ProcessNode* process) {
+  return ConvertContainer<base::flat_set<PageNodeImpl*>,
+                          base::flat_set<const PageNode*>>(
+      GraphImplOperations::GetAssociatedPageNodes(
+          ProcessNodeImpl::FromNode(process)));
+}
+
+// static
+base::flat_set<const ProcessNode*> GraphOperations::GetAssociatedProcessNodes(
+    const PageNode* page) {
+  return ConvertContainer<base::flat_set<ProcessNodeImpl*>,
+                          base::flat_set<const ProcessNode*>>(
+      GraphImplOperations::GetAssociatedProcessNodes(
+          PageNodeImpl::FromNode(page)));
+}
+
+// static
+std::vector<const FrameNode*> GraphOperations::GetFrameNodes(
+    const PageNode* page) {
+  return ConvertContainer<std::vector<FrameNodeImpl*>,
+                          std::vector<const FrameNode*>>(
+      GraphImplOperations::GetFrameNodes(PageNodeImpl::FromNode(page)));
+}
+
+// static
+void GraphOperations::VisitFrameTreePreOrder(const PageNode* page,
+                                             const FrameNodeVisitor& visitor) {
+  GraphImplOperations::VisitFrameTreePreOrder(
+      PageNodeImpl::FromNode(page),
+      [&visitor](FrameNodeImpl* frame_impl) -> bool {
+        const FrameNode* frame = frame_impl;
+        return visitor.Run(frame);
+      });
+}
+
+// static
+void GraphOperations::VisitFrameTreePostOrder(const PageNode* page,
+                                              const FrameNodeVisitor& visitor) {
+  GraphImplOperations::VisitFrameTreePostOrder(
+      PageNodeImpl::FromNode(page),
+      [&visitor](FrameNodeImpl* frame_impl) -> bool {
+        const FrameNode* frame = frame_impl;
+        return visitor.Run(frame);
+      });
+}
+
+// static
+bool GraphOperations::HasFrame(const PageNode* page, const FrameNode* frame) {
+  return GraphImplOperations::HasFrame(PageNodeImpl::FromNode(page),
+                                       FrameNodeImpl::FromNode(frame));
+}
+
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_operations_unittest.cc b/chrome/browser/performance_manager/graph/graph_operations_unittest.cc
new file mode 100644
index 0000000..2c6e9ff
--- /dev/null
+++ b/chrome/browser/performance_manager/graph/graph_operations_unittest.cc
@@ -0,0 +1,139 @@
+// Copyright 2019 The Chromium 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/performance_manager/public/graph/graph_operations.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "chrome/browser/performance_manager/graph/graph_test_harness.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_manager {
+
+namespace {
+
+class GraphOperationsTest : public GraphTestHarness {
+ public:
+  // Sets up two parallel frame trees that span multiple processes each.
+  void SetUp() override {
+    process1_ = CreateNode<ProcessNodeImpl>();
+    process2_ = CreateNode<ProcessNodeImpl>();
+    page1_ = CreateNode<PageNodeImpl>();
+    page2_ = CreateNode<PageNodeImpl>();
+    mainframe1_ =
+        CreateNode<FrameNodeImpl>(process1_.get(), page1_.get(), nullptr, 0);
+    mainframe2_ =
+        CreateNode<FrameNodeImpl>(process2_.get(), page2_.get(), nullptr, 1);
+    childframe1a_ = CreateNode<FrameNodeImpl>(process2_.get(), page1_.get(),
+                                              mainframe1_.get(), 2);
+    childframe1b_ = CreateNode<FrameNodeImpl>(process2_.get(), page1_.get(),
+                                              mainframe1_.get(), 3);
+    childframe2a_ = CreateNode<FrameNodeImpl>(process1_.get(), page2_.get(),
+                                              mainframe2_.get(), 4);
+    childframe2b_ = CreateNode<FrameNodeImpl>(process1_.get(), page2_.get(),
+                                              mainframe2_.get(), 5);
+  }
+
+  TestNodeWrapper<ProcessNodeImpl> process1_;
+  TestNodeWrapper<ProcessNodeImpl> process2_;
+  TestNodeWrapper<PageNodeImpl> page1_;
+  TestNodeWrapper<PageNodeImpl> page2_;
+
+  // Root nodes. |mainframeX_| is in |processX_|.
+  TestNodeWrapper<FrameNodeImpl> mainframe1_;
+  TestNodeWrapper<FrameNodeImpl> mainframe2_;
+
+  // Children of |mainframe1_|, but in |process2_|.
+  TestNodeWrapper<FrameNodeImpl> childframe1a_;
+  TestNodeWrapper<FrameNodeImpl> childframe1b_;
+
+  // Children of |mainframe2_|, but in |process1_|.
+  TestNodeWrapper<FrameNodeImpl> childframe2a_;
+  TestNodeWrapper<FrameNodeImpl> childframe2b_;
+};
+
+const PageNode* ToPublic(PageNodeImpl* page_node) {
+  return page_node;
+}
+
+const FrameNode* ToPublic(FrameNodeImpl* frame_node) {
+  return frame_node;
+}
+
+}  // namespace
+
+TEST_F(GraphOperationsTest, GetAssociatedPageNodes) {
+  auto page_nodes = GraphOperations::GetAssociatedPageNodes(process1_.get());
+  EXPECT_EQ(2u, page_nodes.size());
+  EXPECT_THAT(page_nodes, testing::UnorderedElementsAre(
+                              ToPublic(page1_.get()), ToPublic(page2_.get())));
+}
+
+TEST_F(GraphOperationsTest, GetAssociatedProcessNodes) {
+  auto process_nodes = GraphOperations::GetAssociatedProcessNodes(page1_.get());
+  EXPECT_EQ(2u, process_nodes.size());
+  EXPECT_THAT(process_nodes,
+              testing::UnorderedElementsAre(process1_.get(), process2_.get()));
+}
+
+TEST_F(GraphOperationsTest, GetFrameNodes) {
+  // Add a grandchild frame.
+  auto grandchild = CreateNode<FrameNodeImpl>(process1_.get(), page1_.get(),
+                                              childframe1a_.get(), 6);
+
+  auto frame_nodes = GraphOperations::GetFrameNodes(page1_.get());
+  EXPECT_THAT(frame_nodes,
+              testing::UnorderedElementsAre(
+                  ToPublic(mainframe1_.get()), ToPublic(childframe1a_.get()),
+                  ToPublic(childframe1b_.get()), ToPublic(grandchild.get())));
+  // In a level order the main-frame is first, and the grandchild is last. The
+  // two children can come in any order.
+  EXPECT_EQ(ToPublic(mainframe1_.get()), frame_nodes[0]);
+  EXPECT_EQ(ToPublic(grandchild.get()), frame_nodes[3]);
+}
+
+TEST_F(GraphOperationsTest, VisitFrameTree) {
+  auto frame_nodes = GraphOperations::GetFrameNodes(page1_.get());
+
+  std::vector<const FrameNode*> visited;
+  GraphOperations::VisitFrameTreePreOrder(
+      page1_.get(), base::Bind(
+                        [](std::vector<const FrameNode*>* visited,
+                           const FrameNode* frame_node) -> bool {
+                          visited->push_back(frame_node);
+                          return true;
+                        },
+                        base::Unretained(&visited)));
+  EXPECT_THAT(visited,
+              testing::UnorderedElementsAre(ToPublic(mainframe1_.get()),
+                                            ToPublic(childframe1a_.get()),
+                                            ToPublic(childframe1b_.get())));
+  // In pre-order the main frame is first.
+  EXPECT_EQ(ToPublic(mainframe1_.get()), visited[0]);
+
+  visited.clear();
+  GraphOperations::VisitFrameTreePostOrder(
+      page1_.get(), base::Bind(
+                        [](std::vector<const FrameNode*>* visited,
+                           const FrameNode* frame_node) -> bool {
+                          visited->push_back(frame_node);
+                          return true;
+                        },
+                        base::Unretained(&visited)));
+  EXPECT_THAT(visited,
+              testing::UnorderedElementsAre(ToPublic(mainframe1_.get()),
+                                            ToPublic(childframe1a_.get()),
+                                            ToPublic(childframe1b_.get())));
+  // In post-order the main frame is last.
+  EXPECT_EQ(mainframe1_.get(), visited[2]);
+}
+
+TEST_F(GraphOperationsTest, HasFrame) {
+  EXPECT_TRUE(GraphOperations::HasFrame(page1_.get(), childframe1a_.get()));
+  EXPECT_FALSE(GraphOperations::HasFrame(page1_.get(), childframe2a_.get()));
+}
+
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/node_base_unittest.cc b/chrome/browser/performance_manager/graph/node_base_unittest.cc
index 9c3890e8..be8b08b 100644
--- a/chrome/browser/performance_manager/graph/node_base_unittest.cc
+++ b/chrome/browser/performance_manager/graph/node_base_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/performance_manager/graph/node_base.h"
+#include "chrome/browser/performance_manager/graph/graph_impl_operations.h"
 #include "chrome/browser/performance_manager/graph/graph_test_harness.h"
 #include "chrome/browser/performance_manager/graph/mock_graphs.h"
 #include "chrome/browser/performance_manager/graph/page_node_impl.h"
@@ -24,12 +25,12 @@
   MockSinglePageInSingleProcessGraph mock_graph(graph());
 
   auto pages_associated_with_process =
-      mock_graph.process->GetAssociatedPageNodes();
+      GraphImplOperations::GetAssociatedPageNodes(mock_graph.process.get());
   EXPECT_EQ(1u, pages_associated_with_process.size());
   EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.page.get()));
 
   auto processes_associated_with_page =
-      mock_graph.page->GetAssociatedProcessNodes();
+      GraphImplOperations::GetAssociatedProcessNodes(mock_graph.page.get());
   EXPECT_EQ(1u, processes_associated_with_page.size());
   EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get()));
 }
@@ -38,19 +39,20 @@
   MockMultiplePagesInSingleProcessGraph mock_graph(graph());
 
   auto pages_associated_with_process =
-      mock_graph.process->GetAssociatedPageNodes();
+      GraphImplOperations::GetAssociatedPageNodes(mock_graph.process.get());
   EXPECT_EQ(2u, pages_associated_with_process.size());
   EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.page.get()));
   EXPECT_EQ(1u,
             pages_associated_with_process.count(mock_graph.other_page.get()));
 
   auto processes_associated_with_page =
-      mock_graph.page->GetAssociatedProcessNodes();
+      GraphImplOperations::GetAssociatedProcessNodes(mock_graph.page.get());
   EXPECT_EQ(1u, processes_associated_with_page.size());
   EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get()));
 
   auto processes_associated_with_other_page =
-      mock_graph.other_page->GetAssociatedProcessNodes();
+      GraphImplOperations::GetAssociatedProcessNodes(
+          mock_graph.other_page.get());
   EXPECT_EQ(1u, processes_associated_with_other_page.size());
   EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get()));
 }
@@ -59,18 +61,19 @@
   MockSinglePageWithMultipleProcessesGraph mock_graph(graph());
 
   auto pages_associated_with_process =
-      mock_graph.process->GetAssociatedPageNodes();
+      GraphImplOperations::GetAssociatedPageNodes(mock_graph.process.get());
   EXPECT_EQ(1u, pages_associated_with_process.size());
   EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.page.get()));
 
   auto pages_associated_with_other_process =
-      mock_graph.other_process->GetAssociatedPageNodes();
+      GraphImplOperations::GetAssociatedPageNodes(
+          mock_graph.other_process.get());
   EXPECT_EQ(1u, pages_associated_with_other_process.size());
   EXPECT_EQ(1u,
             pages_associated_with_other_process.count(mock_graph.page.get()));
 
   auto processes_associated_with_page =
-      mock_graph.page->GetAssociatedProcessNodes();
+      GraphImplOperations::GetAssociatedProcessNodes(mock_graph.page.get());
   EXPECT_EQ(2u, processes_associated_with_page.size());
   EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get()));
   EXPECT_EQ(
@@ -81,25 +84,27 @@
   MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph());
 
   auto pages_associated_with_process =
-      mock_graph.process->GetAssociatedPageNodes();
+      GraphImplOperations::GetAssociatedPageNodes(mock_graph.process.get());
   EXPECT_EQ(2u, pages_associated_with_process.size());
   EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.page.get()));
   EXPECT_EQ(1u,
             pages_associated_with_process.count(mock_graph.other_page.get()));
 
   auto pages_associated_with_other_process =
-      mock_graph.other_process->GetAssociatedPageNodes();
+      GraphImplOperations::GetAssociatedPageNodes(
+          mock_graph.other_process.get());
   EXPECT_EQ(1u, pages_associated_with_other_process.size());
   EXPECT_EQ(1u, pages_associated_with_other_process.count(
                     mock_graph.other_page.get()));
 
   auto processes_associated_with_page =
-      mock_graph.page->GetAssociatedProcessNodes();
+      GraphImplOperations::GetAssociatedProcessNodes(mock_graph.page.get());
   EXPECT_EQ(1u, processes_associated_with_page.size());
   EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get()));
 
   auto processes_associated_with_other_page =
-      mock_graph.other_page->GetAssociatedProcessNodes();
+      GraphImplOperations::GetAssociatedProcessNodes(
+          mock_graph.other_page.get());
   EXPECT_EQ(2u, processes_associated_with_other_page.size());
   EXPECT_EQ(
       1u, processes_associated_with_other_page.count(mock_graph.process.get()));
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.cc b/chrome/browser/performance_manager/graph/page_node_impl.cc
index 8b6e75a..0bf7eba 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl.cc
@@ -11,6 +11,7 @@
 #include "base/time/default_tick_clock.h"
 #include "chrome/browser/performance_manager/graph/frame_node_impl.h"
 #include "chrome/browser/performance_manager/graph/graph_impl.h"
+#include "chrome/browser/performance_manager/graph/graph_impl_operations.h"
 #include "chrome/browser/performance_manager/graph/process_node_impl.h"
 #include "chrome/browser/performance_manager/performance_manager_clock.h"
 
@@ -28,18 +29,6 @@
   return kIndex;
 }
 
-// Calls |map_function| for |frame_node| and all its offspring, or until
-// |map_function| returns false.
-template <typename MapFunction>
-void ForFrameAndDescendents(FrameNodeImpl* frame_node,
-                            MapFunction map_function) {
-  if (!map_function(frame_node))
-    return;
-
-  for (FrameNodeImpl* child : frame_node->child_frame_nodes())
-    ForFrameAndDescendents(child, map_function);
-}
-
 }  // namespace
 
 PageNodeImpl::PageNodeImpl(GraphImpl* graph,
@@ -136,24 +125,15 @@
     observer->OnMainFrameNavigationCommitted(this);
 }
 
-base::flat_set<ProcessNodeImpl*> PageNodeImpl::GetAssociatedProcessNodes()
-    const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::flat_set<ProcessNodeImpl*> process_nodes;
-  ForAllFrameNodes([&process_nodes](FrameNodeImpl* frame_node) -> bool {
-    if (auto* process_node = frame_node->process_node())
-      process_nodes.insert(process_node);
-    return true;
-  });
-  return process_nodes;
-}
-
 double PageNodeImpl::GetCPUUsage() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   double cpu_usage = 0;
 
-  for (auto* process_node : GetAssociatedProcessNodes()) {
-    size_t pages_in_process = process_node->GetAssociatedPageNodes().size();
+  // TODO(chrisha/siggi): This should all be ripped out / refactored.
+  for (auto* process_node :
+       GraphImplOperations::GetAssociatedProcessNodes(this)) {
+    size_t pages_in_process =
+        GraphImplOperations::GetAssociatedPageNodes(process_node).size();
     DCHECK_LE(1u, pages_in_process);
     cpu_usage += process_node->cpu_usage() / pages_in_process;
   }
@@ -173,16 +153,6 @@
   return PerformanceManagerClock::NowTicks() - visibility_change_time_;
 }
 
-std::vector<FrameNodeImpl*> PageNodeImpl::GetFrameNodes() const {
-  std::vector<FrameNodeImpl*> all_frames;
-  ForAllFrameNodes([&all_frames](FrameNodeImpl* frame_node) -> bool {
-    all_frames.push_back(frame_node);
-    return true;
-  });
-
-  return all_frames;
-}
-
 FrameNodeImpl* PageNodeImpl::GetMainFrameNode() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (main_frame_nodes_.empty())
@@ -376,21 +346,6 @@
   return main_frame_url();
 }
 
-bool PageNodeImpl::HasFrame(FrameNodeImpl* frame_node) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  bool has_node = false;
-  ForAllFrameNodes([&has_node, &frame_node](FrameNodeImpl* node) -> bool {
-    if (node != frame_node)
-      return true;
-
-    has_node = true;
-    return false;
-  });
-
-  return has_node;
-}
-
 void PageNodeImpl::SetPageAlmostIdle(bool page_almost_idle) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   page_almost_idle_.SetAndMaybeNotify(this, page_almost_idle);
@@ -408,12 +363,13 @@
         resource_coordinator::mojom::InterventionPolicy::kUnknown;
 }
 
+// TODO(chrisha): Move this all out to a decorator.
 void PageNodeImpl::MaybeInvalidateInterventionPolicies(
     FrameNodeImpl* frame_node,
     bool adding_frame) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Ensure that the frame was already added or removed as expected.
-  DCHECK(!adding_frame || HasFrame(frame_node));
+  DCHECK(!adding_frame || GraphImplOperations::HasFrame(this, frame_node));
 
   // Determine whether or not the frames had all reported prior to this change.
   const size_t prior_frame_count = frame_node_count_ + (adding_frame ? -1 : 1);
@@ -447,36 +403,30 @@
 
   resource_coordinator::mojom::InterventionPolicy policy =
       resource_coordinator::mojom::InterventionPolicy::kDefault;
-  ForAllFrameNodes([&policy, &kIndex](FrameNodeImpl* frame) -> bool {
-    // No frame should have an unknown policy, as aggregation should only be
-    // invoked after all frames have checked in.
-    DCHECK_NE(resource_coordinator::mojom::InterventionPolicy::kUnknown,
-              frame->intervention_policy_[kIndex]);
+  GraphImplOperations::VisitFrameTreePreOrder(
+      this, [&policy, &kIndex](FrameNodeImpl* frame) -> bool {
+        // No frame should have an unknown policy, as aggregation should only be
+        // invoked after all frames have checked in.
+        DCHECK_NE(resource_coordinator::mojom::InterventionPolicy::kUnknown,
+                  frame->intervention_policy_[kIndex]);
 
-    // If any frame opts out then the whole frame tree opts out, even if other
-    // frames have opted in.
-    if (frame->intervention_policy_[kIndex] ==
-        resource_coordinator::mojom::InterventionPolicy::kOptOut) {
-      policy = resource_coordinator::mojom::InterventionPolicy::kOptOut;
-      return false;
-    }
+        // If any frame opts out then the whole frame tree opts out, even if
+        // other frames have opted in.
+        if (frame->intervention_policy_[kIndex] ==
+            resource_coordinator::mojom::InterventionPolicy::kOptOut) {
+          policy = resource_coordinator::mojom::InterventionPolicy::kOptOut;
+          return false;
+        }
 
-    // If any frame opts in and none opt out, then the whole tree opts in.
-    if (frame->intervention_policy_[kIndex] ==
-        resource_coordinator::mojom::InterventionPolicy::kOptIn) {
-      policy = resource_coordinator::mojom::InterventionPolicy::kOptIn;
-    }
-    return true;
-  });
+        // If any frame opts in and none opt out, then the whole tree opts in.
+        if (frame->intervention_policy_[kIndex] ==
+            resource_coordinator::mojom::InterventionPolicy::kOptIn) {
+          policy = resource_coordinator::mojom::InterventionPolicy::kOptIn;
+        }
+        return true;
+      });
 
   intervention_policy_[kIndex] = policy;
 }
 
-template <typename MapFunction>
-void PageNodeImpl::ForAllFrameNodes(MapFunction map_function) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (auto* main_frame_node : main_frame_nodes_)
-    ForFrameAndDescendents(main_frame_node, map_function);
-}
-
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.h b/chrome/browser/performance_manager/graph/page_node_impl.h
index 2724985..3c5ce1d 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl.h
+++ b/chrome/browser/performance_manager/graph/page_node_impl.h
@@ -21,8 +21,6 @@
 namespace performance_manager {
 
 class FrameNodeImpl;
-class PageNodeImpl;
-class ProcessNodeImpl;
 
 class PageNodeImpl : public PublicNodeImpl<PageNodeImpl, PageNode>,
                      public TypedNodeBase<PageNodeImpl,
@@ -51,15 +49,11 @@
                                       int64_t navigation_id,
                                       const GURL& url);
 
-  // There is no direct relationship between processes and pages. However,
-  // frames are accessible by both processes and frames, so we find all of the
-  // processes that are reachable from the pages's accessible frames.
-  base::flat_set<ProcessNodeImpl*> GetAssociatedProcessNodes() const;
-
   // Returns the average CPU usage that can be attributed to this page over the
   // last measurement period. CPU usage is expressed as the average percentage
   // of cores occupied over the last measurement interval. One core fully
   // occupied would be 100, while two cores at 5% each would be 10.
+  // TODO(chrisha): Make this 1.0 for 100%, and 0.1 for 10%.
   double GetCPUUsage() const;
 
   // Returns 0 if no navigation has happened, otherwise returns the time since
@@ -71,8 +65,6 @@
   // page node.
   base::TimeDelta TimeSinceLastVisibilityChange() const;
 
-  std::vector<FrameNodeImpl*> GetFrameNodes() const;
-
   // Returns the current main frame node (if there is one), otherwise returns
   // any of the potentially multiple main frames that currently exist. If there
   // are no main frames at the moment, returns nullptr.
@@ -153,9 +145,6 @@
   void JoinGraph() override;
   void LeaveGraph() override;
 
-  // Returns true iff |frame_node| is in the current frame hierarchy.
-  bool HasFrame(FrameNodeImpl* frame_node);
-
   void SetPageAlmostIdle(bool page_almost_idle);
   void SetLifecycleState(LifecycleState lifecycle_state);
 
@@ -175,10 +164,6 @@
   void RecomputeInterventionPolicy(
       resource_coordinator::mojom::PolicyControlledIntervention intervention);
 
-  // Invokes |map_function| for all frame nodes in this pages frame tree.
-  template <typename MapFunction>
-  void ForAllFrameNodes(MapFunction map_function) const;
-
   // The WebContentsProxy associated with this page.
   const WebContentsProxy contents_proxy_;
 
@@ -267,7 +252,7 @@
   ObservedProperty::NotifiesOnlyOnChanges<
       LifecycleState,
       &GraphImplObserver::OnLifecycleStateChanged,
-      &PageNodeObserver::OnLifecycleStateChanged>
+      &PageNodeObserver::OnPageLifecycleStateChanged>
       lifecycle_state_{LifecycleState::kRunning};
 
   // Storage for PageAlmostIdle user data.
diff --git a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
index 95d213bf..51d10f0 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/stl_util.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "chrome/browser/performance_manager/graph/frame_node_impl.h"
+#include "chrome/browser/performance_manager/graph/graph_impl_operations.h"
 #include "chrome/browser/performance_manager/graph/graph_test_harness.h"
 #include "chrome/browser/performance_manager/graph/mock_graphs.h"
 #include "chrome/browser/performance_manager/graph/page_node_impl.h"
@@ -60,7 +61,7 @@
       process_node.get(), page_node.get(), parent_frame.get(), 2);
 
   // Validate that all frames are tallied to the page.
-  EXPECT_EQ(3u, page_node->GetFrameNodes().size());
+  EXPECT_EQ(3u, GraphImplOperations::GetFrameNodes(page_node.get()).size());
 }
 
 TEST_F(PageNodeImplTest, RemoveFrame) {
@@ -70,14 +71,15 @@
                                               page_node.get(), nullptr, 0);
 
   // Ensure correct page-frame relationship has been established.
-  EXPECT_EQ(1u, page_node->GetFrameNodes().size());
-  EXPECT_TRUE(base::Contains(page_node->GetFrameNodes(), frame_node.get()));
+  auto frame_nodes = GraphImplOperations::GetFrameNodes(page_node.get());
+  EXPECT_EQ(1u, frame_nodes.size());
+  EXPECT_TRUE(base::Contains(frame_nodes, frame_node.get()));
   EXPECT_EQ(page_node.get(), frame_node->page_node());
 
   frame_node.reset();
 
   // Parent-child relationships should no longer exist.
-  EXPECT_EQ(0u, page_node->GetFrameNodes().size());
+  EXPECT_EQ(0u, GraphImplOperations::GetFrameNodes(page_node.get()).size());
 }
 
 TEST_F(PageNodeImplTest, CalculatePageCPUUsageForSinglePageInSingleProcess) {
@@ -407,7 +409,7 @@
   MOCK_METHOD1(OnIsVisibleChanged, void(const PageNode*));
   MOCK_METHOD1(OnIsLoadingChanged, void(const PageNode*));
   MOCK_METHOD1(OnUkmSourceIdChanged, void(const PageNode*));
-  MOCK_METHOD1(OnLifecycleStateChanged, void(const PageNode*));
+  MOCK_METHOD1(OnPageLifecycleStateChanged, void(const PageNode*));
   MOCK_METHOD1(OnPageAlmostIdleChanged, void(const PageNode*));
   MOCK_METHOD1(OnMainFrameNavigationCommitted, void(const PageNode*));
   MOCK_METHOD1(OnTitleUpdated, void(const PageNode*));
@@ -462,7 +464,7 @@
   page_node->SetUkmSourceId(static_cast<ukm::SourceId>(0x1234));
   EXPECT_EQ(raw_page_node, obs.TakeNotifiedPageNode());
 
-  EXPECT_CALL(obs, OnLifecycleStateChanged(_))
+  EXPECT_CALL(obs, OnPageLifecycleStateChanged(_))
       .WillOnce(Invoke(&obs, &MockObserver::SetNotifiedPageNode));
   page_node->SetLifecycleStateForTesting(PageNodeImpl::LifecycleState::kFrozen);
   EXPECT_EQ(raw_page_node, obs.TakeNotifiedPageNode());
diff --git a/chrome/browser/performance_manager/graph/process_node_impl.cc b/chrome/browser/performance_manager/graph/process_node_impl.cc
index 2eaf4a9..f72a014 100644
--- a/chrome/browser/performance_manager/graph/process_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/process_node_impl.cc
@@ -80,23 +80,11 @@
   SetProcessImpl(std::move(process), pid, launch_time);
 }
 
-const base::flat_set<FrameNodeImpl*>& ProcessNodeImpl::GetFrameNodes() const {
+const base::flat_set<FrameNodeImpl*>& ProcessNodeImpl::frame_nodes() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return frame_nodes_;
 }
 
-// There is currently not a direct relationship between processes and
-// pages. However, frames are children of both processes and frames, so we
-// find all of the pages that are reachable from the process's child
-// frames.
-base::flat_set<PageNodeImpl*> ProcessNodeImpl::GetAssociatedPageNodes() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::flat_set<PageNodeImpl*> page_nodes;
-  for (auto* frame_node : frame_nodes_)
-    page_nodes.insert(frame_node->page_node());
-  return page_nodes;
-}
-
 PageNodeImpl* ProcessNodeImpl::GetPageNodeIfExclusive() const {
   PageNodeImpl* page_node = nullptr;
   for (auto* frame_node : frame_nodes_) {
@@ -128,6 +116,71 @@
   cumulative_cpu_usage_ = base::TimeDelta();
 }
 
+base::ProcessId ProcessNodeImpl::GetProcessId() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return process_id();
+}
+
+const base::Process& ProcessNodeImpl::GetProcess() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return process();
+}
+
+base::Time ProcessNodeImpl::GetLaunchTime() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return launch_time();
+}
+
+base::Optional<int32_t> ProcessNodeImpl::GetExitStatus() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return exit_status();
+}
+
+void ProcessNodeImpl::VisitFrameNodes(const FrameNodeVisitor& visitor) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (auto* frame_impl : frame_nodes()) {
+    const FrameNode* frame = frame_impl;
+    if (!visitor.Run(frame))
+      return;
+  }
+}
+
+base::flat_set<const FrameNode*> ProcessNodeImpl::GetFrameNodes() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::flat_set<const FrameNode*> frames;
+  const base::flat_set<FrameNodeImpl*>& frame_impls = frame_nodes();
+  for (auto* frame_impl : frame_impls) {
+    const FrameNode* frame = frame_impl;
+    frames.insert(frame);
+  }
+  return frames;
+}
+
+base::TimeDelta ProcessNodeImpl::GetExpectedTaskQueueingDuration() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return expected_task_queueing_duration();
+}
+
+bool ProcessNodeImpl::GetMainThreadTaskLoadIsLow() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return main_thread_task_load_is_low();
+}
+
+double ProcessNodeImpl::GetCpuUsage() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return cpu_usage();
+}
+
+base::TimeDelta ProcessNodeImpl::GetCumulativeCpuUsage() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return cumulative_cpu_usage();
+}
+
+uint64_t ProcessNodeImpl::GetPrivateFootprintKb() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return private_footprint_kb();
+}
+
 void ProcessNodeImpl::OnAllFramesInProcessFrozen() {
   for (auto& observer : observers())
     observer.OnAllFramesInProcessFrozen(this);
diff --git a/chrome/browser/performance_manager/graph/process_node_impl.h b/chrome/browser/performance_manager/graph/process_node_impl.h
index a3c2247..066ad9d 100644
--- a/chrome/browser/performance_manager/graph/process_node_impl.h
+++ b/chrome/browser/performance_manager/graph/process_node_impl.h
@@ -69,8 +69,7 @@
   }
   base::TimeDelta cumulative_cpu_usage() const { return cumulative_cpu_usage_; }
 
-  const base::flat_set<FrameNodeImpl*>& GetFrameNodes() const;
-  base::flat_set<PageNodeImpl*> GetAssociatedPageNodes() const;
+  const base::flat_set<FrameNodeImpl*>& frame_nodes() const;
 
   // If this process is associated with only one page, returns that page.
   // Otherwise, returns nullptr.
@@ -110,6 +109,20 @@
  private:
   friend class FrozenFrameAggregatorAccess;
 
+  // ProcessNode implementation. These are private so that users of the impl use
+  // the private getters rather than the public interface.
+  base::ProcessId GetProcessId() const override;
+  const base::Process& GetProcess() const override;
+  base::Time GetLaunchTime() const override;
+  base::Optional<int32_t> GetExitStatus() const override;
+  void VisitFrameNodes(const FrameNodeVisitor& visitor) const override;
+  base::flat_set<const FrameNode*> GetFrameNodes() const override;
+  base::TimeDelta GetExpectedTaskQueueingDuration() const override;
+  bool GetMainThreadTaskLoadIsLow() const override;
+  double GetCpuUsage() const override;
+  base::TimeDelta GetCumulativeCpuUsage() const override;
+  uint64_t GetPrivateFootprintKb() const override;
+
   void OnAllFramesInProcessFrozen();
 
   void LeaveGraph() override;
diff --git a/chrome/browser/performance_manager/graph/process_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/process_node_impl_unittest.cc
index b114957..35539007 100644
--- a/chrome/browser/performance_manager/graph/process_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/process_node_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/performance_manager/graph/process_node_impl.h"
 
 #include "base/process/process.h"
+#include "base/test/bind_test_util.h"
 #include "chrome/browser/performance_manager/graph/frame_node_impl.h"
 #include "chrome/browser/performance_manager/graph/graph_test_harness.h"
 #include "chrome/browser/performance_manager/graph/mock_graphs.h"
@@ -179,4 +180,66 @@
   graph()->RemoveProcessNodeObserver(&obs);
 }
 
+TEST_F(ProcessNodeImplTest, PublicInterface) {
+  auto process_node = CreateNode<ProcessNodeImpl>();
+  const ProcessNode* public_process_node = process_node.get();
+
+  // Create a small frame-tree so that GetFrameNodes can be well tested.
+  auto page_node = CreateNode<PageNodeImpl>();
+  auto main_frame_node =
+      CreateNode<FrameNodeImpl>(process_node.get(), page_node.get());
+  auto child_frame_node = CreateNode<FrameNodeImpl>(
+      process_node.get(), page_node.get(), main_frame_node.get());
+
+  // Simply test that the public interface impls yield the same result as their
+  // private counterpart.
+
+  const base::Process self = base::Process::Current();
+  const base::Time launch_time = base::Time::Now();
+  process_node->SetProcess(self.Duplicate(), launch_time);
+  EXPECT_EQ(process_node->process_id(), public_process_node->GetProcessId());
+  EXPECT_EQ(&process_node->process(), &public_process_node->GetProcess());
+  EXPECT_EQ(process_node->launch_time(), public_process_node->GetLaunchTime());
+
+  constexpr int32_t kExitStatus = 0xF00;
+  process_node->SetProcessExitStatus(kExitStatus);
+  EXPECT_EQ(process_node->exit_status(), public_process_node->GetExitStatus());
+
+  const auto& frame_nodes = process_node->frame_nodes();
+  auto public_frame_nodes = public_process_node->GetFrameNodes();
+  EXPECT_EQ(frame_nodes.size(), public_frame_nodes.size());
+  for (const auto* frame_node : frame_nodes) {
+    const FrameNode* public_frame_node = frame_node;
+    EXPECT_TRUE(base::Contains(public_frame_nodes, public_frame_node));
+  }
+
+  decltype(public_frame_nodes) visited_frame_nodes;
+  public_process_node->VisitFrameNodes(base::BindLambdaForTesting(
+      [&visited_frame_nodes](const FrameNode* frame_node) -> bool {
+        visited_frame_nodes.insert(frame_node);
+        return true;
+      }));
+  EXPECT_EQ(public_frame_nodes, visited_frame_nodes);
+
+  process_node->SetExpectedTaskQueueingDuration(
+      base::TimeDelta::FromSeconds(1));
+  EXPECT_EQ(process_node->expected_task_queueing_duration(),
+            public_process_node->GetExpectedTaskQueueingDuration());
+
+  process_node->SetMainThreadTaskLoadIsLow(true);
+  EXPECT_EQ(process_node->main_thread_task_load_is_low(),
+            public_process_node->GetMainThreadTaskLoadIsLow());
+
+  process_node->SetCPUUsage(0.5);
+  EXPECT_EQ(process_node->cpu_usage(), public_process_node->GetCpuUsage());
+
+  process_node->set_cumulative_cpu_usage(base::TimeDelta::FromSeconds(1));
+  EXPECT_EQ(process_node->cumulative_cpu_usage(),
+            public_process_node->GetCumulativeCpuUsage());
+
+  process_node->set_private_footprint_kb(628);
+  EXPECT_EQ(process_node->private_footprint_kb(),
+            public_process_node->GetPrivateFootprintKb());
+}
+
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/system_node_impl.cc b/chrome/browser/performance_manager/graph/system_node_impl.cc
index 6a36b125..5b31562b 100644
--- a/chrome/browser/performance_manager/graph/system_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/system_node_impl.cc
@@ -12,6 +12,7 @@
 #include "base/process/process_handle.h"
 #include "chrome/browser/performance_manager/graph/frame_node_impl.h"
 #include "chrome/browser/performance_manager/graph/graph_impl.h"
+#include "chrome/browser/performance_manager/graph/graph_impl_operations.h"
 #include "chrome/browser/performance_manager/graph/page_node_impl.h"
 #include "chrome/browser/performance_manager/graph/process_node_impl.h"
 
@@ -65,7 +66,7 @@
 
       // Distribute the CPU delta to the pages that own the frames in this
       // process.
-      base::flat_set<FrameNodeImpl*> frames = process->GetFrameNodes();
+      const auto& frames = process->frame_nodes();
       if (!frames.empty()) {
         // To make sure we don't systemically truncate the remainder of the
         // delta, simply subtract the remainder and "hold it back" from the
@@ -137,12 +138,12 @@
   // Iterate through the pages involved to distribute the memory to them.
   for (PageNodeImpl* page : pages) {
     uint64_t private_footprint_kb_sum = 0;
-    const auto& frames = page->GetFrameNodes();
+    auto frames = GraphImplOperations::GetFrameNodes(page);
     for (FrameNodeImpl* frame : frames) {
       ProcessNodeImpl* process = frame->process_node();
       if (process) {
         private_footprint_kb_sum +=
-            process->private_footprint_kb() / process->GetFrameNodes().size();
+            process->private_footprint_kb() / process->frame_nodes().size();
       }
     }
 
diff --git a/chrome/browser/performance_manager/observers/metrics_collector.cc b/chrome/browser/performance_manager/observers/metrics_collector.cc
index bf4bbe6..4f40cee 100644
--- a/chrome/browser/performance_manager/observers/metrics_collector.cc
+++ b/chrome/browser/performance_manager/observers/metrics_collector.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/performance_manager/graph/frame_node_impl.h"
 #include "chrome/browser/performance_manager/graph/graph_impl.h"
+#include "chrome/browser/performance_manager/graph/graph_impl_operations.h"
 #include "chrome/browser/performance_manager/graph/page_node_impl.h"
 #include "chrome/browser/performance_manager/graph/process_node_impl.h"
 #include "chrome/browser/performance_manager/performance_manager_clock.h"
@@ -36,8 +37,11 @@
 // associated with a PageNode.
 size_t GetNumCoresidentTabs(const PageNodeImpl* page_node) {
   std::set<NodeBase*> coresident_tabs;
-  for (auto* process_node : page_node->GetAssociatedProcessNodes()) {
-    for (auto* associated_page_node : process_node->GetAssociatedPageNodes()) {
+  auto process_nodes =
+      GraphImplOperations::GetAssociatedProcessNodes(page_node);
+  for (auto* process_node : process_nodes) {
+    auto page_nodes = GraphImplOperations::GetAssociatedPageNodes(process_node);
+    for (auto* associated_page_node : page_nodes) {
       coresident_tabs.insert(associated_page_node);
     }
   }
@@ -129,7 +133,7 @@
   // the process that was sampled.
   const base::TimeDelta& sample =
       process_node->expected_task_queueing_duration();
-  for (auto* frame_node : process_node->GetFrameNodes()) {
+  for (auto* frame_node : process_node->frame_nodes()) {
     if (!frame_node->IsMainFrame())
       continue;
     auto* page_node = frame_node->page_node();
diff --git a/chrome/browser/performance_manager/performance_manager.cc b/chrome/browser/performance_manager/performance_manager.cc
index 75a83825..f7a54a0 100644
--- a/chrome/browser/performance_manager/performance_manager.cc
+++ b/chrome/browser/performance_manager/performance_manager.cc
@@ -262,10 +262,11 @@
     std::unique_ptr<service_manager::Connector> connector) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  graph_.PassToGraph(std::make_unique<FrozenFrameAggregator>());
+
   // Register new |GraphImplObserver| implementations here.
   RegisterObserver(std::make_unique<MetricsCollector>());
   RegisterObserver(std::make_unique<PageAlmostIdleDecorator>());
-  RegisterObserver(std::make_unique<FrozenFrameAggregator>());
   RegisterObserver(std::make_unique<IsolationContextMetrics>());
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/performance_manager/performance_manager_tab_helper_unittest.cc b/chrome/browser/performance_manager/performance_manager_tab_helper_unittest.cc
index d7b5ab7..4a7637e6 100644
--- a/chrome/browser/performance_manager/performance_manager_tab_helper_unittest.cc
+++ b/chrome/browser/performance_manager/performance_manager_tab_helper_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/stl_util.h"
 #include "base/test/bind_test_util.h"
 #include "chrome/browser/performance_manager/graph/frame_node_impl.h"
+#include "chrome/browser/performance_manager/graph/graph_impl_operations.h"
 #include "chrome/browser/performance_manager/graph/page_node_impl.h"
 #include "chrome/browser/performance_manager/performance_manager_test_harness.h"
 #include "chrome/browser/performance_manager/render_process_user_data.h"
@@ -87,15 +88,16 @@
         auto* page = graph->GetAllPageNodes()[0];
 
         // Extra RPHs can and most definitely do exist.
-        auto associated_process_nodes = page->GetAssociatedProcessNodes();
+        auto associated_process_nodes =
+            GraphImplOperations::GetAssociatedProcessNodes(page);
         EXPECT_GE(graph->GetAllProcessNodes().size(),
                   associated_process_nodes.size());
-        EXPECT_GE(num_hosts, page->GetAssociatedProcessNodes().size());
+        EXPECT_GE(num_hosts, associated_process_nodes.size());
 
         for (auto* process_node : associated_process_nodes)
           EXPECT_TRUE(base::Contains(process_nodes, process_node));
 
-        EXPECT_EQ(4u, page->GetFrameNodes().size());
+        EXPECT_EQ(4u, GraphImplOperations::GetFrameNodes(page).size());
         ASSERT_EQ(1u, page->main_frame_nodes().size());
 
         auto* main_frame = page->GetMainFrameNode();
diff --git a/chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.cc b/chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.cc
index 34f76a5d..55ed5181 100644
--- a/chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.cc
@@ -7,6 +7,7 @@
 #include <limits>
 #include <string>
 
+#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/hash/md5.h"
@@ -20,12 +21,15 @@
 #include "build/build_config.h"
 #include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
+#include "third_party/leveldatabase/src/include/leveldb/env.h"
 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
 
 namespace performance_manager {
 
 namespace {
 
+bool g_use_in_memory_db_for_testing = false;
+
 // The name of the following histograms is the same as the one used in the
 // //c/b/resource_coordinator version of this file. It's fine to keep the same
 // name as these 2 codepath will never be enabled at the same time. These
@@ -170,6 +174,10 @@
   // Implementation for the OpenOrCreateDatabase function.
   OpeningType OpenOrCreateDatabaseImpl();
 
+  // A levelDB environment that gets used for testing. This allows using an
+  // in-memory database when needed.
+  std::unique_ptr<leveldb::Env> env_for_testing_;
+
   // The on disk location of the database.
   const base::FilePath db_path_;
   // The connection to the LevelDB database.
@@ -362,6 +370,12 @@
 
   leveldb_env::Options options;
   options.create_if_missing = true;
+
+  if (g_use_in_memory_db_for_testing) {
+    env_for_testing_ = leveldb_chrome::NewMemEnv("LevelDBSiteDataStore");
+    options.env = env_for_testing_.get();
+  }
+
   leveldb::Status status =
       leveldb_env::OpenDB(options, db_path_.AsUTF8Unsafe(), &db_);
 
@@ -476,4 +490,11 @@
   return async_helper_->GetDBForTesting();
 }
 
+// static
+std::unique_ptr<base::AutoReset<bool>>
+LevelDBSiteDataStore::UseInMemoryDBForTesting() {
+  return std::make_unique<base::AutoReset<bool>>(
+      &g_use_in_memory_db_for_testing, true);
+}
+
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.h b/chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.h
index f3c54c0..388b739 100644
--- a/chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.h
+++ b/chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_LEVELDB_SITE_DATA_STORE_H_
 #define CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_LEVELDB_SITE_DATA_STORE_H_
 
+#include "base/auto_reset.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/sequence_checker.h"
@@ -51,6 +52,10 @@
   // thread safe.
   leveldb::DB* GetDBForTesting();
 
+  // Make the new instances of this class use an in memory database rather than
+  // creating it on disk.
+  static std::unique_ptr<base::AutoReset<bool>> UseInMemoryDBForTesting();
+
   static const size_t kDbVersion;
   static const char kDbMetadataKey[];
 
diff --git a/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache.cc b/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache.cc
new file mode 100644
index 0000000..e9e0ec5
--- /dev/null
+++ b/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache.cc
@@ -0,0 +1,85 @@
+// Copyright 2019 The Chromium 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/performance_manager/persistence/site_data/non_recording_site_data_cache.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/performance_manager/persistence/site_data/noop_site_data_writer.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_reader.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_writer.h"
+
+namespace performance_manager {
+
+NonRecordingSiteDataCache::NonRecordingSiteDataCache(
+    const std::string& browser_context_id,
+    SiteDataCacheInspector* data_cache_inspector,
+    SiteDataCache* data_cache_for_readers)
+    : data_cache_for_readers_(data_cache_for_readers),
+      data_cache_inspector_(data_cache_inspector),
+      browser_context_id_(browser_context_id) {
+  DCHECK(data_cache_for_readers_);
+  // Register the debug interface against the browser context.
+  SiteDataCacheFactory::GetInstance()->SetDataCacheInspectorForBrowserContext(
+      this, browser_context_id_);
+}
+
+NonRecordingSiteDataCache::~NonRecordingSiteDataCache() {
+  SiteDataCacheFactory::GetInstance()->SetDataCacheInspectorForBrowserContext(
+      nullptr, browser_context_id_);
+}
+
+std::unique_ptr<SiteDataReader> NonRecordingSiteDataCache::GetReaderForOrigin(
+    const url::Origin& origin) {
+  return data_cache_for_readers_->GetReaderForOrigin(origin);
+}
+
+std::unique_ptr<SiteDataWriter> NonRecordingSiteDataCache::GetWriterForOrigin(
+    const url::Origin& origin,
+    performance_manager::TabVisibility tab_visibility) {
+  // Return a fake data writer.
+  SiteDataWriter* writer = new NoopSiteDataWriter();
+  return base::WrapUnique(writer);
+}
+
+bool NonRecordingSiteDataCache::IsRecordingForTesting() {
+  return false;
+}
+
+const char* NonRecordingSiteDataCache::GetDataCacheName() {
+  return "NonRecordingSiteDataCache";
+}
+
+std::vector<url::Origin> NonRecordingSiteDataCache::GetAllInMemoryOrigins() {
+  if (!data_cache_inspector_)
+    return std::vector<url::Origin>();
+
+  return data_cache_inspector_->GetAllInMemoryOrigins();
+}
+
+void NonRecordingSiteDataCache::GetDataStoreSize(
+    DataStoreSizeCallback on_have_data) {
+  if (!data_cache_inspector_) {
+    std::move(on_have_data).Run(base::nullopt, base::nullopt);
+    return;
+  }
+
+  data_cache_inspector_->GetDataStoreSize(std::move(on_have_data));
+}
+
+bool NonRecordingSiteDataCache::GetDataForOrigin(
+    const url::Origin& origin,
+    bool* is_dirty,
+    std::unique_ptr<SiteDataProto>* data) {
+  if (!data_cache_inspector_)
+    return false;
+
+  return data_cache_inspector_->GetDataForOrigin(origin, is_dirty, data);
+}
+
+NonRecordingSiteDataCache* NonRecordingSiteDataCache::GetDataCache() {
+  return this;
+}
+
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache.h b/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache.h
new file mode 100644
index 0000000..b7d7ddd
--- /dev/null
+++ b/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache.h
@@ -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.
+
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_NON_RECORDING_SITE_DATA_CACHE_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_NON_RECORDING_SITE_DATA_CACHE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_inspector.h"
+
+namespace performance_manager {
+
+// Implementation of a SiteDataCache that ensures that no data gets persisted.
+//
+// This class should be used for off the record profiles.
+class NonRecordingSiteDataCache : public SiteDataCache,
+                                  public SiteDataCacheInspector {
+ public:
+  NonRecordingSiteDataCache(const std::string& browser_context_id,
+                            SiteDataCacheInspector* data_cache_inspector,
+                            SiteDataCache* data_cache_for_readers);
+  ~NonRecordingSiteDataCache() override;
+
+  // SiteDataCache:
+  std::unique_ptr<SiteDataReader> GetReaderForOrigin(
+      const url::Origin& origin) override;
+  std::unique_ptr<SiteDataWriter> GetWriterForOrigin(
+      const url::Origin& origin,
+      performance_manager::TabVisibility tab_visibility) override;
+  bool IsRecordingForTesting() override;
+
+  // SiteDataCacheInspector:
+  const char* GetDataCacheName() override;
+  std::vector<url::Origin> GetAllInMemoryOrigins() override;
+  void GetDataStoreSize(DataStoreSizeCallback on_have_data) override;
+  bool GetDataForOrigin(const url::Origin& origin,
+                        bool* is_dirty,
+                        std::unique_ptr<SiteDataProto>* data) override;
+  NonRecordingSiteDataCache* GetDataCache() override;
+
+ private:
+  // The data cache to use to create the readers served by this data store. E.g.
+  // during an incognito session it should point to the data cache used by the
+  // parent session.
+  SiteDataCache* data_cache_for_readers_;
+
+  // The inspector implementation this instance delegates to.
+  SiteDataCacheInspector* data_cache_inspector_;
+
+  // The ID of the browser context this data store is associated with.
+  const std::string browser_context_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(NonRecordingSiteDataCache);
+};
+
+}  // namespace performance_manager
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_NON_RECORDING_SITE_DATA_CACHE_H_
diff --git a/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc b/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc
new file mode 100644
index 0000000..5213a9ea
--- /dev/null
+++ b/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright 2019 The Chromium 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/performance_manager/persistence/site_data/non_recording_site_data_cache.h"
+
+#include "chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_impl.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_inspector.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace performance_manager {
+
+namespace {
+
+class NonRecordingSiteDataCacheTest : public testing::Test {
+ public:
+  NonRecordingSiteDataCacheTest()
+      : use_in_memory_db_for_testing_(
+            LevelDBSiteDataStore::UseInMemoryDBForTesting()),
+        factory_(SiteDataCacheFactory::CreateForTesting(
+            test_browser_thread_bundle_.GetMainThreadTaskRunner())),
+        off_the_record_profile_(parent_profile_.GetOffTheRecordProfile()) {}
+
+  ~NonRecordingSiteDataCacheTest() override { factory_.reset(); }
+
+  void SetUp() override {
+    recording_data_cache_ = base::WrapUnique(new SiteDataCacheImpl(
+        parent_profile_.UniqueId(), parent_profile_.GetPath()));
+
+    non_recording_data_cache_ = std::make_unique<NonRecordingSiteDataCache>(
+        off_the_record_profile_->UniqueId(), recording_data_cache_.get(),
+        recording_data_cache_.get());
+  }
+
+ protected:
+  const url::Origin kTestOrigin =
+      url::Origin::Create(GURL("http://www.foo.com"));
+
+  content::TestBrowserThreadBundle test_browser_thread_bundle_;
+
+  // Ensure that the database used by the data store owned by
+  // |recording_data_cache_| gets created in memory. This avoid having to wait
+  // for it to be fully closed before destroying |parent_profile_|.
+  std::unique_ptr<base::AutoReset<bool>> use_in_memory_db_for_testing_;
+
+  // The data cache factory that will be used by the caches tested here.
+  std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter> factory_;
+
+  // The on the record profile.
+  TestingProfile parent_profile_;
+  // An off the record profile owned by |parent_profile|.
+  Profile* off_the_record_profile_;
+
+  std::unique_ptr<SiteDataCacheImpl> recording_data_cache_;
+  std::unique_ptr<NonRecordingSiteDataCache> non_recording_data_cache_;
+};
+
+}  // namespace
+
+TEST_F(NonRecordingSiteDataCacheTest, EndToEnd) {
+  // Ensures that the observation made via a writer created by the non
+  // recording data cache aren't recorded.
+  auto reader = non_recording_data_cache_->GetReaderForOrigin(kTestOrigin);
+  EXPECT_TRUE(reader);
+  auto fake_writer = non_recording_data_cache_->GetWriterForOrigin(
+      kTestOrigin, performance_manager::TabVisibility::kBackground);
+  EXPECT_TRUE(fake_writer);
+  auto real_writer = recording_data_cache_->GetWriterForOrigin(
+      kTestOrigin, performance_manager::TabVisibility::kBackground);
+  EXPECT_TRUE(real_writer);
+
+  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
+            reader->UpdatesTitleInBackground());
+  fake_writer->NotifySiteLoaded();
+  fake_writer->NotifyUpdatesTitleInBackground();
+  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
+            reader->UpdatesTitleInBackground());
+
+  real_writer->NotifySiteLoaded();
+  real_writer->NotifyUpdatesTitleInBackground();
+  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
+            reader->UpdatesTitleInBackground());
+
+  // These unload events shouldn't be registered, make sure that they aren't by
+  // unloading the site more time than it has been loaded.
+  fake_writer->NotifySiteUnloaded();
+  fake_writer->NotifySiteUnloaded();
+
+  real_writer->NotifySiteUnloaded();
+}
+
+TEST_F(NonRecordingSiteDataCacheTest, InspectorWorks) {
+  // Make sure the inspector interface was registered at construction.
+  SiteDataCacheInspector* inspector = factory_->GetInspectorForBrowserContext(
+      off_the_record_profile_->UniqueId());
+  EXPECT_NE(nullptr, inspector);
+  EXPECT_EQ(non_recording_data_cache_.get(), inspector);
+
+  EXPECT_STREQ("NonRecordingSiteDataCache", inspector->GetDataCacheName());
+
+  // We expect an empty data cache at the outset.
+  EXPECT_EQ(0U, inspector->GetAllInMemoryOrigins().size());
+  std::unique_ptr<SiteDataProto> data;
+  bool is_dirty = false;
+  EXPECT_FALSE(inspector->GetDataForOrigin(kTestOrigin, &is_dirty, &data));
+  EXPECT_FALSE(is_dirty);
+  EXPECT_EQ(nullptr, data.get());
+
+  {
+    // Add an entry through the writing data cache, see that it's reflected in
+    // the inspector interface.
+    auto writer = recording_data_cache_->GetWriterForOrigin(
+        kTestOrigin, performance_manager::TabVisibility::kBackground);
+
+    EXPECT_EQ(1U, inspector->GetAllInMemoryOrigins().size());
+    EXPECT_TRUE(inspector->GetDataForOrigin(kTestOrigin, &is_dirty, &data));
+    EXPECT_FALSE(is_dirty);
+    ASSERT_NE(nullptr, data.get());
+
+    // Touch the underlying data, see that the dirty bit updates.
+    writer->NotifySiteLoaded();
+    EXPECT_TRUE(inspector->GetDataForOrigin(kTestOrigin, &is_dirty, &data));
+  }
+
+  // Make sure the interface is unregistered from the browser context on
+  // destruction.
+  non_recording_data_cache_.reset();
+  EXPECT_EQ(nullptr, factory_->GetInspectorForBrowserContext(
+                         off_the_record_profile_->UniqueId()));
+}
+
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/persistence/site_data/noop_site_data_writer.h b/chrome/browser/performance_manager/persistence/site_data/noop_site_data_writer.h
index 0542377..3284ef7d 100644
--- a/chrome/browser/performance_manager/persistence/site_data/noop_site_data_writer.h
+++ b/chrome/browser/performance_manager/persistence/site_data/noop_site_data_writer.h
@@ -30,8 +30,9 @@
       uint64_t private_footprint_kb_estimate) override;
 
  private:
-  // Private constructor, these objects are meant to be created by a noop site
-  // data store.
+  friend class NonRecordingSiteDataCache;
+  // Private constructor, these objects are meant to be created by a
+  // NonRecordingSiteDataCache.
   NoopSiteDataWriter();
 
   DISALLOW_COPY_AND_ASSIGN(NoopSiteDataWriter);
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.cc
index 3ca86dc..2ae7465 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.cc
@@ -9,8 +9,8 @@
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
 #include "base/task_runner_util.h"
-#include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/performance_manager/performance_manager.h"
+#include "chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_impl.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_inspector.h"
 #include "content/public/browser/browser_context.h"
@@ -30,7 +30,6 @@
     const scoped_refptr<base::SequencedTaskRunner> task_runner)
     : task_runner_(task_runner) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
 SiteDataCacheFactory::~SiteDataCacheFactory() {
@@ -68,19 +67,24 @@
 // static
 void SiteDataCacheFactory::OnBrowserContextCreatedOnUIThread(
     SiteDataCacheFactory* factory,
-    content::BrowserContext* browser_context) {
+    content::BrowserContext* browser_context,
+    content::BrowserContext* parent_context) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(factory);
 
   // As |factory| will be deleted on its task runner it's safe to pass the raw
   // pointer to BindOnce, it's guaranteed that this task will run before the
   // factory.
+  base::Optional<std::string> parent_context_id;
+  if (parent_context) {
+    DCHECK(browser_context->IsOffTheRecord());
+    parent_context_id = parent_context->UniqueId();
+  }
   factory->task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&SiteDataCacheFactory::OnBrowserContextCreated,
                      base::Unretained(factory), browser_context->UniqueId(),
-                     browser_context->GetPath(),
-                     browser_context->IsOffTheRecord()));
+                     browser_context->GetPath(), parent_context_id));
 }
 
 // static
@@ -97,28 +101,28 @@
                      base::Unretained(factory), browser_context->UniqueId()));
 }
 
-// static
 SiteDataCache* SiteDataCacheFactory::GetDataCacheForBrowserContext(
     const std::string& browser_context_id) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = data_cache_map_.find(browser_context_id);
   if (it != data_cache_map_.end())
     return it->second.get();
   return nullptr;
 }
 
-// static
 SiteDataCacheInspector* SiteDataCacheFactory::GetInspectorForBrowserContext(
     const std::string& browser_context_id) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = data_cache_inspector_map_.find(browser_context_id);
   if (it != data_cache_inspector_map_.end())
     return it->second;
   return nullptr;
 }
 
-// static
 void SiteDataCacheFactory::SetDataCacheInspectorForBrowserContext(
     SiteDataCacheInspector* inspector,
     const std::string& browser_context_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (inspector) {
     DCHECK_EQ(nullptr, GetInspectorForBrowserContext(browser_context_id));
     data_cache_inspector_map_.emplace(
@@ -132,14 +136,23 @@
 void SiteDataCacheFactory::OnBrowserContextCreated(
     const std::string& browser_context_id,
     const base::FilePath& context_path,
-    bool context_is_off_the_record) {
+    base::Optional<std::string> parent_context_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   DCHECK(!base::Contains(data_cache_map_, browser_context_id));
 
-  if (context_is_off_the_record) {
-    // TODO(sebmarchand): Add support for off-the-record contexts.
-    NOTREACHED();
+  if (parent_context_id) {
+    SiteDataCacheInspector* parent_debug =
+        GetInspectorForBrowserContext(parent_context_id.value());
+    DCHECK(parent_debug);
+    DCHECK(base::Contains(data_cache_map_, parent_context_id.value()));
+    SiteDataCache* data_cache_for_readers =
+        data_cache_map_[parent_context_id.value()].get();
+    DCHECK(data_cache_for_readers);
+    data_cache_map_.emplace(std::make_pair(
+        std::move(browser_context_id),
+        std::make_unique<NonRecordingSiteDataCache>(
+            browser_context_id, parent_debug, data_cache_for_readers)));
   } else {
     data_cache_map_.emplace(std::make_pair(
         std::move(browser_context_id),
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h
index 88bdf8cc..f27b4006 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache.h"
@@ -57,9 +58,14 @@
   // destroyed. They should be called from the UI thread, a task will then be
   // posted to the task_runner owned by |factory| to create the data store
   // associated with this browser context.
+  //
+  // If this browser context is inheriting from a parent context (e.g. if it's
+  // off the record) then this parent context should be specified via
+  // |parent_context|.
   static void OnBrowserContextCreatedOnUIThread(
       SiteDataCacheFactory* factory,
-      content::BrowserContext* browser_context);
+      content::BrowserContext* browser_context,
+      content::BrowserContext* parent_context);
   static void OnBrowserContextDestroyedOnUIThread(
       SiteDataCacheFactory* factory,
       content::BrowserContext* browser_context);
@@ -98,7 +104,7 @@
   // that runs on this object's task runner.
   void OnBrowserContextCreated(const std::string& browser_context_id,
                                const base::FilePath& context_path,
-                               bool context_is_off_the_record);
+                               base::Optional<std::string> parent_context_id);
   void OnBrowserContextDestroyed(const std::string& browser_context_id);
 
   // The task runner on which this object lives, this is expected to be the
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc
index 3a4c6a22..3c49d47 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc
@@ -41,7 +41,7 @@
 
 TEST_F(SiteDataCacheFactoryTest, EndToEnd) {
   SiteDataCacheFactory::OnBrowserContextCreatedOnUIThread(factory_.get(),
-                                                          &profile_);
+                                                          &profile_, nullptr);
 
   base::RunLoop run_loop;
   task_runner_->PostTask(
diff --git a/chrome/browser/performance_manager/public/graph/frame_node.h b/chrome/browser/performance_manager/public/graph/frame_node.h
index 23fe48b..85cde2a2 100644
--- a/chrome/browser/performance_manager/public/graph/frame_node.h
+++ b/chrome/browser/performance_manager/public/graph/frame_node.h
@@ -95,7 +95,8 @@
   // that makes sense.
   virtual const base::flat_set<const FrameNode*> GetChildFrameNodes() const = 0;
 
-  // Returns the current lifecycle state of this frame.
+  // Returns the current lifecycle state of this frame. See
+  // FrameNodeObserver::OnFrameLifecycleStateChanged.
   virtual LifecycleState GetLifecycleState() const = 0;
 
   // Returns true if this frame had a non-empty before-unload handler at the
@@ -152,7 +153,7 @@
   virtual void OnNetworkAlmostIdleChanged(const FrameNode* frame_node) = 0;
 
   // Invoked when the |lifecycle_state| property changes.
-  virtual void OnLifecycleStateChanged(const FrameNode* frame_node) = 0;
+  virtual void OnFrameLifecycleStateChanged(const FrameNode* frame_node) = 0;
 
   // Invoked when the |url| property changes.
   virtual void OnURLChanged(const FrameNode* frame_node) = 0;
@@ -180,7 +181,7 @@
   void OnBeforeFrameNodeRemoved(const FrameNode* frame_node) override {}
   void OnIsCurrentChanged(const FrameNode* frame_node) override {}
   void OnNetworkAlmostIdleChanged(const FrameNode* frame_node) override {}
-  void OnLifecycleStateChanged(const FrameNode* frame_node) override {}
+  void OnFrameLifecycleStateChanged(const FrameNode* frame_node) override {}
   void OnURLChanged(const FrameNode* frame_node) override {}
   void OnNonPersistentNotificationCreated(
       const FrameNode* frame_node) override {}
diff --git a/chrome/browser/performance_manager/public/graph/graph.h b/chrome/browser/performance_manager/public/graph/graph.h
index 5bceb6c0..0cc76500 100644
--- a/chrome/browser/performance_manager/public/graph/graph.h
+++ b/chrome/browser/performance_manager/public/graph/graph.h
@@ -6,12 +6,15 @@
 #define CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_H_
 
 #include <cstdint>
+#include <memory>
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 
 namespace performance_manager {
 
 class GraphObserver;
+class GraphOwned;
 class FrameNodeObserver;
 class PageNodeObserver;
 class ProcessNodeObserver;
@@ -42,6 +45,21 @@
   virtual void RemoveProcessNodeObserver(ProcessNodeObserver* observer) = 0;
   virtual void RemoveSystemNodeObserver(SystemNodeObserver* observer) = 0;
 
+  // For convenience, allows you to pass ownership of an object to the graph.
+  // Useful for attaching observers that will live with the graph until it dies.
+  // If you can name the object you can also take it back via "TakeFromGraph".
+  virtual void PassToGraph(std::unique_ptr<GraphOwned> graph_owned) = 0;
+  virtual std::unique_ptr<GraphOwned> TakeFromGraph(
+      GraphOwned* graph_owned) = 0;
+
+  // A TakeFromGraph helper that casts to a derived type. It is up to the caller
+  // to ensure that the cast is safe.
+  template <typename DerivedType>
+  std::unique_ptr<DerivedType> TakeFromGraphAs(GraphOwned* graph_owned) {
+    return base::WrapUnique(
+        static_cast<DerivedType*>(TakeFromGraph(graph_owned).release()));
+  }
+
   // The following functions are implementation detail and should not need to be
   // used by external clients. They provide the ability to safely downcast to
   // the underlying implementation.
@@ -59,18 +77,48 @@
   virtual ~GraphObserver();
 
   // Called before the |graph| associated with this observer disappears. This
-  // allows the observer to do any necessary cleanup work. Note that the graph
-  // is in its destructor while this is being called, so the observer should
-  // refrain from uselessly modifying the graph. This is intended to be used to
-  // facilitate lifetime management of observers.
-  // TODO(chrisha): Make this run before the constructor!
+  // allows the observer to do any necessary cleanup work. Note that the
+  // observer should remove itself from observing the graph using this
+  // callback.
+  // TODO(chrisha): Make this run before the destructor!
   // crbug.com/966840
-  virtual void OnBeforeGraphDestroyed(const Graph* graph) = 0;
+  virtual void OnBeforeGraphDestroyed(Graph* graph) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(GraphObserver);
 };
 
+// Helper class for passing ownership of objects to a graph.
+class GraphOwned {
+ public:
+  GraphOwned();
+  virtual ~GraphOwned();
+
+  // Called when the object is passed into the graph.
+  virtual void OnPassedToGraph(Graph* graph) = 0;
+
+  // Called when the object is removed from the graph, either via an explicit
+  // call to Graph::TakeFromGraph, or prior to the Graph being destroyed.
+  virtual void OnTakenFromGraph(Graph* graph) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GraphOwned);
+};
+
+// A default implementation of GraphOwned.
+class GraphOwnedDefaultImpl : public GraphOwned {
+ public:
+  GraphOwnedDefaultImpl();
+  ~GraphOwnedDefaultImpl() override;
+
+  // GraphOwned implementation:
+  void OnPassedToGraph(Graph* graph) override {}
+  void OnTakenFromGraph(Graph* graph) override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GraphOwnedDefaultImpl);
+};
+
 }  // namespace performance_manager
 
 #endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_H_
diff --git a/chrome/browser/performance_manager/public/graph/graph_operations.h b/chrome/browser/performance_manager/public/graph/graph_operations.h
new file mode 100644
index 0000000..e3567df0
--- /dev/null
+++ b/chrome/browser/performance_manager/public/graph/graph_operations.h
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_OPERATIONS_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_OPERATIONS_H_
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_set.h"
+
+namespace performance_manager {
+
+class FrameNode;
+class PageNode;
+class ProcessNode;
+
+// A collection of utilities for performing common queries and traversals on a
+// graph.
+struct GraphOperations {
+  using FrameNodeVisitor = base::Callback<bool(const FrameNode*)>;
+
+  // Returns the collection of page nodes that are associated with the given
+  // |process|. A page is associated with a process if the page's frame tree
+  // contains 1 or more frames hosted in the given |process|.
+  static base::flat_set<const PageNode*> GetAssociatedPageNodes(
+      const ProcessNode* process);
+
+  // Returns the collection of process nodes associated with the given |page|.
+  // A |process| is associated with a page if the page's frame tree contains 1
+  // or more frames hosted in that |process|.
+  static base::flat_set<const ProcessNode*> GetAssociatedProcessNodes(
+      const PageNode* page);
+
+  // Returns the collection of frame nodes associated with a page. This is
+  // returned in level order, with main frames first (level 0), main frame
+  // children next (level 1), all the way down to the deepest leaf frames.
+  static std::vector<const FrameNode*> GetFrameNodes(const PageNode* page);
+
+  // Traverse the frame tree of a |page| in the given order, invoking the
+  // provided |callable| for each frame node in the tree. If the visitor returns
+  // false then then the iteration is halted.
+  static void VisitFrameTreePreOrder(const PageNode* page,
+                                     const FrameNodeVisitor& visitor);
+  static void VisitFrameTreePostOrder(const PageNode* page,
+                                      const FrameNodeVisitor& visitor);
+
+  // Returns true if the given |frame| is in the frame tree associated with the
+  // given |page|.
+  static bool HasFrame(const PageNode* page, const FrameNode* frame);
+};
+
+}  // namespace performance_manager
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_OPERATIONS_H_
diff --git a/chrome/browser/performance_manager/public/graph/page_node.h b/chrome/browser/performance_manager/public/graph/page_node.h
index fe647859..1b2e951 100644
--- a/chrome/browser/performance_manager/public/graph/page_node.h
+++ b/chrome/browser/performance_manager/public/graph/page_node.h
@@ -47,7 +47,7 @@
 
   // Returns the lifecycle state of this page. This is aggregated from the
   // lifecycle state of each frame in the frame tree. See
-  // PageNodeObserver::OnLifecycleStateChanged.
+  // PageNodeObserver::OnPageLifecycleStateChanged.
   virtual LifecycleState GetLifecycleState() const = 0;
 
   // Returns the navigation ID associated with the last committed navigation
@@ -90,7 +90,7 @@
   virtual void OnUkmSourceIdChanged(const PageNode* page_node) = 0;
 
   // Invoked when the |lifecycle_state| property changes.
-  virtual void OnLifecycleStateChanged(const PageNode* page_node) = 0;
+  virtual void OnPageLifecycleStateChanged(const PageNode* page_node) = 0;
 
   // Invoked when the |page_almost_idle| property changes.
   virtual void OnPageAlmostIdleChanged(const PageNode* page_node) = 0;
@@ -127,7 +127,7 @@
   void OnIsVisibleChanged(const PageNode* page_node) override {}
   void OnIsLoadingChanged(const PageNode* page_node) override {}
   void OnUkmSourceIdChanged(const PageNode* page_node) override {}
-  void OnLifecycleStateChanged(const PageNode* page_node) override {}
+  void OnPageLifecycleStateChanged(const PageNode* page_node) override {}
   void OnPageAlmostIdleChanged(const PageNode* page_node) override {}
   void OnMainFrameNavigationCommitted(const PageNode* page_node) override {}
   void OnTitleUpdated(const PageNode* page_node) override {}
diff --git a/chrome/browser/performance_manager/public/graph/process_node.h b/chrome/browser/performance_manager/public/graph/process_node.h
index dcd0548b..63fa26c 100644
--- a/chrome/browser/performance_manager/public/graph/process_node.h
+++ b/chrome/browser/performance_manager/public/graph/process_node.h
@@ -5,11 +5,19 @@
 #ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_PROCESS_NODE_H_
 #define CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_PROCESS_NODE_H_
 
+#include "base/callback_forward.h"
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
+#include "base/process/process.h"
 #include "chrome/browser/performance_manager/public/graph/node.h"
 
+namespace base {
+class Process;
+}  // namespace base
+
 namespace performance_manager {
 
+class FrameNode;
 class ProcessNodeObserver;
 
 // A process node follows the lifetime of a RenderProcessHost.
@@ -22,14 +30,68 @@
 //    process fails to start, this state may not occur.
 // 3. Process died or failed to start, have exit status.
 // 4. Back to 2.
+//
+// It is only valid to access this object on the sequence of the graph that owns
+// it.
 class ProcessNode : public Node {
  public:
+  using FrameNodeVisitor = base::Callback<bool(const FrameNode*)>;
   using Observer = ProcessNodeObserver;
   class ObserverDefaultImpl;
 
   ProcessNode();
   ~ProcessNode() override;
 
+  // Returns the process ID associated with this process. Use this in preference
+  // to querying GetProcess.Pid(). It's always valid to access, but will return
+  // kNullProcessId if the process has yet started. It will also retain the
+  // process ID for a process that has exited (at least until the underlying
+  // RenderProcessHost gets reused in the case of a crash). Refrain from using
+  // this as a unique identifier as on some platforms PIDs are reused
+  // aggressively. See GetLaunchTime for more information.
+  virtual base::ProcessId GetProcessId() const = 0;
+
+  // Returns the base::Process backing this process. This will be an invalid
+  // process if it has not yet started, or if it has exited.
+  virtual const base::Process& GetProcess() const = 0;
+
+  // Returns the launch time associated with the process. Combined with the
+  // process ID this can be used as a unique identifier for the process.
+  virtual base::Time GetLaunchTime() const = 0;
+
+  // Returns the exit status of this process. This will be empty if the process
+  // has not yet exited.
+  virtual base::Optional<int32_t> GetExitStatus() const = 0;
+
+  // Visits the frame nodes that are hosted in this process. The iteration is
+  // halted if the visitor returns false.
+  virtual void VisitFrameNodes(const FrameNodeVisitor& visitor) const = 0;
+
+  // Returns the set of frame nodes that are hosted in this process. Note that
+  // calling this causes the set of nodes to be generated.
+  virtual base::flat_set<const FrameNode*> GetFrameNodes() const = 0;
+
+  // Returns the current expected task queuing duration in the process. This is
+  // measure of main thread latency. See
+  // ProcessNodeObserver::OnExpectedTaskQueueingDurationSample.
+  virtual base::TimeDelta GetExpectedTaskQueueingDuration() const = 0;
+
+  // Returns true if the main thread task load is low (below some threshold
+  // of usage). See ProcessNodeObserver::OnMainThreadTaskLoadIsLow.
+  virtual bool GetMainThreadTaskLoadIsLow() const = 0;
+
+  // Returns the current renderer process CPU usage. A value of 1.0 can mean 1
+  // core at 100%, or 2 cores at 50% each, for example.
+  virtual double GetCpuUsage() const = 0;
+
+  // Returns the cumulative CPU usage of the renderer process over its entire
+  // lifetime, expressed as CPU seconds.
+  virtual base::TimeDelta GetCumulativeCpuUsage() const = 0;
+
+  // Returns the most recently measured private memory footprint of the render
+  // process, in kilobytes.
+  virtual uint64_t GetPrivateFootprintKb() const = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ProcessNode);
 };
diff --git a/chrome/browser/performance_manager/web_contents_proxy_unittest.cc b/chrome/browser/performance_manager/web_contents_proxy_unittest.cc
index 3d6076c..6526ec68 100644
--- a/chrome/browser/performance_manager/web_contents_proxy_unittest.cc
+++ b/chrome/browser/performance_manager/web_contents_proxy_unittest.cc
@@ -35,9 +35,9 @@
 
   auto deref_proxy = base::BindLambdaForTesting(
       [&proxy_contents](const WebContentsProxy& proxy,
-                        base::RepeatingClosure quit_loop) {
+                        base::OnceClosure quit_loop) {
         proxy_contents = proxy.Get();
-        quit_loop.Run();
+        std::move(quit_loop).Run();
       });
 
   // Bounce over to the PM sequence, retrieve the proxy, bounce back to the UI
@@ -49,13 +49,15 @@
   {
     base::RunLoop run_loop;
     PerformanceManager::GetInstance()->CallOnGraph(
-        FROM_HERE, base::BindLambdaForTesting([&run_loop, &deref_proxy,
-                                               page_node](GraphImpl* graph) {
-          base::PostTaskWithTraits(
-              FROM_HERE, {content::BrowserThread::UI},
-              base::BindOnce(deref_proxy, page_node->contents_proxy(),
-                             run_loop.QuitClosure()));
-        }));
+        FROM_HERE,
+        base::BindLambdaForTesting(
+            [&deref_proxy, page_node,
+             quit_loop = run_loop.QuitClosure()](GraphImpl* graph) {
+              base::PostTaskWithTraits(
+                  FROM_HERE, {content::BrowserThread::UI},
+                  base::BindOnce(deref_proxy, page_node->contents_proxy(),
+                                 std::move(quit_loop)));
+            }));
     run_loop.Run();
 
     EXPECT_EQ(contents.get(), proxy_contents);
@@ -67,15 +69,16 @@
     base::RunLoop run_loop;
     PerformanceManager::GetInstance()->CallOnGraph(
         FROM_HERE,
-        base::BindLambdaForTesting([&contents, &run_loop, &deref_proxy,
-                                    page_node](GraphImpl* graph) {
+        base::BindLambdaForTesting([&contents, &deref_proxy, page_node,
+                                    quit_loop = run_loop.QuitClosure()](
+                                       GraphImpl* graph) {
           base::PostTaskWithTraits(
               FROM_HERE, {content::BrowserThread::UI},
               base::BindLambdaForTesting([&contents]() { contents.reset(); }));
           base::PostTaskWithTraits(
               FROM_HERE, {content::BrowserThread::UI},
               base::BindOnce(deref_proxy, page_node->contents_proxy(),
-                             run_loop.QuitClosure()));
+                             std::move(quit_loop)));
         }));
     run_loop.Run();
 
diff --git a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
index 8fa0daee..73b7e5c 100644
--- a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/cloud/cloud_policy_test_utils.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -49,6 +48,7 @@
 #include "components/policy/proto/chrome_settings.pb.h"
 #include "components/policy/proto/cloud_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/local_policy_test_server.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
diff --git a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
index 5619a67..e858c62 100644
--- a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_paths.h"
@@ -32,6 +31,7 @@
 #include "components/policy/proto/chrome_extension_policy.pb.h"
 #include "components/policy/proto/cloud_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/local_policy_test_server.h"
 #include "extensions/common/extension.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "net/url_request/url_request_context_getter.h"
diff --git a/chrome/browser/policy/cloud/device_management_service_browsertest.cc b/chrome/browser/policy/cloud/device_management_service_browsertest.cc
index 64ab626b9..d35990da 100644
--- a/chrome/browser/policy/cloud/device_management_service_browsertest.cc
+++ b/chrome/browser/policy/cloud/device_management_service_browsertest.cc
@@ -13,12 +13,12 @@
 #include "base/stl_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/system_network_context_manager.h"
-#include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "components/policy/core/common/cloud/dm_auth.h"
 #include "components/policy/core/common/cloud/mock_device_management_service.h"
+#include "components/policy/test_support/local_policy_test_server.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_data_stream.h"
@@ -178,8 +178,9 @@
   }
 
   void StartTestServer() {
-    test_server_.reset(
-        new LocalPolicyTestServer("device_management_service_browsertest"));
+    test_server_.reset(new LocalPolicyTestServer(
+        "chrome/test/data/policy/"
+        "policy_device_management_service_browsertest.json"));
     ASSERT_TRUE(test_server_->Start());
   }
 
diff --git a/chrome/browser/policy/cloud/machine_level_user_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/machine_level_user_cloud_policy_browsertest.cc
index 8dc105c5..34edb9b9 100644
--- a/chrome/browser/policy/cloud/machine_level_user_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/machine_level_user_cloud_policy_browsertest.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/fake_browser_dm_token_storage.h"
 #include "chrome/browser/policy/machine_level_user_cloud_policy_controller.h"
-#include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_result_codes.h"
@@ -41,6 +40,7 @@
 #include "components/policy/core/common/cloud/mock_cloud_external_data_manager.h"
 #include "components/policy/core/common/cloud/mock_device_management_service.h"
 #include "components/policy/core/common/policy_switches.h"
+#include "components/policy/test_support/local_policy_test_server.h"
 #include "content/public/browser/network_service_instance.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_data_stream.h"
@@ -302,7 +302,8 @@
 
   void StartTestServer() {
     test_server_.reset(new LocalPolicyTestServer(
-        "machine_level_user_cloud_policy_service_browsertest"));
+        "chrome/test/data/policy/"
+        "policy_machine_level_user_cloud_policy_service_browsertest.json"));
     ASSERT_TRUE(test_server_->Start());
   }
 
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index a7f67aa..e1ae927 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -4250,12 +4250,6 @@
   browser()->profile()->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage,
                                                true);
 
-#if defined(OS_WIN)
-  // Do not show the Windows 10 promo page.
-  g_browser_process->local_state()->SetBoolean(prefs::kHasSeenWin10PromoPage,
-                                               true);
-#endif
-
   // Open some tabs to verify if they are restored after the browser restarts.
   // Most policy settings override this, except kPrefValueLast which enforces
   // a restore.
diff --git a/chrome/browser/policy/policy_conversions.cc b/chrome/browser/policy/policy_conversions.cc
index c852c12a..643a813 100644
--- a/chrome/browser/policy/policy_conversions.cc
+++ b/chrome/browser/policy/policy_conversions.cc
@@ -370,13 +370,13 @@
 
 const LocalizedString kPolicySources[POLICY_SOURCE_COUNT] = {
     {"sourceEnterpriseDefault", IDS_POLICY_SOURCE_ENTERPRISE_DEFAULT},
-    {"sourceCloud", IDS_POLICY_SOURCE_CLOUD},
+    {"cloud", IDS_POLICY_SOURCE_CLOUD},
     {"sourceActiveDirectory", IDS_POLICY_SOURCE_ACTIVE_DIRECTORY},
     {"sourceDeviceLocalAccountOverride",
      IDS_POLICY_SOURCE_DEVICE_LOCAL_ACCOUNT_OVERRIDE},
-    {"sourcePlatform", IDS_POLICY_SOURCE_PLATFORM},
-    {"sourcePriorityCloud", IDS_POLICY_SOURCE_CLOUD},
-    {"sourceMerged", IDS_POLICY_SOURCE_MERGED},
+    {"platform", IDS_POLICY_SOURCE_PLATFORM},
+    {"priorityCloud", IDS_POLICY_SOURCE_CLOUD},
+    {"merged", IDS_POLICY_SOURCE_MERGED},
 };
 
 Value GetAllPolicyValuesAsArray(content::BrowserContext* context,
diff --git a/chrome/browser/predictors/preconnect_manager.cc b/chrome/browser/predictors/preconnect_manager.cc
index 149244a..bc57f4f 100644
--- a/chrome/browser/predictors/preconnect_manager.cc
+++ b/chrome/browser/predictors/preconnect_manager.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/optional.h"
 #include "base/task/post_task.h"
 #include "base/trace_event/trace_event.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
@@ -163,7 +162,7 @@
 
   // TODO(mmenke): Use an appropriate NetworkIsolationKey().
   network_context->PreconnectSockets(num_sockets, url, load_flags, privacy_mode,
-                                     base::nullopt);
+                                     net::NetworkIsolationKey());
 }
 
 std::unique_ptr<ResolveHostClientImpl> PreconnectManager::PreresolveUrl(
diff --git a/chrome/browser/predictors/preconnect_manager_unittest.cc b/chrome/browser/predictors/preconnect_manager_unittest.cc
index 1aa6aa2f2..4bda676 100644
--- a/chrome/browser/predictors/preconnect_manager_unittest.cc
+++ b/chrome/browser/predictors/preconnect_manager_unittest.cc
@@ -139,8 +139,7 @@
                     const GURL& url,
                     int32_t load_flags,
                     bool privacy_mode_enabled,
-                    const base::Optional<net::NetworkIsolationKey>&
-                        network_isolation_key));
+                    const net::NetworkIsolationKey& network_isolation_key));
 
  private:
   bool IsHangingHost(const GURL& url) const {
@@ -215,7 +214,7 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
   mock_network_context_->CompleteHostLookup(url_to_preconnect.host(), net::OK);
 }
@@ -251,14 +250,14 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, requests.back().origin, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_network_context_,
               ResolveHostProxy(requests.back().origin.host()));
   for (size_t i = 0; i < count; ++i) {
     EXPECT_CALL(*mock_network_context_,
                 PreconnectSockets(1, requests[i].origin, kNormalLoadFlags,
                                   false /* privacy_mode_enabled */,
-                                  base::Optional<net::NetworkIsolationKey>()));
+                                  net::NetworkIsolationKey()));
     EXPECT_CALL(*mock_network_context_,
                 ResolveHostProxy(requests[i].origin.host()));
   }
@@ -296,7 +295,7 @@
     EXPECT_CALL(*mock_network_context_,
                 PreconnectSockets(1, requests[i].origin, kNormalLoadFlags,
                                   false /* privacy_mode_enabled */,
-                                  base::Optional<net::NetworkIsolationKey>()));
+                                  net::NetworkIsolationKey()));
   }
 
   preconnect_manager_->Start(
@@ -335,11 +334,11 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, requests[count - 1].origin, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, requests[count].origin, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
 
   mock_network_context_->CompleteHostLookup(requests[count - 1].origin.host(),
                                             net::OK);
@@ -400,11 +399,11 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect_1, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect_2, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   preconnect_manager_->Start(main_frame_url_2,
                              {PreconnectRequest(url_to_preconnect_1, 1),
                               PreconnectRequest(url_to_preconnect_2, 1)});
@@ -448,11 +447,11 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect_1, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect_2, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
   preconnect_manager_->Start(main_frame_url,
                              {PreconnectRequest(url_to_preconnect_1, 1),
@@ -536,7 +535,7 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect1, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url1));
   mock_network_context_->CompleteHostLookup(url_to_preconnect1.host(), net::OK);
   // No preconnect for the second url.
@@ -564,7 +563,7 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect1, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url1));
   mock_network_context_->CompleteHostLookup(url_to_preconnect1.host(), net::OK);
 }
@@ -605,7 +604,7 @@
   EXPECT_CALL(
       *mock_network_context_,
       PreconnectSockets(1, origin, kPrivateLoadFlags, !allow_credentials,
-                        base::Optional<net::NetworkIsolationKey>()));
+                        net::NetworkIsolationKey()));
   mock_network_context_->CompleteHostLookup(origin.host(), net::OK);
 
   // Non http url shouldn't be preconnected.
@@ -661,7 +660,7 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
   mock_network_context_->CompleteProxyLookup(url_to_preconnect,
                                              GetIndirectProxyInfo());
@@ -690,11 +689,11 @@
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_network_context_,
               PreconnectSockets(1, url_to_preconnect2, kNormalLoadFlags,
                                 false /* privacy_mode_enabled */,
-                                base::Optional<net::NetworkIsolationKey>()));
+                                net::NetworkIsolationKey()));
   EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
   mock_network_context_->CompleteHostLookup(url_to_preconnect.host(), net::OK);
   mock_network_context_->CompleteHostLookup(url_to_preconnect2.host(), net::OK);
diff --git a/chrome/browser/predictors/predictor_table_base.h b/chrome/browser/predictors/predictor_table_base.h
index 52b1e53..0bd711c 100644
--- a/chrome/browser/predictors/predictor_table_base.h
+++ b/chrome/browser/predictors/predictor_table_base.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 
 namespace base {
 class SequencedTaskRunner;
@@ -47,7 +47,7 @@
   bool CantAccessDatabase();
 
  private:
-  base::CancellationFlag cancelled_;
+  base::AtomicFlag cancelled_;
 
   friend class base::RefCountedThreadSafe<PredictorTableBase>;
 
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 79abb0c..861dc88 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -164,7 +164,7 @@
 #include "extensions/browser/api/runtime/runtime_api.h"
 #include "extensions/browser/extension_prefs.h"
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 #include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h"
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.h"
 #include "chrome/browser/chromeos/settings/stats_reporting_controller.h"
@@ -244,9 +244,10 @@
 #include "chrome/browser/chromeos/cryptauth/cryptauth_device_id_provider_impl.h"
 #include "chrome/browser/chromeos/customization/customization_document.h"
 #include "chrome/browser/chromeos/extensions/echo_private_api.h"
-#include "chrome/browser/chromeos/extensions/login/login_api.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/login_api.h"
 #include "chrome/browser/chromeos/file_system_provider/registry.h"
 #include "chrome/browser/chromeos/first_run/first_run.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_pref_names.h"
 #include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_mode_detector.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_mode_resources_remover.h"
@@ -460,6 +461,11 @@
 // Deprecated 6/2019
 const char kMediaCacheSize[] = "browser.media_cache_size";
 
+#if defined(OS_WIN)
+// Deprecated 6/2019
+const char kHasSeenWin10PromoPage[] = "browser.has_seen_win10_promo_page";
+#endif  // defined(OS_WIN)
+
 // Register prefs used only for migration (clearing or moving to a new key).
 void RegisterProfilePrefsForMigration(
     user_prefs::PrefRegistrySyncable* registry) {
@@ -669,9 +675,10 @@
   ThirdPartyConflictsManager::RegisterLocalStatePrefs(registry);
 #endif  // defined(GOOGLE_CHROME_BUILD)
 
+  registry->RegisterBooleanPref(kHasSeenWin10PromoPage, false);  // DEPRECATED
   registry->RegisterBooleanPref(kResetHasSeenWin10PromoPage, false);
   registry->RegisterStringPref(kLastWelcomedOSVersion, std::string());
-#endif
+#endif  // defined(OS_WIN)
 
   // Obsolete. See MigrateObsoleteBrowserPrefs().
   registry->RegisterIntegerPref(metrics::prefs::kStabilityExecutionPhase, 0);
@@ -863,6 +870,7 @@
   crostini::prefs::RegisterProfilePrefs(registry);
   extensions::EPKPChallengeUserKey::RegisterProfilePrefs(registry);
   flags_ui::PrefServiceFlagsStorage::RegisterProfilePrefs(registry);
+  guest_os::prefs::RegisterProfilePrefs(registry);
   lock_screen_apps::StateController::RegisterProfilePrefs(registry);
   plugin_vm::prefs::RegisterProfilePrefs(registry);
   policy::AppInstallEventLogger::RegisterProfilePrefs(registry);
@@ -1005,6 +1013,11 @@
   // Added 12/2018
   local_state->ClearPref(prefs::kCarrierDealPromoShown);
 #endif
+
+#if defined(OS_WIN)
+  // Added 6/2019.
+  local_state->ClearPref(kHasSeenWin10PromoPage);
+#endif  // defined(OS_WIN)
 }
 
 // This method should be periodically pruned of year+ old migrations.
@@ -1049,7 +1062,7 @@
 #endif
 
   // Added 4/2019
-  crostini::CrostiniSharePath::MigratePersistedPathsToMultiVM(profile_prefs);
+  guest_os::GuestOsSharePath::MigratePersistedPathsToMultiVM(profile_prefs);
 #endif
 
   // Added 1/2019.
diff --git a/chrome/browser/prefs/incognito_mode_prefs.cc b/chrome/browser/prefs/incognito_mode_prefs.cc
index 7968090f..3ad84b96 100644
--- a/chrome/browser/prefs/incognito_mode_prefs.cc
+++ b/chrome/browser/prefs/incognito_mode_prefs.cc
@@ -123,14 +123,10 @@
 }  // namespace
 #endif  // OS_WIN
 
-namespace {
-static constexpr IncognitoModePrefs::Availability kDefaultAvailability =
-#if defined(INCOGNITO_DEFAULT_DISABLED)
-    IncognitoModePrefs::DISABLED;
-#else
-    IncognitoModePrefs::ENABLED;
-#endif
-}  // namespace
+// static
+// Sadly, this is required until c++17.
+constexpr IncognitoModePrefs::Availability
+    IncognitoModePrefs::kDefaultAvailability;
 
 // static
 bool IncognitoModePrefs::IntToAvailability(int in_value,
diff --git a/chrome/browser/prefs/incognito_mode_prefs.h b/chrome/browser/prefs/incognito_mode_prefs.h
index 9bbd431..07fe795 100644
--- a/chrome/browser/prefs/incognito_mode_prefs.h
+++ b/chrome/browser/prefs/incognito_mode_prefs.h
@@ -39,6 +39,13 @@
     AVAILABILITY_NUM_TYPES
   };
 
+  static constexpr Availability kDefaultAvailability =
+#if defined(INCOGNITO_DEFAULT_DISABLED)
+      DISABLED;
+#else
+      ENABLED;
+#endif
+
   // Register incognito related preferences.
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
diff --git a/chrome/browser/prefs/incognito_mode_prefs_unittest.cc b/chrome/browser/prefs/incognito_mode_prefs_unittest.cc
index 9f75dbc8..0fe41ca 100644
--- a/chrome/browser/prefs/incognito_mode_prefs_unittest.cc
+++ b/chrome/browser/prefs/incognito_mode_prefs_unittest.cc
@@ -32,9 +32,9 @@
   EXPECT_EQ(IncognitoModePrefs::FORCED, incognito);
 
   EXPECT_FALSE(IncognitoModePrefs::IntToAvailability(10, &incognito));
-  EXPECT_EQ(IncognitoModePrefs::ENABLED, incognito);
+  EXPECT_EQ(IncognitoModePrefs::kDefaultAvailability, incognito);
   EXPECT_FALSE(IncognitoModePrefs::IntToAvailability(-1, &incognito));
-  EXPECT_EQ(IncognitoModePrefs::ENABLED, incognito);
+  EXPECT_EQ(IncognitoModePrefs::kDefaultAvailability, incognito);
 }
 
 TEST_F(IncognitoModePrefsTest, GetAvailability) {
diff --git a/chrome/browser/previews/hints_fetcher_browsertest.cc b/chrome/browser/previews/hints_fetcher_browsertest.cc
index fdf8582..46a231c 100644
--- a/chrome/browser/previews/hints_fetcher_browsertest.cc
+++ b/chrome/browser/previews/hints_fetcher_browsertest.cc
@@ -363,18 +363,19 @@
   // in each of the follow histograms as One Platform Hints are enabled.
   EXPECT_GE(RetryForHistogramUntilCountReached(
                 histogram_tester,
-                "Previews.HintsFetcher.GetHintsRequest.HostCount", 1),
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1),
             1);
 
-  EXPECT_GE(
-      RetryForHistogramUntilCountReached(
-          histogram_tester, "Previews.HintsFetcher.GetHintsRequest.Status", 1),
-      1);
+  EXPECT_GE(RetryForHistogramUntilCountReached(
+                histogram_tester,
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.Status", 1),
+            1);
 
   histogram_tester->ExpectBucketCount(
-      "Previews.HintsFetcher.GetHintsRequest.Status", net::HTTP_OK, 1);
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.Status", net::HTTP_OK, 1);
   histogram_tester->ExpectBucketCount(
-      "Previews.HintsFetcher.GetHintsRequest.NetErrorCode", net::OK, 1);
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.NetErrorCode", net::OK,
+      1);
 }
 
 IN_PROC_BROWSER_TEST_F(HintsFetcherDisabledBrowserTest, HintsFetcherDisabled) {
@@ -383,7 +384,7 @@
   // Expect that the histogram for HintsFetcher to be 0 because the OnePlatform
   // is not enabled.
   histogram_tester->ExpectTotalCount(
-      "Previews.HintsFetcher.GetHintsRequest.HostCount", 0);
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 0);
 }
 
 // This test creates a new browser and seeds the Site Engagement Service with
@@ -410,12 +411,12 @@
   // for.
   EXPECT_GE(RetryForHistogramUntilCountReached(
                 histogram_tester,
-                "Previews.HintsFetcher.GetHintsRequest.HostCount", 1),
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1),
             1);
 
   // Only the 2 HTTPS hosts should be requested hints for.
   histogram_tester->ExpectBucketCount(
-      "Previews.HintsFetcher.GetHintsRequest.HostCount", 2, 1);
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 2, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -431,13 +432,13 @@
   // in each of the follow histograms as One Platform Hints are enabled.
   EXPECT_GE(RetryForHistogramUntilCountReached(
                 histogram_tester,
-                "Previews.HintsFetcher.GetHintsRequest.HostCount", 1),
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1),
             1);
 
-  EXPECT_GE(
-      RetryForHistogramUntilCountReached(
-          histogram_tester, "Previews.HintsFetcher.GetHintsRequest.Status", 1),
-      1);
+  EXPECT_GE(RetryForHistogramUntilCountReached(
+                histogram_tester,
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.Status", 1),
+            1);
 
   LoadHintsForUrl(https_url());
 
@@ -471,30 +472,35 @@
   // in each of the follow histograms as One Platform Hints are enabled.
   EXPECT_GE(RetryForHistogramUntilCountReached(
                 histogram_tester,
-                "Previews.HintsFetcher.GetHintsRequest.HostCount", 1),
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1),
             1);
 
   // Wait until histograms have been updated before performing checks for
   // correct behavior based on the response.
-  EXPECT_GE(
-      RetryForHistogramUntilCountReached(
-          histogram_tester, "Previews.HintsFetcher.GetHintsRequest.Status", 1),
-      1);
+  EXPECT_GE(RetryForHistogramUntilCountReached(
+                histogram_tester,
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.Status", 1),
+            1);
   if (response_type == HintsFetcherRemoteResponseType::kSuccessful) {
     histogram_tester->ExpectBucketCount(
-        "Previews.HintsFetcher.GetHintsRequest.Status", net::HTTP_OK, 1);
+        "OptimizationGuide.HintsFetcher.GetHintsRequest.Status", net::HTTP_OK,
+        1);
     histogram_tester->ExpectBucketCount(
-        "Previews.HintsFetcher.GetHintsRequest.NetErrorCode", net::OK, 1);
+        "OptimizationGuide.HintsFetcher.GetHintsRequest.NetErrorCode", net::OK,
+        1);
   } else if (response_type == HintsFetcherRemoteResponseType::kUnsuccessful) {
     histogram_tester->ExpectBucketCount(
-        "Previews.HintsFetcher.GetHintsRequest.Status", net::HTTP_NOT_FOUND, 1);
+        "OptimizationGuide.HintsFetcher.GetHintsRequest.Status",
+        net::HTTP_NOT_FOUND, 1);
   } else if (response_type == HintsFetcherRemoteResponseType::kMalformed) {
     // A malformed GetHintsResponse will still register as successful fetch with
     // respect to the network.
     histogram_tester->ExpectBucketCount(
-        "Previews.HintsFetcher.GetHintsRequest.Status", net::HTTP_OK, 1);
+        "OptimizationGuide.HintsFetcher.GetHintsRequest.Status", net::HTTP_OK,
+        1);
     histogram_tester->ExpectBucketCount(
-        "Previews.HintsFetcher.GetHintsRequest.NetErrorCode", net::OK, 1);
+        "OptimizationGuide.HintsFetcher.GetHintsRequest.NetErrorCode", net::OK,
+        1);
 
     LoadHintsForUrl(https_url());
 
@@ -538,13 +544,13 @@
   // in each of the follow histograms as OnePlatform Hints are enabled.
   EXPECT_GE(RetryForHistogramUntilCountReached(
                 histogram_tester,
-                "Previews.HintsFetcher.GetHintsRequest.HostCount", 1),
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1),
             1);
 
-  EXPECT_GE(
-      RetryForHistogramUntilCountReached(
-          histogram_tester, "Previews.HintsFetcher.GetHintsRequest.Status", 1),
-      1);
+  EXPECT_GE(RetryForHistogramUntilCountReached(
+                histogram_tester,
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.Status", 1),
+            1);
 
   LoadHintsForUrl(https_url());
 
@@ -610,17 +616,17 @@
   // in each of the follow histograms as OnePlatform Hints are enabled.
   EXPECT_GE(RetryForHistogramUntilCountReached(
                 histogram_tester,
-                "Previews.HintsFetcher.GetHintsRequest.HostCount", 1),
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1),
             1);
 
   // There should be 2 sites in the engagement service.
   histogram_tester->ExpectBucketCount(
-      "Previews.HintsFetcher.GetHintsRequest.HostCount", 2, 1);
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 2, 1);
 
-  EXPECT_GE(
-      RetryForHistogramUntilCountReached(
-          histogram_tester, "Previews.HintsFetcher.GetHintsRequest.Status", 1),
-      1);
+  EXPECT_GE(RetryForHistogramUntilCountReached(
+                histogram_tester,
+                "OptimizationGuide.HintsFetcher.GetHintsRequest.Status", 1),
+            1);
 
   LoadHintsForUrl(https_url());
 
diff --git a/chrome/browser/previews/previews_lite_page_url_loader_interceptor.cc b/chrome/browser/previews/previews_lite_page_url_loader_interceptor.cc
index b3e7e77..16a32270 100644
--- a/chrome/browser/previews/previews_lite_page_url_loader_interceptor.cc
+++ b/chrome/browser/previews/previews_lite_page_url_loader_interceptor.cc
@@ -10,9 +10,12 @@
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
+#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
+#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/profiles/profile_io_data.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/previews/core/previews_features.h"
@@ -44,26 +47,48 @@
   return true;
 }
 
-net::HttpRequestHeaders GetChromeProxyHeaders(content::ResourceContext* context,
-                                              uint64_t page_id) {
+net::HttpRequestHeaders GetChromeProxyHeaders(
+    content::BrowserContext* browser_context,
+    content::ResourceContext* resource_context,
+    uint64_t page_id) {
   net::HttpRequestHeaders headers;
   // Return empty headers for unittests.
-  if (!context)
+  if (!resource_context && !browser_context)
     return headers;
-  // TODO(ryansturm): If this switches to the UI thread, this needs to be
-  // re-worked. This information is all available on the UI thread.
-  // https://crbug.com/931786
-  auto* io_data = ProfileIOData::FromResourceContext(context);
-  if (io_data && io_data->data_reduction_proxy_io_data()) {
-    DCHECK(data_reduction_proxy::params::IsEnabledWithNetworkService());
-    data_reduction_proxy::DataReductionProxyRequestOptions* request_options =
-        io_data->data_reduction_proxy_io_data()->request_options();
-    request_options->AddRequestHeader(&headers, page_id != 0U ? page_id : 1);
 
-    headers.SetHeader(data_reduction_proxy::chrome_proxy_ect_header(),
-                      net::GetNameForEffectiveConnectionType(
-                          io_data->data_reduction_proxy_io_data()
-                              ->GetEffectiveConnectionType()));
+  DCHECK(!(resource_context && browser_context));
+  if (resource_context) {
+    auto* io_data = ProfileIOData::FromResourceContext(resource_context);
+    if (io_data && io_data->data_reduction_proxy_io_data()) {
+      DCHECK(data_reduction_proxy::params::IsEnabledWithNetworkService());
+      data_reduction_proxy::DataReductionProxyRequestOptions* request_options =
+          io_data->data_reduction_proxy_io_data()->request_options();
+      request_options->AddRequestHeader(&headers, page_id != 0U ? page_id : 1);
+
+      headers.SetHeader(data_reduction_proxy::chrome_proxy_ect_header(),
+                        net::GetNameForEffectiveConnectionType(
+                            io_data->data_reduction_proxy_io_data()
+                                ->GetEffectiveConnectionType()));
+    }
+  } else {
+    DCHECK(browser_context);
+    auto* settings =
+        DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+            browser_context);
+    if (settings) {
+      DCHECK(data_reduction_proxy::params::IsEnabledWithNetworkService());
+      std::string header;
+      if (settings->GetProxyRequestHeaders().GetHeader(
+              data_reduction_proxy::chrome_proxy_header(), &header)) {
+        data_reduction_proxy::DataReductionProxyRequestOptions::
+            AddRequestHeader(&headers, page_id != 0U ? page_id : 1, header);
+      }
+
+      headers.SetHeader(data_reduction_proxy::chrome_proxy_ect_header(),
+                        net::GetNameForEffectiveConnectionType(
+                            settings->data_reduction_proxy_service()
+                                ->GetEffectiveConnectionType()));
+    }
   }
 
   return headers;
@@ -84,6 +109,7 @@
 
 void PreviewsLitePageURLLoaderInterceptor::MaybeCreateLoader(
     const network::ResourceRequest& tentative_resource_request,
+    content::BrowserContext* browser_context,
     content::ResourceContext* resource_context,
     content::URLLoaderRequestInterceptor::LoaderCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -117,8 +143,8 @@
   }
 
   if (ShouldCreateLoader(tentative_resource_request)) {
-    CreateRedirectLoader(tentative_resource_request, resource_context,
-                         std::move(callback));
+    CreateRedirectLoader(tentative_resource_request, browser_context,
+                         resource_context, std::move(callback));
     return;
   }
   RecordInterceptAttempt(false);
@@ -127,6 +153,7 @@
 
 void PreviewsLitePageURLLoaderInterceptor::CreateRedirectLoader(
     const network::ResourceRequest& tentative_resource_request,
+    content::BrowserContext* browser_context,
     content::ResourceContext* resource_context,
     content::URLLoaderRequestInterceptor::LoaderCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -141,7 +168,7 @@
 
   // |redirect_url_loader_| can be null after this call.
   redirect_url_loader_->StartRedirectToPreview(
-      GetChromeProxyHeaders(resource_context, page_id_),
+      GetChromeProxyHeaders(browser_context, resource_context, page_id_),
       network_loader_factory_, frame_tree_node_id_);
 }
 
diff --git a/chrome/browser/previews/previews_lite_page_url_loader_interceptor.h b/chrome/browser/previews/previews_lite_page_url_loader_interceptor.h
index 804a279..23f68cb 100644
--- a/chrome/browser/previews/previews_lite_page_url_loader_interceptor.h
+++ b/chrome/browser/previews/previews_lite_page_url_loader_interceptor.h
@@ -36,6 +36,7 @@
   // content::URLLaoderRequestInterceptor:
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      content::BrowserContext* browser_context,
       content::ResourceContext* resource_context,
       content::URLLoaderRequestInterceptor::LoaderCallback callback) override;
 
@@ -43,6 +44,7 @@
   // Begins an attempt at fetching the lite page version of the URL.
   void CreateRedirectLoader(
       const network::ResourceRequest& tentative_resource_request,
+      content::BrowserContext* browser_context,
       content::ResourceContext* resource_context,
       content::URLLoaderRequestInterceptor::LoaderCallback callback);
 
diff --git a/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc b/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
index 727062d..c8c636b 100644
--- a/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
@@ -103,7 +103,7 @@
   // Check that we don't trigger when previews are not allowed.
   request.previews_state = content::PREVIEWS_OFF;
   interceptor().MaybeCreateLoader(
-      request, nullptr,
+      request, nullptr, nullptr,
       base::BindOnce(&PreviewsLitePageURLLoaderInterceptorTest::HandlerCallback,
                      base::Unretained(this)));
 
@@ -122,7 +122,7 @@
   // Check that we trigger when previews are allowed.
   request.previews_state = content::LITE_PAGE_REDIRECT_ON;
   interceptor().MaybeCreateLoader(
-      request, nullptr,
+      request, nullptr, nullptr,
       base::BindOnce(&PreviewsLitePageURLLoaderInterceptorTest::HandlerCallback,
                      base::Unretained(this)));
 
@@ -150,7 +150,7 @@
       net::URLRequestStatus::SUCCESS);
 
   interceptor().MaybeCreateLoader(
-      request, nullptr,
+      request, nullptr, nullptr,
       base::BindOnce(&PreviewsLitePageURLLoaderInterceptorTest::HandlerCallback,
                      base::Unretained(this)));
 
@@ -175,7 +175,7 @@
       net::URLRequestStatus::SUCCESS);
 
   interceptor().MaybeCreateLoader(
-      request, nullptr,
+      request, nullptr, nullptr,
       base::BindOnce(&PreviewsLitePageURLLoaderInterceptorTest::HandlerCallback,
                      base::Unretained(this)));
 
@@ -200,7 +200,7 @@
       "Fake Body", net::HTTP_FORBIDDEN, net::URLRequestStatus::SUCCESS);
 
   interceptor().MaybeCreateLoader(
-      request, nullptr,
+      request, nullptr, nullptr,
       base::BindOnce(&PreviewsLitePageURLLoaderInterceptorTest::HandlerCallback,
                      base::Unretained(this)));
 
@@ -224,7 +224,7 @@
       "Fake Body", net::HTTP_OK, net::URLRequestStatus::FAILED);
 
   interceptor().MaybeCreateLoader(
-      request, nullptr,
+      request, nullptr, nullptr,
       base::BindOnce(&PreviewsLitePageURLLoaderInterceptorTest::HandlerCallback,
                      base::Unretained(this)));
 
diff --git a/chrome/browser/previews/previews_prober.cc b/chrome/browser/previews/previews_prober.cc
index 15856e3..c4bd1b8 100644
--- a/chrome/browser/previews/previews_prober.cc
+++ b/chrome/browser/previews/previews_prober.cc
@@ -4,6 +4,13 @@
 
 #include "chrome/browser/previews/previews_prober.h"
 
+#include <math.h>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/time/default_tick_clock.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_service_instance.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_status_code.h"
@@ -23,37 +30,138 @@
   }
 }
 
+// Computes the time delta for a given Backoff algorithm, a base interval, and
+// the count of how many attempts have been made thus far.
+base::TimeDelta ComputeNextTimeDeltaForBackoff(PreviewsProber::Backoff backoff,
+                                               base::TimeDelta base_interval,
+                                               size_t attempts_so_far) {
+  switch (backoff) {
+    case PreviewsProber::Backoff::kLinear:
+      return base_interval;
+    case PreviewsProber::Backoff::kExponential:
+      return base_interval * pow(2, attempts_so_far);
+  }
+}
+
 }  // namespace
 
+PreviewsProber::RetryPolicy::RetryPolicy() = default;
+PreviewsProber::RetryPolicy::~RetryPolicy() = default;
+PreviewsProber::RetryPolicy::RetryPolicy(PreviewsProber::RetryPolicy const&) =
+    default;
+PreviewsProber::TimeoutPolicy::TimeoutPolicy() = default;
+PreviewsProber::TimeoutPolicy::~TimeoutPolicy() = default;
+PreviewsProber::TimeoutPolicy::TimeoutPolicy(
+    PreviewsProber::TimeoutPolicy const&) = default;
+
 PreviewsProber::PreviewsProber(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& name,
     const GURL& url,
-    const HttpMethod http_method)
+    const HttpMethod http_method,
+    const net::HttpRequestHeaders headers,
+    const RetryPolicy& retry_policy,
+    const TimeoutPolicy& timeout_policy)
+    : PreviewsProber(url_loader_factory,
+                     name,
+                     url,
+                     http_method,
+                     headers,
+                     retry_policy,
+                     timeout_policy,
+                     base::DefaultTickClock::GetInstance()) {}
+
+PreviewsProber::PreviewsProber(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const std::string& name,
+    const GURL& url,
+    const HttpMethod http_method,
+    const net::HttpRequestHeaders headers,
+    const RetryPolicy& retry_policy,
+    const TimeoutPolicy& timeout_policy,
+    const base::TickClock* tick_clock)
     : name_(name),
       url_(url),
       http_method_(http_method),
+      headers_(headers),
+      retry_policy_(retry_policy),
+      timeout_policy_(timeout_policy),
+      successive_retry_count_(0),
+      successive_timeout_count_(0),
+      tick_clock_(tick_clock),
       is_active_(false),
       last_probe_status_(base::nullopt),
-      url_loader_factory_(url_loader_factory) {}
+      network_connection_tracker_(nullptr),
+      url_loader_factory_(url_loader_factory),
+      weak_factory_(this) {
+  // The NetworkConnectionTracker can only be used directly on the UI thread.
+  // Otherwise we use the cross-thread call.
+  if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI) &&
+      content::GetNetworkConnectionTracker()) {
+    AddSelfAsNetworkConnectionObserver(content::GetNetworkConnectionTracker());
+  } else {
+    content::GetNetworkConnectionTrackerFromUIThread(
+        base::BindOnce(&PreviewsProber::AddSelfAsNetworkConnectionObserver,
+                       weak_factory_.GetWeakPtr()));
+  }
+}
 
-PreviewsProber::~PreviewsProber() = default;
+PreviewsProber::~PreviewsProber() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (network_connection_tracker_)
+    network_connection_tracker_->RemoveNetworkConnectionObserver(this);
+}
+
+void PreviewsProber::AddSelfAsNetworkConnectionObserver(
+    network::NetworkConnectionTracker* network_connection_tracker) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  network_connection_tracker_ = network_connection_tracker;
+  network_connection_tracker_->AddNetworkConnectionObserver(this);
+}
+
+void PreviewsProber::ResetState() {
+  is_active_ = false;
+  successive_retry_count_ = 0;
+  successive_timeout_count_ = 0;
+  retry_timer_.reset();
+  timeout_timer_.reset();
+  url_loader_.reset();
+}
 
 void PreviewsProber::SendNowIfInactive() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (is_active_)
     return;
+
+  CreateAndStartURLLoader();
+}
+
+void PreviewsProber::OnConnectionChanged(network::mojom::ConnectionType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // If a probe is already in flight we don't want to continue to use it since
+  // the network has just changed. Reset all state and start again.
+  ResetState();
   CreateAndStartURLLoader();
 }
 
 void PreviewsProber::CreateAndStartURLLoader() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!is_active_);
+  DCHECK(!is_active_ || successive_retry_count_ > 0);
+  DCHECK(!retry_timer_ || !retry_timer_->IsRunning());
   DCHECK(!url_loader_);
 
   is_active_ = true;
 
+  GURL url = url_;
+  if (retry_policy_.use_random_urls) {
+    std::string query = "guid=" + base::GenerateGUID();
+    GURL::Replacements replacements;
+    replacements.SetQuery(query.c_str(), url::Component(0, query.length()));
+    url = url.ReplaceComponents(replacements);
+  }
+
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("previews_prober", R"(
         semantics {
@@ -77,20 +185,32 @@
           policy_exception_justification: "Not implemented."
         })");
   auto request = std::make_unique<network::ResourceRequest>();
-  request->url = url_;
+  request->url = url;
   request->method = HttpMethodToString(http_method_);
+  request->headers = headers_;
   request->load_flags = net::LOAD_DISABLE_CACHE;
   request->allow_credentials = false;
 
-  // TODO(crbug/977603): Set retry options.
   url_loader_ =
       network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+  url_loader_->SetAllowHttpErrorResults(true);
 
   url_loader_->DownloadToString(
       url_loader_factory_.get(),
       base::BindOnce(&PreviewsProber::OnURLLoadComplete,
                      base::Unretained(this)),
       1024);
+
+  // We don't use SimpleURLLoader's timeout functionality because it is not
+  // possible to test by PreviewsProberTest.
+  base::TimeDelta ttl = ComputeNextTimeDeltaForBackoff(
+      timeout_policy_.backoff, timeout_policy_.base_timeout,
+      successive_timeout_count_);
+  timeout_timer_ = std::make_unique<base::OneShotTimer>(tick_clock_);
+  // base::Unretained is safe because |timeout_timer_| is owned by this.
+  timeout_timer_->Start(FROM_HERE, ttl,
+                        base::BindOnce(&PreviewsProber::ProcessProbeTimeout,
+                                       base::Unretained(this)));
 }
 
 void PreviewsProber::OnURLLoadComplete(
@@ -103,11 +223,65 @@
   }
 
   // TODO(crbug/977603): Replace with delegate check.
-  last_probe_status_ =
+  bool was_successful =
       url_loader_->NetError() == net::OK && response_code == net::HTTP_OK;
 
+  timeout_timer_.reset();
   url_loader_.reset();
-  is_active_ = false;
+  successive_timeout_count_ = 0;
+
+  if (was_successful) {
+    ProcessProbeSuccess();
+    return;
+  }
+  ProcessProbeFailure();
+}
+
+void PreviewsProber::ProcessProbeTimeout() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(url_loader_);
+
+  url_loader_.reset();
+  successive_timeout_count_++;
+  ProcessProbeFailure();
+}
+
+void PreviewsProber::ProcessProbeFailure() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!retry_timer_ || !retry_timer_->IsRunning());
+  DCHECK(!timeout_timer_ || !timeout_timer_->IsRunning());
+  DCHECK(!url_loader_);
+  DCHECK(is_active_);
+
+  last_probe_status_ = false;
+
+  if (retry_policy_.max_retries > successive_retry_count_) {
+    base::TimeDelta interval = ComputeNextTimeDeltaForBackoff(
+        retry_policy_.backoff, retry_policy_.base_interval,
+        successive_retry_count_);
+
+    retry_timer_ = std::make_unique<base::OneShotTimer>(tick_clock_);
+    // base::Unretained is safe because |retry_timer_| is owned by this.
+    retry_timer_->Start(FROM_HERE, interval,
+                        base::BindOnce(&PreviewsProber::CreateAndStartURLLoader,
+                                       base::Unretained(this)));
+
+    successive_retry_count_++;
+    return;
+  }
+
+  ResetState();
+}
+
+void PreviewsProber::ProcessProbeSuccess() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!retry_timer_ || !retry_timer_->IsRunning());
+  DCHECK(!timeout_timer_ || !timeout_timer_->IsRunning());
+  DCHECK(!url_loader_);
+  DCHECK(is_active_);
+
+  last_probe_status_ = true;
+  ResetState();
 }
 
 base::Optional<bool> PreviewsProber::LastProbeWasSuccessful() const {
diff --git a/chrome/browser/previews/previews_prober.h b/chrome/browser/previews/previews_prober.h
index d7f9ad9..c1682b3 100644
--- a/chrome/browser/previews/previews_prober.h
+++ b/chrome/browser/previews/previews_prober.h
@@ -10,11 +10,17 @@
 
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/sequence_checker.h"
+#include "base/time/tick_clock.h"
+#include "base/timer/timer.h"
+#include "net/http/http_request_headers.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
 #include "url/gurl.h"
 
 namespace network {
+class NetworkConnectionTracker;
 class SimpleURLLoader;
 class SharedURLLoaderFactory;
 }  // namespace network
@@ -22,8 +28,60 @@
 // This class is a utility to probe a given URL with a given set of behaviors.
 // This can be used for determining whether a specific network resource is
 // available or accessible by Chrome.
-class PreviewsProber {
+// This class may live on either UI or IO thread but should remain on the thread
+// that it was created on.
+class PreviewsProber
+    : public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
+  // This enum describes the different algorithms that can be used to calculate
+  // a time delta between probe events like retries or timeout ttl.
+  enum class Backoff {
+    // Use the same time delta for each event.
+    kLinear,
+
+    // Use an exponentially increasing time delta, base 2.
+    kExponential,
+  };
+
+  struct RetryPolicy {
+    RetryPolicy();
+    RetryPolicy(const RetryPolicy& other);
+    ~RetryPolicy();
+
+    // The maximum number of retries (not including the original probe) to
+    // attempt.
+    size_t max_retries = 3;
+
+    // How to compute the time interval between successive retries.
+    Backoff backoff = Backoff::kLinear;
+
+    // Time between probes as the base value. For example, given |backoff|:
+    //   LINEAR: |base_interval| between the end of last probe and start of next
+    //           probe.
+    //   EXPONENTIAL: (|base_interval| * 2 ^ |successive_retry_count_|) between
+    //                the end of last retry and start of next retry.
+    base::TimeDelta base_interval = base::TimeDelta();
+
+    // If true, this attaches a random GUID query param to the URL of every
+    // probe, including the first probe.
+    bool use_random_urls = false;
+  };
+
+  struct TimeoutPolicy {
+    TimeoutPolicy();
+    TimeoutPolicy(const TimeoutPolicy& other);
+    ~TimeoutPolicy();
+
+    // How to compute the TTL of probes.
+    Backoff backoff = Backoff::kLinear;
+
+    // The TTL base value. For example,
+    //   LINEAR: Each probe times out in |base_timeout|.
+    //   EXPONENTIAL: Each probe times out in
+    //                (|base_timeout| * 2 ^ |successive_timeout_count_|).
+    base::TimeDelta base_timeout = base::TimeDelta::FromSeconds(60);
+  };
+
   enum class HttpMethod {
     kGet,
     kHead,
@@ -33,8 +91,11 @@
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& name,
       const GURL& url,
-      HttpMethod http_method);
-  ~PreviewsProber();
+      HttpMethod http_method,
+      const net::HttpRequestHeaders headers,
+      const RetryPolicy& retry_policy,
+      const TimeoutPolicy& timeout_policy);
+  ~PreviewsProber() override;
 
   // Sends a probe now if the prober is currently inactive. If the probe is
   // active (i.e.: there are probes in flight), this is a no-op.
@@ -43,9 +104,33 @@
   // Returns the successfulness of the last probe, if there was one.
   base::Optional<bool> LastProbeWasSuccessful() const;
 
+  // True if probes are being attempted, including retries.
+  bool is_active() const { return is_active_; }
+
+  // network::NetworkConnectionTracker::NetworkConnectionObserver:
+  void OnConnectionChanged(network::mojom::ConnectionType type) override;
+
+ protected:
+  // Exposes |tick_clock| for testing.
+  PreviewsProber(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const std::string& name,
+      const GURL& url,
+      HttpMethod http_method,
+      const net::HttpRequestHeaders headers,
+      const RetryPolicy& retry_policy,
+      const TimeoutPolicy& timeout_policy,
+      const base::TickClock* tick_clock);
+
  private:
+  void ResetState();
   void CreateAndStartURLLoader();
   void OnURLLoadComplete(std::unique_ptr<std::string> response_body);
+  void ProcessProbeTimeout();
+  void ProcessProbeFailure();
+  void ProcessProbeSuccess();
+  void AddSelfAsNetworkConnectionObserver(
+      network::NetworkConnectionTracker* network_connection_tracker);
 
   // The name given to this prober instance, used in metrics, prefs, and traffic
   // annotations.
@@ -57,12 +142,42 @@
   // The HTTP method used for probing.
   const HttpMethod http_method_;
 
+  // Additional headers to send on every probe. These are subject to CORS
+  // checks.
+  const net::HttpRequestHeaders headers_;
+
+  // The retry policy to use in this prober.
+  const RetryPolicy retry_policy_;
+
+  // The timeout policy to use in this prober.
+  const TimeoutPolicy timeout_policy_;
+
+  // The number of retries that have been attempted. This count does not include
+  // the original probe.
+  size_t successive_retry_count_;
+
+  // The number of timeouts that have occurred.
+  size_t successive_timeout_count_;
+
+  // If a retry is being attempted, this will be running until the next attempt.
+  std::unique_ptr<base::OneShotTimer> retry_timer_;
+
+  // If a probe is being attempted, this will be running until the TTL.
+  std::unique_ptr<base::OneShotTimer> timeout_timer_;
+
+  // The tick clock used within this class.
+  const base::TickClock* tick_clock_;
+
   // Whether the prober is currently sending probes.
   bool is_active_;
 
   // The status of the last completed probe, if any.
   base::Optional<bool> last_probe_status_;
 
+  // This reference is kept around for unregistering |this| as an observer on
+  // any thread.
+  network::NetworkConnectionTracker* network_connection_tracker_;
+
   // Used for setting up the |url_loader_|.
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
@@ -71,6 +186,8 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  base::WeakPtrFactory<PreviewsProber> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(PreviewsProber);
 };
 
diff --git a/chrome/browser/previews/previews_prober_browsertest.cc b/chrome/browser/previews/previews_prober_browsertest.cc
new file mode 100644
index 0000000..3a715bb
--- /dev/null
+++ b/chrome/browser/previews/previews_prober_browsertest.cc
@@ -0,0 +1,150 @@
+// Copyright 2019 The Chromium 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/command_line.h"
+#include "base/run_loop.h"
+#include "chrome/browser/previews/previews_prober.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/system_connector.h"
+#include "content/public/common/network_service_util.h"
+#include "content/public/common/service_names.mojom.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/network/public/mojom/network_service_test.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace {
+const char kName[] = "testing";
+
+void WaitForCompletedProbe(PreviewsProber* prober) {
+  while (true) {
+    if (prober->LastProbeWasSuccessful().has_value())
+      return;
+    base::RunLoop().RunUntilIdle();
+  }
+}
+
+void SimulateNetworkChange(network::mojom::ConnectionType type) {
+  if (!content::IsInProcessNetworkService()) {
+    network::mojom::NetworkServiceTestPtr network_service_test;
+    content::GetSystemConnector()->BindInterface(
+        content::mojom::kNetworkServiceName, &network_service_test);
+    base::RunLoop run_loop;
+    network_service_test->SimulateNetworkChange(type, run_loop.QuitClosure());
+    run_loop.Run();
+    return;
+  }
+  net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
+      net::NetworkChangeNotifier::ConnectionType(type));
+}
+
+}  // namespace
+
+class PreviewsProberBrowserTest : public InProcessBrowserTest {
+ public:
+  PreviewsProberBrowserTest() = default;
+  ~PreviewsProberBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    https_server_.reset(
+        new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS));
+    https_server_->RegisterRequestHandler(base::BindRepeating(
+        &PreviewsProberBrowserTest::HandleRequest, base::Unretained(this)));
+    ASSERT_TRUE(https_server_->Start());
+  }
+
+  void SetUpCommandLine(base::CommandLine* cmd) override {
+    cmd->AppendSwitchASCII("host-rules", "MAP * 127.0.0.1");
+  }
+
+  void TearDownOnMainThread() override {
+    EXPECT_TRUE(https_server_->ShutdownAndWaitUntilComplete());
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
+  GURL TestURLWithPath(const std::string& path) const {
+    return https_server_->GetURL("test.com", path);
+  }
+
+ private:
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) {
+    std::string path = request.GetURL().path();
+    if (path == "/ok") {
+      std::unique_ptr<net::test_server::BasicHttpResponse> response =
+          std::make_unique<net::test_server::BasicHttpResponse>();
+      response->set_code(net::HTTP_OK);
+      return response;
+    }
+
+    if (path == "/timeout") {
+      std::unique_ptr<net::test_server::HungResponse> response =
+          std::make_unique<net::test_server::HungResponse>();
+      return response;
+    }
+
+    NOTREACHED() << path << " is not handled";
+    return nullptr;
+  }
+
+  std::unique_ptr<net::EmbeddedTestServer> https_server_;
+
+  DISALLOW_COPY_AND_ASSIGN(PreviewsProberBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PreviewsProberBrowserTest, OK) {
+  GURL url = TestURLWithPath("/ok");
+  net::HttpRequestHeaders headers;
+  PreviewsProber::RetryPolicy retry_policy;
+  PreviewsProber::TimeoutPolicy timeout_policy;
+
+  PreviewsProber prober(browser()->profile()->GetURLLoaderFactory(), kName, url,
+                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
+                        timeout_policy);
+  prober.SendNowIfInactive();
+  WaitForCompletedProbe(&prober);
+
+  EXPECT_TRUE(prober.LastProbeWasSuccessful().value());
+}
+
+IN_PROC_BROWSER_TEST_F(PreviewsProberBrowserTest, Timeout) {
+  GURL url = TestURLWithPath("/timeout");
+  net::HttpRequestHeaders headers;
+
+  PreviewsProber::RetryPolicy retry_policy;
+  retry_policy.max_retries = 0;
+
+  PreviewsProber::TimeoutPolicy timeout_policy;
+  timeout_policy.base_timeout = base::TimeDelta::FromMilliseconds(1);
+
+  PreviewsProber prober(browser()->profile()->GetURLLoaderFactory(), kName, url,
+                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
+                        timeout_policy);
+  prober.SendNowIfInactive();
+  WaitForCompletedProbe(&prober);
+
+  EXPECT_FALSE(prober.LastProbeWasSuccessful().value());
+}
+
+IN_PROC_BROWSER_TEST_F(PreviewsProberBrowserTest, NetworkChange) {
+  GURL url = TestURLWithPath("/ok");
+  net::HttpRequestHeaders headers;
+  PreviewsProber::RetryPolicy retry_policy;
+  PreviewsProber::TimeoutPolicy timeout_policy;
+
+  PreviewsProber prober(browser()->profile()->GetURLLoaderFactory(), kName, url,
+                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
+                        timeout_policy);
+  SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_4G);
+  WaitForCompletedProbe(&prober);
+
+  EXPECT_TRUE(prober.LastProbeWasSuccessful().value());
+}
diff --git a/chrome/browser/previews/previews_prober_unittest.cc b/chrome/browser/previews/previews_prober_unittest.cc
index 165bd6d..711069e 100644
--- a/chrome/browser/previews/previews_prober_unittest.cc
+++ b/chrome/browser/previews/previews_prober_unittest.cc
@@ -4,13 +4,14 @@
 
 #include "chrome/browser/previews/previews_prober.h"
 
-#include "base/test/scoped_task_environment.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_network_connection_tracker.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,41 +21,115 @@
 const char kName[] = "testing";
 }  // namespace
 
+class TestPreviewsProber : public PreviewsProber {
+ public:
+  TestPreviewsProber(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const std::string& name,
+      const GURL& url,
+      const HttpMethod http_method,
+      const net::HttpRequestHeaders headers,
+      const RetryPolicy& retry_policy,
+      const TimeoutPolicy& timeout_policy,
+      const base::TickClock* tick_clock)
+      : PreviewsProber(url_loader_factory,
+                       name,
+                       url,
+                       http_method,
+                       headers,
+                       retry_policy,
+                       timeout_policy,
+                       tick_clock) {}
+};
+
 class PreviewsProberTest : public testing::Test {
  public:
   PreviewsProberTest()
-      : test_shared_loader_factory_(
+      : thread_bundle_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
+        test_shared_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)) {}
 
   std::unique_ptr<PreviewsProber> NewProber() {
-    return std::make_unique<PreviewsProber>(test_shared_loader_factory_, kName,
-                                            kTestUrl,
-                                            PreviewsProber::HttpMethod::kGet);
+    return NewProberWithPolicies(PreviewsProber::RetryPolicy(),
+                                 PreviewsProber::TimeoutPolicy());
   }
 
-  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
+  std::unique_ptr<PreviewsProber> NewProberWithRetryPolicy(
+      const PreviewsProber::RetryPolicy& retry_policy) {
+    return NewProberWithPolicies(retry_policy, PreviewsProber::TimeoutPolicy());
+  }
 
-  void SetResponse(net::HttpStatusCode http_status, net::Error net_error) {
+  std::unique_ptr<PreviewsProber> NewProberWithPolicies(
+      const PreviewsProber::RetryPolicy& retry_policy,
+      const PreviewsProber::TimeoutPolicy& timeout_policy) {
+    net::HttpRequestHeaders headers;
+    headers.SetHeader("X-Testing", "Hello world");
+    return std::make_unique<TestPreviewsProber>(
+        test_shared_loader_factory_, kName, kTestUrl,
+        PreviewsProber::HttpMethod::kGet, headers, retry_policy, timeout_policy,
+        thread_bundle_.GetMockTickClock());
+  }
+
+  void RunUntilIdle() { thread_bundle_.RunUntilIdle(); }
+
+  void FastForward(base::TimeDelta delta) {
+    thread_bundle_.FastForwardBy(delta);
+  }
+
+  void MakeResponseAndWait(net::HttpStatusCode http_status,
+                           net::Error net_error) {
+    network::TestURLLoaderFactory::PendingRequest* request =
+        test_url_loader_factory_.GetPendingRequest(0);
+
+    ASSERT_EQ(request->request.url.host(), kTestUrl.host());
+    ASSERT_EQ(request->request.url.scheme(), kTestUrl.scheme());
+
     network::ResourceResponseHead head =
         network::CreateResourceResponseHead(http_status);
     network::URLLoaderCompletionStatus status(net_error);
-    test_url_loader_factory_.AddResponse(kTestUrl, head, "content", status);
+    test_url_loader_factory_.AddResponse(request->request.url, head, "content",
+                                         status);
+    RunUntilIdle();
+    // Clear responses in the network service so we can inspect the next request
+    // that comes in before it is responded to.
+    ClearResponses();
   }
 
-  void VerifyRequest() {
+  void ClearResponses() { test_url_loader_factory_.ClearResponses(); }
+
+  void VerifyNoRequests() {
+    EXPECT_EQ(test_url_loader_factory_.NumPending(), 0);
+  }
+
+  void VerifyRequest(bool expect_random_guid = false) {
     ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
 
+    std::string testing_header;
     network::TestURLLoaderFactory::PendingRequest* request =
         test_url_loader_factory_.GetPendingRequest(0);
-    EXPECT_EQ(request->request.url, kTestUrl);
+    request->request.headers.GetHeader("X-Testing", &testing_header);
+
+    EXPECT_EQ(testing_header, "Hello world");
     EXPECT_EQ(request->request.method, "GET");
     EXPECT_EQ(request->request.load_flags, net::LOAD_DISABLE_CACHE);
     EXPECT_FALSE(request->request.allow_credentials);
+    if (expect_random_guid) {
+      EXPECT_NE(request->request.url, kTestUrl);
+      EXPECT_TRUE(request->request.url.query().find("guid=") !=
+                  std::string::npos);
+      EXPECT_EQ(request->request.url.query().length(),
+                5U /* len("guid=") */ + 36U /* len(hex guid with hyphens) */);
+      // We don't check for the randomness of successive GUIDs on the assumption
+      // base::GenerateGUID() is always correct.
+    } else {
+      EXPECT_EQ(request->request.url, kTestUrl);
+    }
   }
 
  private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  content::TestBrowserThreadBundle thread_bundle_;
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
 };
@@ -66,9 +141,9 @@
   prober->SendNowIfInactive();
   VerifyRequest();
 
-  SetResponse(net::HTTP_OK, net::OK);
-  RunUntilIdle();
+  MakeResponseAndWait(net::HTTP_OK, net::OK);
   EXPECT_TRUE(prober->LastProbeWasSuccessful().value());
+  EXPECT_FALSE(prober->is_active());
 }
 
 TEST_F(PreviewsProberTest, MultipleStart) {
@@ -83,6 +158,18 @@
   VerifyRequest();
 }
 
+TEST_F(PreviewsProberTest, NetworkChangeStartsProber) {
+  std::unique_ptr<PreviewsProber> prober = NewProber();
+  EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
+  EXPECT_FALSE(prober->is_active());
+
+  network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_4G);
+  RunUntilIdle();
+
+  EXPECT_TRUE(prober->is_active());
+}
+
 TEST_F(PreviewsProberTest, NetError) {
   std::unique_ptr<PreviewsProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
@@ -90,9 +177,9 @@
   prober->SendNowIfInactive();
   VerifyRequest();
 
-  SetResponse(net::HTTP_OK, net::ERR_FAILED);
-  RunUntilIdle();
+  MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED);
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_FALSE(prober->is_active());
 }
 
 TEST_F(PreviewsProberTest, HttpError) {
@@ -102,7 +189,166 @@
   prober->SendNowIfInactive();
   VerifyRequest();
 
-  SetResponse(net::HTTP_NOT_FOUND, net::OK);
-  RunUntilIdle();
+  MakeResponseAndWait(net::HTTP_NOT_FOUND, net::OK);
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_FALSE(prober->is_active());
+}
+
+TEST_F(PreviewsProberTest, RandomGUID) {
+  PreviewsProber::RetryPolicy retry_policy;
+  retry_policy.use_random_urls = true;
+  retry_policy.max_retries = 0;
+
+  std::unique_ptr<PreviewsProber> prober =
+      NewProberWithRetryPolicy(retry_policy);
+  EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
+
+  prober->SendNowIfInactive();
+  VerifyRequest(true /* expect_random_guid */);
+
+  MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED);
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_FALSE(prober->is_active());
+}
+
+TEST_F(PreviewsProberTest, RetryLinear) {
+  PreviewsProber::RetryPolicy retry_policy;
+  retry_policy.max_retries = 2;
+  retry_policy.backoff = PreviewsProber::Backoff::kLinear;
+  retry_policy.base_interval = base::TimeDelta::FromMilliseconds(1000);
+
+  std::unique_ptr<PreviewsProber> prober =
+      NewProberWithRetryPolicy(retry_policy);
+  EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
+
+  prober->SendNowIfInactive();
+  VerifyRequest();
+  MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED);
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_TRUE(prober->is_active());
+
+  // First retry.
+  FastForward(base::TimeDelta::FromMilliseconds(999));
+  VerifyNoRequests();
+  FastForward(base::TimeDelta::FromMilliseconds(1));
+  VerifyRequest();
+  MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED);
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_TRUE(prober->is_active());
+
+  // Second retry should be another 1000ms later and be the final one.
+  FastForward(base::TimeDelta::FromMilliseconds(999));
+  VerifyNoRequests();
+  FastForward(base::TimeDelta::FromMilliseconds(1));
+  VerifyRequest();
+  MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED);
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_FALSE(prober->is_active());
+}
+
+TEST_F(PreviewsProberTest, RetryExponential) {
+  PreviewsProber::RetryPolicy retry_policy;
+  retry_policy.max_retries = 2;
+  retry_policy.backoff = PreviewsProber::Backoff::kExponential;
+  retry_policy.base_interval = base::TimeDelta::FromMilliseconds(1000);
+
+  std::unique_ptr<PreviewsProber> prober =
+      NewProberWithRetryPolicy(retry_policy);
+  EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
+
+  prober->SendNowIfInactive();
+  VerifyRequest();
+  MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED);
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_TRUE(prober->is_active());
+
+  // First retry.
+  FastForward(base::TimeDelta::FromMilliseconds(999));
+  VerifyNoRequests();
+  FastForward(base::TimeDelta::FromMilliseconds(1));
+  VerifyRequest();
+  MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED);
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_TRUE(prober->is_active());
+
+  // Second retry should be another 2000ms later and be the final one.
+  FastForward(base::TimeDelta::FromMilliseconds(1999));
+  VerifyNoRequests();
+  FastForward(base::TimeDelta::FromMilliseconds(1));
+  VerifyRequest();
+  MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED);
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_FALSE(prober->is_active());
+}
+
+TEST_F(PreviewsProberTest, TimeoutLinear) {
+  PreviewsProber::RetryPolicy retry_policy;
+  retry_policy.max_retries = 1;
+  retry_policy.base_interval = base::TimeDelta::FromMilliseconds(10);
+
+  PreviewsProber::TimeoutPolicy timeout_policy;
+  timeout_policy.backoff = PreviewsProber::Backoff::kLinear;
+  timeout_policy.base_timeout = base::TimeDelta::FromMilliseconds(1000);
+
+  std::unique_ptr<PreviewsProber> prober =
+      NewProberWithPolicies(retry_policy, timeout_policy);
+  EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
+
+  // First attempt.
+  prober->SendNowIfInactive();
+  VerifyRequest();
+  FastForward(base::TimeDelta::FromMilliseconds(999));
+  VerifyRequest();
+  FastForward(base::TimeDelta::FromMilliseconds(1));
+  VerifyNoRequests();
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_TRUE(prober->is_active());
+
+  // Fast forward to the start of the next attempt.
+  FastForward(base::TimeDelta::FromMilliseconds(10));
+
+  // Second attempt should have the same timeout.
+  VerifyRequest();
+  FastForward(base::TimeDelta::FromMilliseconds(999));
+  VerifyRequest();
+  FastForward(base::TimeDelta::FromMilliseconds(1));
+  VerifyNoRequests();
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_FALSE(prober->is_active());
+}
+
+TEST_F(PreviewsProberTest, TimeoutExponential) {
+  PreviewsProber::RetryPolicy retry_policy;
+  retry_policy.max_retries = 1;
+  retry_policy.base_interval = base::TimeDelta::FromMilliseconds(10);
+
+  PreviewsProber::TimeoutPolicy timeout_policy;
+  timeout_policy.backoff = PreviewsProber::Backoff::kExponential;
+  timeout_policy.base_timeout = base::TimeDelta::FromMilliseconds(1000);
+
+  std::unique_ptr<PreviewsProber> prober =
+      NewProberWithPolicies(retry_policy, timeout_policy);
+  EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
+
+  // First attempt.
+  prober->SendNowIfInactive();
+  VerifyRequest();
+  FastForward(base::TimeDelta::FromMilliseconds(999));
+  VerifyRequest();
+  FastForward(base::TimeDelta::FromMilliseconds(1));
+  VerifyNoRequests();
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_TRUE(prober->is_active());
+
+  // Fast forward to the start of the next attempt.
+  FastForward(base::TimeDelta::FromMilliseconds(10));
+
+  // Second attempt should have a 2s timeout.
+  VerifyRequest();
+  FastForward(base::TimeDelta::FromMilliseconds(1999));
+  VerifyRequest();
+  FastForward(base::TimeDelta::FromMilliseconds(1));
+  VerifyNoRequests();
+  EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
+  EXPECT_FALSE(prober->is_active());
 }
diff --git a/chrome/browser/previews/previews_top_host_provider_impl.cc b/chrome/browser/previews/previews_top_host_provider_impl.cc
index 8a2408a1..3f9209b7 100644
--- a/chrome/browser/previews/previews_top_host_provider_impl.cc
+++ b/chrome/browser/previews/previews_top_host_provider_impl.cc
@@ -79,7 +79,8 @@
   }
 
   UMA_HISTOGRAM_COUNTS_1000(
-      "Previews.HintsFetcher.TopHostProvider.BlacklistSize.OnInitialize",
+      "OptimizationGuide.HintsFetcher.TopHostProvider.BlacklistSize."
+      "OnInitialize",
       top_host_blacklist->size());
 
   pref_service_->Set(optimization_guide::prefs::kHintsFetcherTopHostBlacklist,
@@ -175,7 +176,8 @@
     top_host_blacklist =
         pref_service_->GetDictionary(kHintsFetcherTopHostBlacklist);
     UMA_HISTOGRAM_COUNTS_1000(
-        "Previews.HintsFetcher.TopHostProvider.BlacklistSize.OnRequest",
+        "OptimizationGuide.HintsFetcher.TopHostProvider.BlacklistSize."
+        "OnRequest",
         top_host_blacklist->size());
     // This check likely should not be needed as the process of removing hosts
     // from the blacklist should check and update the pref state.
diff --git a/chrome/browser/printing/printing_message_filter.cc b/chrome/browser/printing/printing_message_filter.cc
index 505d662..706617b7 100644
--- a/chrome/browser/printing/printing_message_filter.cc
+++ b/chrome/browser/printing/printing_message_filter.cc
@@ -101,7 +101,7 @@
           ->Subscribe(base::Bind(&PrintingMessageFilter::ShutdownOnUIThread,
                                  base::Unretained(this)));
   is_printing_enabled_.Init(prefs::kPrintingEnabled, profile->GetPrefs());
-  is_printing_enabled_.MoveToThread(
+  is_printing_enabled_.MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
 }
 
diff --git a/chrome/browser/profile_resetter/profile_resetter.cc b/chrome/browser/profile_resetter/profile_resetter.cc
index 89d00b7..9e645a08 100644
--- a/chrome/browser/profile_resetter/profile_resetter.cc
+++ b/chrome/browser/profile_resetter/profile_resetter.cc
@@ -11,7 +11,7 @@
 
 #include "base/bind.h"
 #include "base/stl_util.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/threading/scoped_blocking_call.h"
diff --git a/chrome/browser/profile_resetter/profile_resetter.h b/chrome/browser/profile_resetter/profile_resetter.h
index 43d5901..a4ee584 100644
--- a/chrome/browser/profile_resetter/profile_resetter.h
+++ b/chrome/browser/profile_resetter/profile_resetter.h
@@ -26,7 +26,7 @@
 class Profile;
 
 namespace base {
-class CancellationFlag;
+class AtomicFlag;
 }
 
 namespace {
@@ -129,7 +129,7 @@
 // Path to shortcut and command line arguments.
 typedef std::pair<base::FilePath, base::string16> ShortcutCommand;
 
-typedef base::RefCountedData<base::CancellationFlag> SharedCancellationFlag;
+typedef base::RefCountedData<base::AtomicFlag> SharedCancellationFlag;
 
 #if defined(OS_WIN)
 // On Windows returns all the shortcuts which launch Chrome and corresponding
diff --git a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
index 26e8cf5..3f4eb539 100644
--- a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
+++ b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
@@ -12,7 +12,7 @@
 #include "base/hash/md5.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task_runner_util.h"
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index 5622c69..980f8f9 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -562,7 +562,7 @@
 }
 
 const content::SharedCorsOriginAccessList*
-OffTheRecordProfileImpl::GetSharedCorsOriginAccessList() const {
+OffTheRecordProfileImpl::GetSharedCorsOriginAccessList() {
   return profile_->GetSharedCorsOriginAccessList();
 }
 
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.h b/chrome/browser/profiles/off_the_record_profile_impl.h
index 3a07baba..6153a3a 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.h
+++ b/chrome/browser/profiles/off_the_record_profile_impl.h
@@ -129,7 +129,7 @@
       std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
       base::OnceClosure closure) override;
   const content::SharedCorsOriginAccessList* GetSharedCorsOriginAccessList()
-      const override;
+      override;
 
  private:
   void InitIoData();
diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc
index bc71f148..6fd8462 100644
--- a/chrome/browser/profiles/off_the_record_profile_io_data.cc
+++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc
@@ -86,16 +86,16 @@
   initialized_ = true;
   io_data_->safe_browsing_enabled()->Init(prefs::kSafeBrowsingEnabled,
       profile_->GetPrefs());
-  io_data_->safe_browsing_enabled()->MoveToThread(
+  io_data_->safe_browsing_enabled()->MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
   io_data_->safe_browsing_whitelist_domains()->Init(
       prefs::kSafeBrowsingWhitelistDomains, profile_->GetPrefs());
-  io_data_->safe_browsing_whitelist_domains()->MoveToThread(
+  io_data_->safe_browsing_whitelist_domains()->MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
 #if BUILDFLAG(ENABLE_PLUGINS)
   io_data_->always_open_pdf_externally()->Init(
       prefs::kPluginsAlwaysOpenPdfExternally, profile_->GetPrefs());
-  io_data_->always_open_pdf_externally()->MoveToThread(
+  io_data_->always_open_pdf_externally()->MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
 #endif
   io_data_->InitializeOnUIThread(profile_);
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index e5367a3..648065f 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -1278,7 +1278,7 @@
 }
 
 const content::SharedCorsOriginAccessList*
-ProfileImpl::GetSharedCorsOriginAccessList() const {
+ProfileImpl::GetSharedCorsOriginAccessList() {
   return shared_cors_origin_access_list_.get();
 }
 
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index 52a2d3c..4b17d9d 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -101,7 +101,7 @@
       std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
       base::OnceClosure closure) override;
   const content::SharedCorsOriginAccessList* GetSharedCorsOriginAccessList()
-      const override;
+      override;
   std::unique_ptr<service_manager::Service> HandleServiceRequest(
       const std::string& service_name,
       service_manager::mojom::ServiceRequest request) override;
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 84b9af03..053faa6 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -46,7 +46,6 @@
 #include "components/data_reduction_proxy/core/browser/data_store_impl.h"
 #include "components/net_log/chrome_net_log.h"
 #include "components/network_session_configurator/browser/network_session_configurator.h"
-#include "components/offline_pages/buildflags/buildflags.h"
 #include "components/prefs/json_pref_store.h"
 #include "components/prefs/pref_filter.h"
 #include "components/prefs/pref_member.h"
@@ -77,10 +76,6 @@
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "storage/browser/quota/special_storage_policy.h"
 
-#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
-#include "chrome/browser/offline_pages/offline_page_request_interceptor.h"
-#endif
-
 using content::BrowserThread;
 
 ProfileImplIOData::Handle::Handle(Profile* profile)
@@ -178,16 +173,16 @@
   PrefService* pref_service = profile_->GetPrefs();
   io_data_->safe_browsing_enabled()->Init(prefs::kSafeBrowsingEnabled,
       pref_service);
-  io_data_->safe_browsing_enabled()->MoveToThread(
+  io_data_->safe_browsing_enabled()->MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
   io_data_->safe_browsing_whitelist_domains()->Init(
       prefs::kSafeBrowsingWhitelistDomains, pref_service);
-  io_data_->safe_browsing_whitelist_domains()->MoveToThread(
+  io_data_->safe_browsing_whitelist_domains()->MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
 #if BUILDFLAG(ENABLE_PLUGINS)
   io_data_->always_open_pdf_externally()->Init(
       prefs::kPluginsAlwaysOpenPdfExternally, pref_service);
-  io_data_->always_open_pdf_externally()->MoveToThread(
+  io_data_->always_open_pdf_externally()->MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
 #endif
   io_data_->InitializeOnUIThread(profile_);
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 75d93e5..720c38bc 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -321,17 +321,17 @@
   profile_params_ = std::move(params);
 
   force_google_safesearch_.Init(prefs::kForceGoogleSafeSearch, pref_service);
-  force_google_safesearch_.MoveToThread(
+  force_google_safesearch_.MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
   force_youtube_restrict_.Init(prefs::kForceYouTubeRestrict, pref_service);
-  force_youtube_restrict_.MoveToThread(
+  force_youtube_restrict_.MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
   allowed_domains_for_apps_.Init(prefs::kAllowedDomainsForApps, pref_service);
-  allowed_domains_for_apps_.MoveToThread(
+  allowed_domains_for_apps_.MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
   signed_exchange_enabled_.Init(prefs::kSignedHTTPExchangeEnabled,
                                 pref_service);
-  signed_exchange_enabled_.MoveToThread(
+  signed_exchange_enabled_.MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
 
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
@@ -342,33 +342,33 @@
   if (!IsOffTheRecord()) {
     google_services_user_account_id_.Init(prefs::kGoogleServicesUserAccountId,
                                           pref_service);
-    google_services_user_account_id_.MoveToThread(io_task_runner);
+    google_services_user_account_id_.MoveToSequence(io_task_runner);
     sync_suppress_start_.Init(syncer::prefs::kSyncSuppressStart, pref_service);
-    sync_suppress_start_.MoveToThread(io_task_runner);
+    sync_suppress_start_.MoveToSequence(io_task_runner);
     sync_first_setup_complete_.Init(syncer::prefs::kSyncFirstSetupComplete,
                                     pref_service);
-    sync_first_setup_complete_.MoveToThread(io_task_runner);
+    sync_first_setup_complete_.MoveToSequence(io_task_runner);
   }
 
 #if !defined(OS_CHROMEOS)
   signin_scoped_device_id_.Init(prefs::kGoogleServicesSigninScopedDeviceId,
                                 pref_service);
-  signin_scoped_device_id_.MoveToThread(io_task_runner);
+  signin_scoped_device_id_.MoveToSequence(io_task_runner);
 #endif
 
   network_prediction_options_.Init(prefs::kNetworkPredictionOptions,
                                    pref_service);
 
-  network_prediction_options_.MoveToThread(io_task_runner);
+  network_prediction_options_.MoveToSequence(io_task_runner);
 
   incognito_availibility_pref_.Init(prefs::kIncognitoModeAvailability,
                                     pref_service);
-  incognito_availibility_pref_.MoveToThread(io_task_runner);
+  incognito_availibility_pref_.MoveToSequence(io_task_runner);
 
 #if defined(OS_CHROMEOS)
   account_consistency_mirror_required_pref_.Init(
       prefs::kAccountConsistencyMirrorRequired, pref_service);
-  account_consistency_mirror_required_pref_.MoveToThread(io_task_runner);
+  account_consistency_mirror_required_pref_.MoveToSequence(io_task_runner);
 #endif
 
   // We need to make sure that content initializes its own data structures that
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index debc7ad..44f2b97 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -900,7 +900,7 @@
   ASSERT_TRUE(profile);
   EXPECT_FALSE(profile->IsOffTheRecord());
   PrefService* prefs = profile->GetPrefs();
-  EXPECT_EQ(IncognitoModePrefs::ENABLED,
+  EXPECT_EQ(IncognitoModePrefs::kDefaultAvailability,
             IncognitoModePrefs::GetAvailability(prefs));
 
   ASSERT_TRUE(profile->GetOffTheRecordProfile());
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.cc
index 43114981..6306d82 100644
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.cc
+++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.cc
@@ -45,6 +45,24 @@
   return ret;
 }
 
+const char* FeatureTypeToFeatureName(
+    const LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures feature) {
+  switch (feature) {
+    case LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
+        kFaviconUpdate:
+      return "FaviconUpdateInBackground";
+    case LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
+        kTitleUpdate:
+      return "TitleUpdateInBackground";
+    case LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
+        kAudioUsage:
+      return "AudioUsageInBackground";
+    case LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
+        kNotificationUsageUsage:
+      return "NotificationsUsageInBackground";
+  }
+}
+
 }  // namespace
 
 void LocalSiteCharacteristicsDataImpl::NotifySiteLoaded() {
@@ -139,24 +157,24 @@
 void LocalSiteCharacteristicsDataImpl::NotifyUpdatesFaviconInBackground() {
   NotifyFeatureUsage(
       site_characteristics_.mutable_updates_favicon_in_background(),
-      "FaviconUpdateInBackground");
+      TrackedBackgroundFeatures::kFaviconUpdate);
 }
 
 void LocalSiteCharacteristicsDataImpl::NotifyUpdatesTitleInBackground() {
   NotifyFeatureUsage(
       site_characteristics_.mutable_updates_title_in_background(),
-      "TitleUpdateInBackground");
+      TrackedBackgroundFeatures::kTitleUpdate);
 }
 
 void LocalSiteCharacteristicsDataImpl::NotifyUsesAudioInBackground() {
   NotifyFeatureUsage(site_characteristics_.mutable_uses_audio_in_background(),
-                     "AudioUsageInBackground");
+                     TrackedBackgroundFeatures::kAudioUsage);
 }
 
 void LocalSiteCharacteristicsDataImpl::NotifyUsesNotificationsInBackground() {
   NotifyFeatureUsage(
       site_characteristics_.mutable_uses_notifications_in_background(),
-      "NotificationsUsageInBackground");
+      TrackedBackgroundFeatures::kNotificationUsageUsage);
 }
 
 void LocalSiteCharacteristicsDataImpl::NotifyLoadTimePerformanceMeasurement(
@@ -181,6 +199,15 @@
     IncrementFeatureObservationDuration(iter, longest_observation_window);
 }
 
+void LocalSiteCharacteristicsDataImpl::RegisterFeatureUsageCallbackForTesting(
+    const TrackedBackgroundFeatures feature_type,
+    base::OnceClosure callback) {
+  DCHECK(
+      !feature_usage_callback_for_testing_[static_cast<size_t>(feature_type)]);
+  feature_usage_callback_for_testing_[static_cast<size_t>(feature_type)] =
+      std::move(callback);
+}
+
 LocalSiteCharacteristicsDataImpl::LocalSiteCharacteristicsDataImpl(
     const url::Origin& origin,
     OnDestroyDelegate* delegate,
@@ -310,7 +337,7 @@
 
 void LocalSiteCharacteristicsDataImpl::NotifyFeatureUsage(
     SiteDataFeatureProto* feature_proto,
-    const char* feature_name) {
+    const TrackedBackgroundFeatures feature_type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(IsLoaded());
   DCHECK_GT(loaded_tabs_in_background_count_, 0U);
@@ -321,7 +348,7 @@
     base::UmaHistogramCustomTimes(
         base::StringPrintf(
             "ResourceCoordinator.LocalDB.ObservationTimeBeforeFirstUse.%s",
-            feature_name),
+            FeatureTypeToFeatureName(feature_type)),
         InternalRepresentationToTimeDelta(
             feature_proto->observation_duration()),
         base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(1), 100);
@@ -330,6 +357,12 @@
   feature_proto->Clear();
   feature_proto->set_use_timestamp(
       TimeDeltaToInternalRepresentation(GetTickDeltaSinceEpoch()));
+
+  if (feature_usage_callback_for_testing_[static_cast<size_t>(feature_type)]) {
+    std::move(
+        feature_usage_callback_for_testing_[static_cast<size_t>(feature_type)])
+        .Run();
+  }
 }
 
 void LocalSiteCharacteristicsDataImpl::OnInitCallback(
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h
index d455d2a77..ec9c4d4 100644
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h
+++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h
@@ -66,6 +66,14 @@
         LocalSiteCharacteristicsDataImpl* impl) = 0;
   };
 
+  enum class TrackedBackgroundFeatures {
+    kFaviconUpdate,
+    kTitleUpdate,
+    kAudioUsage,
+    kNotificationUsageUsage,
+    kMaxValue = kNotificationUsageUsage,
+  };
+
   // Must be called when a load event is received for this site, this can be
   // invoked several times if instances of this class are shared between
   // multiple tabs.
@@ -153,6 +161,10 @@
 
   bool fully_initialized_for_testing() const { return fully_initialized_; }
 
+  void RegisterFeatureUsageCallbackForTesting(
+      const TrackedBackgroundFeatures feature_type,
+      base::OnceClosure callback);
+
  protected:
   friend class base::RefCounted<LocalSiteCharacteristicsDataImpl>;
   friend class resource_coordinator::LocalSiteCharacteristicsDataStore;
@@ -219,7 +231,7 @@
   // Helper function to update a given |SiteDataFeatureProto| when a
   // feature gets used.
   void NotifyFeatureUsage(SiteDataFeatureProto* feature_proto,
-                          const char* feature_name);
+                          const TrackedBackgroundFeatures feature_type);
 
   bool IsLoaded() const { return loaded_tabs_count_ > 0U; }
 
@@ -289,6 +301,9 @@
   // initialized.
   std::vector<base::OnceClosure> data_loaded_callbacks_;
 
+  base::OnceClosure feature_usage_callback_for_testing_[static_cast<size_t>(
+      TrackedBackgroundFeatures::kMaxValue) + 1];
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<LocalSiteCharacteristicsDataImpl> weak_factory_;
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h
index 056e4b14..da6b005 100644
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h
+++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h
@@ -38,7 +38,7 @@
   bool DataLoaded() const override;
   void RegisterDataLoadedCallback(base::OnceClosure&& callback) override;
 
-  const internal::LocalSiteCharacteristicsDataImpl* impl_for_testing() const {
+  internal::LocalSiteCharacteristicsDataImpl* impl_for_testing() const {
     return impl_.get();
   }
 
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_database_browsertest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_database_browsertest.cc
index ea0b03f..2e8ddeb 100644
--- a/chrome/browser/resource_coordinator/local_site_characteristics_database_browsertest.cc
+++ b/chrome/browser/resource_coordinator/local_site_characteristics_database_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/path_service.h"
+#include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -70,13 +71,17 @@
                    params.audio_usage_grace_period});
 }
 
+// Returns the LocalSiteCharacteristicsDataImpl that backs |reader|.
+internal::LocalSiteCharacteristicsDataImpl* GetImplFromReader(
+    SiteCharacteristicsDataReader* reader) {
+  return static_cast<LocalSiteCharacteristicsDataReader*>(reader)
+      ->impl_for_testing();
+}
+
 // Returns the SiteDataProto that backs |reader|.
 const SiteDataProto* GetSiteDataProtoFromReader(
     SiteCharacteristicsDataReader* reader) {
-  const internal::LocalSiteCharacteristicsDataImpl* impl =
-      static_cast<LocalSiteCharacteristicsDataReader*>(reader)
-          ->impl_for_testing();
-  return &impl->site_characteristics_for_testing();
+  return &GetImplFromReader(reader)->site_characteristics_for_testing();
 }
 
 }  // namespace
@@ -138,12 +143,13 @@
     EXPECT_TRUE(data_store);
     std::unique_ptr<SiteCharacteristicsDataReader> reader =
         data_store->GetReaderForOrigin(origin);
+    base::RunLoop run_loop;
+    reader->RegisterDataLoadedCallback(run_loop.QuitClosure());
+    run_loop.Run();
 
     const internal::LocalSiteCharacteristicsDataImpl* impl =
-        static_cast<LocalSiteCharacteristicsDataReader*>(reader.get())
-            ->impl_for_testing();
-    while (!impl->fully_initialized_for_testing())
-      base::RunLoop().RunUntilIdle();
+        GetImplFromReader(reader.get());
+    EXPECT_TRUE(impl->fully_initialized_for_testing());
     return reader;
   }
 
@@ -162,15 +168,17 @@
   void TestFeatureUsageDetection(
       performance_manager::SiteFeatureUsage (
           SiteCharacteristicsDataReader::*feature_detection_method)() const,
+      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures
+          feature_type,
       base::RepeatingClosure triggering_closure,
       base::RepeatingClosure allowing_closure = base::DoNothing::Repeatedly()) {
     // Test that feature usage is tracked correctly before the expiration of its
     // observation window.
-    TestFeatureUsageDetectionImpl(feature_detection_method, allowing_closure,
-                                  triggering_closure, false);
+    TestFeatureUsageDetectionImpl(feature_detection_method, feature_type,
+                                  allowing_closure, triggering_closure, false);
     // Test that feature usage is tracked correctly after the expiration of its
     // observation window.
-    TestFeatureUsageDetectionImpl(feature_detection_method,
+    TestFeatureUsageDetectionImpl(feature_detection_method, feature_type,
                                   std::move(allowing_closure),
                                   std::move(triggering_closure), true);
   }
@@ -207,7 +215,6 @@
     content::WebContents* active_webcontents = GetActiveWebContents();
     active_webcontents->WasShown();
     PlayAudioInActiveWebContents();
-
     // Wait for the audio to start playing.
     while (!active_webcontents->WasEverAudible())
       base::RunLoop().RunUntilIdle();
@@ -245,6 +252,8 @@
   void TestFeatureUsageDetectionImpl(
       performance_manager::SiteFeatureUsage (
           SiteCharacteristicsDataReader::*feature_detection_method)() const,
+      const internal::LocalSiteCharacteristicsDataImpl::
+          TrackedBackgroundFeatures feature_type,
       base::OnceClosure allowing_closure,
       base::RepeatingClosure triggering_closure,
       bool wait_for_observation_window_to_expire);
@@ -260,6 +269,8 @@
 void LocalSiteCharacteristicsDatabaseTest::TestFeatureUsageDetectionImpl(
     performance_manager::SiteFeatureUsage (
         SiteCharacteristicsDataReader::*feature_detection_method)() const,
+    const internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures
+        feature_type,
     base::OnceClosure allowing_closure,
     base::RepeatingClosure triggering_closure,
     bool wait_for_observation_window_to_expire) {
@@ -302,13 +313,18 @@
               (reader.get()->*feature_detection_method)());
   }
 
+  base::RunLoop run_loop;
+  GetImplFromReader(reader.get())
+      ->RegisterFeatureUsageCallbackForTesting(feature_type,
+                                               run_loop.QuitClosure());
+
   // Cause the feature to be used.
   triggering_closure.Run();
 
-  while ((reader.get()->*feature_detection_method)() !=
-         performance_manager::SiteFeatureUsage::kSiteFeatureInUse) {
-    base::RunLoop().RunUntilIdle();
-  }
+  run_loop.Run();
+
+  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
+            (reader.get()->*feature_detection_method)());
 
   // Advance the clock, make sure that the feature usage status doesn't
   // change.
@@ -395,6 +411,8 @@
                        AudioFeatureUsage) {
   TestFeatureUsageDetection(
       &SiteCharacteristicsDataReader::UsesAudioInBackground,
+      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
+          kAudioUsage,
       base::BindRepeating(
           &LocalSiteCharacteristicsDatabaseTest::PlayAudioInActiveWebContents,
           base::Unretained(this)),
@@ -411,6 +429,8 @@
                        DISABLED_NotificationFeatureUsage) {
   TestFeatureUsageDetection(
       &SiteCharacteristicsDataReader::UsesNotificationsInBackground,
+      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
+          kNotificationUsageUsage,
       base::BindRepeating(
           &LocalSiteCharacteristicsDatabaseTest::
               TriggerNonPersistentNotificationInActiveWebContents,
@@ -424,6 +444,8 @@
                        TitleUpdateFeatureUsage) {
   TestFeatureUsageDetection(
       &SiteCharacteristicsDataReader::UpdatesTitleInBackground,
+      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
+          kTitleUpdate,
       base::BindRepeating(
           &LocalSiteCharacteristicsDatabaseTest::ChangeTitleOfActiveWebContents,
           base::Unretained(this)),
@@ -438,6 +460,8 @@
                        FaviconUpdateFeatureUsage) {
   TestFeatureUsageDetection(
       &SiteCharacteristicsDataReader::UpdatesFaviconInBackground,
+      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
+          kFaviconUpdate,
       base::BindRepeating(&LocalSiteCharacteristicsDatabaseTest::
                               ChangeFaviconOfActiveWebContents,
                           base::Unretained(this)),
@@ -472,8 +496,7 @@
   }
 
   const internal::LocalSiteCharacteristicsDataImpl* impl =
-      static_cast<LocalSiteCharacteristicsDataReader*>(test_reader.get())
-          ->impl_for_testing();
+      GetImplFromReader(test_reader.get());
   EXPECT_TRUE(impl);
   EXPECT_EQ(3U, impl->loaded_tabs_count_for_testing());
   EXPECT_EQ(3U, impl->loaded_tabs_in_background_count_for_testing());
@@ -521,13 +544,19 @@
 
   test_clock().Advance(GetLongestGracePeriod());
 
+  base::RunLoop run_loop;
+  GetImplFromReader(reader.get())
+      ->RegisterFeatureUsageCallbackForTesting(
+          internal::LocalSiteCharacteristicsDataImpl::
+              TrackedBackgroundFeatures::kTitleUpdate,
+          run_loop.QuitClosure());
+
   // Cause the "title update in background" feature to be used.
   ChangeTitleOfActiveWebContents();
 
-  while (reader->UpdatesTitleInBackground() !=
-         performance_manager::SiteFeatureUsage::kSiteFeatureInUse) {
-    base::RunLoop().RunUntilIdle();
-  }
+  run_loop.Run();
+  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
+            reader->UpdatesTitleInBackground());
 }
 
 IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
@@ -562,13 +591,19 @@
 
   test_clock().Advance(GetLongestGracePeriod());
 
+  base::RunLoop run_loop;
+  GetImplFromReader(reader.get())
+      ->RegisterFeatureUsageCallbackForTesting(
+          internal::LocalSiteCharacteristicsDataImpl::
+              TrackedBackgroundFeatures::kTitleUpdate,
+          run_loop.QuitClosure());
+
   // Cause the "title update in background" feature to be used.
   ChangeTitleOfActiveWebContents();
 
-  while (reader->UpdatesTitleInBackground() !=
-         performance_manager::SiteFeatureUsage::kSiteFeatureInUse) {
-    base::RunLoop().RunUntilIdle();
-  }
+  run_loop.Run();
+  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
+            reader->UpdatesTitleInBackground());
 
   HistoryServiceFactory::GetForProfile(browser()->profile(),
                                        ServiceAccessType::IMPLICIT_ACCESS)
diff --git a/chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.cc b/chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.cc
index c1154561..9f1ee11 100644
--- a/chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.cc
@@ -63,7 +63,7 @@
   // the process that was sampled.
   const base::TimeDelta& duration =
       process_node->expected_task_queueing_duration();
-  for (auto* frame_node : process_node->GetFrameNodes()) {
+  for (auto* frame_node : process_node->frame_nodes()) {
     if (!frame_node->IsMainFrame())
       continue;
     auto* page_node = frame_node->page_node();
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 29462c9..3c7fd2d 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -5,10 +5,6 @@
 import("//chrome/common/features.gni")
 import("//tools/grit/grit_rule.gni")
 
-if (is_chromeos) {
-  import("//chrome/browser/chromeos/kiosk_next_home/kiosk_next.gni")
-}
-
 assert(!is_ios, "Chromium/iOS shouldn't use anything in //chrome")
 
 if (closure_compile) {
@@ -111,24 +107,6 @@
       defines += [ "enable_hangout_services_extension" ]
     }
 
-    if (is_chromeos) {
-      if (enable_kiosk_next) {
-        defines += [ "_kiosk_next" ]
-      }
-
-      # The .grd contains references to generated files.
-      source_is_generated = true
-
-      deps = [
-        "//chrome/browser/resources/chromeos/kiosk_next_home:mojom_bin",
-      ]
-
-      grit_flags = [
-        "-E",
-        "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
-      ]
-    }
-
     outputs = [
       "grit/component_extension_resources.h",
       "grit/component_extension_resources_map.cc",
diff --git a/chrome/browser/resources/app_management/arc_permission_view.html b/chrome/browser/resources/app_management/arc_permission_view.html
index 877a539..5df2176 100644
--- a/chrome/browser/resources/app_management/arc_permission_view.html
+++ b/chrome/browser/resources/app_management/arc_permission_view.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="browser_proxy.html">
+<link rel="import" href="icons.html">
 <link rel="import" href="metadata_view.html">
 <link rel="import" href="permission_item.html">
 <link rel="import" href="permission_view_header.html">
@@ -40,7 +41,15 @@
             icon="cr:mic" permission-label="$i18n{microphone}"
             permission-type="MICROPHONE">
           </app-management-permission-item>
-          <div class="subpermission-row separated-row"
+          <app-management-permission-item class="subpermission-row"
+            icon="app-management:contacts"
+            permission-label="$i18n{contacts}" permission-type="CONTACTS">
+          </app-management-permission-item>
+          <app-management-permission-item class="subpermission-row"
+            icon="app-management:storage"
+            permission-label="$i18n{storage}" permission-type="STORAGE">
+          </app-management-permission-item>
+          <div class="subpermission-row separated-row clickable"
             on-click="onClickNativeSettingsButton_">
             <div class="header-text">$i18n{morePermissions}</div>
             <div class="permission-row-controls">
diff --git a/chrome/browser/resources/app_management/chrome_app_permission_view.html b/chrome/browser/resources/app_management/chrome_app_permission_view.html
index 75c40da8..32124a0 100644
--- a/chrome/browser/resources/app_management/chrome_app_permission_view.html
+++ b/chrome/browser/resources/app_management/chrome_app_permission_view.html
@@ -62,8 +62,13 @@
             app. -->
           <!-- TODO:(crbug.com/958269) Change behaviour of "more settings"
             based on the app. -->
-          <div id="more-settings" class="subpermission-row separated-row"
+          <div id="more-settings"
+            class="subpermission-row separated-row clickable"
             on-click="onClickExtensionsSettingsButton_">
+          <div id="more-settings"
+            class="subpermission-row separated-row clickable"
+            on-click="onClickExtensionsSettingsButton_"
+            hidden$="[[app_.hideMoreSettings]]">
             <div class="header-text">More settings</div>
             <cr-icon-button class="native-settings-icon icon-external"
               tabindex="0">
diff --git a/chrome/browser/resources/app_management/chrome_app_permission_view.js b/chrome/browser/resources/app_management/chrome_app_permission_view.js
index 594ba00e..f697f557 100644
--- a/chrome/browser/resources/app_management/chrome_app_permission_view.js
+++ b/chrome/browser/resources/app_management/chrome_app_permission_view.js
@@ -75,5 +75,5 @@
    */
   hasPermissions_: function(messages) {
     return messages.length > 0;
-  }
+  },
 });
diff --git a/chrome/browser/resources/app_management/fake_page_handler.js b/chrome/browser/resources/app_management/fake_page_handler.js
index db437db1..bdf351fa 100644
--- a/chrome/browser/resources/app_management/fake_page_handler.js
+++ b/chrome/browser/resources/app_management/fake_page_handler.js
@@ -48,6 +48,8 @@
         ArcPermissionType.LOCATION,
         ArcPermissionType.MICROPHONE,
         ArcPermissionType.NOTIFICATIONS,
+        ArcPermissionType.CONTACTS,
+        ArcPermissionType.STORAGE,
       ];
 
       const permissions = {};
@@ -93,6 +95,7 @@
         isPolicyPinned: apps.mojom.OptionalBool.kFalse,
         installSource: apps.mojom.InstallSource.kUser,
         permissions: {},
+        hideMoreSettings: false,
       };
 
       if (optConfig) {
diff --git a/chrome/browser/resources/app_management/icons.html b/chrome/browser/resources/app_management/icons.html
new file mode 100644
index 0000000..ccb1632
--- /dev/null
+++ b/chrome/browser/resources/app_management/icons.html
@@ -0,0 +1,17 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
+
+<iron-iconset-svg name="app-management" size="24">
+  <svg>
+    <!--
+    These icons are copied from material.io and kept in sorted order.
+    See http://goo.gl/Y1OdAq for instructions on adding additional icons.
+    -->
+    <defs>
+      <!-- From https://material.io/tools/icons/?icon=account_box&style=baseline -->
+      <g id="contacts"><path d="M19 3H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5a2 2 0 0 0-2-2zm-7 3c1.65 0 3 1.35 3 3 0 1.66-1.35 3-3 3s-3-1.34-3-3c0-1.65 1.35-3 3-3zm6 12H6v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1z"></path><path fill="none" d="M0 0h24v24H0z"></path></g>
+      <!-- From https://material.io/tools/icons/?icon=storage&style=baseline -->
+      <g id="storage"><path d="M2 20h20v-4H2v4zm2-3h2v2H4v-2zM2 4v4h20V4H2zm4 3H4V5h2v2zm-4 7h20v-4H2v4zm2-3h2v2H4v-2z"></path></g>
+    </defs>
+  </svg>
+</iron-iconset-svg>
diff --git a/chrome/browser/resources/app_management/notifications_view.html b/chrome/browser/resources/app_management/notifications_view.html
index 20b54a3..c66bcb2c 100644
--- a/chrome/browser/resources/app_management/notifications_view.html
+++ b/chrome/browser/resources/app_management/notifications_view.html
@@ -20,7 +20,8 @@
         align-items: center;
         display: flex;
         margin: 24px auto;
-        width: var(--card-width)
+        max-width: var(--card-max-width);
+        min-width: var(--card-min-width);
       }
 
       #notification-title {
diff --git a/chrome/browser/resources/app_management/permission_view_header.html b/chrome/browser/resources/app_management/permission_view_header.html
index e4aba4c..d0c6f0c 100644
--- a/chrome/browser/resources/app_management/permission_view_header.html
+++ b/chrome/browser/resources/app_management/permission_view_header.html
@@ -12,7 +12,8 @@
         align-items: center;
         display: flex;
         margin: 24px auto;
-        width: var(--card-width);
+        max-width: var(--card-max-width);
+        min-width: var(--card-min-width);
       }
 
       #permission-view-header-icon {
diff --git a/chrome/browser/resources/app_management/pwa_permission_view.html b/chrome/browser/resources/app_management/pwa_permission_view.html
index 10aae831..2d73d954 100644
--- a/chrome/browser/resources/app_management/pwa_permission_view.html
+++ b/chrome/browser/resources/app_management/pwa_permission_view.html
@@ -42,7 +42,7 @@
             permission-label="$i18n{microphone}"
             permission-type="CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC">
           </app-management-permission-item>
-          <div class="subpermission-row separated-row"
+          <div class="subpermission-row separated-row clickable"
             on-click="onClickSiteSettingsButton_">
             <div class="header-text">$i18n{morePermissions}</div>
             <div class="permission-row-controls">
diff --git a/chrome/browser/resources/app_management/shared_style.html b/chrome/browser/resources/app_management/shared_style.html
index 5fb4f69..2750489 100644
--- a/chrome/browser/resources/app_management/shared_style.html
+++ b/chrome/browser/resources/app_management/shared_style.html
@@ -13,8 +13,8 @@
         display: flex;
         flex-direction: column;
         margin: 24px auto;
-        max-width: var(--card-width);
-        min-width: 550px;
+        max-width: var(--card-max-width);
+        min-width: var(--card-min-width);
       }
 
       .separated-row {
@@ -25,10 +25,13 @@
 
       .permission-card-row {
         border-top: var(--card-separator);
-        cursor: pointer;
         padding: 0 24px;
       }
 
+      .clickable {
+        cursor: pointer;
+      }
+
       .permission-card-row:first-child {
         border-style: none;
       }
diff --git a/chrome/browser/resources/app_management/shared_vars.html b/chrome/browser/resources/app_management/shared_vars.html
index 0c46e3a..667ce2c 100644
--- a/chrome/browser/resources/app_management/shared_vars.html
+++ b/chrome/browser/resources/app_management/shared_vars.html
@@ -8,8 +8,9 @@
   html {
     --app-management-font-size: 13px;
     --app-management-line-height: 1.54; /* 20px */
+    --card-max-width: 676px;
+    --card-min-width: 550px;
     --card-separator: 1px solid rgba(0, 0, 0, 0.12);
-    --card-width: 676px;
     --control-separator-color: rgb(218, 220, 224);
     --expanded-permission-row-height: 48px;
     --header-font-weight: 500;
diff --git a/chrome/browser/resources/bookmarks/shared_style.html b/chrome/browser/resources/bookmarks/shared_style.html
index cca71c3..870a3ca 100644
--- a/chrome/browser/resources/bookmarks/shared_style.html
+++ b/chrome/browser/resources/bookmarks/shared_style.html
@@ -68,6 +68,10 @@
         -webkit-mask-image: url(images/folder_open.svg);
       }
 
+      :host-context([dir=rtl]) .folder-icon {
+        transform: scaleX(-1);
+      }
+
       .website-icon {
         background-repeat: no-repeat;
         height: 16px;
diff --git a/chrome/browser/resources/chromeos/BUILD.gn b/chrome/browser/resources/chromeos/BUILD.gn
index 587c3ab..cb82aaf 100644
--- a/chrome/browser/resources/chromeos/BUILD.gn
+++ b/chrome/browser/resources/chromeos/BUILD.gn
@@ -41,7 +41,6 @@
     "braille_ime:closure_compile",
     "internet_config_dialog:closure_compile",
     "internet_detail_dialog:closure_compile",
-    "kiosk_next_home:closure_compile",
     "login:closure_compile",
     "machine_learning:closure_compile",
     "multidevice_setup:closure_compile",
diff --git a/chrome/browser/resources/chromeos/add_supervision/add_supervision.html b/chrome/browser/resources/chromeos/add_supervision/add_supervision.html
index a1d0917..239104a 100644
--- a/chrome/browser/resources/chromeos/add_supervision/add_supervision.html
+++ b/chrome/browser/resources/chromeos/add_supervision/add_supervision.html
@@ -11,18 +11,80 @@
     <script src="chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom-lite.js"></script>
     <script src="post_message_api.js"></script>
     <script src="add_supervision_api_server.js"></script>
-    <script src="add_supervision.js"></script>
+    <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+    <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+    <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
     <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-    <style>
-      html,
-      body {
-        height: 100%;
-        overflow: auto;
-        overflow-y: hidden;
-      }
-    </style>
   </head>
   <body>
-    <webview id="webview"></webview>
+    <dom-module id="add-supervision-ui">
+      <template>
+        <style>
+          html,
+          body {
+            height: 100%;
+            overflow: auto;
+            overflow-y: hidden;
+          }
+
+          .title {
+            color: var(--google-grey-900);
+            font-family: 'Google Sans', Roboto, sans-serif;
+            font-size: 28px;
+            padding: 32px 0 16px 0;
+          }
+
+          .sub-title {
+            color: var(--google-grey-900);
+            font: 13px Roboto, sans-serif;
+          }
+
+          .error-icon {
+            fill: var(--google-blue-600);
+          }
+
+          #offlineContentDiv {
+            padding-inline-start: 50px;
+          }
+
+          #networkUnavailableDiv {
+            padding-inline-start: 190px;
+            padding-top: 50px;
+          }
+
+          #closeButtonDiv {
+            padding-inline-start: 600px;
+            padding-top: 100px;
+            width: 100%;
+          }
+        </style>
+        <div id="offlineContentDiv">
+          <div>
+            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="error-icon">
+              <path d="M11 15h2v2h-2v-2zm0-8h2v6h-2V7zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z">
+              <path fill="none" d="M0 0h24v24H0V0z">
+            </svg>
+          </div>
+          <div class="title">$i18n{networkDownHeading}</div>
+          <div class="sub-title">$i18n{networkDownDescription}</div>
+          <div id="networkUnavailableDiv">
+            <img src="images/network_unavailable.svg">
+          </div>
+          <div id="closeButtonDiv">
+            <cr-button class="action-button" on-click="closeDialog_"
+                tabindex="0">
+              $i18n{networkDownButtonLabel}
+            </cr-button>
+          </div>
+        </div>
+        <div id="webviewDiv">
+          <webview id="webview"></webview>
+        </div>
+      </template>
+    <script src="add_supervision.js"></script>
+    </dom-module>
+
+    <add-supervision-ui></add-supervision-ui>
   </body>
+
 </html>
diff --git a/chrome/browser/resources/chromeos/add_supervision/add_supervision.js b/chrome/browser/resources/chromeos/add_supervision/add_supervision.js
index 9c8ffde..573f06a 100644
--- a/chrome/browser/resources/chromeos/add_supervision/add_supervision.js
+++ b/chrome/browser/resources/chromeos/add_supervision/add_supervision.js
@@ -35,40 +35,67 @@
 let server = null;
 const proxy = addSupervision.mojom.AddSupervisionHandler.getProxy();
 
-document.addEventListener('DOMContentLoaded', () => {
-  proxy.getOAuthToken().then((result) => {
-    const webviewUrl = loadTimeData.getString('webviewUrl');
-    if (!webviewUrl.startsWith('https://families.google.com')) {
-      console.error('webviewUrl is not from https://families.google.com');
-      return;
-    }
-    const eventOriginFilter = loadTimeData.getString('eventOriginFilter');
-    const webview =
-        /** @type {!WebView} */ (document.querySelector('#webview'));
+Polymer({
+  is: 'add-supervision-ui',
 
-    const accessToken = result.oauthToken;
-    const flowType = loadTimeData.getString('flowType');
-    const platformVersion = loadTimeData.getString('platformVersion');
+  /** Attempts to close the dialog */
+  closeDialog_: function() {
+    server.requestClose();
+  },
 
-    const url = new URL(webviewUrl);
-    url.searchParams.set('flowType', flowType);
-    url.searchParams.set('platformVersion', platformVersion);
-    url.searchParams.set('accessToken', accessToken);
+  /** @override */
+  ready: function() {
+    // Initialize and listen for online/offline state.
+    this.webviewDiv = this.$.webviewDiv;
+    this.webviewDiv.hidden = !navigator.onLine;
 
-    // Allow guest webview content to open links in new windows.
-    webview.addEventListener('newwindow', function(e) {
-      window.open(e.targetUrl);
+    this.offlineContentDiv = this.$.offlineContentDiv;
+    this.offlineContentDiv.hidden = navigator.onLine;
+
+    window.addEventListener('online', () => {
+      this.webviewDiv.style.hidden = false;
+      this.offlineContentDiv.hidden = true;
     });
 
-    // Block any requests to URLs other than one specified
-    // by eventOriginFilter.
-    webview.request.onBeforeRequest.addListener(function(details) {
-      return {cancel: !isAllowedRequest(details)};
-    }, {urls: ['<all_urls>']}, ['blocking']);
+    window.addEventListener('offline', () => {
+      this.webviewDiv.hidden = true;
+      this.offlineContentDiv.hidden = false;
+    });
 
-    webview.src = url.toString();
+    proxy.getOAuthToken().then((result) => {
+      const webviewUrl = loadTimeData.getString('webviewUrl');
+      if (!webviewUrl.startsWith('https://families.google.com')) {
+        console.error('webviewUrl is not from https://families.google.com');
+        return;
+      }
+      const eventOriginFilter = loadTimeData.getString('eventOriginFilter');
+      const webview =
+          /** @type {!WebView} */ (this.$.webview);
 
-    // Set up the server.
-    server = new AddSupervisionAPIServer(webview, url, eventOriginFilter);
-  });
+      const accessToken = result.oauthToken;
+      const flowType = loadTimeData.getString('flowType');
+      const platformVersion = loadTimeData.getString('platformVersion');
+
+      const url = new URL(webviewUrl);
+      url.searchParams.set('flowType', flowType);
+      url.searchParams.set('platformVersion', platformVersion);
+      url.searchParams.set('accessToken', accessToken);
+
+      // Allow guest webview content to open links in new windows.
+      webview.addEventListener('newwindow', function(e) {
+        window.open(e.targetUrl);
+      });
+
+      // Block any requests to URLs other than one specified
+      // by eventOriginFilter.
+      webview.request.onBeforeRequest.addListener(function(details) {
+        return {cancel: !isAllowedRequest(details)};
+      }, {urls: ['<all_urls>']}, ['blocking']);
+
+      webview.src = url.toString();
+
+      // Set up the server.
+      server = new AddSupervisionAPIServer(webview, url, eventOriginFilter);
+    });
+  },
 });
diff --git a/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js b/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
index 038d09af..a112831 100644
--- a/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
+++ b/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
@@ -5,7 +5,9 @@
 /**
  * The methods to expose to the client.
  */
-const METHOD_LIST = ['logOut', 'getInstalledArcApps'];
+const METHOD_LIST = [
+  'logOut', 'getInstalledArcApps', 'requestClose', 'notifySupervisionEnabled'
+];
 
 /**
  * Class that implements the server side of the AddSupervision postMessage
@@ -31,12 +33,14 @@
     this.registerMethod('logOut', this.logOut.bind(this));
     this.registerMethod(
         'getInstalledArcApps', this.getInstalledArcApps.bind(this));
+    this.registerMethod('requestClose', this.requestClose.bind(this));
+    this.registerMethod(
+        'notifySupervisionEnabled', this.notifySupervisionEnabled.bind(this));
   }
 
   /**
    * Logs out of the device.
    * @param {!Array} unused Placeholder unused empty parameter.
-   * @return {Promise} This promise is never actually resolved.
    */
   logOut(unused) {
     return this.proxy_.logOut();
@@ -52,4 +56,25 @@
   getInstalledArcApps(unused) {
     return this.proxy_.getInstalledArcApps();
   }
+
+  /**
+   * Attempts to close the widget hosting the Add Supervision flow.
+   * If supervision has already been enabled, this will prompt the
+   * user to sign out.
+   * @param {!Array} unused Placeholder unused empty parameter.
+   * @return {Promise <{closed: boolean}>} If the dialog is not closed
+   *     this promise will
+   * resolve with boolean result indicating whether the dialog was closed.
+   */
+  requestClose(unused) {
+    return this.proxy_.requestClose();
+  }
+
+  /**
+   * Signals to the API that supervision has been enabled for the current user.
+   * @param {!Array} unused Placeholder unused empty parameter.
+   */
+  notifySupervisionEnabled(unused) {
+    return this.proxy_.notifySupervisionEnabled();
+  }
 }
diff --git a/chrome/browser/resources/chromeos/add_supervision/images/network_unavailable.svg b/chrome/browser/resources/chromeos/add_supervision/images/network_unavailable.svg
new file mode 100644
index 0000000..dfb5a34
--- /dev/null
+++ b/chrome/browser/resources/chromeos/add_supervision/images/network_unavailable.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="264" height="264"><g fill="none" fill-rule="evenodd"><path fill="#EA4335" d="M171.605 82.336l-6.413-6.413a2.268 2.268 0 0 1 0-3.206l6.413-6.413a2.267 2.267 0 0 1 3.207 0l6.412 6.413a2.266 2.266 0 0 1 0 3.206l-6.412 6.413a2.269 2.269 0 0 1-3.207 0"/><path fill="#FBBC04" d="M105.869 48.101l-6.413-6.413a2.268 2.268 0 0 1 0-3.206l6.413-6.413a2.269 2.269 0 0 1 3.207 0l6.413 6.413a2.268 2.268 0 0 1 0 3.206l-6.413 6.413a2.269 2.269 0 0 1-3.207 0"/><path fill="#F1F3F4" d="M96.554 123.415l6.413-6.413a2.269 2.269 0 0 1 3.207 0l6.412 6.413a2.267 2.267 0 0 1 0 3.207l-6.412 6.412a2.267 2.267 0 0 1-3.207 0l-6.413-6.412a2.269 2.269 0 0 1 0-3.207"/><path fill="#4285F4" d="M129.012 65.68a5.667 5.667 0 0 1 0-8.017l4.81-4.81a5.67 5.67 0 0 1 8.016 8.016l-4.81 4.81a5.67 5.67 0 0 1-8.016 0"/><path fill="#F1F3F4" d="M133.766 102.358a5.667 5.667 0 0 1 0-8.016l4.81-4.81a5.67 5.67 0 0 1 8.016 8.016l-4.81 4.81a5.67 5.67 0 0 1-8.016 0"/><path fill="#FBBC04" d="M146.85 97.29l.742-.742a5.667 5.667 0 1 0-8.016-8.016l-4.81 4.81a5.62 5.62 0 0 0-1.46 2.601c4.62.068 9.146.527 13.545 1.346"/><path fill="#34A853" d="M87.07 75.282l11.947 3.201c1.686.452 2.25 2.56 1.016 3.793l-8.745 8.746c-1.235 1.234-3.342.67-3.794-1.016L84.293 78.06c-.452-1.686 1.09-3.229 2.777-2.777"/><path fill="#F1F3F4" d="M151.384 118.925l11.947 3.2c1.686.453 2.25 2.56 1.016 3.794l-8.745 8.746c-1.235 1.234-3.342.67-3.794-1.016l-3.2-11.947c-.453-1.686 1.09-3.23 2.776-2.777"/><path fill="#34A853" d="M159 17a7 7 0 1 1-14 0 7 7 0 0 1 14 0"/><path fill="#DADCE0" d="M132 180.473c6.834 0 13.031 2.774 17.52 7.253l7.008-6.993c-6.284-6.27-14.96-10.154-24.528-10.154-9.568 0-18.244 3.884-24.528 10.154l7.008 6.993c4.489-4.479 10.686-7.253 17.52-7.253m10.517 14.24A14.843 14.843 0 0 0 132 190.368a14.843 14.843 0 0 0-10.517 4.347L132 205.208l10.517-10.494z"/><path fill="#DADCE0" d="M132 160.686c12.302 0 23.457 4.993 31.536 13.055l7.008-6.993c-9.875-9.853-23.509-15.956-38.544-15.956-15.035 0-28.669 6.103-38.544 15.956l7.008 6.993c8.079-8.062 19.234-13.055 31.536-13.055"/><path stroke="#DADCE0" stroke-width="4" d="M212 175c0 44.183-35.817 80-80 80s-80-35.817-80-80 35.817-80 80-80 80 35.817 80 80zM98 144l62 62"/></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/camera/src/js/background.js b/chrome/browser/resources/chromeos/camera/src/js/background.js
index 54a419a..8fa77cc2 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/background.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/background.js
@@ -40,10 +40,18 @@
  * won't connect to the main.html target before the window is created, otherwise
  * the window might disappear.
  * @type {boolean}
+ * @deprecated This flag would be removed after we migrate CCA Tast tests.
  */
 cca.bg.appWindowCreated = false;
 
 /**
+ * It's used in test to ensure that we won't connect to the main.html target
+ * before the window is created, otherwise the window might disappear.
+ * @type {?function(): undefined}
+ */
+cca.bg.onAppWindowCreatedForTesting = null;
+
+/**
  * Creates the window. Note, that only one window at once is supported.
  */
 cca.bg.create = function() {
@@ -82,6 +90,9 @@
         chrome.storage.local.set({fullscreen: inAppWindow.isFullscreen()});
       });
       cca.bg.appWindowCreated = true;
+      if (cca.bg.onAppWindowCreatedForTesting !== null) {
+        cca.bg.onAppWindowCreatedForTesting();
+      }
     });
   });
 };
diff --git a/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js b/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
index d8ce9e1..36da568 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
@@ -22,6 +22,37 @@
 cca.mojo.PhotoCapabilities;
 
 /**
+ * The mojo interface of CrOS Image Capture API. It provides a singleton
+ * instance.
+ */
+cca.mojo.MojoInterface = function() {
+  /**
+   * @type {cca.mojo.MojoInterface} The singleton instance of this object.
+   */
+  cca.mojo.MojoInterface.instance_ = this;
+
+  /**
+   * @type {cros.mojom.CrosImageCaptureProxy} A interface proxy that used to
+   *     construct the mojo interface.
+   */
+  this.proxy = cros.mojom.CrosImageCapture.getProxy();
+
+  // End of properties, seal the object.
+  Object.seal(this);
+};
+
+/**
+ * Gets the mojo interface proxy which could be used to communicate with Chrome.
+ * @return {cros.mojom.CrosImageCaptureProxy} The mojo interface proxy.
+ */
+cca.mojo.MojoInterface.getProxy = function() {
+  if (!cca.mojo.MojoInterface.instance_) {
+    new cca.mojo.MojoInterface();
+  }
+  return cca.mojo.MojoInterface.instance_.proxy;
+};
+
+/**
  * Creates the wrapper of JS image-capture and Mojo image-capture.
  * @param {MediaStreamTrack} videoTrack A video track whose still images will be
  *     taken.
@@ -39,12 +70,6 @@
    */
   this.capture_ = new ImageCapture(videoTrack);
 
-  /**
-   * @type {cros.mojom.CrosImageCaptureProxy}
-   * @private
-   */
-  this.mojoCapture_ = cros.mojom.CrosImageCapture.getProxy();
-
   // End of properties, seal the object.
   Object.seal(this);
 };
@@ -129,7 +154,7 @@
   return Promise
       .all([
         this.capture_.getPhotoCapabilities(),
-        this.mojoCapture_.getCameraInfo(this.deviceId_),
+        cca.mojo.MojoInterface.getProxy().getCameraInfo(this.deviceId_),
       ])
       .then(([capabilities, {cameraInfo}]) => {
         const staticMetadata = cameraInfo.staticCameraCharacteristics;
@@ -157,7 +182,8 @@
   const takes = [];
   if (photoEffects) {
     photoEffects.forEach((effect) => {
-      takes.push((this.mojoCapture_.setReprocessOption(this.deviceId_, effect))
+      takes.push((cca.mojo.MojoInterface.getProxy().setReprocessOption(
+                      this.deviceId_, effect))
                      .then(({status, blob}) => {
                        if (status != 0) {
                          throw new Error('Mojo image capture error: ' + status);
@@ -188,35 +214,36 @@
   const typeIndex = 3;
   const numElementPerEntry = 4;
 
-  const mojoCapture = cros.mojom.CrosImageCapture.getProxy();
-  return mojoCapture.getCameraInfo(deviceId).then(({cameraInfo}) => {
-    const staticMetadata = cameraInfo.staticCameraCharacteristics;
-    const streamConfigs = cca.mojo.getMetadataData_(
-        staticMetadata,
-        cros.mojom.CameraMetadataTag
-            .ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
-    // The data of |streamConfigs| looks like:
-    // streamConfigs: [FORMAT_1, WIDTH_1, HEIGHT_1, TYPE_1,
-    //                 FORMAT_2, WIDTH_2, HEIGHT_2, TYPE_2, ...]
-    if (streamConfigs.length % numElementPerEntry != 0) {
-      throw new Error('Unexpected length of stream configurations');
-    }
-
-    let supportedResolutions = [];
-    for (let configIdx = 0, configBase = 0; configBase < streamConfigs.length;
-         configIdx++, configBase = configIdx * numElementPerEntry) {
-      const format = streamConfigs[configBase + formatIndex];
-      if (format === formatBlob) {
-        const type = streamConfigs[configBase + typeIndex];
-        if (type === typeOutputStream) {
-          const width = streamConfigs[configBase + widthIndex];
-          const height = streamConfigs[configBase + heightIndex];
-          supportedResolutions.push([width, height]);
+  return cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId).then(
+      ({cameraInfo}) => {
+        const staticMetadata = cameraInfo.staticCameraCharacteristics;
+        const streamConfigs = cca.mojo.getMetadataData_(
+            staticMetadata,
+            cros.mojom.CameraMetadataTag
+                .ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+        // The data of |streamConfigs| looks like:
+        // streamConfigs: [FORMAT_1, WIDTH_1, HEIGHT_1, TYPE_1,
+        //                 FORMAT_2, WIDTH_2, HEIGHT_2, TYPE_2, ...]
+        if (streamConfigs.length % numElementPerEntry != 0) {
+          throw new Error('Unexpected length of stream configurations');
         }
-      }
-    }
-    return supportedResolutions;
-  });
+
+        let supportedResolutions = [];
+        for (let configIdx = 0, configBase = 0;
+             configBase < streamConfigs.length;
+             configIdx++, configBase = configIdx * numElementPerEntry) {
+          const format = streamConfigs[configBase + formatIndex];
+          if (format === formatBlob) {
+            const type = streamConfigs[configBase + typeIndex];
+            if (type === typeOutputStream) {
+              const width = streamConfigs[configBase + widthIndex];
+              const height = streamConfigs[configBase + heightIndex];
+              supportedResolutions.push([width, height]);
+            }
+          }
+        }
+        return supportedResolutions;
+      });
 };
 
 /**
@@ -236,36 +263,37 @@
   const durationIndex = 3;
   const numElementPerEntry = 4;
 
-  var mojoCapture = cros.mojom.CrosImageCapture.getProxy();
-  return mojoCapture.getCameraInfo(deviceId).then(({cameraInfo}) => {
-    const staticMetadata = cameraInfo.staticCameraCharacteristics;
-    const minFrameDurationConfigs = cca.mojo.getMetadataData_(
-        staticMetadata,
-        cros.mojom.CameraMetadataTag
-            .ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
-    // The data of |minFrameDurationConfigs| looks like:
-    // minFrameDurationCOnfigs: [FORMAT_1, WIDTH_1, HEIGHT_1, DURATION_1,
-    //                           FORMAT_2, WIDTH_2, HEIGHT_2, DURATION_2, ...]
-    if (minFrameDurationConfigs.length % numElementPerEntry != 0) {
-      throw new Error('Unexpected length of frame durations configs');
-    }
+  return cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId).then(
+      ({cameraInfo}) => {
+        const staticMetadata = cameraInfo.staticCameraCharacteristics;
+        const minFrameDurationConfigs = cca.mojo.getMetadataData_(
+            staticMetadata,
+            cros.mojom.CameraMetadataTag
+                .ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+        // The data of |minFrameDurationConfigs| looks like:
+        // minFrameDurationCOnfigs: [FORMAT_1, WIDTH_1, HEIGHT_1, DURATION_1,
+        //                           FORMAT_2, WIDTH_2, HEIGHT_2, DURATION_2,
+        //                           ...]
+        if (minFrameDurationConfigs.length % numElementPerEntry != 0) {
+          throw new Error('Unexpected length of frame durations configs');
+        }
 
-    let supportedConfigs = [];
-    for (let configIdx = 0, configBase = 0;
-         configBase < minFrameDurationConfigs.length;
-         configIdx++, configBase = configIdx * numElementPerEntry) {
-      const format = minFrameDurationConfigs[configBase + formatIndex];
-      if (format === formatYuv) {
-        const width = minFrameDurationConfigs[configBase + widthIndex];
-        const height = minFrameDurationConfigs[configBase + heightIndex];
-        const fps = Math.round(
-            oneSecondInNs /
-            minFrameDurationConfigs[configBase + durationIndex]);
-        supportedConfigs.push([width, height, fps]);
-      }
-    }
-    return supportedConfigs;
-  });
+        let supportedConfigs = [];
+        for (let configIdx = 0, configBase = 0;
+             configBase < minFrameDurationConfigs.length;
+             configIdx++, configBase = configIdx * numElementPerEntry) {
+          const format = minFrameDurationConfigs[configBase + formatIndex];
+          if (format === formatYuv) {
+            const width = minFrameDurationConfigs[configBase + widthIndex];
+            const height = minFrameDurationConfigs[configBase + heightIndex];
+            const fps = Math.round(
+                oneSecondInNs /
+                minFrameDurationConfigs[configBase + durationIndex]);
+            supportedConfigs.push([width, height, fps]);
+          }
+        }
+        return supportedConfigs;
+      });
 };
 
 /**
@@ -275,8 +303,8 @@
  * @return {Promise<cros.mojom.CameraFacing>} Promise of device facing.
  */
 cca.mojo.getCameraFacing = function(deviceId) {
-  var mojoCapture = cros.mojom.CrosImageCapture.getProxy();
-  return mojoCapture.getCameraInfo(deviceId).then(({cameraInfo}) => {
-    return cameraInfo.facing;
-  });
+  return cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId).then(
+      ({cameraInfo}) => {
+        return cameraInfo.facing;
+      });
 };
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
index 63a1129..144c2a9 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
@@ -133,9 +133,10 @@
   // Remove the deprecated values.
   chrome.storage.local.remove(['effectIndex', 'toggleMulti', 'toggleMirror']);
 
-  // TODO(yuli): Replace with devicechanged event.
   this.maybeRefreshVideoDeviceIds_();
-  setInterval(() => this.maybeRefreshVideoDeviceIds_(), 1000);
+  navigator.mediaDevices.addEventListener('devicechange', () => {
+    this.maybeRefreshVideoDeviceIds_();
+  });
 };
 
 /**
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_nl.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_nl.xtb
index 8b19764..fdc54b6 100644
--- a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_nl.xtb
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_nl.xtb
@@ -58,7 +58,7 @@
 <translation id="7337660886763914220">Bestandssysteemfouten.</translation>
 <translation id="7557677699350329807">Naar volgende camera</translation>
 <translation id="7608223098072244877">4 bij 4</translation>
-<translation id="7649070708921625228">Help</translation>
+<translation id="7649070708921625228">Hulp</translation>
 <translation id="7658239707568436148">Annuleren</translation>
 <translation id="8049189770492311300">Timer</translation>
 <translation id="8145038249676204903">Naar foto maken</translation>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/language_switching.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/language_switching.js
index 717d41c..819aca3c 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/language_switching.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/language_switching.js
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview Provides language switching services for ChromeVox.
+ * @fileoverview Provides language switching services for ChromeVox, which
+ * uses language detection information to automatically change the ChromeVox
+ * output language.
  */
 
 goog.provide('LanguageSwitching');
@@ -12,79 +14,162 @@
  * The current output language.
  * @private {string}
  */
-LanguageSwitching.currentLanguage = '';
+// TODO(akihiroota): Initialize this to current ChromeVox language.
+LanguageSwitching.currentLanguage_ = '';
 
 /**
- * Maps languages to array of country codes that are considered equivalent.
- * @private {!Object<string, !Array<string>>}
+ * Confidence threshold to meet before assigning inner-node language.
+ * @const
+ * @type {number}
  */
-LanguageSwitching.equivalentDialects = {
-  'en': ['au', 'ca', 'gb', 'ie', 'nz', 'us', '']
-  // TODO(akihiroota): Populate this object.
+LanguageSwitching.PROBABILITY_THRESHOLD = 0.9;
+
+/*
+ * Main language switching function.
+ * Cut up string attribute value into multiple spans with different
+ * languages. Ranges and associated language information are returned by the
+ * languageAnnotationsForStringAttribute() function.
+ * @param {AutomationNode} node
+ * @param {string} stringAttribute The string attribute for which we want to
+ * get a language annotation
+ * @param {Function<outputString: string, newLanguage: string>}
+ *     appendStringWithLanguage
+ * A callback that appends outputString to the output buffer in newLanguage.
+ */
+LanguageSwitching.assignLanguagesForStringAttribute = function(
+    node, stringAttribute, appendStringWithLanguage) {
+  if (!node)
+    return;
+  var languageAnnotation =
+      node.languageAnnotationForStringAttribute(stringAttribute);
+  var stringAttributeValue = node[stringAttribute];
+  // If no language annotation is found, append entire stringAttributeValue to
+  // buffer and do not switch languages.
+  // TODO(akihiroota): Decide if we simply want to return if
+  // stringAttributeValue is null.
+  if (!languageAnnotation || languageAnnotation.length === 0) {
+    appendStringWithLanguage(
+        stringAttributeValue || '', LanguageSwitching.currentLanguage_);
+    return;
+  }
+
+  // Split output based on language annotation.
+  // Each object in languageAnnotation contains a language, probability,
+  // and start/end indices that define a substring.
+  for (var i = 0; i < languageAnnotation.length; ++i) {
+    var speechProps = new Output.SpeechProperties();
+    var startIndex = languageAnnotation[i].startIndex;
+    var endIndex = languageAnnotation[i].endIndex;
+    var language = languageAnnotation[i].language;
+    var probability = languageAnnotation[i].probability;
+
+    var outputString = LanguageSwitching.buildOutputString(
+        stringAttributeValue, startIndex, endIndex);
+    var newLanguage =
+        LanguageSwitching.decideNewLanguage(node, language, probability);
+    if (LanguageSwitching.didLanguageSwitch(newLanguage)) {
+      LanguageSwitching.currentLanguage_ = newLanguage;
+      // TODO(akihiroota): Pass in human-readable language instead of language
+      // code if language switches.
+      // Prepend the language to outputString if language switched.
+      outputString = Msgs.getMsg(
+          'language_switch',
+          [LanguageSwitching.currentLanguage_, outputString]);
+    }
+    appendStringWithLanguage(outputString, newLanguage);
+  }
 };
 
 /**
- * Updates current language and returns the output language for node.
- * @param {AutomationNode} node
+ * Run error checks on language data and decide new output language.
+ * @param {!AutomationNode} node
+ * @param {string} innerNodeLanguage
+ * @param {number} probability
  * @return {string}
  */
-LanguageSwitching.updateCurrentLanguageForNode = function(node) {
-  if (!node)
-    return LanguageSwitching.currentLanguage;
-  // Use detected language. If unavailable, fallback on author-provided
-  // language.
-  var targetLanguage = node.detectedLanguage || node.language;
-  // If targetLanguage is still empty, then do not switch languages.
-  if (!targetLanguage)
-    return LanguageSwitching.currentLanguage;
+LanguageSwitching.decideNewLanguage = function(
+    node, innerNodeLanguage, probability) {
+  // Use the following priority rankings when deciding language.
+  // 1. Inner-node language. If we can detect inner-node language with a high
+  // enough probability of accuracy, then we should use it.
+  // 2. Node-level detected language.
+  // 3. Author-provided language. This language is also assigned at the node
+  // level.
+  // 4. LanguageSwitching.currentLanguage_. If we do not have enough language
+  // data, then we should not switch languages.
 
-  targetLanguage = targetLanguage.toLowerCase();
-  // Validate targetLanguage.
-  // Each language code should be of length 2 or 5 AND each component should
-  // be of length 2.
-  // Ex: en or en-us
-  if (!(targetLanguage.length === 2 || targetLanguage.length === 5))
-    return LanguageSwitching.currentLanguage;
-  var arr = targetLanguage.split('-');
-  for (var i = 0; i < arr.length; ++i) {
-    if (!(arr[i].length === 2))
-      return LanguageSwitching.currentLanguage;
-  }
+  // Use innerNodeLanguage if probability exceeds threshold.
+  if (probability > LanguageSwitching.PROBABILITY_THRESHOLD)
+    return innerNodeLanguage.toLowerCase();
 
-  // Only switch languages if targetLanguage is different than currentLanguage.
-  if (!LanguageSwitching.isEquivalentToCurrentLanguage(targetLanguage))
-    LanguageSwitching.currentLanguage = targetLanguage;
-  return LanguageSwitching.currentLanguage;
+  // Use detected language as nodeLevelLanguage, if present.
+  // If no detected language, use author-provided language.
+  var nodeLevelLanguage = node.detectedLanguage || node.language;
+  // If nodeLevelLanguage is null, then do not switch languages.
+  // We do not have enough information to make a confident language assignment,
+  // so we will just stick with the current language.
+  if (!nodeLevelLanguage)
+    return LanguageSwitching.currentLanguage_;
+
+  nodeLevelLanguage = nodeLevelLanguage.toLowerCase();
+
+  // TODO(akihiroota): Move validation into separate function.
+  // Validate nodeLevelLanguage, since there's the possibility that it comes
+  // from the author.
+  // There are five possible components of a language code. See link for more
+  // details: http://userguide.icu-project.org/locale
+  // The TTS Engine handles parsing language codes, but it needs to have a
+  // valid language component for the engine not to crash.
+  // For example, given the language code 'en-US', 'en' is the language
+  // component.
+  var langComponentArray = nodeLevelLanguage.split('-');
+  if (!langComponentArray || (langComponentArray.length === 0))
+    return LanguageSwitching.currentLanguage_;
+
+  // The language component should have length of either two or three.
+  if (langComponentArray[0].length !== 2 && langComponentArray[0].length !== 3)
+    return LanguageSwitching.currentLanguage_;
+
+  return nodeLevelLanguage;
 };
 
 /**
- * Returns true if targetLanguage is equivalent to current language.
- * @param {!string} targetLanguage
+ * Returns a unicode-aware substring of text from startIndex to endIndex.
+ * @param {string} text
+ * @param {number} startIndex
+ * @param {number} endIndex
+ * @return {string}
+ */
+LanguageSwitching.buildOutputString = function(text, startIndex, endIndex) {
+  var result = '';
+  var textSymbolArray = [...text];
+  for (var i = startIndex; i < endIndex; ++i) {
+    result += textSymbolArray[i];
+  }
+  return result;
+};
+
+// TODO(akihiroota): Some languages may have the same language code, but be
+// distinctly different. For example, there are some dialects of Chinese that
+// are very different from each other. For these cases, comparing just the
+// language components is not enough to differentiate the languages.
+/**
+ * Returns true if newLanguage is different than current language.
+ * Only compares the language components of the language code.
+ * Note: Language code validation is the responsibility of the caller. This
+ * function assumes valid language codes.
+ * Ex: 'fr-fr' and 'fr-ca' have the same language component, but different
+ * locales. We would return false in the above case. Ex: 'fr-ca' and 'en-ca' are
+ * different language components, but same locales. We would return true in the
+ * above case.
+ * @param {string} newLanguage The language for current output.
  * @return {boolean}
  */
-LanguageSwitching.isEquivalentToCurrentLanguage = function(targetLanguage) {
-  // Language codes are composed of two components: language and country.
-  // Ex: en-us represents American English.
-  // Note: language codes sometimes come without the country component.
-
-  // Compare the language codes of current and target language.
-  if (LanguageSwitching.currentLanguage.substring(0, 2) !==
-      targetLanguage.substring(0, 2)) {
-    return false;
-  }
-
-  // Lookup the equivalent country codes for language.
-  var countryCodes =
-      LanguageSwitching.equivalentDialects[LanguageSwitching.currentLanguage
-                                               .substring(0, 2)];
-
-  // If language not in object, return false.
-  if (countryCodes === undefined)
-    return false;
-
-  var currentCountryCode = LanguageSwitching.currentLanguage.substring(3);
-  var targetCountryCode = targetLanguage.substring(3);
-
-  return countryCodes.includes(currentCountryCode) &&
-      countryCodes.includes(targetCountryCode);
+LanguageSwitching.didLanguageSwitch = function(newLanguage) {
+  // Compare language components of current and new language codes.
+  var newLanguageComponents = newLanguage.split('-');
+  var currentLanguageComponents = LanguageSwitching.currentLanguage_.split('-');
+  if (newLanguageComponents[0] !== currentLanguageComponents[0])
+    return true;
+  return false;
 };
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/language_switching_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/language_switching_test.extjs
index ac8cbd1..098f5946e 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/language_switching_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/language_switching_test.extjs
@@ -50,7 +50,7 @@
     <p lang="en">Hello.</p>
     <p lang="es">Hola.</p>
     <p lang="fr">Salut.</p>
-    <span lang="it">Ciao.</span>
+    <span lang="it">Ciao amico.</span>
   */},
 
   nestedLanguagesDoc: function() {/*!
@@ -62,19 +62,65 @@
 </p>
   */},
 
-  dialectsDoc: function() {/*!
-    <p lang="en-gb">Spoken in GB English.</p>
-    <p lang="en-us">This text should also be in GB English.</p>
-    <p lang="en-us">So should this text.</p>
-    */},
-
   buttonAndLinkDoc: function() {/*!
     <body lang="es">
-      <p>This is a paragraph, spoken in Spanish.</p>
+      <p>This is a paragraph, spoken in English.</p>
       <button type="submit">This is a button, spoken in Spanish.</button>
       <a href="https://www.google.com">This is a link, spoken in Spanish.</a>
     </body>
   */},
+
+  japaneseAndEnglishDoc: function() {/*!
+    <head>
+    <meta charset="utf-8">
+    <p>Hello, my name is 太田あきひろ. It's a pleasure to meet you. どうぞよろしくお願いします.</p>
+    </head>
+  */},
+
+  switchWhenUnlabeledDoc: function() {/*!
+  <head>
+  <meta charset="utf-8">
+  <p>This text should be read in English. 차에 한하여 중임할 수. Followed by English.</p>
+  </head>
+  */},
+
+  noSwitchEnglishFrenchDoc: function() {/*!
+    <p>
+      This entire object should be read in English, even the following French
+      passage: salut mon ami! Ca va? Bien, et toi? It's hard to differentiate
+      between latin-based languages.
+    </p>
+  */},
+
+  japaneseAndChineseUnlabeledDoc: function() {/*!
+    <meta charset="utf-8">
+    <p>
+      天気はいいですね. 右万諭全中結社原済権人点掲年難出面者会追
+    </p>
+  */},
+
+  japaneseAndKoreanNotEnoughJapaneseDoc: function() {/*!
+    <meta charset="utf-8">
+    <p lang="ko">
+      私は. 법률이 정하는 바에 의하여 대법관이 아닌 법관을 둘 수 있다
+    </p>
+  */},
+
+  unlabeledSingleCharacterDoc: function() {/*!
+    <meta charset="utf-8">
+    <p>ど</p>
+  */},
+
+  // The purpose of this doc is to test functionality with three-letter language
+  // codes. Asturian has a language code of 'ast'. It is a language spoken
+  // in Principality of Asturias, Spain.
+  asturianAndJapaneseDoc: function() {/*!
+    <meta charset="utf-8">
+    <p lang="ja">ど</p>
+    <p lang="ast">
+      Pretend that this text is Asturian. Testing three-letter language code logic.
+    </p>
+  */},
 };
 
 TEST_F('ChromeVoxLanguageSwitchingTest', 'MultipleLanguagesTest', function() {
@@ -82,11 +128,10 @@
   this.runWithLoadedTree(this.multipleLanguagesDoc, function() {
     // Turn on language switching.
     localStorage['languageSwitching'] = 'true';
-
-    mockFeedback.call(doCmd('jumpToTop')).expectSpeechWithLanguage('en', 'Hello.');
-    mockFeedback.call(doCmd('nextLine')).expectSpeechWithLanguage('es', 'Hola.');
-    mockFeedback.call(doCmd('nextLine')).expectSpeechWithLanguage('fr', 'Salut.');
-    mockFeedback.call(doCmd('nextLine')).expectSpeechWithLanguage('it', 'Ciao.');
+    mockFeedback.call(doCmd('jumpToTop')).expectSpeechWithLanguage('en', 'en: Hello.');
+    mockFeedback.call(doCmd('nextLine')).expectSpeechWithLanguage('es', 'es: Hola.');
+    mockFeedback.call(doCmd('nextLine')).expectSpeechWithLanguage('fr', 'fr: Salut.');
+    mockFeedback.call(doCmd('nextLine')).expectSpeechWithLanguage('it', 'it: Ciao amico.');
     mockFeedback.replay();
   });
 });
@@ -95,48 +140,37 @@
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.nestedLanguagesDoc, function() {
     localStorage['languageSwitching'] = 'true';
-
+    // We should be able to switch languages when each component is labeled
+    // with a language.
     mockFeedback.call(doCmd('jumpToTop'))
-        .expectSpeechWithLanguage('en', 'In the morning, I sometimes eat breakfast.');
+        .expectSpeechWithLanguage('en', 'en: In the morning, I sometimes eat breakfast.');
     mockFeedback.call(doCmd('nextLine'))
-        .expectSpeechWithLanguage('fr', "Dans l'apres-midi, je dejeune.");
+        .expectSpeechWithLanguage('fr', "fr: Dans l'apres-midi, je dejeune.");
     mockFeedback.call(doCmd('nextLine'))
-        .expectSpeechWithLanguage('en', "Hello it's a pleasure to meet you. ");
+        .expectSpeechWithLanguage('en', "en: Hello it's a pleasure to meet you. ");
     mockFeedback.call(doCmd('nextLine'))
-        .expectSpeechWithLanguage('fr', 'Comment ca va?');
+        .expectSpeechWithLanguage('fr', 'fr: Comment ca va?');
     mockFeedback.call(doCmd('nextLine'))
-        .expectSpeechWithLanguage('en', 'Switching back to English.');
+        .expectSpeechWithLanguage('en', 'en: Switching back to English.');
     mockFeedback.call(doCmd('nextLine'))
-        .expectSpeechWithLanguage('es', 'Hola.');
+        .expectSpeechWithLanguage('es', 'es: Hola.');
     mockFeedback.call(doCmd('nextLine'))
-        .expectSpeechWithLanguage('en', 'Goodbye.');
+        .expectSpeechWithLanguage('en', 'en: Goodbye.');
     mockFeedback.replay();
   });
 });
 
-TEST_F('ChromeVoxLanguageSwitchingTest', 'DialectTest', function() {
-  var mockFeedback = this.createMockFeedback();
-  this.runWithLoadedTree(this.dialectsDoc, function(root) {
-    localStorage['languageSwitching'] = 'true';
-
-    mockFeedback.call(doCmd('jumpToTop'))
-      .call(doCmd('nextLine'))
-      .call(doCmd('nextLine'))
-      .expectSpeechWithLanguage('en-gb', 'Spoken in GB English.',
-      'This text should also be in GB English.', 'So should this text.');
-    mockFeedback.replay();
-  });
-});
-
-TEST_F('ChromeVoxLanguageSwitchingTest', 'ButtonAndLinkTest', function() {
+// TODO(akihiroota): Investigate why this test fails on linux-cros-rel.
+TEST_F('ChromeVoxLanguageSwitchingTest', 'DISABLED_ButtonAndLinkTest', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.buttonAndLinkDoc, function(root) {
     localStorage['languageSwitching'] = 'true';
-
     mockFeedback.call(doCmd('jumpToTop'))
-      .expectSpeechWithLanguage('es', 'This is a paragraph, spoken in Spanish.')
+      // Inner-node language detection is able to label this as 'en' and
+      // overwrite the author-provided language of 'es'.
+      .expectSpeechWithLanguage('en', 'en: This is a paragraph, spoken in English.')
       .call(doCmd('nextObject'))
-      .expectSpeechWithLanguage('es', 'This is a button, spoken in Spanish.')
+      .expectSpeechWithLanguage('es', 'es: This is a button, spoken in Spanish.')
       .expectSpeechWithLanguage(undefined, 'Button', 'Press Search+Space to activate.')
       .call(doCmd('nextObject'))
       .expectSpeechWithLanguage('es', 'This is a link, spoken in Spanish.')
@@ -144,3 +178,98 @@
     mockFeedback.replay();
   });
 });
+
+TEST_F('ChromeVoxLanguageSwitchingTest', 'JapaneseAndEnglishUnlabeledTest', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.japaneseAndEnglishDoc, function(root) {
+    localStorage['languageSwitching'] = 'true';
+    // We are able to separate out English and Japanese because they use
+    // different scripts.
+    mockFeedback.call(doCmd('jumpToTop'))
+      .expectSpeechWithLanguage('en', 'en: Hello, my name is ')
+      .expectSpeechWithLanguage('ja', 'ja: 太田あきひろ. ')
+      // Expect 'en-us' because inner-node language of 'en' doesn't come with
+      // high enough probability. We fall back on node-level detected language,
+      // which is 'en-us'.
+      .expectSpeechWithLanguage('en-us', "en-us: It's a pleasure to meet you. ")
+      .expectSpeechWithLanguage('ja', 'ja: どうぞよろしくお願いします.');
+    mockFeedback.replay();
+  });
+});
+
+
+TEST_F('ChromeVoxLanguageSwitchingTest', 'SwitchWhenUnlabeledTest', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.switchWhenUnlabeledDoc, function(root) {
+    localStorage['languageSwitching'] = 'true';
+    // We are able to separate out English and Korean because they use
+    // different scripts.
+    mockFeedback.call(doCmd('jumpToTop'))
+      .expectSpeechWithLanguage('en', 'en: This text should be read in English. ')
+      .expectSpeechWithLanguage('ko', 'ko: 차에 한하여 중임할 수. ')
+      // Expect 'en-us' because inner-node language of 'en' doesn't come with
+      // high enough probability. We fall back on node-level detected language,
+      // which is 'en-us'.
+      .expectSpeechWithLanguage('en-us', 'en-us: Followed by English.');
+    mockFeedback.replay();
+  });
+});
+
+
+TEST_F('ChromeVoxLanguageSwitchingTest', 'NoSwitchEnglishFrenchTest', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.noSwitchEnglishFrenchDoc, function(root) {
+    localStorage['languageSwitching'] = 'true';
+    // Unable to separate out English and French when unlabeled.
+    mockFeedback.call(doCmd('jumpToTop'))
+      .expectSpeechWithLanguage('en', "en: This entire object should be read in English, even the following French passage: salut mon ami! Ca va? Bien, et toi? It's hard to differentiate between latin-based languages.");
+    mockFeedback.replay();
+  });
+});
+
+TEST_F('ChromeVoxLanguageSwitchingTest', 'UnlabeledSingleCharacterTest', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.unlabeledSingleCharacterDoc, function(root) {
+    localStorage['languageSwitching'] = 'true';
+    // We are able to detect and switch at the character level if the character
+    // is unique to a certian script. In this case, 'ど' only appears in
+    // Japanese, and therefore we can confidently switch languages.
+    mockFeedback.call(doCmd('jumpToTop'))
+        .expectSpeechWithLanguage('ja', 'ja: ど');
+    mockFeedback.replay();
+  });
+});
+
+TEST_F('ChromeVoxLanguageSwitchingTest', 'JapaneseAndChineseUnlabeled', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.japaneseAndChineseUnlabeledDoc, function(root) {
+    localStorage['languageSwitching'] = 'true';
+    // Unable to separate out Japanese and Chinese if unlabeled.
+    mockFeedback.call(doCmd('jumpToTop'))
+        .expectSpeechWithLanguage('ja', 'ja: 天気はいいですね. 右万諭全中結社原済権人点掲年難出面者会追');
+    mockFeedback.replay();
+  });
+});
+
+TEST_F('ChromeVoxLanguageSwitchingTest', 'JapaneseAndKoreanNotEnoughJapanese', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.japaneseAndKoreanNotEnoughJapaneseDoc, function(root) {
+    localStorage['languageSwitching'] = 'true';
+    // Unable to separate out Japanese and Korean if unlabeled.
+    mockFeedback.call(doCmd('jumpToTop'))
+        .expectSpeechWithLanguage('ko', 'ko: 私は. 법률이 정하는 바에 의하여 대법관이 아닌 법관을 둘 수 있다');
+    mockFeedback.replay();
+  });
+});
+
+TEST_F('ChromeVoxLanguageSwitchingTest', 'AsturianTest', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.asturianAndJapaneseDoc, function(root) {
+    localStorage['languageSwitching'] = 'true';
+    mockFeedback.call(doCmd('jumpToTop'))
+        .expectSpeechWithLanguage('ja', 'ja: ど')
+        .call(doCmd('nextObject'))
+        .expectSpeechWithLanguage('ast', 'ast: Pretend that this text is Asturian. Testing three-letter language code logic.');
+    mockFeedback.replay();
+  });
+});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index 3af25d6b..effb218 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -1241,14 +1241,39 @@
           if (node.activeDescendantFor && node.activeDescendantFor.length > 0)
             options.annotation.push(new Output.SelectionSpan(0, 0));
 
-          this.append_(buff, node.name || '', options);
-          ruleStr.writeTokenWithValue(token, node.name);
           // Language Switching. Only execute if feature is enabled.
           if (localStorage['languageSwitching'] === 'true') {
-            speechProps = new Output.SpeechProperties();
-            speechProps['lang'] =
-                LanguageSwitching.updateCurrentLanguageForNode(node);
+            /**
+             * Passed as a callback to assignLanguagesForStringAttribute.
+             * Appends outputString to the output buffer in newLanguage.
+             * @param {!Array<Spannable>} buff
+             * @param {{isUnique: (boolean|undefined),
+             *      annotation: !Array<*>}} opt_options
+             * @param {string} outputString
+             * @param {string} newLanguage
+             */
+            var appendStringWithLanguage = function(
+                                               buff, options, outputString,
+                                               newLanguage) {
+              var speechProps = new Output.SpeechProperties();
+              // Set output language.
+              speechProps['lang'] = newLanguage;
+              // Append outputString to buff.
+              this.append_(buff, outputString, options);
+              // Attach associated SpeechProperties.
+              buff[buff.length - 1].setSpan(speechProps, 0, 0);
+            }.bind(this, buff, options);
+            // Cut up node name into multiple spans with different languages.
+            LanguageSwitching.assignLanguagesForStringAttribute(
+                node, 'name', appendStringWithLanguage);
+          } else {
+            // Append entire node name.
+            // TODO(akihiroota): Follow-up with dtseng about why we append empty
+            // string.
+            this.append_(buff, node.name || '', options);
           }
+          ruleStr.writeTokenWithValue(token, node.name);
+
         } else if (token == 'description') {
           if (node.name == node.description)
             return;
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index 17226b5..83d4ec0 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -3710,6 +3710,9 @@
       <message desc="A string to specify time units in hours." name="IDS_CHROMEVOX_HOURS">
         {COUNT, plural, =1 {hour}other {hours}}
       </message>
+      <message desc="Appends language in front of content." name="IDS_CHROMEVOX_LANGUAGE_SWITCH">
+        <ph name="language">$1<ex>English</ex></ph>: <ph name="content">$2<ex>This is example content</ex></ph>
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/browser/resources/chromeos/emulator/battery_settings.html b/chrome/browser/resources/chromeos/emulator/battery_settings.html
index 7067464..6ec40869 100644
--- a/chrome/browser/resources/chromeos/emulator/battery_settings.html
+++ b/chrome/browser/resources/chromeos/emulator/battery_settings.html
@@ -7,7 +7,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="icons.html">
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
index 16198790..0430906aa 100644
--- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
+++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
@@ -81,7 +81,7 @@
       <div class="start layout horizontal center">
         <cr-network-icon
             show-technology-badge="[[showTechnologyBadge_]]"
-            network-state="[[networkProperties]]">
+            network-state="[[getNetworkState_(networkProperties)]]">
         </cr-network-icon>
         <div id="networkName" class="title">
           [[getNameText_(networkProperties)]]
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js
index efca633fd..a65ce0b 100644
--- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js
+++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js
@@ -147,6 +147,14 @@
   },
 
   /**
+   * @param {!CrOnc.NetworkProperties} properties
+   * @return {!OncMojo.NetworkStateProperties}
+   */
+  getNetworkState_: function(properties) {
+    return OncMojo.oncPropertiesToNetworkState(properties);
+  },
+
+  /**
    * @param {!chrome.networkingPrivate.NetworkConfigProperties} onc The ONC
    *     network properties.
    * @private
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/BUILD.gn b/chrome/browser/resources/chromeos/kiosk_next_home/BUILD.gn
deleted file mode 100644
index 20e1b9dd..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/BUILD.gn
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/closure_compiler/compile_js.gni")
-
-js_type_check("closure_compile") {
-  deps = [
-    ":kiosk_next_api",
-  ]
-}
-
-js_library("kiosk_next_api") {
-  sources = [
-    "api.js",
-    "api_impl.js",
-  ]
-  deps = [
-    "//chrome/browser/chromeos/kiosk_next_home/mojom:mojom_js_library_for_compile",
-  ]
-}
-
-js_binary("mojom_bin") {
-  sources = []
-  deps = [
-    "//chrome/browser/chromeos/kiosk_next_home/mojom:mojom_js_library_for_compile",
-  ]
-}
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/OWNERS b/chrome/browser/resources/chromeos/kiosk_next_home/OWNERS
deleted file mode 100644
index 7fb73f6..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-file://ash/kiosk_next/OWNERS
-
-# COMPONENT: UI>Shell>KioskNext
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/api.js b/chrome/browser/resources/chromeos/kiosk_next_home/api.js
deleted file mode 100644
index 4e2f1da7..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/api.js
+++ /dev/null
@@ -1,270 +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.
-
-/**
- * @fileoverview Chrome OS Kiosk Next Home API definition.
- */
-
-/**
- * Namespace for the Kiosk Next Home bridge and related data.
- * @const
- */
-var kioskNextHome = {};
-
-/**
- * System bridge API for the Kiosk Next Home.
- *
- * @interface
- */
-kioskNextHome.Bridge = class {
-  /**
-   * Adds listener for system events.
-   * @param {!kioskNextHome.Listener} listener Listener for system events.
-   * @deprecated Use setListener instead.
-   * TODO(ltenorio): Remove usage and remove this method.
-   */
-  addListener(listener) {}
-
-  /**
-   * Sets the current listener for system events. If null, we will no longer
-   * send events to the previous listener.
-   *
-   * @param {?kioskNextHome.Listener} listener Listener for system events.
-   */
-  setListener(listener) {}
-
-  /**
-   * @return {!Promise<string>} Promise for the user's given name.
-   */
-  getUserGivenName() {}
-
-  /**
-   * @return {!Promise<string>} Promise for the user's display name.
-   */
-  getUserDisplayName() {}
-
-  /**
-   * Gets the obfuscated account Gaia ID associated with the current user
-   * session.
-   * @return {!Promise<string>} Promise for the obfuscated account Gaia ID.
-   */
-  getAccountId() {}
-
-  /**
-   * Returns an access token with the requested scopes.
-   * @param {!Array<string>} scopes List of scopes to use when obtaining access
-   *     token.
-   * @return {!Promise<string>} Promise for the access token.
-   * @deprecated Use fetchAccessToken instead. This version doesn't return the
-   *    token expiration.
-   */
-  getAccessToken(scopes) {}
-
-  /**
-   * Returns an access token with the requested scopes.
-   * @param {!Array<string>} scopes List of scopes to use when obtaining access
-   *     token.
-   * @return {!Promise<kioskNextHome.AccessToken>} Promise for the access
-   *     token.
-   */
-  fetchAccessToken(scopes) {}
-
-  /**
-   * Returns the Android ID for the ARC++ container. This call might fail if the
-   * ARC provisioning is not complete (i.e. user has not finished the ARC setup
-   * flow).
-   * @return {!Promise<string>} Promise for the Android ID.
-   */
-  getAndroidId() {}
-
-  /**
-   * Returns a list of apps installed in the user session.
-   * @return {!Promise<!Array<!kioskNextHome.App>>} Promise for the list of
-   *     apps.
-   */
-  getApps() {}
-
-  /**
-   * Launches the app with the given appId.
-   *
-   * Note: This is a fire and forget operation, given the VMs involved and
-   * their IPC interfaces, we can't tell if the operation was successful.
-   * @param {string} appId Chrome OS identifier for the app.
-   */
-  launchApp(appId) {}
-
-  /**
-   * Launches an allowed ARC intent. Intents are checked via
-   * chromeos::kiosk_next_home::IntentConfigHelper.
-   * @param {string} intent
-   * @return {!Promise} Resolves if intent was allowed and launched; rejects
-   *     otherwise.
-   */
-  launchIntent(intent) {}
-
-  /**
-   * Opens the website in the customized KioskNext Browser.
-   * @param {!url.mojom.Url} url
-   */
-  launchKioskNextWebsite(url) {}
-
-  /**
-   * Opens the website in the tabbed browser.
-   * @param {!url.mojom.Url} url
-   */
-  launchWebsite(url) {}
-
-  /**
-   * Shows a prompt to uninstall the app with the given appId.
-   *
-   * We will notify listeners when the app is uninstalled through a readiness
-   * state change.
-   * @param {string} appId App to uninstall.
-   */
-  uninstallApp(appId) {}
-
-  /**
-   * Returns current device network state.
-   * @return {kioskNextHome.NetworkState}
-   */
-  getNetworkState() {}
-};
-
-/**
- * A record representing an access token.
- * @record
- */
-kioskNextHome.AccessToken = class {
-  constructor() {
-    /** @type {string} The access token. */
-    this.token;
-    /**
-     * @type {number} Time when this token will expire in milliseconds from the
-     * Unix epoch.
-     */
-    this.expirationTime;
-  }
-};
-
-/**
- * Types of installed apps on Chrome OS.
- * @enum {string}
- */
-kioskNextHome.AppType = {
-  /**
-   * The app type could not be determined or is not relevant to Kiosk Next (e.g.
-   * Linux apps).
-   */
-  UNKNOWN: 'unknown',
-  /** The app is an ARC++ app (Android app). */
-  ARC: 'arc',
-  /**
-   * The app is a Chrome OS app.
-   *
-   * Note: This is an artificial grouping of the many different types of apps
-   * that Chrome OS supports. Built-in apps (e.g. Settings), Extensions (e.g.
-   * Files app) and pinned web apps. They mostly behave in the same way from
-   * the point of view of Kiosk Next.
-   */
-  CHROME: 'chrome',
-};
-
-/**
- * Readiness status for apps.
- * These values loosely map to AppService's apps.mojom.Readiness enum.
- * @enum {string}
- */
-kioskNextHome.AppReadiness = {
-  /**
-   * App readiness could not be determined.
-   * This can happen if the current app is unavailable for unknown or
-   * unexpected reasons, like a renderer crash for Extensions or an unsupported
-   * operation (such as blacklisted web apps).
-   */
-  UNKNOWN: 'unknown',
-  /** Installed and launchable. */
-  READY: 'ready',
-  /** App is disabled by policy. */
-  DISABLED: 'disabled',
-  /** App was uninstalled by user. */
-  UNINSTALLED: 'uninstalled',
-};
-
-/**
- * A record representing an installed app on the system.
- * @record
- */
-kioskNextHome.App = class {
-  constructor() {
-    /** @type {string} Unique Chrome OS identifier for the app. */
-    this.appId;
-    /** @type {kioskNextHome.AppType} */
-    this.type;
-    /**
-     * @type {string | undefined} Android package name, if it's an ARC++ app.
-     */
-    this.packageName;
-    /** @type {string} Readable name to display. */
-    this.displayName;
-    /** @type {string | undefined} Base64-encoded thumbnail image, fallback. */
-    this.thumbnailImage;
-    /**
-     * @type {kioskNextHome.AppReadiness} Current readiness state for the app.
-     */
-    this.readiness;
-  }
-};
-
-/**
- * Current network state of the device.
- * @enum {string}
- */
-kioskNextHome.NetworkState = {
-  ONLINE: 'online',
-  OFFLINE: 'offline',
-};
-
-/**
- * Current status for ARC instance.
- * @enum {string}
- */
-kioskNextHome.ArcStatus = {
-  STOPPED: 'stopped',
-  READY: 'ready',
-};
-
-/**
- * Interface for a listener of system events, subscribed via
- * {!kioskNextHome.Bridge}.
- *
- * @interface
- */
-kioskNextHome.Listener = class {
-  /**
-   * Called when an app state changes.
-   * @param {!kioskNextHome.App} app The app whose state changed.
-   */
-  onAppChanged(app) {}
-
-  /**
-   * Called when the ARC status changes. Called at first when the listener is
-   * set.
-   * @param {!kioskNextHome.ArcStatus} status The new ARC status.
-   */
-  onArcStatusChanged(status) {}
-
-  /**
-   * Called when the network state changes.
-   * @param {kioskNextHome.NetworkState} networkState Current network state of
-   *     the device.
-   */
-  onNetworkStateChanged(networkState) {}
-};
-
-/**
- * Provides bridge implementation.
- * @return {!kioskNextHome.Bridge} Bridge instance that can be used to interact
- *     with Chrome OS.
- */
-kioskNextHome.getChromeOsBridge = function() {};
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/api_impl.js b/chrome/browser/resources/chromeos/kiosk_next_home/api_impl.js
deleted file mode 100644
index e309771..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/api_impl.js
+++ /dev/null
@@ -1,269 +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.
-
-/**
- * @fileoverview Kiosk Next Home API implementation.
- */
-
-/**
- * Gets the app type from the mojo representation of an app.
- * @param {!chromeos.kioskNextHome.mojom.App} mojoApp
- * @return {!kioskNextHome.AppType}
- */
-function getAppType(mojoApp) {
-  switch (mojoApp.type) {
-    case apps.mojom.AppType.kArc:
-      return kioskNextHome.AppType.ARC;
-    case apps.mojom.AppType.kBuiltIn:
-    case apps.mojom.AppType.kExtension:
-    case apps.mojom.AppType.kWeb:
-      return kioskNextHome.AppType.CHROME;
-    default:
-      return kioskNextHome.AppType.UNKNOWN;
-  }
-}
-
-/**
- * Gets the app readiness from the mojo representation of an app.
- * @param {!chromeos.kioskNextHome.mojom.App} mojoApp
- * @return {!kioskNextHome.AppReadiness}
- */
-function getReadiness(mojoApp) {
-  switch (mojoApp.readiness) {
-    case apps.mojom.Readiness.kReady:
-      return kioskNextHome.AppReadiness.READY;
-    case apps.mojom.Readiness.kDisabledByPolicy:
-      return kioskNextHome.AppReadiness.DISABLED;
-    case apps.mojom.Readiness.kUninstalledByUser:
-      return kioskNextHome.AppReadiness.UNINSTALLED;
-    default:
-      return kioskNextHome.AppReadiness.UNKNOWN;
-  }
-}
-
-/**
- * Converts the given mojo time to milliseconds since the Unix epoch.
- * @param {!mojoBase.mojom.Time} mojoTime
- * @return {number}
- */
-function getMillisecondsSinceUnixEpoch(mojoTime) {
-  // The mojo time representation is based on base::Time, which in turn
-  // represents the number of microseconds since the Windows epoch. The
-  // following code then mostly reimplements the base::Time::ToJSTime function.
-  const windowsEpoch = Date.UTC(1601, 0, 1, 0, 0, 0, 0);
-  const unixEpoch = Date.UTC(1970, 0, 1, 0, 0, 0, 0);
-  const epochDelta = unixEpoch - windowsEpoch;
-
-  // Converting to milliseconds and from BigInt to Number.
-  return Number(mojoTime.internalValue / BigInt(1000)) - epochDelta;
-}
-
-/**
- * Builds an app from its mojo representation coming from the AppController.
- * @param {!chromeos.kioskNextHome.mojom.App} mojoApp
- * @return {!kioskNextHome.App} A bridge representation of an app.
- */
-function buildApp(mojoApp) {
-  return {
-    appId: mojoApp.appId,
-    type: getAppType(mojoApp),
-    displayName: mojoApp.displayName,
-    packageName: mojoApp.androidPackageName,
-    readiness: getReadiness(mojoApp),
-    // We append the intended size of the icon in density-independent
-    // pixels, in this case 128x128 dips.
-    thumbnailImage: 'chrome://app-icon/' + mojoApp.appId + '/128',
-  };
-}
-
-/** @implements {kioskNextHome.Bridge} */
-class KioskNextHomeBridge {
-  constructor() {
-    /** @private {?kioskNextHome.Listener} */
-    this.listener_ = null;
-    /** @private @const */
-    this.identityAccessorProxy_ = new identity.mojom.IdentityAccessorProxy();
-    /** @private @const */
-    this.identityControllerProxy_ =
-        new chromeos.kioskNextHome.mojom.IdentityControllerProxy();
-    /** @private @const */
-    this.appControllerProxy_ =
-        new chromeos.kioskNextHome.mojom.AppControllerProxy();
-    /** @private @const */
-    this.appControllerClientCallbackRouter_ =
-        new chromeos.kioskNextHome.mojom.AppControllerClientCallbackRouter();
-    /** @private @const */
-    this.websiteControllerProxy_ =
-        new chromeos.kioskNextHome.mojom.WebsiteControllerProxy();
-
-    const kioskNextHomeInterfaceBrokerProxy =
-        chromeos.kioskNextHome.mojom.KioskNextHomeInterfaceBroker.getProxy();
-    kioskNextHomeInterfaceBrokerProxy.getIdentityAccessor(
-        this.identityAccessorProxy_.$.createRequest());
-    kioskNextHomeInterfaceBrokerProxy.getIdentityController(
-        this.identityControllerProxy_.$.createRequest());
-    kioskNextHomeInterfaceBrokerProxy.getAppController(
-        this.appControllerProxy_.$.createRequest());
-    kioskNextHomeInterfaceBrokerProxy.getWebsiteController(
-        this.websiteControllerProxy_.$.createRequest());
-
-    // Attaching app listeners.
-    this.appControllerClientCallbackRouter_.onAppChanged.addListener(
-        mojoApp => {
-          if (this.listener_) {
-            this.listener_.onAppChanged(buildApp(mojoApp));
-          }
-        });
-    this.appControllerClientCallbackRouter_.onArcStatusChanged.addListener(
-        mojoArcStatus => {
-          const status =
-              mojoArcStatus == chromeos.kioskNextHome.mojom.ArcStatus.kReady ?
-              kioskNextHome.ArcStatus.READY :
-              kioskNextHome.ArcStatus.STOPPED;
-          if (this.listener_) {
-            this.listener_.onArcStatusChanged(status);
-          }
-        });
-
-    // Attaching network status listeners.
-    window.addEventListener(
-        'online',
-        () => this.notifyNetworkStateChange(kioskNextHome.NetworkState.ONLINE));
-    window.addEventListener(
-        'offline',
-        () =>
-            this.notifyNetworkStateChange(kioskNextHome.NetworkState.OFFLINE));
-  }
-
-  /** @override */
-  setListener(listener) {
-    this.listener_ = listener;
-    this.appControllerProxy_.setClient(
-        this.appControllerClientCallbackRouter_.createProxy());
-  }
-
-  /** @override */
-  addListener(listener) {
-    console.warn(
-        'Setting bridge listener by using deprecated method addListener(). ' +
-        'Use setListener() instead.');
-    this.setListener(listener);
-  }
-
-  /** @override */
-  getUserGivenName() {
-    return this.identityControllerProxy_.getUserInfo().then(
-        result => result.userInfo.givenName);
-  }
-
-  /** @override */
-  getUserDisplayName() {
-    return this.identityControllerProxy_.getUserInfo().then(
-        result => result.userInfo.displayName);
-  }
-
-  /** @override */
-  getAccountId() {
-    return this.identityAccessorProxy_.getPrimaryAccountWhenAvailable().then(
-        account => account.accountInfo.gaia);
-  }
-
-  /** @override */
-  getAccessToken(scopes) {
-    return this.fetchAccessToken(scopes).then(accessToken => accessToken.token);
-  }
-
-  /** @override */
-  fetchAccessToken(scopes) {
-    return this.identityAccessorProxy_.getPrimaryAccountWhenAvailable()
-        .then(account => {
-          return this.identityAccessorProxy_.getAccessToken(
-              account.accountInfo.accountId, {'scopes': scopes},
-              'kiosk_next_home');
-        })
-        .then(tokenInfo => {
-          if (tokenInfo.token) {
-            return {
-              token: tokenInfo.token,
-              expirationTime:
-                  getMillisecondsSinceUnixEpoch(tokenInfo.expirationTime),
-            };
-          }
-
-          throw 'Unable to get access token.';
-        });
-  }
-
-  /** @override */
-  getAndroidId() {
-    return this.appControllerProxy_.getArcAndroidId().then(response => {
-      if (response.success) {
-        return response.androidId;
-      }
-      throw 'Unable to get Android id.';
-    });
-  }
-
-  /** @override */
-  getApps() {
-    return this.appControllerProxy_.getApps().then(response => {
-      return response.apps.map(buildApp);
-    });
-  }
-
-  /** @override */
-  launchApp(appId) {
-    this.appControllerProxy_.launchApp(appId);
-  }
-
-  /** @override */
-  launchIntent(intent) {
-    return this.appControllerProxy_.launchIntent(intent).then(result => {
-      if (!result.launched) {
-        throw result.errorMessage;
-      }
-    });
-  }
-
-  /** @override */
-  launchKioskNextWebsite(url) {
-    this.websiteControllerProxy_.launchKioskNextWebsite(url);
-  }
-
-  /** @override */
-  launchWebsite(url) {
-    this.websiteControllerProxy_.launchWebsite(url);
-  }
-
-  /** @override */
-  uninstallApp(appId) {
-    this.appControllerProxy_.uninstallApp(appId);
-  }
-
-  /** @override */
-  getNetworkState() {
-    return navigator.onLine ? kioskNextHome.NetworkState.ONLINE :
-                              kioskNextHome.NetworkState.OFFLINE;
-  }
-
-  /**
-   * Notifies listeners about changes in network connection state.
-   * @param {kioskNextHome.NetworkState} networkState Indicates current network
-   *     state.
-   */
-  notifyNetworkStateChange(networkState) {
-    if (this.listener_) {
-      this.listener_.onNetworkStateChanged(networkState);
-    }
-  }
-}
-
-/**
- * Provides bridge implementation.
- * @return {!kioskNextHome.Bridge} Bridge instance that can be used to interact
- *     with Chrome OS.
- */
-kioskNextHome.getChromeOsBridge = function() {
-  return new KioskNextHomeBridge();
-};
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/bg.js b/chrome/browser/resources/chromeos/kiosk_next_home/bg.js
deleted file mode 100644
index 416a4d0..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/bg.js
+++ /dev/null
@@ -1,10 +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.
-
-chrome.app.runtime.onLaunched.addListener(() => {
-  const windowOptions = {state: 'maximized', frame: 'none'};
-  chrome.app.window.create('main.html', windowOptions, (newWindow) => {
-    newWindow.maximize();
-  });
-});
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/kiosk_next_resources.grdp b/chrome/browser/resources/chromeos/kiosk_next_home/kiosk_next_resources.grdp
deleted file mode 100644
index c6e6373..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/kiosk_next_resources.grdp
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<grit-part>
-  <include name="IDR_KIOSK_NEXT_BG_JS" file="chromeos/kiosk_next_home/bg.js" type="BINDATA" />
-  <include name="IDR_KIOSK_NEXT_MAIN_HTML" file="chromeos/kiosk_next_home/main.html" type="chrome_html" />
-  <include name="IDR_KIOSK_NEXT_ICON_192" file="chromeos/kiosk_next_home/static/icon192.png" type="BINDATA" />
-  <include name="IDR_KIOSK_NEXT_API_JS" file="chromeos/kiosk_next_home/api.js" type="BINDATA" />
-  <include name="IDR_KIOSK_NEXT_API_IMPL_JS" file="chromeos/kiosk_next_home/api_impl.js" type="BINDATA" />
-  <include name="IDR_KIOSK_NEXT_HOME_MOJOM_JS" file="${root_gen_dir}/chrome/browser/resources/chromeos/kiosk_next_home/mojom_bin.js" type="BINDATA" use_base_dir="false" />
-</grit-part>
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/main.html b/chrome/browser/resources/chromeos/kiosk_next_home/main.html
deleted file mode 100644
index 7721026..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/main.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!doctype html>
-<head>
-  <script src="kiosk_next_home.mojom.js"></script>
-  <script src="api.js"></script>
-  <script src="api_impl.js"></script>
-</head>
-<body>
-  <div>Kiosk Next Home</div>
-</body>
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/manifest.json b/chrome/browser/resources/chromeos/kiosk_next_home/manifest.json
deleted file mode 100644
index f1a4295..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/manifest.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  // chrome-extension://nbaolgedfgoedkjbfmpediclncanmpbc/
-  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq8KnOSFXgBTWY7w+jQ/T1kPD9r821cEgvPdcot2XBdA2OVF2+TJjyLmLRa/AVQzpsMrEWzPbbXSil+GFjWsiWzcohuLphMQe5pHzRVXd19ouJZNr+KL16/e74fZ77ECI9R/D0Vh6it/QICdCaLgHbjWo2AS7vhJGtp2GJcWpXG5sbG8W8BDayY5qySwAE35dFjpeeR0bDTz44+9LFE0s+sd65LDwn37nc+pJvDNNYipGP2lYC9eMk1wAydz9x3c2iYRzcGyHjbX1Z7gQvM4w8Amdjsb8f5mZeXGNKE+gvcD7kyiR7rXgK1EfaNvDCzl/uIXXPfIh5oUK9iUkdX6oNwIDAQAB",
-  "name": "Kiosk Next Home",
-  "version": "0.1.0",
-  "manifest_version": 2,
-  "description": "Kiosk Next Home",
-  "display_in_launcher": false,
-  "icons": {
-    "192": "static/icon192.png"
-  },
-  "permissions": [
-    "https://*.googleapis.com",
-    "chrome://app-icon/",
-    "mojoPrivate"
-  ],
-  "app": {
-    "background": {
-      "scripts": ["bg.js"]
-    },
-    "content_security_policy": "script-src 'self'; object-src 'self'; img-src 'self' data: https://lh3.googleusercontent.com chrome://app-icon"
-  }
-}
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/static/icon192.png b/chrome/browser/resources/chromeos/kiosk_next_home/static/icon192.png
deleted file mode 100644
index d1418cb..0000000
--- a/chrome/browser/resources/chromeos/kiosk_next_home/static/icon192.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index e38b6086..1662551 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -235,6 +235,7 @@
   deps = [
     "//ui/webui/resources/cr_elements/chromeos/network:cr_network_select",
     "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types",
+    "//ui/webui/resources/js/chromeos:onc_mojo",
   ]
 }
 
diff --git a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
index 3c0dd763..253ea772 100644
--- a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
+++ b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
@@ -62,8 +62,8 @@
         </div>
       </div>
       <div slot="bottom-buttons" class="flex layout horizontal">
-        <oobe-back-button id="arc-tos-back-button" on-click="onBack_">
-        </oobe-back-button>
+        <oobe-back-button id="arc-tos-back-button"
+            on-click="onBack_"></oobe-back-button>
         <div class="flex">
         </div>
         <oobe-text-button id="arc-tos-skip-button" border on-tap="onSkip_"
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.html b/chrome/browser/resources/chromeos/login/custom_elements_login.html
index c9d86d00..0da14681 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.html
@@ -34,5 +34,6 @@
 <include src="marketing_opt_in.html">
 <include src="../assistant_optin/assistant_optin_flow.html">
 <include src="multidevice_setup_first_run.html">
+<include src="security_token_pin.html">
 
 <script src="chrome://oobe/custom_elements.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.js b/chrome/browser/resources/chromeos/login/custom_elements_login.js
index bba2c5d..c54e649 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.js
@@ -40,3 +40,4 @@
 // <include src="multidevice_setup_first_run.js">
 // <include src="enrollment_license_card.js">
 // <include src="enterprise_enrollment.js">
+// <include src="security_token_pin.js">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
index f3f3ee16..325a184 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
@@ -44,5 +44,6 @@
 <include src="marketing_opt_in.html">
 <include src="../assistant_optin/assistant_optin_flow.html">
 <include src="multidevice_setup_first_run.html">
+<include src="security_token_pin.html">
 
 <script src="chrome://oobe/custom_elements.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
index 787435f4..ecf87b55 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
@@ -55,3 +55,4 @@
 // <include src="marketing_opt_in.js">
 // <include src="../assistant_optin/assistant_optin_flow.js">
 // <include src="multidevice_setup_first_run.js">
+// <include src="security_token_pin.js">
diff --git a/chrome/browser/resources/chromeos/login/md_login_screens.html b/chrome/browser/resources/chromeos/login/md_login_screens.html
index f66f4c2..ffab614 100644
--- a/chrome/browser/resources/chromeos/login/md_login_screens.html
+++ b/chrome/browser/resources/chromeos/login/md_login_screens.html
@@ -27,6 +27,3 @@
 <include src="screen_discover.html">
 <include src="screen_marketing_opt_in.html">
 <include src="screen_multidevice_setup.html">
-<supervision-onboarding id="supervision-onboarding" class="step right hidden"
-    hidden>
-</supervision-onboarding>
diff --git a/chrome/browser/resources/chromeos/login/network_select_login.html b/chrome/browser/resources/chromeos/login/network_select_login.html
index 0163055..87a0abb 100644
--- a/chrome/browser/resources/chromeos/login/network_select_login.html
+++ b/chrome/browser/resources/chromeos/login/network_select_login.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_select.html">
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/html/chromeos/onc_mojo.html">
 
 <dom-module id="network-select-login">
   <style>
diff --git a/chrome/browser/resources/chromeos/login/network_select_login.js b/chrome/browser/resources/chromeos/login/network_select_login.js
index 0a7372af..c7834e1 100644
--- a/chrome/browser/resources/chromeos/login/network_select_login.js
+++ b/chrome/browser/resources/chromeos/login/network_select_login.js
@@ -7,9 +7,10 @@
  */
 
 {
+  const mojom = chromeos.networkConfig.mojom;
+
   /**
    * Custom data that is stored with network element to trigger action.
-   *
    * @typedef {{onTap: !function()}}
    */
   let networkCustomItemCustomData;
@@ -217,13 +218,14 @@
 
     /**
      * Event triggered when the default network state may have changed.
-     * @param {!CustomEvent<CrOnc.NetworkStateProperties>} event
+     * @param {!CustomEvent<OncMojo.NetworkStateProperties>} event
      * @private
      */
     onDefaultNetworkChanged_: function(event) {
-      var state = event.detail;
-      this.isConnected =
-          state && state.ConnectionState == CrOnc.ConnectionState.CONNECTED;
+      // Note: event.detail will be {} if there is no default network.
+      var networkState = event.detail.type ? event.detail : undefined;
+      this.isConnected = !!networkState &&
+          OncMojo.connectionStateIsConnected(networkState.connectionState);
       if (!this.isConnected || !this.is_shown_)
         return;
       this.attemptApplyConfiguration_();
@@ -231,20 +233,20 @@
 
     /**
      * Event triggered when a cr-network-list-item connection state changes.
-     * @param {!CustomEvent<!CrOnc.NetworkStateProperties>} event
+     * @param {!CustomEvent<!OncMojo.NetworkStateProperties>} event
      * @private
      */
     onNetworkConnectChanged_: function(event) {
-      var state = event.detail;
-      if (state && state.GUID == this.networkLastSelectedGuid_ &&
-          state.ConnectionState == CrOnc.ConnectionState.CONNECTED) {
+      var networkState = event.detail;
+      if (networkState && networkState.guid == this.networkLastSelectedGuid_ &&
+          OncMojo.connectionStateIsConnected(networkState.connectionState)) {
         this.onSelectedNetworkConnected_();
       }
     },
 
     /**
      * Event triggered when a list of networks get changed.
-     * @param {!CustomEvent<!Array<!CrOnc.NetworkStateProperties>>} event
+     * @param {!CustomEvent<!Array<!OncMojo.NetworkStateProperties>>} event
      * @private
      */
     onNetworkListChanged_: function(event) {
@@ -269,13 +271,12 @@
         return;
       }
       var defaultNetwork = this.$.networkSelect.getDefaultNetwork();
-      if (configuration.networkUseConnected && defaultNetwork) {
-        if (defaultNetwork.ConnectionState == CrOnc.ConnectionState.CONNECTED) {
-          window.setTimeout(
-              this.handleNetworkSelection_.bind(this, defaultNetwork), 0);
-          this.configuration_applied_ = true;
-          return;
-        }
+      if (configuration.networkUseConnected && defaultNetwork &&
+          OncMojo.connectionStateIsConnected(defaultNetwork.connectionState)) {
+        window.setTimeout(
+            this.handleNetworkSelection_.bind(this, defaultNetwork), 0);
+        this.configuration_applied_ = true;
+        return;
       }
       if (configuration.networkSelectGuid) {
         var network =
@@ -291,8 +292,7 @@
 
     /**
      * This is called when user taps on network entry in networks list.
-     *
-     * @param {!CustomEvent<!CrOnc.NetworkStateProperties>} event
+     * @param {!CustomEvent<!OncMojo.NetworkStateProperties>} event
      * @private
      */
     onNetworkListNetworkItemSelected_: function(event) {
@@ -301,16 +301,18 @@
 
     /**
      * Handles selection of particular network.
-     *
-     * @param {!CrOnc.NetworkStateProperties} state network state.
+     * @param {!OncMojo.NetworkStateProperties} networkState
      * @private
      */
-    handleNetworkSelection_: function(state) {
-      assert(state);
+    handleNetworkSelection_: function(networkState) {
+      assert(networkState);
+
+      var isConnected =
+          OncMojo.connectionStateIsConnected(networkState.connectionState);
+
       // If |configureConnected| is false and a connected network is selected,
       // continue to the next screen.
-      if (!this.configureConnected &&
-          state.ConnectionState == CrOnc.ConnectionState.CONNECTED) {
+      if (!this.configureConnected && isConnected) {
         this.onSelectedNetworkConnected_();
         return;
       }
@@ -319,33 +321,32 @@
       // is pending connection attempt. So even if new selection is currently
       // connected, it may get disconnected at any time.
       // So just send one more connection request to cancel current attempts.
-      this.networkLastSelectedGuid_ = (state ? state.GUID : '');
-
-      if (!state)
-        return;
+      this.networkLastSelectedGuid_ = networkState.guid;
 
       var self = this;
-      var networkStateCopy = Object.assign({}, state);
+      var oncType = OncMojo.getNetworkTypeString(networkState.type);
+      var guid = networkState.guid;
 
       // Cellular should normally auto connect. If it is selected, show the
       // details UI since there is no configuration UI for Cellular.
-      if (state.Type == chrome.networkingPrivate.NetworkType.CELLULAR) {
-        chrome.send('showNetworkDetails', [state.Type, state.GUID]);
+      if (networkState.type ==
+          chromeos.networkConfig.mojom.NetworkType.kCellular) {
+        chrome.send('showNetworkDetails', [oncType, guid]);
         return;
       }
 
       // Allow proxy to be set for connected networks.
-      if (state.ConnectionState == CrOnc.ConnectionState.CONNECTED) {
-        chrome.send('showNetworkDetails', [state.Type, state.GUID]);
+      if (isConnected) {
+        chrome.send('showNetworkDetails', [oncType, guid]);
         return;
       }
 
-      if (state.Connectable === false || state.ErrorState) {
-        chrome.send('showNetworkConfig', [state.GUID]);
+      if (!networkState.connectable || networkState.errorState) {
+        chrome.send('showNetworkConfig', [guid]);
         return;
       }
 
-      chrome.networkingPrivate.startConnect(state.GUID, () => {
+      chrome.networkingPrivate.startConnect(guid, () => {
         const lastError = chrome.runtime.lastError;
         if (!lastError)
           return;
@@ -356,8 +357,8 @@
         }
         console.error(
             'networkingPrivate.startConnect error: ' + message +
-            ' For: ' + state.GUID);
-        chrome.send('showNetworkConfig', [state.GUID]);
+            ' For: ' + guid);
+        chrome.send('showNetworkConfig', [guid]);
       });
     },
 
diff --git a/chrome/browser/resources/chromeos/login/offline_ad_login.html b/chrome/browser/resources/chromeos/login/offline_ad_login.html
index 6453138..c5d5e7e 100644
--- a/chrome/browser/resources/chromeos/login/offline_ad_login.html
+++ b/chrome/browser/resources/chromeos/login/offline_ad_login.html
@@ -4,7 +4,7 @@
 
 <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/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.html b/chrome/browser/resources/chromeos/login/oobe_eula.html
index 52ba307..c81c471 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.html
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.html
@@ -78,8 +78,8 @@
         </div>
       </div>
       <div slot="bottom-buttons" class="flex layout horizontal">
-        <oobe-back-button on-click="onEulaBackButtonPressed_">
-        </oobe-back-button>
+        <oobe-back-button
+            on-click="onEulaBackButtonPressed_"></oobe-back-button>
         <div class="flex">
         </div>
         <oobe-text-button id="acceptButton" inverse on-tap="eulaAccepted_"
diff --git a/chrome/browser/resources/chromeos/login/oobe_i18n_dropdown.html b/chrome/browser/resources/chromeos/login/oobe_i18n_dropdown.html
index 5433913..35e8ce4 100644
--- a/chrome/browser/resources/chromeos/login/oobe_i18n_dropdown.html
+++ b/chrome/browser/resources/chromeos/login/oobe_i18n_dropdown.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 
 <dom-module id="oobe-i18n-dropdown">
   <template>
diff --git a/chrome/browser/resources/chromeos/login/oobe_screens.html b/chrome/browser/resources/chromeos/login/oobe_screens.html
index 1101982..be7c5ff4 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screens.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screens.html
@@ -37,6 +37,3 @@
 <include src="screen_discover.html">
 <include src="screen_marketing_opt_in.html">
 <include src="screen_multidevice_setup.html">
-<supervision-onboarding id="supervision-onboarding" class="step right hidden"
-    hidden>
-</supervision-onboarding>
diff --git a/chrome/browser/resources/chromeos/login/oobe_types.js b/chrome/browser/resources/chromeos/login/oobe_types.js
index 086d958..02989e10 100644
--- a/chrome/browser/resources/chromeos/login/oobe_types.js
+++ b/chrome/browser/resources/chromeos/login/oobe_types.js
@@ -98,3 +98,35 @@
  * }}
  */
 OobeTypes.OobeConfiguration;
+
+/**
+ * Specifies the type of the information that is requested by the security token
+ * PIN dialog.
+ * @enum {number}
+ */
+OobeTypes.SecurityTokenPinDialogType = {
+  PIN: 0,
+  PUK: 1,
+};
+
+/**
+ * Specifies the type of the error that is displayed in the security token PIN
+ * dialog.
+ * @enum {number}
+ */
+OobeTypes.SecurityTokenPinDialogErrorType = {
+  UNKNOWN_ERROR: 0,
+  INVALID_PIN: 1,
+  INVALID_PUK: 2,
+  MAX_ATTEMPTS_EXCEEDED: 3,
+};
+
+/**
+ * Configuration of the security token PIN dialog.
+ * @typedef {{
+ *   type: OobeTypes.SecurityTokenPinDialogType,
+ *   errorType: (OobeTypes.SecurityTokenPinDialogErrorType|undefined),
+ *   attemptsLeft: number,
+ * }}
+ */
+OobeTypes.SecurityTokenPinDialogParameters;
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
index 3646633f..8902e62 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
@@ -8,10 +8,10 @@
   <link rel="stylesheet" href="screen_gaia_signin.css">
 
   <template>
-
     <oobe-dialog id="signin-frame-dialog" class="gaia-dialog" role="dialog"
         has-buttons no-header no-footer-padding no-buttons-padding
-        hidden="[[!isSigninFrameDialogVisible_(screenMode_)]]">
+        hidden="[[!isSigninFrameDialogVisible_(screenMode_,
+                                               pinDialogParameters_)]]">
       <div slot="footer" class="flex layout vertical">
         <div id="signin-frame-container"
             class$="[[getSigninFrameContainerClass_(isLoadingUiShown_)]]">
@@ -31,17 +31,23 @@
       </div>
     </oobe-dialog>
     <offline-gaia id="offline-gaia" class="gaia-dialog"
-        hidden="[[!isOfflineGaiaVisible_(screenMode_, isLoadingUiShown_)]]">
+        hidden="[[!isOfflineGaiaVisible_(screenMode_, isLoadingUiShown_,
+                                         pinDialogParameters_)]]">
     </offline-gaia>
     <offline-ad-login id="offline-ad-auth" class="gaia-dialog"
-        hidden="[[!isOfflineAdAuthVisible_(screenMode_, isLoadingUiShown_)]]"
+        hidden="[[!isOfflineAdAuthVisible_(screenMode_, isLoadingUiShown_,
+                                           pinDialogParameters_)]]"
         i18n-values="ad-welcome-message:loginWelcomeMessage">
     </offline-ad-login>
+    <security-token-pin id="pinDialog" parameters="[[pinDialogParameters_]]"
+        hidden="[[!isPinDialogVisible_(pinDialogParameters_)]]"
+        on-cancel="onPinDialogCanceled_" on-completed="onPinDialogCompleted_">
+    </security-token-pin>
     <div id="gaia-step-contents" class="step-contents">
       <div id="gaia-signin-form-container">
         <saml-interstitial id="saml-interstitial" class="fit gaia-dialog"
-            hidden="[[!isSamlInterstitialVisible_(screenMode_,
-                                                  isLoadingUiShown_)]]">
+            hidden="[[!isSamlInterstitialVisible_(
+                screenMode_, isLoadingUiShown_, pinDialogParameters_)]]">
         </saml-interstitial>
       </div>
     </div>
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index adb264c6..ee806bd 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -63,6 +63,8 @@
     'monitorOfflineIdle',
     'showWhitelistCheckFailedError',
     'invalidateAd',
+    'showPinDialog',
+    'closePinDialog',
   ],
 
   properties: {
@@ -102,6 +104,17 @@
       type: Boolean,
       value: false,
     },
+
+    /**
+     * Contains the security token PIN dialog parameters object when the dialog
+     * is shown. Is null when no PIN dialog is shown.
+     * @type {OobeTypes.SecurityTokenPinDialogParameter}
+     * @private
+     */
+    pinDialogParameters_: {
+      type: Object,
+      value: null,
+    },
   },
 
   /**
@@ -198,6 +211,14 @@
    */
   authenticator_: undefined,
 
+  /**
+   * Whether the result was reported to the handler for the most recent PIN
+   * dialog.
+   * @type {boolean}
+   * @private
+   */
+  pinDialogResultReported_: false,
+
   /** @override */
   ready: function() {
     this.authenticator_ = new cr.login.Authenticator(this.getSigninFrame_());
@@ -404,13 +425,14 @@
   /**
    * Whether the signin-frame-dialog element should be visible.
    * @param {number} screenMode
+   * @param {OobeTypes.SecurityTokenPinDialogParameters} pinDialogParameters
    * @return {boolean}
    * @private
    */
-  isSigninFrameDialogVisible_: function(screenMode) {
+  isSigninFrameDialogVisible_: function(screenMode, pinDialogParameters) {
     // See the comment in getSigninFrameContainerClass_() for the explanation on
     // why our element shouldn't be hidden during loading.
-    return screenMode == ScreenMode.DEFAULT;
+    return screenMode == ScreenMode.DEFAULT && pinDialogParameters === null;
   },
 
   /**
@@ -431,33 +453,52 @@
    * Whether the offline-gaia element should be visible.
    * @param {number} screenMode
    * @param {boolean} isLoadingUiShown
+   * @param {OobeTypes.SecurityTokenPinDialogParameters} pinDialogParameters
    * @return {boolean}
    * @private
    */
-  isOfflineGaiaVisible_: function(screenMode, isLoadingUiShown) {
-    return screenMode == ScreenMode.OFFLINE && !isLoadingUiShown;
+  isOfflineGaiaVisible_: function(
+      screenMode, isLoadingUiShown, pinDialogParameters) {
+    return screenMode == ScreenMode.OFFLINE && !isLoadingUiShown &&
+        pinDialogParameters === null;
   },
 
   /**
    * Whether the saml-interstitial element should be visible.
    * @param {number} screenMode
    * @param {boolean} isLoadingUiShown
+   * @param {OobeTypes.SecurityTokenPinDialogParameters} pinDialogParameters
    * @return {boolean}
    * @private
    */
-  isSamlInterstitialVisible_: function(screenMode, isLoadingUiShown) {
-    return screenMode == ScreenMode.SAML_INTERSTITIAL && !isLoadingUiShown;
+  isSamlInterstitialVisible_: function(
+      screenMode, isLoadingUiShown, pinDialogParameters) {
+    return screenMode == ScreenMode.SAML_INTERSTITIAL && !isLoadingUiShown &&
+        pinDialogParameters === null;
   },
 
   /**
    * Whether the offline-ad-auth element should be visible.
    * @param {number} screenMode
    * @param {boolean} isLoadingUiShown
+   * @param {OobeTypes.SecurityTokenPinDialogParameters} pinDialogParameters
    * @return {boolean}
    * @private
    */
-  isOfflineAdAuthVisible_: function(screenMode, isLoadingUiShown) {
-    return screenMode == ScreenMode.AD_AUTH && !isLoadingUiShown;
+  isOfflineAdAuthVisible_: function(
+      screenMode, isLoadingUiShown, pinDialogParameters) {
+    return screenMode == ScreenMode.AD_AUTH && !isLoadingUiShown &&
+        pinDialogParameters === null;
+  },
+
+  /**
+   * Whether the pinDialog element should be visible.
+   * @param {OobeTypes.SecurityTokenPinDialogParameters} pinDialogParameters
+   * @return {boolean}
+   * @private
+   */
+  isPinDialogVisible_: function(pinDialogParameters) {
+    return pinDialogParameters !== null;
   },
 
   /**
@@ -695,6 +736,9 @@
     this.isSaml_ = false;
     this.samlPasswordConfirmAttempt_ = 0;
 
+    // Reset the PIN dialog, in case it's shown.
+    this.closePinDialog();
+
     this.updateSigninFrameContainers_();
 
     let params = {};
@@ -1282,5 +1326,51 @@
     this.isLoadingUiShown_ = false;
   },
 
+  /**
+   * Shows the PIN dialog according to the given parameters.
+   *
+   * In case the dialog is already shown, updates it according to the new
+   * parameters.
+   * @param {!OobeTypes.SecurityTokenPinDialogParameters} parameters
+   */
+  showPinDialog: function(parameters) {
+    assert(parameters);
+
+    // If currently shown, reset and send the cancellation result if not yet.
+    this.closePinDialog();
+    this.$.pinDialog.reset();
+
+    this.pinDialogParameters_ = parameters;
+    this.pinDialogResultReported_ = false;
+  },
+
+  /**
+   * Closes the PIN dialog (that was previously opened using showPinDialog()).
+   */
+  closePinDialog: function() {
+    if (this.pinDialogParameters_ && !this.pinDialogResultReported_) {
+      this.pinDialogResultReported_ = true;
+      // TODO(crbug.com/964069): Send the "canceled" result to the C++ side.
+    }
+    this.pinDialogParameters_ = null;
+  },
+
+  /**
+   * Invoked when the user cancels the PIN dialog.
+   * @param {!CustomEvent} e
+   */
+  onPinDialogCanceled_: function(e) {
+    this.closePinDialog();
+  },
+
+  /**
+   * Invoked when the PIN dialog is completed.
+   * @param {!CustomEvent<string>} e Event with the entered PIN as the payload.
+   */
+  onPinDialogCompleted_: function(e) {
+    this.pinDialogResultReported_ = true;
+    // TODO(crbug.com/964069): Send the PIN to the C++ side.
+  },
+
 });
 })();
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.css b/chrome/browser/resources/chromeos/login/security_token_pin.css
new file mode 100644
index 0000000..a77c7b9e
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/security_token_pin.css
@@ -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. */
+
+#pinKeyboardContainer {
+  padding-top: 30px;
+}
+
+#pinKeyboard {
+  --pin-keyboard-pin-input-style: {
+    padding-bottom: 30px;
+  };
+  --pin-keyboard-backspace-paper-ripple-offset: 9px;
+  --pin-keyboard-digit-button: {
+    margin: 0 15px;
+    width: 48px;
+  };
+  --pin-keyboard-number-color: var(--google-grey-900);
+  --pin-keyboard-paper-ripple: {
+    border-radius: 50%;
+    color: rgba(0, 0, 0, .34);
+    height: 60px;
+    left: -6px;
+    position: absolute;
+    top: -6px;
+    width: 60px;
+  };
+}
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.html b/chrome/browser/resources/chromeos/login/security_token_pin.html
new file mode 100644
index 0000000..c640c64
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/security_token_pin.html
@@ -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. -->
+
+<!-- TODO(crbug.com/964069): Make string localized, once they are finalized. -->
+
+<dom-module id="security-token-pin">
+  <link rel="stylesheet" href="oobe_dialog_host.css">
+  <link rel="stylesheet" href="oobe_flex_layout.css">
+  <link rel="stylesheet" href="security_token_pin.css">
+
+  <template>
+
+    <oobe-dialog class="gaia-dialog" has-buttons>
+      <h1 slot="title">Enter your smart card PIN</h1>
+      <div slot="subtitle">
+        <p>Provide the PIN that's associated with your smart card.</p>
+      </div>
+      <div slot="footer">
+        <div id="pinKeyboardContainer">
+          <pin-keyboard id="pinKeyboard"></pin-keyboard>
+        </div>
+      </div>
+      <div slot="bottom-buttons" class="layout horizontal justified">
+        <oobe-back-button on-tap="onBackClicked_"></oobe-back-button>
+        <oobe-next-button on-tap="onNextClicked_"
+            disabled="[[processingCompletion_]]"></oobe-next-button>
+      </div>
+    </oobe-dialog>
+
+  </template>
+</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.js b/chrome/browser/resources/chromeos/login/security_token_pin.js
new file mode 100644
index 0000000..765eb55
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/security_token_pin.js
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Polymer element for the security token PIN dialog shown during
+ * sign-in.
+ */
+
+Polymer({
+  is: 'security-token-pin',
+
+  behaviors: [OobeDialogHostBehavior],
+
+  properties: {
+    /**
+     * Contains the OobeTypes.SecurityTokenPinDialogParameters object. It can be
+     * null when our element isn't used.
+     */
+    parameters: {
+      type: Object,
+    },
+
+    /**
+     * Whether the current state is the wait for the processing completion
+     * (i.e., the backend is verifying the entered PIN).
+     * @private
+     */
+    processingCompletion_: {
+      type: Boolean,
+      value: false,
+    },
+  },
+
+  /**
+   * Resets the dialog to the initial state.
+   */
+  reset: function() {
+    this.$.pinKeyboard.value = '';
+    this.processingCompletion_ = false;
+  },
+
+  /**
+   * Invoked when the "Back" button is clicked.
+   * @private
+   */
+  onBackClicked_: function() {
+    this.fire('cancel');
+  },
+
+  /**
+   * Invoked when the "Next" button is clicked.
+   * @private
+   */
+  onNextClicked_: function() {
+    this.processingCompletion_ = true;
+    this.fire('completed', this.$.pinKeyboard.value);
+  },
+});
diff --git a/chrome/browser/resources/chromeos/network_ui/network_ui.js b/chrome/browser/resources/chromeos/network_ui/network_ui.js
index a6813c6c..0e672d7 100644
--- a/chrome/browser/resources/chromeos/network_ui/network_ui.js
+++ b/chrome/browser/resources/chromeos/network_ui/network_ui.js
@@ -144,11 +144,7 @@
     const icon = /** @type {!CrNetworkIconElement} */ (
         document.createElement('cr-network-icon'));
     icon.isListItem = true;
-    icon.networkState = {
-      GUID: '',
-      Type: /** @type{chrome.networkingPrivate.NetworkType} */ (
-          OncMojo.getNetworkTypeString(state.type)),
-    };
+    icon.networkState = OncMojo.getDefaultNetworkState(state.type);
     cell.appendChild(icon);
     return cell;
   };
@@ -514,25 +510,25 @@
    * Handles clicks on network items in the <cr-network-select> element by
    * attempting a connection to the selected network or requesting a password
    * if the network requires a password.
-   * @param {!Event<!CrOnc.NetworkStateProperties>} event
+   * @param {!Event<!OncMojo.NetworkStateProperties>} event
    */
   const onNetworkItemSelected = function(event) {
-    const state = event.detail;
+    const networkState = event.detail;
 
     // If the network is already connected, show network details.
-    if (state.ConnectionState == CrOnc.ConnectionState.CONNECTED) {
-      chrome.send('showNetworkDetails', [state.GUID]);
+    if (OncMojo.connectionStateIsConnected(networkState.connectionState)) {
+      chrome.send('showNetworkDetails', [networkState.guid]);
       return;
     }
 
     // If the network is not connectable, show a configuration dialog.
-    if (state.Connectable === false || state.ErrorState) {
-      chrome.send('showNetworkConfig', [state.GUID]);
+    if (networkState.connectable === false || networkState.errorState) {
+      chrome.send('showNetworkConfig', [networkState.guid]);
       return;
     }
 
     // Otherwise, connect.
-    chrome.networkingPrivate.startConnect(state.GUID, () => {
+    chrome.networkingPrivate.startConnect(networkState.guid, () => {
       const lastError = chrome.runtime.lastError;
       if (!lastError)
         return;
@@ -543,8 +539,8 @@
       }
       console.error(
           'networkingPrivate.startConnect error: ' + message +
-          ' For: ' + state.GUID);
-      chrome.send('showNetworkConfig', [state.GUID]);
+          ' For: ' + networkState.guid);
+      chrome.send('showNetworkConfig', [networkState.guid]);
     });
   };
 
diff --git a/chrome/browser/resources/chromeos/password_change/confirm_password_change.html b/chrome/browser/resources/chromeos/password_change/confirm_password_change.html
new file mode 100644
index 0000000..b599ceb
--- /dev/null
+++ b/chrome/browser/resources/chromeos/password_change/confirm_password_change.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<head>
+  <meta charset="utf-8">
+
+  <link rel="import" href="chrome://resources/html/polymer.html">
+  <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+  <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+  <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
+  <link rel="import" href="chrome://resources/cr_elements/icons.html">
+  <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+  <link rel="import" href="chrome://resources/html/load_time_data.html">
+  <link rel="import" href="chrome://resources/html/i18n_behavior.html">
+  <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+  <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+  <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+
+  <script src="confirm_password_change.js"></script>
+  <script src="strings.js"></script>
+
+  <dom-module id="confirm-password-change">
+    <template>
+      <style>
+        ::part(dialog) {
+          /* We don't show the dialogs border using CSS since we show this
+          dialog inside a system dialog - we don't want two borders. */
+          border-radius: 0;
+          height: 100%;
+          width: 100%;
+        }
+
+        .label {
+          @apply --cr-form-field-label;
+        }
+
+        #prompt {
+          margin-bottom: 20px;
+        }
+
+        cr-input[type='password'] {
+          font-size: 20px;
+        }
+
+        #password-match-error-container {
+          margin-top: -16px;
+        }
+
+        #error-icon {
+        --iron-icon-fill-color: var(--google-red-600);
+        }
+
+        #password-match-error {
+          color: var(--google-red-600);
+          display: inline-block;
+        }
+      </style>
+
+      <cr-dialog id="dialog" exportparts="dialog">
+        <div slot="title">[[i18n('title')]]</div>
+
+        <div slot="body" spellcheck="false">
+          <div id="prompt">[[i18n('bothPasswordsPrompt')]]</div>
+
+          <div>
+            <cr-input type="password" value="{{old_password_}}"
+                label="[[i18n('oldPassword')]]"
+                invalid="[[invalidOldPassword_(currentValidationError_)]]">
+            </cr-input>
+          </div>
+
+          <div>
+            <cr-input type="password" value="{{new_password_}}"
+                label="[[i18n('newPassword')]]"
+                invalid="[[invalidNewPassword_(currentValidationError_)]]">
+            </cr-input>
+            <cr-input type="password" value="{{confirm_new_password_}}"
+                label="[[i18n('confirmNewPassword')]]"
+                invalid=
+                    "[[invalidConfirmNewPassword_(currentValidationError_)]]">
+            </cr-input>
+          </div>
+
+          <div id="password-match-error-container"
+              hidden="[[!passwordsDoNotMatch_(currentValidationError_)]]">
+            <iron-icon id="error-icon" icon="cr:warning"></iron-icon>
+            <div id="password-match-error">[[i18n('matchError')]]</div>
+          </div>
+        </div>
+
+        <div slot="button-container">
+          <cr-button class="action-button" on-tap="onSaveTap_">
+            [[i18n('save')]]
+          </cr-button>
+        </div>
+      </cr-dialog>
+    </template>
+  </dom-module>
+</head>
+
+<body>
+  <confirm-password-change id="main-element"></confirm-password-change>
+</body>
+</html>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/password_change/confirm_password_change.js b/chrome/browser/resources/chromeos/password_change/confirm_password_change.js
new file mode 100644
index 0000000..dee18c433
--- /dev/null
+++ b/chrome/browser/resources/chromeos/password_change/confirm_password_change.js
@@ -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.
+
+/**
+ * @fileoverview 'confirm-password-change' is a dialog so that the user can
+ * either confirm their old password, or confirm their new password (twice),
+ * or both, as part of an in-session password change.
+ */
+
+// TODO(https://crbug.com/930109): Logic is not done. Need to add logic to
+// show a spinner, to show only some of the password fields,
+// and to handle clicks on the save button.
+
+/** @enum{number} */
+const ValidationErrorType = {
+  NO_ERROR: 0,
+  MISSING_OLD_PASSWORD: 1,
+  MISSING_NEW_PASSWORD: 2,
+  MISSING_CONFIRM_NEW_PASSWORD: 3,
+  PASSWORDS_DO_NOT_MATCH: 4,
+};
+
+Polymer({
+  is: 'confirm-password-change',
+
+  behaviors: [I18nBehavior, WebUIListenerBehavior],
+
+  properties: {
+    /** @private {string} */
+    old_password_: {
+      type: String,
+      value: '',
+    },
+
+    /** @private {string} */
+    new_password_: {
+      type: String,
+      value: '',
+    },
+
+    /** @private {string} */
+    confirm_new_password_: {
+      type: String,
+      value: '',
+    },
+
+    /** @private {!ValidationErrorType} */
+    currentValidationError_: {
+      type: Number,
+      value: ValidationErrorType.NO_ERROR,
+    },
+  },
+
+  /** @override */
+  attached: function() {
+    this.$.dialog.showModal();
+  },
+
+  /** @private */
+  cancel_: function() {
+    this.$.dialog.cancel();
+  },
+
+  /**
+   * @private
+   */
+  onSaveTap_: function() {
+    this.currentValidationError_ = this.findFirstError_();
+    if (this.currentValidationError_ == ValidationErrorType.NO_ERROR) {
+      // TODO(olsen): Send a message to a handler to change the password,
+      // instead of just showing an alert.
+      alert(
+          'changePassword(' + this.old_password_ + ', ' + this.new_password_ +
+          ')');
+    }
+  },
+
+  /**
+   * @return {!ValidationErrorType}
+   * @private
+   */
+  findFirstError_: function() {
+    if (!this.old_password_) {
+      return ValidationErrorType.MISSING_OLD_PASSWORD;
+    }
+    if (!this.new_password_) {
+      return ValidationErrorType.MISSING_NEW_PASSWORD;
+    }
+    if (!this.confirm_new_password_) {
+      return ValidationErrorType.MISSING_CONFIRM_NEW_PASSWORD;
+    }
+    if (this.new_password_ != this.confirm_new_password_) {
+      return ValidationErrorType.PASSWORDS_DO_NOT_MATCH;
+    }
+    return ValidationErrorType.NO_ERROR;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  invalidOldPassword_: function() {
+    return this.currentValidationError_ ==
+        ValidationErrorType.MISSING_OLD_PASSWORD;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  invalidNewPassword_: function() {
+    return this.currentValidationError_ ==
+        ValidationErrorType.MISSING_NEW_PASSWORD;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  invalidConfirmNewPassword_: function() {
+    return this.currentValidationError_ ==
+        ValidationErrorType.MISSING_CONFIRM_NEW_PASSWORD ||
+        this.passwordsDoNotMatch_();
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  passwordsDoNotMatch_: function() {
+    return this.currentValidationError_ ==
+        ValidationErrorType.PASSWORDS_DO_NOT_MATCH;
+  },
+});
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html
index 0d49dd9..4349147 100644
--- a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html
+++ b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html
@@ -11,7 +11,7 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/load_time_data.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="set_time_browser_proxy.html">
 
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
index d2f34b3..556a25e 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
@@ -476,6 +476,10 @@
             !this.dailyRefreshInfo_.enabled;
         if (!$('refresh').hidden) {
           this.addEventToButton_($('refresh'), () => {
+            if (this.pendingDailyRefreshInfo_) {
+              // There's already a refresh in progress, ignore this request.
+              return;
+            }
             this.pendingDailyRefreshInfo_ = this.dailyRefreshInfo_;
             this.setDailyRefreshWallpaper_();
           });
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index 83d54e25..21fb2005 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -91,9 +91,6 @@
         <include name="IDR_ARC_SUPPORT_ICON_HEADER" file="chromeos/arc_support/images/header.png" type="BINDATA" />
         <include name="IDR_ARC_SUPPORT_RECOMMEND_APP_LIST_VIEW_JS" file="chromeos/arc_support/recommend_app_list_view.js" type="BINDATA" />
         <include name="IDR_ARC_SUPPORT_RECOMMEND_APP_LIST_VIEW_HTML" file="chromeos/arc_support/recommend_app_list_view.html" type="chrome_html" flattenhtml="true" />
-        <if expr="_kiosk_next">
-          <part file="chromeos/kiosk_next_home/kiosk_next_resources.grdp" />
-        </if>
       </if>
       <if expr="enable_plugins">
         <!-- Note that resources included here also must be included in
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 7bbd71e5a..d072413 100644
--- a/chrome/browser/resources/device_log_ui/device_log_ui.html
+++ b/chrome/browser/resources/device_log_ui/device_log_ui.html
@@ -16,6 +16,7 @@
   </div>
   <div id="log-checkbox-container">
     <button id="log-refresh">$i18n{logRefreshText}</button>
+    <button id="log-clear">$i18n{logClearText}</button>
     <label id="log-checkbox-show">$i18n{logLevelShowText}</label>
     <label>
       <input id="log-level-error" type="checkbox">
diff --git a/chrome/browser/resources/device_log_ui/device_log_ui.js b/chrome/browser/resources/device_log_ui/device_log_ui.js
index 24be02b2..1110307 100644
--- a/chrome/browser/resources/device_log_ui/device_log_ui.js
+++ b/chrome/browser/resources/device_log_ui/device_log_ui.js
@@ -97,11 +97,14 @@
    * @param {Object} data A JSON structure of event log entries.
    */
   const getLogCallback = function(data) {
+    const container = $('log-container');
     try {
       createEventLog(JSON.parse(data));
+      if (container.textContent == '') {
+        container.textContent = loadTimeData.getString('logNoEntriesText');
+      }
     } catch (e) {
-      const container = $('log-container');
-      container.textContent = 'No log entries';
+      container.textContent = loadTimeData.getString('logNoEntriesText');
     }
   };
 
@@ -112,6 +115,11 @@
     chrome.send('DeviceLog.getLog');
   };
 
+  const clearLog = function() {
+    chrome.send('DeviceLog.clearLog');
+    requestLog();
+  };
+
   /**
    * Sets refresh rate if the interval is found in the url.
    */
@@ -143,6 +151,7 @@
     $('log-timedetail').checked = false;
 
     $('log-refresh').onclick = requestLog;
+    $('log-clear').onclick = clearLog;
     checkboxes = document.querySelectorAll(
         '#log-checkbox-container input[type="checkbox"]');
     for (let i = 0; i < checkboxes.length; ++i) {
diff --git a/chrome/browser/resources/discards/discards_tab.html b/chrome/browser/resources/discards/discards_tab.html
index da52398a..9f8b7882 100644
--- a/chrome/browser/resources/discards/discards_tab.html
+++ b/chrome/browser/resources/discards/discards_tab.html
@@ -9,7 +9,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/icon.html">
diff --git a/chrome/browser/resources/downloads/item.html b/chrome/browser/resources/downloads/item.html
index c9bab20..16252c86 100644
--- a/chrome/browser/resources/downloads/item.html
+++ b/chrome/browser/resources/downloads/item.html
@@ -13,7 +13,7 @@
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_row.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_row_behavior.html">
diff --git a/chrome/browser/resources/extensions/detail_view.html b/chrome/browser/resources/extensions/detail_view.html
index 0b64fcf3..afc2970e 100644
--- a/chrome/browser/resources/extensions/detail_view.html
+++ b/chrome/browser/resources/extensions/detail_view.html
@@ -11,7 +11,7 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
diff --git a/chrome/browser/resources/extensions/item.html b/chrome/browser/resources/extensions/item.html
index 6829b68..e52cbce9 100644
--- a/chrome/browser/resources/extensions/item.html
+++ b/chrome/browser/resources/extensions/item.html
@@ -8,7 +8,7 @@
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
diff --git a/chrome/browser/resources/extensions/keyboard_shortcuts.html b/chrome/browser/resources/extensions/keyboard_shortcuts.html
index de1fd4bc..540f2be4 100644
--- a/chrome/browser/resources/extensions/keyboard_shortcuts.html
+++ b/chrome/browser/resources/extensions/keyboard_shortcuts.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="item_behavior.html">
 <link rel="import" href="shortcut_input.html">
diff --git a/chrome/browser/resources/extensions/runtime_host_permissions.html b/chrome/browser/resources/extensions/runtime_host_permissions.html
index 5215c32..038343a 100644
--- a/chrome/browser/resources/extensions/runtime_host_permissions.html
+++ b/chrome/browser/resources/extensions/runtime_host_permissions.html
@@ -8,10 +8,10 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="runtime_hosts_dialog.html">
 <link rel="import" href="shared_style.html">
diff --git a/chrome/browser/resources/local_ntp/OWNERS b/chrome/browser/resources/local_ntp/OWNERS
index 81e7a847..e2897dd 100644
--- a/chrome/browser/resources/local_ntp/OWNERS
+++ b/chrome/browser/resources/local_ntp/OWNERS
@@ -3,7 +3,6 @@
 kristipark@chromium.org
 mathp@chromium.org
 ramyan@chromium.org
-treib@chromium.org
 
 per-file local_ntp_resources.grd=file://components/search/OWNERS
 
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index 1908843f..8152e8e 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -168,10 +168,23 @@
   RESTORE_DEFAULT: 3,
 };
 
+/**
+ * The semi-transparent, gradient overlay for the custom background. Intended
+ * to improve readability of NTP elements/text.
+ * @type {string}
+ * @const
+ */
 customize.CUSTOM_BACKGROUND_OVERLAY =
     'linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.3))';
 
-// These shound match the corresponding values in local_ntp.js, that control the
+/**
+ * Number of rows in the custom background dialog to preload.
+ * @type {number}
+ * @const
+ */
+customize.ROWS_TO_PRELOAD = 3;
+
+// These should match the corresponding values in local_ntp.js, that control the
 // mv-notice element.
 customize.delayedHideNotification = -1;
 customize.NOTIFICATION_TIMEOUT = 10000;
@@ -189,23 +202,14 @@
 customize.selectedBackgroundTile = null;
 
 /**
- * Number of rows in the custom background dialog to preload.
- * @type {number}
- * @const
- */
-customize.ROWS_TO_PRELOAD = 3;
-
-/**
  * Called when the error notification should be shown.
  * @type {?Function}
- * @private
  */
 customize.showErrorNotification = null;
 
 /**
  * Called when the custom link notification should be hidden.
  * @type {?Function}
- * @private
  */
 customize.hideCustomLinkNotification = null;
 
@@ -213,14 +217,12 @@
  * The currently selected submenu (i.e. Background, Shortcuts, etc.) in the
  * richer picker. Corresponds to the submenu's button element in the sidebar.
  * @type {?Element}
- * @private
  */
 customize.richerPicker_selectedSubmenu = null;
 
 /**
  * The preselected options for Shortcuts in the richer picker.
  * @type {Object}
- * @private
  */
 customize.preselectedShortcutOptions = {
   // Contains the selected type's DOM element, i.e. either custom links or most visited.
@@ -231,7 +233,6 @@
 /**
  * The currently selected options for Shortcuts in the richer picker.
  * @type {Object}
- * @private
  */
 customize.selectedShortcutOptions = {
   // Contains the preselected type's DOM element, i.e. either custom links or most visited.
@@ -889,7 +890,7 @@
 };
 
 /**
- * Show dialog for selecting an image. Image data should previous have been
+ * Show dialog for selecting an image. Image data should previously have been
  * loaded into collImg via
  * chrome-search://local-ntp/ntp-background-images.js?collection_id=<collection_id>
  * @param {string} dialogTitle The title to be displayed at the top of the
@@ -1080,7 +1081,6 @@
 /**
  * Load the NTPBackgroundCollections script. It'll create a global
  * variable name "coll" which is a dict of background collections data.
- * @private
  */
 customize.loadChromeBackgrounds = function() {
   const collElement = $('ntp-collection-loader');
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index a75da42..7e67625 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -345,7 +345,6 @@
  *
  * @param {!Object} info Theme background information.
  * @return {boolean} Whether the chips should be dark.
- * @private
  */
 function getUseDarkChips(info) {
   return info.usingDarkMode && !info.imageUrl;
@@ -353,7 +352,6 @@
 
 /**
  * Updates the NTP based on the current theme.
- * @private
  */
 function renderTheme() {
   const info = getThemeBackgroundInfo();
@@ -454,7 +452,6 @@
 
 /**
  * Sends the current theme info to the most visited iframe.
- * @private
  */
 function sendThemeInfoToMostVisitedIframe() {
   const info = getThemeBackgroundInfo();
@@ -483,7 +480,6 @@
 
 /**
  * Sends the current theme info to the edit custom link iframe.
- * @private
  */
 function sendThemeInfoToEditCustomLinkIframe() {
   if (!configData.isGooglePage) {
@@ -504,7 +500,6 @@
 /**
  * Updates the OneGoogleBar (if it is loaded) based on the current theme.
  * TODO(crbug.com/918582): Add support for OGB dark mode.
- * @private
  */
 function renderOneGoogleBarTheme() {
   if (!window.gbar) {
@@ -524,7 +519,6 @@
 
 /**
  * Callback for embeddedSearch.newTabPage.onthemechange.
- * @private
  */
 function onThemeChange() {
   // Save the current dark mode state to check if dark mode has changed.
@@ -545,7 +539,6 @@
 /**
  * Updates the NTP style according to theme.
  * @param {Object} themeInfo The information about the theme.
- * @private
  */
 function setCustomThemeStyle(themeInfo) {
   let textColor = '';
@@ -571,7 +564,6 @@
  * @param {string} url The URL of the attribution image, if any.
  * @param {string} themeBackgroundAlignment The alignment of the theme
  *     background image. This is used to compute the attribution's alignment.
- * @private
  */
 function updateThemeAttribution(url, themeBackgroundAlignment) {
   if (!url) {
@@ -597,7 +589,6 @@
 /**
  * Sets the visibility of the theme attribution.
  * @param {boolean} show True to show the attribution.
- * @private
  */
 function setAttributionVisibility_(show) {
   $(IDS.ATTRIBUTION).style.display = show ? '' : 'none';
@@ -607,7 +598,6 @@
  * Converts an Array of color components into RGBA format "rgba(R,G,B,A)".
  * @param {Array<number>} color Array of rgba color components.
  * @return {string} CSS color in RGBA format.
- * @private
  */
 function convertToRGBAColor(color) {
   return 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' +
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css
index c2b093f..1bf0ca72 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.css
+++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -42,7 +42,6 @@
 a:active,
 a:hover,
 a:visited {
-  color: inherit;
   text-decoration: none;
 }
 
@@ -125,6 +124,7 @@
 .md-tile {
   border-radius: 4px;
   box-sizing: border-box;
+  color: rgb(var(--tile-title-color));
   cursor: pointer;
   height: var(--md-tile-size);
   margin-bottom: var(--md-tile-margin);
@@ -141,6 +141,7 @@
   border-radius: 4px;
   box-shadow: 0 1px 3px 0 rgba(var(--GG800-rgb), 0.3),
       0 4px 8px 3px rgba(var(--GG800-rgb), 0.15);
+  color: rgb(var(--GG800-rgb));
   transition-duration: 200ms;
 }
 
@@ -148,6 +149,7 @@
   background-color: rgb(var(--dark-mode-bg-rgb));
   box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4),
       0 4px 8px 3px rgba(0, 0, 0, 0.25);
+  color: rgb(var(--GG100-rgb));
 }
 
 .reorder .md-tile-inner {
@@ -239,7 +241,6 @@
 }
 
 .md-title {
-  color: rgb(var(--tile-title-color));
   font-size: var(--md-title-font-size);
   font-weight: 500;
   max-height: var(--md-title-max-height);
@@ -250,18 +251,6 @@
   width: 88px;
 }
 
-/* During reorder background is white. Therefore use dark grey for
- * title. */
-.reorder {
-  color: rgb(var(--GG800-rgb));
-}
-
-/* During reorder in dark mode background is dark. Therefore use light
- * grey for title. */
-html[darkmode=true] .reorder {
-  color: rgb(var(--GG100-rgb));
-}
-
 .md-title span {
   line-height: var(--md-title-height);
 }
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
index 1c9b605..88adc75c 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
@@ -251,13 +251,9 @@
 
 #sink-search-input {
   --cr-input-background-color: white;
-  --cr-input-container: {
-    border-radius: 0;
-  }
+  --cr-input-border-bottom: 1px solid var(--paper-grey-800);
+  --cr-input-border-radius: 0;
   --cr-input-error-display: none;
-  --cr-input-input: {
-    border-bottom: 1px solid var(--paper-grey-800);
-  }
   --cr-input-padding-end: 0;
   --cr-input-padding-start: 0;
   --cr-input-padding-bottom: 2px;
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.html b/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
index c60d15d5..cb0f988 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_slider/cr_slider.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html b/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
index d7c9c12b..312969d6 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
+++ b/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
@@ -16,22 +16,9 @@
 
       #pageselector {
         --cr-input-focus-color: transparent;
-        --cr-input-container: {
-          overflow: initial;
-        }
         --cr-input-error-display: none;
         --cr-input-background-color: transparent;
         --cr-input-color: white;
-        --cr-input-inner-container: {
-          margin-inline-end: -3px;
-        }
-        --cr-input-input: {
-          text-align: end;
-          caret-color: #fff;
-          border-radius: 2px;
-          box-sizing: content-box;
-          margin-inline-start: -3px;
-        }
         --cr-input-padding-end: 3px;
         --cr-input-padding-start: 3px;
         --cr-input-padding-bottom: 0;
@@ -41,6 +28,23 @@
         width: 1ch;
       }
 
+      /* TODO(johntlee): Figure out a way to align the input without
+       * accessing all these parts. */
+      #pageselector::part(input-container) {
+        overflow: initial;
+      }
+
+      #pageselector::part(inner-input-container) {
+        margin-inline-end: -3px;
+      }
+
+      #pageselector::part(input) {
+        box-sizing: content-box;
+        caret-color: #fff;
+        margin-inline-start: -3px;
+        text-align: end;
+      }
+
       #pageselector[focused_],
       #pageselector:hover {
         --cr-input-background-color: rgba(0, 0, 0, 0.5);
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_js.js b/chrome/browser/resources/print_preview/cloud_print_interface_js.js
index 96361c6c3..cde3343 100644
--- a/chrome/browser/resources/print_preview/cloud_print_interface_js.js
+++ b/chrome/browser/resources/print_preview/cloud_print_interface_js.js
@@ -18,8 +18,9 @@
      *     Auth2 tokens.
      * @param {boolean} isInAppKioskMode Whether the print preview is in App
      *     Kiosk mode.
+     * @param {string} uiLocale The UI locale.
      */
-    constructor(baseUrl, nativeLayer, isInAppKioskMode) {
+    constructor(baseUrl, nativeLayer, isInAppKioskMode, uiLocale) {
       /**
        * The base URL of the Google Cloud Print API.
        * @private {string}
@@ -40,6 +41,13 @@
       this.isInAppKioskMode_ = isInAppKioskMode;
 
       /**
+       * The UI locale, used to get printer information in the correct locale
+       * from Google Cloud Print.
+       * @private {string}
+       */
+      this.uiLocale_ = uiLocale;
+
+      /**
        * Currently logged in users (identified by email) mapped to the Google
        * session index.
        * @private {!Object<number>}
@@ -220,7 +228,7 @@
       }
 
       // Add locale
-      searchParams.append('hl', window.navigator.language);
+      searchParams.append('hl', this.uiLocale_);
       let body = null;
       if (params) {
         if (method == 'GET') {
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_manager.js b/chrome/browser/resources/print_preview/cloud_print_interface_manager.js
index fcf7923..83d98d4 100644
--- a/chrome/browser/resources/print_preview/cloud_print_interface_manager.js
+++ b/chrome/browser/resources/print_preview/cloud_print_interface_manager.js
@@ -15,15 +15,17 @@
    * @param {!print_preview.NativeLayer} nativeLayer Native layer instance.
    * @param {boolean} isInAppKioskMode Whether the print preview is in App
    *     Kiosk mode.
+   * @param {string} uiLocale The UI locale, for example "en-US" or "fr".
    * @return {!cloudprint.CloudPrintInterface}
    */
-  function getCloudPrintInterface(baseUrl, nativeLayer, isInAppKioskMode) {
+  function getCloudPrintInterface(
+      baseUrl, nativeLayer, isInAppKioskMode, uiLocale) {
     if (instance === null) {
       if (loadTimeData.getBoolean('cloudPrinterHandlerEnabled')) {
         instance = new cloudprint.CloudPrintInterfaceNative();
       } else {
         instance = new cloudprint.CloudPrintInterfaceJS(
-            baseUrl, nativeLayer, isInAppKioskMode);
+            baseUrl, nativeLayer, isInAppKioskMode, uiLocale);
       }
     }
     return instance;
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index 7b3f097..b09c017 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -31,6 +31,7 @@
  * @typedef {{
  *   isInKioskAutoPrintMode: boolean,
  *   isInAppKioskMode: boolean,
+ *   uiLocale: string,
  *   thousandsDelimeter: string,
  *   decimalDelimeter: string,
  *   unitType: !print_preview.MeasurementSystemUnitType,
diff --git a/chrome/browser/resources/print_preview/ui/advanced_settings_item.html b/chrome/browser/resources/print_preview/ui/advanced_settings_item.html
index 06a4b7e..2dd7432 100644
--- a/chrome/browser/resources/print_preview/ui/advanced_settings_item.html
+++ b/chrome/browser/resources/print_preview/ui/advanced_settings_item.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/cr_elements/search_highlight_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../print_preview_utils.html">
 <link rel="import" href="../data/destination.html">
 <link rel="import" href="highlight_utils.html">
diff --git a/chrome/browser/resources/print_preview/ui/app.js b/chrome/browser/resources/print_preview/ui/app.js
index a4ea3f8..afdf54fe 100644
--- a/chrome/browser/resources/print_preview/ui/app.js
+++ b/chrome/browser/resources/print_preview/ui/app.js
@@ -262,7 +262,8 @@
       // sidebar, so that cloud printers can be selected automatically.
       if (settings.cloudPrintURL) {
         this.initializeCloudPrint_(
-            settings.cloudPrintURL, settings.isInAppKioskMode);
+            settings.cloudPrintURL, settings.isInAppKioskMode,
+            settings.uiLocale);
       }
       this.$.documentInfo.init(
           settings.previewModifiable, settings.documentTitle,
@@ -294,12 +295,13 @@
    * Called when Google Cloud Print integration is enabled.
    * @param {string} cloudPrintUrl The URL to use for cloud print servers.
    * @param {boolean} appKioskMode Whether the browser is in app kiosk mode.
+   * @param {string} uiLocale The UI locale.
    * @private
    */
-  initializeCloudPrint_: function(cloudPrintUrl, appKioskMode) {
+  initializeCloudPrint_: function(cloudPrintUrl, appKioskMode, uiLocale) {
     assert(!this.cloudPrintInterface_);
     this.cloudPrintInterface_ = cloudprint.getCloudPrintInterface(
-        cloudPrintUrl, assert(this.nativeLayer_), appKioskMode);
+        cloudPrintUrl, assert(this.nativeLayer_), appKioskMode, uiLocale);
     this.tracker_.add(
         assert(this.cloudPrintInterface_).getEventTarget(),
         cloudprint.CloudPrintInterfaceEventType.SUBMIT_DONE,
diff --git a/chrome/browser/resources/print_preview/ui/color_settings.html b/chrome/browser/resources/print_preview/ui/color_settings.html
index 9d3b121..030305a 100644
--- a/chrome/browser/resources/print_preview/ui/color_settings.html
+++ b/chrome/browser/resources/print_preview/ui/color_settings.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="select_behavior.html">
 <link rel="import" href="settings_behavior.html">
diff --git a/chrome/browser/resources/print_preview/ui/destination_dialog.html b/chrome/browser/resources/print_preview/ui/destination_dialog.html
index 689f7b1..ca88d2b 100644
--- a/chrome/browser/resources/print_preview/ui/destination_dialog.html
+++ b/chrome/browser/resources/print_preview/ui/destination_dialog.html
@@ -10,11 +10,11 @@
   div to create the cloud print "Sign in" link when this element is
   attached. -->
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/event_tracker.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/list_property_update_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="../icons.html">
 <link rel="import" href="../metrics.html">
diff --git a/chrome/browser/resources/print_preview/ui/destination_select.html b/chrome/browser/resources/print_preview/ui/destination_select.html
index d8369b50..2b7cb3c 100644
--- a/chrome/browser/resources/print_preview/ui/destination_select.html
+++ b/chrome/browser/resources/print_preview/ui/destination_select.html
@@ -2,7 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
diff --git a/chrome/browser/resources/print_preview/ui/duplex_settings.html b/chrome/browser/resources/print_preview/ui/duplex_settings.html
index 834247c0..a1382d8b 100644
--- a/chrome/browser/resources/print_preview/ui/duplex_settings.html
+++ b/chrome/browser/resources/print_preview/ui/duplex_settings.html
@@ -2,7 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-meta/iron-meta.html">
diff --git a/chrome/browser/resources/print_preview/ui/layout_settings.html b/chrome/browser/resources/print_preview/ui/layout_settings.html
index 8ec9173..b3c22b2 100644
--- a/chrome/browser/resources/print_preview/ui/layout_settings.html
+++ b/chrome/browser/resources/print_preview/ui/layout_settings.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="select_behavior.html">
 <link rel="import" href="settings_behavior.html">
diff --git a/chrome/browser/resources/print_preview/ui/margins_settings.html b/chrome/browser/resources/print_preview/ui/margins_settings.html
index 49f34fe..f02aa28 100644
--- a/chrome/browser/resources/print_preview/ui/margins_settings.html
+++ b/chrome/browser/resources/print_preview/ui/margins_settings.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../data/margins.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="select_behavior.html">
diff --git a/chrome/browser/resources/print_preview/ui/pages_per_sheet_settings.html b/chrome/browser/resources/print_preview/ui/pages_per_sheet_settings.html
index de31288..13a454c 100644
--- a/chrome/browser/resources/print_preview/ui/pages_per_sheet_settings.html
+++ b/chrome/browser/resources/print_preview/ui/pages_per_sheet_settings.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../data/margins.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="select_behavior.html">
diff --git a/chrome/browser/resources/print_preview/ui/pages_settings.html b/chrome/browser/resources/print_preview/ui/pages_settings.html
index cddc0ba..4d6c107 100644
--- a/chrome/browser/resources/print_preview/ui/pages_settings.html
+++ b/chrome/browser/resources/print_preview/ui/pages_settings.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
 <link rel="import" href="../data/document_info.html">
 <link rel="import" href="../print_preview_utils.html">
diff --git a/chrome/browser/resources/print_preview/ui/scaling_settings.html b/chrome/browser/resources/print_preview/ui/scaling_settings.html
index 58b147e..d4cd7a3 100644
--- a/chrome/browser/resources/print_preview/ui/scaling_settings.html
+++ b/chrome/browser/resources/print_preview/ui/scaling_settings.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
 <link rel="import" href="number_settings_section.html">
 <link rel="import" href="print_preview_shared_css.html">
diff --git a/chrome/browser/resources/print_preview/ui/settings_select.html b/chrome/browser/resources/print_preview/ui/settings_select.html
index b5d8e61..dd4a716 100644
--- a/chrome/browser/resources/print_preview/ui/settings_select.html
+++ b/chrome/browser/resources/print_preview/ui/settings_select.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../print_preview_utils.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="select_behavior.html">
diff --git a/chrome/browser/resources/settings/a11y_page/BUILD.gn b/chrome/browser/resources/settings/a11y_page/BUILD.gn
index 0be94d4..cb0f25da 100644
--- a/chrome/browser/resources/settings/a11y_page/BUILD.gn
+++ b/chrome/browser/resources/settings/a11y_page/BUILD.gn
@@ -7,6 +7,8 @@
 js_type_check("closure_compile") {
   deps = [
     ":a11y_page",
+    ":captions_browser_proxy",
+    ":captions_subpage",
   ]
 
   if (is_chromeos) {
@@ -19,6 +21,16 @@
   }
 }
 
+js_library("captions_subpage") {
+  deps = [
+    "../appearance_page:fonts_browser_proxy",
+    "../controls:settings_dropdown_menu",
+    "//ui/webui/resources/cr_elements/cr_slider:cr_slider",
+    "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
+  ]
+}
+
 js_library("a11y_page") {
   deps = [
     "..:route",
@@ -61,3 +73,9 @@
 
 js_library("externs") {
 }
+
+js_library("captions_browser_proxy") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+}
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index c13d604..a4acf13 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -1,5 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="captions_browser_proxy.html">
+<link rel="import" href="captions_subpage.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="../i18n_setup.html">
@@ -17,76 +19,100 @@
 <dom-module id="settings-a11y-page">
   <template>
     <style include="settings-shared"></style>
-<if expr="chromeos">
-    <template is="dom-if" if="[[pageVisibility.webstoreLink]]">
-      <settings-animated-pages id="pages" current-route="{{currentRoute}}"
-          section="a11y" focus-config="[[focusConfig_]]">
+    <settings-animated-pages id="pages" current-route="{{currentRoute}}"
+        section="a11y" focus-config="[[focusConfig_]]">
+      <if expr="not chromeos">
         <div route-path="default">
-          <settings-toggle-button
-              id="a11yImageLabels"
-              hidden$="[[!showAccessibilityLabelsSetting_]]"
-              pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
-              on-change="onToggleAccessibilityImageLabels_"
-              label="$i18n{accessibleImageLabelsTitle}"
-              sub-label="$i18n{accessibleImageLabelsSubtitle}">
-          </settings-toggle-button>
-          <settings-toggle-button id="optionsInMenuToggle"
-              label="$i18n{optionsInMenuLabel}"
-              pref="{{prefs.settings.a11y.enable_menu}}">
-          </settings-toggle-button>
-          <cr-link-row class="hr" id="subpage-trigger"
-              label="$i18n{manageAccessibilityFeatures}"
-              on-click="onManageAccessibilityFeaturesTap_"
-              sub-label="$i18n{moreFeaturesLinkDescription}">
-          </cr-link-row>
+          <template is="dom-if" if="[[showCaptionSettings_]]">
+            <cr-link-row class="hr" id="captions" label="$i18n{captionsTitle}"
+                on-click="onTapCaptions_">
+            </cr-link-row>
+          </template>
+          <if expr="not chromeos">
+            <settings-toggle-button id="a11yImageLabels"
+                hidden$="[[!showAccessibilityLabelsSetting_]]"
+                pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
+                on-change="onToggleAccessibilityImageLabels_" 
+                label="$i18n{accessibleImageLabelsTitle}"
+                sub-label="$i18n{accessibleImageLabelsSubtitle}">
+            </settings-toggle-button>
+            <cr-link-row class="hr" label="$i18n{moreFeaturesLink}"
+                on-click="onMoreFeaturesLinkClick_"
+                sub-label="$i18n{a11yWebStore}" external></cr-link-row>
+          </if>
         </div>
-
-        <template is="dom-if" route-path="/manageAccessibility">
-          <settings-subpage
-              associated-control="[[$$('#subpage-trigger')]]"
-              page-title="$i18n{manageAccessibilityFeatures}">
-            <settings-manage-a11y-page prefs="{{prefs}}">
-            </settings-manage-a11y-page>
-          </settings-subpage>
-        </template>
-        <template is="dom-if" route-path="/manageAccessibility/tts">
-          <settings-subpage
-              associated-control="[[$$('#subpage-trigger')]]"
-              page-title="$i18n{manageTtsSettings}">
-            <settings-tts-subpage prefs="{{prefs}}">
-            </settings-tts-subpage>
-          </settings-subpage>
-        </template>
-        <template is="dom-if" if="[[showExperimentalSwitchAccess_]]">
-          <template is="dom-if" route-path="/manageAccessibility/switchAccess">
+      </if>
+      <if expr="chromeos">
+        <template is="dom-if" if="[[pageVisibility.webstoreLink]]">
+          <div route-path="default">
+            <template is="dom-if" if="[[showCaptionSettings_]]">
+              <cr-link-row class="hr" id="captions"
+                  label="$i18n{captionsTitle}"
+                  on-click="onTapCaptions_">
+              </cr-link-row>
+            </template>
+            <settings-toggle-button id="a11yImageLabels"
+                hidden$="[[!showAccessibilityLabelsSetting_]]"
+                pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
+                on-change="onToggleAccessibilityImageLabels_"
+                label="$i18n{accessibleImageLabelsTitle}"
+                sub-label="$i18n{accessibleImageLabelsSubtitle}">
+            </settings-toggle-button>
+            <settings-toggle-button id="optionsInMenuToggle"
+                label="$i18n{optionsInMenuLabel}"
+                pref="{{prefs.settings.a11y.enable_menu}}">
+            </settings-toggle-button>
+            <cr-link-row class="hr" id="subpage-trigger"
+                label="$i18n{manageAccessibilityFeatures}"
+                on-click="onManageAccessibilityFeaturesTap_"
+                sub-label="$i18n{moreFeaturesLinkDescription}">
+            </cr-link-row>
+          </div>
+          <template is="dom-if" route-path="/manageAccessibility">
             <settings-subpage associated-control="[[$$('#subpage-trigger')]]"
-                page-title="$i18n{manageSwitchAccessSettings}">
-              <settings-switch-access-subpage prefs="{{prefs.settings.a11y}}">
-              </settings-switch-access-subpage>
+                page-title="$i18n{manageAccessibilityFeatures}">
+              <settings-manage-a11y-page prefs="{{prefs}}">
+              </settings-manage-a11y-page>
+            </settings-subpage>
+          </template>
+          <template is="dom-if" route-path="/manageAccessibility/tts">
+            <settings-subpage
+                associated-control="[[$$('#subpage-trigger')]]"
+                page-title="$i18n{manageTtsSettings}">
+              <settings-tts-subpage prefs="{{prefs}}">
+              </settings-tts-subpage>
+            </settings-subpage>
+          </template>
+          <template is="dom-if" if="[[showExperimentalSwitchAccess_]]">
+            <template is="dom-if"
+                route-path="/manageAccessibility/switchAccess">
+              <settings-subpage associated-control="[[$$('#subpage-trigger')]]"
+                  page-title="$i18n{manageSwitchAccessSettings}">
+                <settings-switch-access-subpage prefs="{{prefs.settings.a11y}}">
+                </settings-switch-access-subpage>
+              </settings-subpage>
+            </template>
+          </template>
+        </template>
+        <cr-link-row class="hr"
+            label="$i18n{moreFeaturesLink}"
+            on-click="onMoreFeaturesLinkClick_"
+            sub-label="$i18n{a11yWebStore}"
+            hidden="[[pageVisibility.webstoreLink]]" external></cr-link-row>
+      </if>
+
+      <if expr="chromeos or is_linux or is_win">
+        <template is="dom-if" if="[[showCaptionSettings_]]">
+          <template is="dom-if" route-path="/captions">
+            <settings-subpage
+                associated-control="[[$$('#captions')]]"
+                page-title="$i18n{captionsTitle}">
+              <settings-captions prefs="{{prefs}}"></settings-captions>
             </settings-subpage>
           </template>
         </template>
-      </settings-animated-pages>
-    </template>
-    <cr-link-row class="hr" label="$i18n{moreFeaturesLink}"
-        on-click="onMoreFeaturesLinkClick_" sub-label="$i18n{a11yWebStore}"
-        hidden="[[pageVisibility.webstoreLink]]"
-        external></cr-link-row>
-</if>
-
-<if expr="not chromeos">
-    <settings-toggle-button
-        id="a11yImageLabels"
-        hidden$="[[!showAccessibilityLabelsSetting_]]"
-        pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
-        on-change="onToggleAccessibilityImageLabels_"
-        label="$i18n{accessibleImageLabelsTitle}"
-        sub-label="$i18n{accessibleImageLabelsSubtitle}">
-    </settings-toggle-button>
-    <cr-link-row class="hr" label="$i18n{moreFeaturesLink}"
-        on-click="onMoreFeaturesLinkClick_" sub-label="$i18n{a11yWebStore}"
-        external></cr-link-row>
-</if>
+      </if>
+    </settings-animated-pages>
 
   </template>
   <script src="a11y_page.js"></script>
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.js b/chrome/browser/resources/settings/a11y_page/a11y_page.js
index 9f0a574d..e1cfc426 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.js
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.js
@@ -43,6 +43,9 @@
       type: Object,
       value: function() {
         const map = new Map();
+        if (settings.routes.CAPTIONS) {
+          map.set(settings.routes.CAPTIONS.path, '#captions');
+        }
         // <if expr="chromeos">
         if (settings.routes.MANAGE_ACCESSIBILITY) {
           map.set(
@@ -53,6 +56,17 @@
       },
     },
 
+    /**
+     * Whether to show the link to caption settings.
+     * @private {boolean}
+     */
+    showCaptionSettings_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('enableCaptionSettings');
+      },
+    },
+
     // <if expr="chromeos">
     /**
      * Whether to show experimental accessibility features.
@@ -121,4 +135,29 @@
     window.open(
         'https://chrome.google.com/webstore/category/collection/accessibility');
   },
+
+  /** @private */
+  onTapCaptions_: function() {
+    // Open the system captions dialog for Mac.
+    // <if expr="is_macosx">
+    settings.CaptionsBrowserProxyImpl.getInstance().openSystemCaptionsDialog();
+    // </if>
+
+    // Open the system captions dialog for Windows 10+ or navigate to the
+    // caption settings page for older versions of Windows
+    // <if expr="is_win">
+    if (loadTimeData.getBoolean('isWindows10OrNewer')) {
+      settings.CaptionsBrowserProxyImpl.getInstance()
+          .openSystemCaptionsDialog();
+    } else {
+      settings.navigateTo(settings.routes.CAPTIONS);
+    }
+    // </if>
+
+    // Navigate to the caption settings page for ChromeOS and Linux as they
+    // do not have system caption settings.
+    // <if expr="chromeos or is_linux">
+    settings.navigateTo(settings.routes.CAPTIONS);
+    // </if>
+  },
 });
diff --git a/chrome/browser/resources/settings/a11y_page/captions_browser_proxy.html b/chrome/browser/resources/settings/a11y_page/captions_browser_proxy.html
new file mode 100644
index 0000000..92b4cd1
--- /dev/null
+++ b/chrome/browser/resources/settings/a11y_page/captions_browser_proxy.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="captions_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/a11y_page/captions_browser_proxy.js b/chrome/browser/resources/settings/a11y_page/captions_browser_proxy.js
new file mode 100644
index 0000000..bf86a7e
--- /dev/null
+++ b/chrome/browser/resources/settings/a11y_page/captions_browser_proxy.js
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A helper object used from the Chrome captions section to
+ * interact with the browser. Used on operating system that is not Chrome OS.
+ */
+
+cr.define('settings', function() {
+  /** @interface */
+  class CaptionsBrowserProxy {
+    /**
+     * Open the native captions system dialog.
+     */
+    openSystemCaptionsDialog() {}
+  }
+
+  /**
+   * @implements {settings.CaptionsBrowserProxy}
+   */
+  class CaptionsBrowserProxyImpl {
+    /** @override */
+    openSystemCaptionsDialog() {
+      chrome.send('openSystemCaptionsDialog');
+    }
+  }
+
+  cr.addSingletonGetter(CaptionsBrowserProxyImpl);
+
+  return {
+    CaptionsBrowserProxy: CaptionsBrowserProxy,
+    CaptionsBrowserProxyImpl: CaptionsBrowserProxyImpl,
+  };
+});
diff --git a/chrome/browser/resources/settings/a11y_page/captions_subpage.html b/chrome/browser/resources/settings/a11y_page/captions_subpage.html
new file mode 100644
index 0000000..31146eb
--- /dev/null
+++ b/chrome/browser/resources/settings/a11y_page/captions_subpage.html
@@ -0,0 +1,89 @@
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../appearance_page/fonts_browser_proxy.html">
+<link rel="import" href="../controls/settings_dropdown_menu.html">
+<link rel="import" href="../controls/settings_slider.html">
+<link rel="import" href="../settings_shared_css.html">
+
+<dom-module id="settings-captions">
+  <template>
+    <style include="settings-shared"></style>
+    <div class="settings-box">
+      <div class="start">$i18n{captionsTextSize}</div>
+      <settings-dropdown-menu id="captionsTextSize"
+          label="$i18n{captionsTextSize}"
+          pref="{{prefs.accessibility.captions.text_size}}"
+          menu-options="[[textSizeOptions_]]">
+      </settings-dropdown-menu>
+    </div>
+    <div class="settings-box">
+      <div class="start">$i18n{captionsTextFont}</div>
+      <settings-dropdown-menu id="captionsTextFont"
+          label="$i18n{captionsTextFont}"
+          pref="{{prefs.accessibility.captions.text_font}}"
+          menu-options="[[textFontOptions_]]">
+      </settings-dropdown-menu>
+    </div>
+    <div class="settings-box">
+      <div class="start">$i18n{captionsTextColor}</div>
+      <settings-dropdown-menu id="captionsTextColor"
+          label="$i18n{captionsTextColor}"
+          pref="{{prefs.accessibility.captions.text_color}}"
+          menu-options="[[colorOptions_]]">
+      </settings-dropdown-menu>
+    </div>
+    <div class="settings-box">
+      <div class="start">$i18n{captionsTextOpacity}</div>
+      <settings-slider id="captionsTextOpacity"
+          ticks="[[textOpacityRange_]]"
+          label-min="$i18n{captionsOpacityMin}"
+          label-max="$i18n{captionsOpacityMax}"
+          pref="{{prefs.accessibility.captions.text_opacity}}">
+      </settings-slider>
+    </div>
+    <div class="settings-box">
+      <div class="start">$i18n{captionsTextShadow}</div>
+      <settings-dropdown-menu id="captionsTextShadow"
+          label="$i18n{captionsTextShadow}"
+          pref="{{prefs.accessibility.captions.text_shadow}}"
+          menu-options="[[textShadowOptions_]]">
+      </settings-dropdown-menu>
+    </div>
+    <div class="settings-box">
+      <div class="start">$i18n{captionsBackgroundColor}</div>
+      <settings-dropdown-menu id="captionsBackgroundColor"
+          label="$i18n{captionsBackgroundColor}"
+          pref="{{prefs.accessibility.captions.background_color}}"
+          menu-options="[[colorOptions_]]">
+      </settings-dropdown-menu>
+    </div>
+    <div class="settings-box">
+      <div class="start">$i18n{captionsBackgroundOpacity}</div>
+      <settings-slider id="captionsBackgroundOpacity"
+          ticks="[[textOpacityRange_]]"
+          label-min="$i18n{captionsOpacityMin}"
+          label-max="$i18n{captionsOpacityMax}"
+          pref="{{prefs.accessibility.captions.background_opacity}}">
+      </settings-slider>
+    </div>
+    <div class="list-frame">
+      <div class="list-item">
+        <span style="
+              font-size:[[prefs.accessibility.captions.text_size.value]];
+              font-family:[[prefs.accessibility.captions.text_font.value]];
+              background-color: [[computeBackgroundColor_(
+                  prefs.accessibility.captions.background_opacity.value,
+                  prefs.accessibility.captions.background_color.value)]];
+              color: [[computeTextColor_(
+                  prefs.accessibility.captions.text_opacity.value,
+                  prefs.accessibility.captions.text_color.value)]];
+              text-shadow: [[prefs.accessibility.captions.text_shadow.value]];
+              padding: [[computePadding_(
+                  prefs.accessibility.captions.text_size.value)]]">
+          $i18n{quickBrownFox}
+        </span>
+      </div>
+    </div>
+  </template>
+  <script src="captions_subpage.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/a11y_page/captions_subpage.js b/chrome/browser/resources/settings/a11y_page/captions_subpage.js
new file mode 100644
index 0000000..df973af
--- /dev/null
+++ b/chrome/browser/resources/settings/a11y_page/captions_subpage.js
@@ -0,0 +1,217 @@
+// Copyright 2019 The Chromium 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 'settings-captions' is a component for showing captions
+ * settings subpage (chrome://settings/captions).
+ */
+(function() {
+'use strict';
+
+
+/** @type {!Array<number>} */
+const TEXT_OPACITY_RANGE = [
+  0,  5,  10, 15, 20, 25, 30, 35, 40, 45, 50,
+  55, 60, 65, 70, 75, 80, 85, 90, 95, 100
+];
+
+/**
+ * @param {!Array<number>} ticks
+ * @return {!Array<!cr_slider.SliderTick>}
+ */
+function ticksWithLabels(ticks) {
+  return ticks.map(x => ({label: `${x}`, value: x}));
+}
+
+Polymer({
+  is: 'settings-captions',
+
+  behaviors: [I18nBehavior, WebUIListenerBehavior],
+
+  properties: {
+    prefs: {
+      type: Object,
+      notify: true,
+    },
+
+    /**
+     * List of fonts populated by the fonts browser proxy.
+     * @private {!DropdownMenuOptionList} */
+    textFontOptions_: Object,
+
+    /**
+     * Reasonable, text opacity range.
+     * @private {!Array<!cr_slider.SliderTick>}
+     */
+    textOpacityRange_: {
+      readOnly: true,
+      type: Array,
+      value: ticksWithLabels(TEXT_OPACITY_RANGE),
+    },
+
+    /**
+     * List of options for the text size drop-down menu.
+     * @type {!DropdownMenuOptionList}
+     */
+    textSizeOptions_: {
+      readOnly: true,
+      type: Array,
+      value: function() {
+        return [
+          {value: '50%', name: loadTimeData.getString('verySmall')},
+          {value: '75%', name: loadTimeData.getString('small')},
+          {value: '', name: loadTimeData.getString('medium')}, // Default = 100%
+          {value: '150%', name: loadTimeData.getString('large')},
+          {value: '200%', name: loadTimeData.getString('veryLarge')},
+        ];
+      },
+    },
+
+    /**
+     * List of options for the color drop-down menu.
+     * @type {!DropdownMenuOptionList}
+     */
+    colorOptions_: {
+      readOnly: true,
+      type: Array,
+      value: function() {
+        return [
+          {value: '', name: loadTimeData.getString('captionsDefaultSetting')},
+          {value: '0,0,0', name: loadTimeData.getString('captionsColorBlack')},
+          {
+            value: '255,255,255',
+            name: loadTimeData.getString('captionsColorWhite')
+          },
+          {value: '255,0,0', name: loadTimeData.getString('captionsColorRed')},
+          {
+            value: '0,255,0',
+            name: loadTimeData.getString('captionsColorGreen')
+          },
+          {value: '0,0,255', name: loadTimeData.getString('captionsColorBlue')},
+          {
+            value: '255,255,0',
+            name: loadTimeData.getString('captionsColorYellow')
+          },
+          {
+            value: '0,255,255',
+            name: loadTimeData.getString('captionsColorCyan')
+          },
+          {
+            value: '255,0,255',
+            name: loadTimeData.getString('captionsColorMagenta')
+          },
+        ];
+      },
+    },
+
+    /**
+     * List of options for the text shadow drop-down menu.
+     * @type {!DropdownMenuOptionList}
+     */
+    textShadowOptions_: {
+      readOnly: true,
+      type: Array,
+      value: function() {
+        return [
+          {value: '', name: loadTimeData.getString('captionsTextShadowNone')},
+          {
+            value: '-2px -2px 4px rgba(0, 0, 0, 0.5)',
+            name: loadTimeData.getString('captionsTextShadowRaised')
+          },
+          {
+            value: '2px 2px 4px rgba(0, 0, 0, 0.5)',
+            name: loadTimeData.getString('captionsTextShadowDepressed')
+          },
+          {
+            value: '-1px 0px 0px black, ' +
+                '0px -1px 0px black, 1px 0px 0px black, 0px  1px 0px black',
+            name: loadTimeData.getString('captionsTextShadowUniform')
+          },
+          {
+            value: '0px 0px 2px rgba(0, 0, 0, 0.5), 2px 2px 2px black',
+            name: loadTimeData.getString('captionsTextShadowDropShadow')
+          },
+        ];
+      },
+    },
+  },
+
+  /** @private {?settings.FontsBrowserProxy} */
+  browserProxy_: null,
+
+  /** @override */
+  created: function() {
+    this.browserProxy_ = settings.FontsBrowserProxyImpl.getInstance();
+  },
+
+  /** @override */
+  ready: function() {
+    this.browserProxy_.observeAdvancedFontExtensionAvailable();
+
+    this.browserProxy_.fetchFontsData().then(this.setFontsData_.bind(this));
+  },
+
+  /**
+   * @param {!FontsData} response A list of fonts.
+   * @private
+   */
+  setFontsData_: function(response) {
+    const fontMenuOptions =
+        [{value: '', name: loadTimeData.getString('captionsDefaultSetting')}];
+    for (const fontData of response.fontList) {
+      fontMenuOptions.push({value: fontData[0], name: fontData[1]});
+    }
+    this.textFontOptions_ = fontMenuOptions;
+  },
+
+  /**
+   * Get the background color as a RGBA string.
+   * @return {string}
+   * @private
+   */
+  computeBackgroundColor_: function() {
+    return this.formatRGAString_(
+        'prefs.accessibility.captions.background_color.value',
+        'prefs.accessibility.captions.background_opacity.value');
+  },
+
+  /**
+   * Get the text color as a RGBA string.
+   * @return {string}
+   * @private
+   */
+  computeTextColor_: function() {
+    return this.formatRGAString_(
+        'prefs.accessibility.captions.text_color.value',
+        'prefs.accessibility.captions.text_opacity.value');
+  },
+
+  /**
+   * Formats the color as an RGBA string.
+   * @param {string} colorPreference The name of the preference containing the
+   * RGB values as a comma-separated string.
+   * @param {string} opacityPreference The name of the preference containing
+   * the opacity value as a percentage.
+   * @return {string} The formatted RGBA string.
+   * @private
+   */
+  formatRGAString_: function(colorPreference, opacityPreference) {
+    return 'rgba(' + this.get(colorPreference) + ',' +
+        parseInt(this.get(opacityPreference), 10) / 100.0 + ')';
+  },
+
+  /**
+   * @param {string} size The font size of the captions text as a percentage.
+   * @return {string} The padding around the captions text as a percentage.
+   * @private
+   */
+  computePadding_: function(size) {
+    if (size == '') {
+      return '1%';
+    }
+
+    return `${+size.slice(0, -1) / 100}%`;
+  }
+});
+})();
diff --git a/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html b/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html
index 409a9582..ac2e45a 100644
--- a/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html
+++ b/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html
@@ -1,7 +1,7 @@
 <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/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../controls/settings_dropdown_menu.html">
 <link rel="import" href="../controls/settings_slider.html">
 <link rel="import" href="../controls/settings_toggle_button.html">
diff --git a/chrome/browser/resources/settings/a11y_page/tts_subpage.html b/chrome/browser/resources/settings/a11y_page/tts_subpage.html
index dcf7a58..78606eb 100644
--- a/chrome/browser/resources/settings/a11y_page/tts_subpage.html
+++ b/chrome/browser/resources/settings/a11y_page/tts_subpage.html
@@ -5,7 +5,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="../controls/settings_slider.html">
 <link rel="import" href="../i18n_setup.html">
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html
index 9656740c..17e2388 100644
--- a/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -103,15 +103,19 @@
               src URL -->
             <!-- Set the icon from the iconset (when it's obsolete/EOL and
               when update is done) or set the src (when it's updating). -->
+<if expr="not chromeos">
             <div class="icon-container"
                 hidden="[[!shouldShowIcons_(showUpdateStatus_)]]">
               <iron-icon
-<if expr="not chromeos">
                   icon$="[[getUpdateStatusIcon_(
                       obsoleteSystemInfo_, currentUpdateStatusEvent_)]]"
                   src="[[getThrobberSrcIfUpdating_(obsoleteSystemInfo_, currentUpdateStatusEvent_)]]">
 </if>
 <if expr="chromeos">
+            <div class="icon-container"
+                hidden="[[!shouldShowIcons_(showUpdateStatus_,
+                    showOsSettings_)]]">
+              <iron-icon
                   icon$="[[getUpdateStatusIcon_(
                       hasEndOfLife_, currentUpdateStatusEvent_)]]"
                   src="[[getThrobberSrcIfUpdating_(hasEndOfLife_, currentUpdateStatusEvent_)]]">
@@ -223,7 +227,9 @@
 <if expr="chromeos">
           <cr-link-row class="hr" id="detailed-build-info-trigger"
               on-click="onDetailedBuildInfoTap_"
-              label="$i18n{aboutDetailedBuildInfo}"></cr-link-row>
+              label="$i18n{aboutDetailedBuildInfo}"
+              hidden$="[[!showOsSettings_]]">
+          </cr-link-row>
 </if>
           <cr-link-row class="hr" on-click="onManagementPageTap_"
               start-icon="cr:domain" label="$i18n{managementPage}"
@@ -249,10 +255,9 @@
         <div class="info-section">
           <div class="secondary">$i18nRaw{aboutProductLicense}</div>
 <if expr="chromeos">
-          <div class="secondary"
+          <div id="crostiniLicense" class="secondary"
               inner-h-t-m-l="[[getAboutProductOsLicense_(
-                  showCrostiniLicense_)]]">
-          </div>
+                  showCrostiniLicense_)]]" hidden$="[[!showOsSettings_]]"></div>
 </if>
         </div>
 <if expr="_google_chrome">
@@ -261,7 +266,8 @@
       </div>
 <if expr="chromeos">
       <div class="settings-box padded block" id="regulatoryInfo"
-          hidden$="[[!shouldShowRegulatoryOrSafetyInfo_(regulatoryInfo_)]]">
+          hidden$="[[!shouldShowRegulatoryOrSafetyInfo_(regulatoryInfo_,
+              showOsSettings_)]]">
 <if expr="_google_chrome">
         <div class="secondary" hidden$="[[!shouldShowSafetyInfo_()]]">
           <a target="_blank" href="$i18n{aboutProductSafetyURL}">
diff --git a/chrome/browser/resources/settings/about_page/about_page.js b/chrome/browser/resources/settings/about_page/about_page.js
index 437e286..889229d 100644
--- a/chrome/browser/resources/settings/about_page/about_page.js
+++ b/chrome/browser/resources/settings/about_page/about_page.js
@@ -63,9 +63,20 @@
       value: false,
     },
 
-    /** @private */
     showCrostini: Boolean,
 
+    /**
+     * When the SplitSettings feature is disabled, the about page shows the OS-
+     * specific parts. When SplitSettings is enabled, the OS-specific parts
+     * will only show up in chrome://os-settings/help.
+     * TODO(aee): remove after SplitSettings feature flag is removed.
+     * @private
+     */
+    showOsSettings_: {
+      type: Boolean,
+      value: () => loadTimeData.getBoolean('showOSSettings'),
+    },
+
     /** @private */
     showCrostiniLicense_: {
       type: Boolean,
@@ -191,6 +202,10 @@
         settings.LifetimeBrowserProxyImpl.getInstance();
 
     // <if expr="chromeos">
+    if (!this.showOsSettings_) {
+      return;
+    }
+
     this.addEventListener('target-channel-changed', e => {
       this.targetChannel_ = e.detail;
     });
@@ -620,7 +635,8 @@
    * @private
    */
   shouldShowRegulatoryOrSafetyInfo_: function() {
-    return this.shouldShowSafetyInfo_() || this.shouldShowRegulatoryInfo_();
+    return this.showOsSettings_ &&
+        (this.shouldShowSafetyInfo_() || this.shouldShowRegulatoryInfo_());
   },
 
   /** @private */
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chrome/browser/resources/settings/appearance_page/appearance_page.html
index fd6ed00a..584f47f 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.html
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="appearance_browser_proxy.html">
 <link rel="import" href="../controls/controlled_radio_button.html">
 <link rel="import" href="../controls/extension_controlled_indicator.html">
diff --git a/chrome/browser/resources/settings/autofill_page/address_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/address_edit_dialog.html
index a8d2308..ea5962b 100644
--- a/chrome/browser/resources/settings/autofill_page/address_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/address_edit_dialog.html
@@ -6,7 +6,7 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../settings_shared_css.html">
 <link rel="import" href="../settings_vars_css.html">
 <link rel="import" href="../controls/settings_textarea.html">
diff --git a/chrome/browser/resources/settings/autofill_page/blocking_request_manager.js b/chrome/browser/resources/settings/autofill_page/blocking_request_manager.js
index 69d4eb6..b82aa59 100644
--- a/chrome/browser/resources/settings/autofill_page/blocking_request_manager.js
+++ b/chrome/browser/resources/settings/autofill_page/blocking_request_manager.js
@@ -8,9 +8,13 @@
  */
 cr.define('settings', function() {
   class BlockingRequestManager {
-    /** @param {Function} makeRequest Function to initiate flow for request. */
+    /**
+     * @param {Function=} makeRequest Function to initiate flow for request. If
+     *     no function is provided, it defaults to this.resolve, i.e. it
+     *     immediately resolves all requests.
+     */
     constructor(makeRequest) {
-      this.makeRequest_ = makeRequest;
+      this.makeRequest_ = makeRequest || this.resolve;
       /**
        * @private {Function} callback Provided in requests and called when the
        *     request is resolved.
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html
index 499ce774..79f81ebe 100644
--- a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html
@@ -5,7 +5,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../settings_shared_css.html">
 <link rel="import" href="../settings_vars_css.html">
 
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
index 1190eed..6cb9d2b 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
@@ -35,18 +35,16 @@
         justify-content: initial;
       }
 
-      #passwordInput {
-        --cr-input-input: {
+      #passwordInput::part(input) {
 <if expr="chromeos or is_linux">
-          font-family: 'DejaVu Sans Mono', monospace;
+        font-family: 'DejaVu Sans Mono', monospace;
 </if>
 <if expr="is_win">
-          font-family: 'Consolas', monospace;
+        font-family: 'Consolas', monospace;
 </if>
 <if expr="is_macosx">
-          font-family: 'Menlo', monospace;
+        font-family: 'Menlo', monospace;
 </if>
-        }
       }
 
       cr-icon-button {
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.js b/chrome/browser/resources/settings/autofill_page/passwords_section.js
index 1603dc8..1f552fd 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.js
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.js
@@ -202,8 +202,16 @@
     this.passwordManager_ = PasswordManagerImpl.getInstance();
 
     // <if expr="chromeos">
-    this.tokenRequestManager_ = new settings.BlockingRequestManager(
-        () => this.showPasswordPromptDialog_ = true);
+    // If the user's account supports the password check, an auth token will be
+    // required in order for them to view or export passwords. Otherwise there
+    // is no additional security so |tokenRequestManager_| will immediately
+    // resolve requests.
+    if (loadTimeData.getBoolean('userCannotManuallyEnterPassword')) {
+      this.tokenRequestManager_ = new settings.BlockingRequestManager();
+    } else {
+      this.tokenRequestManager_ = new settings.BlockingRequestManager(
+          () => this.showPasswordPromptDialog_ = true);
+    }
     // </if>
 
     // Request initial data.
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 4991d67..33f3634 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -63,6 +63,8 @@
 group("closure_compile") {
   deps = [
     "os_a11y_page:closure_compile",
+    "os_downloads_page:closure_compile",
+    "os_languages_page:closure_compile",
     "os_people_page:closure_compile",
     "os_privacy_page:closure_compile",
     "os_reset_page:closure_compile",
diff --git a/chrome/browser/resources/settings/chromeos/os_downloads_page/smb_shares_page.html b/chrome/browser/resources/settings/chromeos/os_downloads_page/smb_shares_page.html
index 0370a60..41c6d99 100644
--- a/chrome/browser/resources/settings/chromeos/os_downloads_page/smb_shares_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_downloads_page/smb_shares_page.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="../../route.html">
 <link rel="import" href="../../settings_shared_css.html">
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_languages_page/BUILD.gn
index 91a4040..f0511cce 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/BUILD.gn
@@ -41,7 +41,7 @@
     ":os_languages",
     "../..:lifetime_browser_proxy",
     "../..:route",
-    "../../langauges_page:languages_types",
+    "../../languages_page:languages_types",
     "../../settings_page:settings_animated_pages",
     "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
     "//ui/webui/resources/cr_elements/cr_expand_button:cr_expand_button",
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages.js b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages.js
index c9a18e6..20cee18 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages.js
@@ -17,29 +17,11 @@
 
 const MoveType = chrome.languageSettingsPrivate.MoveType;
 
-// Translate server treats some language codes the same.
-// See also: components/translate/core/common/translate_util.cc.
-const kLanguageCodeToTranslateCode = {
-  'nb': 'no',
-  'fil': 'tl',
-  'zh-HK': 'zh-TW',
-  'zh-MO': 'zh-TW',
-  'zh-SG': 'zh-CN',
-};
-
-// Some ISO 639 language codes have been renamed, e.g. "he" to "iw", but
-// Translate still uses the old versions. TODO(michaelpg): Chrome does too.
-// Follow up with Translate owners to understand the right thing to do.
-const kTranslateLanguageSynonyms = {
-  'he': 'iw',
-  'jv': 'jw',
-};
-
 // The fake language name used for ARC IMEs. The value must be in sync with the
 // one in ui/base/ime/chromeos/extension_ime_util.h.
 const kArcImeLanguage = '_arc_ime_language_';
 
-const preferredLanguagesPrefName = 'settings.language.preferred_languages';
+const kPreferredLanguagesPrefName = 'settings.language.preferred_languages';
 
 /**
  * Singleton element that generates the languages model on start-up and
@@ -140,9 +122,7 @@
     // |languages| property.
     'prospectiveUILanguageChanged_(prefs.intl.app_locale.value, languages)',
     'preferredLanguagesPrefChanged_(' +
-        'prefs.' + preferredLanguagesPrefName + '.value, languages)',
-    'translateLanguagesPrefChanged_(' +
-        'prefs.translate_blocked_languages.value.*, languages)',
+        'prefs.' + kPreferredLanguagesPrefName + '.value, languages)',
     'updateRemovableLanguages_(' +
         'prefs.intl.app_locale.value, languages.enabled)',
     'updateRemovableLanguages_(' +
@@ -182,28 +162,23 @@
 
     // Wait until prefs are initialized before creating the model, so we can
     // include information about enabled languages.
-    promises[0] = CrSettingsPrefs.initialized;
+    promises.push(CrSettingsPrefs.initialized);
 
     // Get the language list.
-    promises[1] = new Promise(resolve => {
+    promises.push(new Promise(resolve => {
       this.languageSettingsPrivate_.getLanguageList(resolve);
-    });
+    }));
 
-    // Get the translate target language.
-    promises[2] = new Promise(resolve => {
-      this.languageSettingsPrivate_.getTranslateTargetLanguage(resolve);
-    });
-
-    promises[3] = new Promise(resolve => {
+    promises.push(new Promise(resolve => {
       this.languageSettingsPrivate_.getInputMethodLists(function(lists) {
         resolve(
             lists.componentExtensionImes.concat(lists.thirdPartyExtensionImes));
       });
-    });
+    }));
 
-    promises[4] = new Promise(resolve => {
+    promises.push(new Promise(resolve => {
       this.inputMethodPrivate_.getCurrentInputMethod(resolve);
-    });
+    }));
 
     // Fetch the starting UI language, which affects which actions should be
     // enabled.
@@ -220,7 +195,9 @@
         return;
       }
 
-      this.createModel_(results[1], results[2], results[3], results[4]);
+      // Skip results[0].
+      const [, languages, inputMethods, currentInputMethod] = results;
+      this.createModel_(languages, inputMethods, currentInputMethod);
       this.resolver_.resolve();
     });
 
@@ -268,8 +245,8 @@
       return;
     }
 
-    const enabledLanguageStates = this.getEnabledLanguageStates_(
-        this.languages.translateTarget, this.languages.prospectiveUILanguage);
+    const enabledLanguageStates =
+        this.getEnabledLanguageStates_(this.languages.prospectiveUILanguage);
 
     // Recreate the enabled language set before updating languages.enabled.
     this.enabledLanguageSet_.clear();
@@ -278,41 +255,12 @@
     }
 
     this.set('languages.enabled', enabledLanguageStates);
-
-    // Update translate target language.
-    new Promise(resolve => {
-      this.languageSettingsPrivate_.getTranslateTargetLanguage(resolve);
-    }).then(result => {
-      this.set('languages.translateTarget', result);
-    });
-  },
-
-  /** @private */
-  translateLanguagesPrefChanged_: function() {
-    if (this.prefs == undefined || this.languages == undefined) {
-      return;
-    }
-
-    const translateBlockedPref = this.getPref('translate_blocked_languages');
-    const translateBlockedSet = this.makeSetFromArray_(
-        /** @type {!Array<string>} */ (translateBlockedPref.value));
-
-    for (let i = 0; i < this.languages.enabled.length; i++) {
-      const language = this.languages.enabled[i].language;
-      const translateEnabled = this.isTranslateEnabled_(
-          language.code, !!language.supportsTranslate, translateBlockedSet,
-          this.languages.translateTarget, this.languages.prospectiveUILanguage);
-      this.set(
-          'languages.enabled.' + i + '.translateEnabled', translateEnabled);
-    }
   },
 
   /**
    * Constructs the languages model.
    * @param {!Array<!chrome.languageSettingsPrivate.Language>}
    *     supportedLanguages
-   * @param {string} translateTarget Language code of the default translate
-   *     target language.
    * @param {!Array<!chrome.languageSettingsPrivate.InputMethod>}
    *     supportedInputMethods Input methods.
    * @param {string} currentInputMethodId ID of the currently used
@@ -320,8 +268,7 @@
    * @private
    */
   createModel_: function(
-      supportedLanguages, translateTarget, supportedInputMethods,
-      currentInputMethodId) {
+      supportedLanguages, supportedInputMethods, currentInputMethodId) {
     // Populate the hash map of supported languages.
     for (let i = 0; i < supportedLanguages.length; i++) {
       const language = supportedLanguages[i];
@@ -341,7 +288,7 @@
 
     // Create a list of enabled languages from the supported languages.
     const enabledLanguageStates =
-        this.getEnabledLanguageStates_(translateTarget, prospectiveUILanguage);
+        this.getEnabledLanguageStates_(prospectiveUILanguage);
     // Populate the hash set of enabled languages.
     for (let l = 0; l < enabledLanguageStates.length; l++) {
       this.enabledLanguageSet_.add(enabledLanguageStates[l].language.code);
@@ -350,7 +297,6 @@
     const model = /** @type {!LanguagesModel} */ ({
       supported: supportedLanguages,
       enabled: enabledLanguageStates,
-      translateTarget: translateTarget,
     });
 
     model.prospectiveUILanguage = prospectiveUILanguage;
@@ -399,23 +345,17 @@
   /**
    * Returns a list of LanguageStates for each enabled language in the supported
    * languages list.
-   * @param {string} translateTarget Language code of the default translate
-   *     target language.
    * @param {(string|undefined)} prospectiveUILanguage Prospective UI display
    *     language.
    * @return {!Array<!LanguageState>}
    * @private
    */
-  getEnabledLanguageStates_: function(translateTarget, prospectiveUILanguage) {
+  getEnabledLanguageStates_: function(prospectiveUILanguage) {
     assert(CrSettingsPrefs.isInitialized);
 
-    const pref = this.getPref(preferredLanguagesPrefName);
+    const pref = this.getPref(kPreferredLanguagesPrefName);
     const enabledLanguageCodes = pref.value.split(',');
 
-    const translateBlockedPref = this.getPref('translate_blocked_languages');
-    const translateBlockedSet = this.makeSetFromArray_(
-        /** @type {!Array<string>} */ (translateBlockedPref.value));
-
     const enabledLanguageStates = [];
     for (let i = 0; i < enabledLanguageCodes.length; i++) {
       const code = enabledLanguageCodes[i];
@@ -426,38 +366,15 @@
       }
       const languageState = /** @type {LanguageState} */ ({});
       languageState.language = language;
-      languageState.translateEnabled = this.isTranslateEnabled_(
-          code, !!language.supportsTranslate, translateBlockedSet,
-          translateTarget, prospectiveUILanguage);
+      // TODO(crbug.com/967965): Skip this field and don't use LanguageState.
+      // Just return an array of string language codes.
+      languageState.translateEnabled = false;
       enabledLanguageStates.push(languageState);
     }
     return enabledLanguageStates;
   },
 
   /**
-   * True iff we translate pages that are in the given language.
-   * @param {string} code Language code.
-   * @param {boolean} supportsTranslate If translation supports the given
-   *     language.
-   * @param {!Set<string>} translateBlockedSet Set of languages for which
-   *     translation is blocked.
-   * @param {string} translateTarget Language code of the default translate
-   *     target language.
-   * @param {(string|undefined)} prospectiveUILanguage Prospective UI display
-   *     language.
-   * @return {boolean}
-   * @private
-   */
-  isTranslateEnabled_: function(
-      code, supportsTranslate, translateBlockedSet, translateTarget,
-      prospectiveUILanguage) {
-    const translateCode = this.convertLanguageCodeForTranslate(code);
-    return supportsTranslate && !translateBlockedSet.has(translateCode) &&
-        translateCode != translateTarget &&
-        (!prospectiveUILanguage || code != prospectiveUILanguage);
-  },
-
-  /**
    * Returns a list of enabled input methods.
    * @return {!Array<!chrome.languageSettingsPrivate.InputMethod>}
    * @private
@@ -545,6 +462,8 @@
   // LanguageHelper implementation.
   // TODO(michaelpg): replace duplicate docs with @override once b/24294625
   // is fixed.
+  // TODO(crbug.com/967965): Introduce a smaller interface class for use by the
+  // OS add language dialog and don't implement LanguageHelper.
 
   /** @return {!Promise} */
   whenReady: function() {
@@ -630,13 +549,13 @@
   },
 
   /**
+   * TODO(crbug.com/967965): Stop implementing the LanguageHelper interface and
+   * remove this method.
    * @param {!LanguageState} languageState
    * @return {boolean}
    */
   isOnlyTranslateBlockedLanguage: function(languageState) {
-    return !languageState.translateEnabled &&
-        this.languages.enabled.filter(lang => !lang.translateEnabled).length ==
-        1;
+    assertNotReached();
   },
 
   /**
@@ -654,11 +573,6 @@
       return false;
     }
 
-    // Cannot disable the last translate blocked language.
-    if (this.isOnlyTranslateBlockedLanguage(languageState)) {
-      return false;
-    }
-
     // If this is the only enabled language that is supported by all enabled
     // component IMEs, it cannot be disabled because we need those IMEs.
     const otherInputMethodsEnabled =
@@ -722,46 +636,36 @@
   /**
    * Enables translate for the given language by removing the translate
    * language from the blocked languages preference.
+   * TODO(crbug.com/967965): Stop implementing the LanguageHelper interface and
+   * remove this method.
    * @param {string} languageCode
    */
   enableTranslateLanguage: function(languageCode) {
-    this.languageSettingsPrivate_.setEnableTranslationForLanguage(
-        languageCode, true);
+    assertNotReached();
   },
 
   /**
    * Disables translate for the given language by adding the translate
    * language to the blocked languages preference.
+   * TODO(crbug.com/967965): Stop implementing the LanguageHelper interface and
+   * remove this method.
    * @param {string} languageCode
    */
   disableTranslateLanguage: function(languageCode) {
-    this.languageSettingsPrivate_.setEnableTranslationForLanguage(
-        languageCode, false);
+    assertNotReached();
   },
 
   /**
    * Converts the language code for translate. There are some differences
    * between the language set the Translate server uses and that for
    * Accept-Language.
+   * TODO(crbug.com/967965): Stop implementing the LanguageHelper interface and
+   * remove this method.
    * @param {string} languageCode
    * @return {string} The converted language code.
    */
   convertLanguageCodeForTranslate: function(languageCode) {
-    if (languageCode in kLanguageCodeToTranslateCode) {
-      return kLanguageCodeToTranslateCode[languageCode];
-    }
-
-    const main = languageCode.split('-')[0];
-    if (main == 'zh') {
-      // In Translate, general Chinese is not used, and the sub code is
-      // necessary as a language code for the Translate server.
-      return languageCode;
-    }
-    if (main in kTranslateLanguageSynonyms) {
-      return kTranslateLanguageSynonyms[main];
-    }
-
-    return main;
+    assertNotReached();
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.html b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.html
index 9124cec4..174991b 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.html
@@ -6,7 +6,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
@@ -81,8 +81,7 @@
         margin-inline-end: 0;
       }
 
-      #uiLanguageItem:focus,
-      #offerTranslations:focus {
+      #uiLanguageItem:focus {
         background-color: transparent;
       }
 
diff --git a/chrome/browser/resources/settings/controls/settings_dropdown_menu.html b/chrome/browser/resources/settings/controls/settings_dropdown_menu.html
index f842fca2..39d0615a 100644
--- a/chrome/browser/resources/settings/controls/settings_dropdown_menu.html
+++ b/chrome/browser/resources/settings/controls/settings_dropdown_menu.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
 <link rel="import" href="pref_control_behavior.html">
diff --git a/chrome/browser/resources/settings/device_page/display.html b/chrome/browser/resources/settings/device_page/display.html
index c7ca073d..5b906f0 100644
--- a/chrome/browser/resources/settings/device_page/display.html
+++ b/chrome/browser/resources/settings/device_page/display.html
@@ -8,7 +8,7 @@
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="display_layout.html">
 <link rel="import" href="display_overscan_dialog.html">
diff --git a/chrome/browser/resources/settings/device_page/power.html b/chrome/browser/resources/settings/device_page/power.html
index 1bc1d4b8..8df6cf9f 100644
--- a/chrome/browser/resources/settings/device_page/power.html
+++ b/chrome/browser/resources/settings/device_page/power.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="../controls/settings_toggle_button.html">
 <link rel="import" href="../route.html">
diff --git a/chrome/browser/resources/settings/downloads_page/smb_shares_page.html b/chrome/browser/resources/settings/downloads_page/smb_shares_page.html
index 6ab41385..a5dfd95 100644
--- a/chrome/browser/resources/settings/downloads_page/smb_shares_page.html
+++ b/chrome/browser/resources/settings/downloads_page/smb_shares_page.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="../route.html">
 <link rel="import" href="../settings_shared_css.html">
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
index b6f30b7..06db35a 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="google_assistant_browser_proxy.html">
 <link rel="import" href="../controls/controlled_button.html">
 <link rel="import" href="../controls/settings_toggle_button.html">
diff --git a/chrome/browser/resources/settings/internet_page/BUILD.gn b/chrome/browser/resources/settings/internet_page/BUILD.gn
index a5443e5..936eea4 100644
--- a/chrome/browser/resources/settings/internet_page/BUILD.gn
+++ b/chrome/browser/resources/settings/internet_page/BUILD.gn
@@ -96,10 +96,10 @@
     ":internet_page_browser_proxy",
     "..:route",
     "//ui/webui/resources/cr_elements/chromeos/network:cr_network_listener_behavior",
-    "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types",
     "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior",
     "//ui/webui/resources/js:assert",
     "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js/chromeos:onc_mojo",
   ]
   externs_list = [ "$externs_path/networking_private.js" ]
   extra_sources = [ "$interfaces_path/networking_private_interface.js" ]
@@ -124,6 +124,7 @@
     "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types",
     "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior",
     "//ui/webui/resources/js:assert",
+    "//ui/webui/resources/js/chromeos:onc_mojo",
   ]
   extra_sources = [ "$interfaces_path/networking_private_interface.js" ]
 }
@@ -134,6 +135,7 @@
     "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior",
     "//ui/webui/resources/js:assert",
     "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js/chromeos:onc_mojo",
   ]
   extra_sources = [ "$interfaces_path/networking_private_interface.js" ]
 }
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.html b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
index bdd1b9e..a49a1d3 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
@@ -32,7 +32,7 @@
 
 <dom-module id="settings-internet-detail-page">
   <template>
-    <style include="internet-shared iron-flex">
+    <style include="internet-shared settings-shared iron-flex">
       :host {
         padding-bottom: 40px;
       }
@@ -66,13 +66,17 @@
       paper-spinner-lite {
         @apply --cr-icon-height-width;
       }
+      .warning {
+          color: var(--cr-secondary-text-color);
+          margin-inline-start: var(--settings-controlled-by-spacing);
+      }
     </style>
     <!-- Title section: Icon + name + connection state. -->
     <div id="titleDiv" class="settings-box first">
       <div class="start layout horizontal center">
         <cr-network-icon
             show-technology-badge="[[showTechnologyBadge_]]"
-            network-state="[[networkProperties_]]">
+            network-state="[[getNetworkState_(networkProperties_)]]">
         </cr-network-icon>
         <div id="networkState" class="title settings-box-text"
             connected$="[[isConnectedState_(networkProperties_)]]"
@@ -184,6 +188,16 @@
             pref="{{autoConnect_}}"
             label="[[getAutoConnectToggleLabel_(networkProperties_)]]">
         </settings-toggle-button>
+        <!-- Hidden Network Warning -->
+        <template is="dom-if"
+            if="[[showHiddenNetworkWarning_(autoConnect_.*,
+                networkProperties_)]]"
+            restamp>
+          <div class="warning">
+            <iron-icon icon="cr:warning"></iron-icon>
+            [[i18n('hiddenNetworkWarning')]]
+          </div>
+        </template>
       </template>
       <!-- Always-on VPN. -->
       <template is="dom-if"
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index 48d5f42..2931475 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -81,7 +81,7 @@
 
     /**
      * Highest priority connected network or null.
-     * @type {?CrOnc.NetworkStateProperties}
+     * @type {?OncMojo.NetworkStateProperties}
      */
     defaultNetwork: {
       type: Object,
@@ -203,9 +203,20 @@
   /** @private  {settings.InternetPageBrowserProxy} */
   browserProxy_: null,
 
+  /**
+   * This UI will use both the networkingPrivate extension API and the
+   * networkConfig mojo API until we provide all of the required functionality
+   * in networkConfig. TODO(stevenjb): Remove use of networkingPrivate api.
+   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy}
+   */
+  networkConfigProxy_: null,
+
   /** @override */
   created: function() {
     this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance();
+    this.networkConfigProxy_ =
+        network_config.MojoInterfaceProviderImpl.getInstance()
+            .getMojoServiceProxy();
   },
 
   /**
@@ -442,8 +453,9 @@
   getNetworkDetails_: function() {
     assert(this.guid);
     if (this.isSecondaryUser_) {
-      this.networkingPrivate.getState(
-          this.guid, this.getStateCallback_.bind(this));
+      this.networkConfigProxy_.getNetworkState(this.guid).then(response => {
+        this.getStateCallback_(response.result);
+      });
     } else {
       this.networkingPrivate.getManagedProperties(
           this.guid, this.getPropertiesCallback_.bind(this));
@@ -492,27 +504,39 @@
   },
 
   /**
-   * networkingPrivate.getState callback.
-   * @param {CrOnc.NetworkStateProperties} state The network state properties.
+   * @param {?OncMojo.NetworkStateProperties} networkState
    * @private
    */
-  getStateCallback_: function(state) {
-    if (!state) {
+  getStateCallback_: function(networkState) {
+    if (!networkState) {
       // Edge case, may occur when disabling. Close this.
       this.close();
       return;
     }
+    const type = /** @type {CrOnc.Type} */ (
+        OncMojo.getNetworkTypeString(networkState.type));
+    const connectionState = /** @type {CrOnc.ConnectionState} */ (
+        OncMojo.getConnectionStateTypeString(networkState.connectionState));
     this.networkProperties_ = {
-      GUID: state.GUID,
-      Type: state.Type,
-      Connectable: state.Connectable,
-      ConnectionState: state.ConnectionState,
+      GUID: networkState.guid,
+      Type: type,
+      Connectable: networkState.connectable,
+      ConnectionState: connectionState,
     };
     this.networkPropertiesReceived_ = true;
     this.outOfRange_ = false;
   },
 
   /**
+   * @param {!CrOnc.NetworkProperties} properties
+   * @return {!OncMojo.NetworkStateProperties|undefined}
+   */
+  getNetworkState_: function(properties) {
+    return properties ? OncMojo.oncPropertiesToNetworkState(properties) :
+                        undefined;
+  },
+
+  /**
    * @param {!chrome.networkingPrivate.NetworkConfigProperties} onc The ONC
    *     network properties.
    * @private
@@ -850,7 +874,7 @@
 
   /**
    * @param {!CrOnc.NetworkProperties} networkProperties
-   * @param {?CrOnc.NetworkStateProperties} defaultNetwork
+   * @param {?OncMojo.NetworkStateProperties} defaultNetwork
    * @param {boolean} networkPropertiesReceived
    * @param {boolean} outOfRange
    * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy
@@ -940,20 +964,33 @@
 
   /** @private */
   onConnectTap_: function() {
-    if (CrOnc.shouldShowTetherDialogBeforeConnection(this.networkProperties_)) {
+    // For Tether networks that have not connected to a host, show a dialog.
+    if (this.networkProperties_.Type == CrOnc.Type.TETHER &&
+        (!this.networkProperties_.Tether ||
+         !this.networkProperties_.Tether.HasConnectedToHost)) {
       this.showTetherDialog_();
       return;
     }
-    this.fire('network-connect', {networkProperties: this.networkProperties_});
+    this.fireNetworkConnect_(/*bypassDialog=*/ false);
   },
 
   /** @private */
   onTetherConnect_: function() {
     this.getTetherDialog_().close();
-    this.fire('network-connect', {
-      networkProperties: this.networkProperties_,
-      bypassConnectionDialog: true
-    });
+    this.fireNetworkConnect_(/*bypassDialog=*/ true);
+  },
+
+  /**
+   * @param {boolean} bypassDialog
+   * @private
+   */
+  fireNetworkConnect_: function(bypassDialog) {
+    assert(this.networkProperties_);
+    const networkState =
+        OncMojo.oncPropertiesToNetworkState(this.networkProperties_);
+    this.fire(
+        'network-connect',
+        {networkState: networkState, bypassConnectionDialog: bypassDialog});
   },
 
   /** @private */
@@ -982,7 +1019,11 @@
       return;
     }
 
-    this.fire('show-config', this.networkProperties_);
+    this.fire('show-config', {
+      guid: this.networkProperties_.GUID,
+      type: this.networkProperties_.Type,
+      name: CrOnc.getNetworkName(this.networkProperties_)
+    });
   },
 
   /** @private */
@@ -1000,6 +1041,17 @@
   },
 
   /**
+   * @return {boolean}
+   * @private
+   */
+  showHiddenNetworkWarning_: function() {
+    return loadTimeData.getBoolean('showHiddenNetworkWarning') &&
+        !!this.autoConnect_ && !!this.autoConnect_.value &&
+        !!this.networkProperties_ && !!this.networkProperties_.WiFi &&
+        !!CrOnc.getActiveValue(this.networkProperties_.WiFi.HiddenSSID);
+  },
+
+  /**
    * Event triggered for elements associated with network properties.
    * @param {!CustomEvent<!{
    *     field: string,
diff --git a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
index c9db3283..42dcf86 100644
--- a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
@@ -34,13 +34,12 @@
       <template is="dom-repeat" items="[[networkStateList_]]"
           filter="networkIsPreferred_">
         <div class="list-item">
-          <cr-link-row
-              embedded
-              label="[[item.Name]]"
+          <cr-link-row embedded label="[[getNetworkDisplayName_(item)]]"
               on-click="fireShowDetails_">
-            <template is="dom-if" if="[[isPolicySource(item.Source))]]">
+            <template is="dom-if" if="[[isPolicySourceMojo(item.source))]]">
               <cr-policy-indicator on-click="doNothing_"
-                  indicator-type="[[getIndicatorTypeForSource(item.Source)]]">
+                  indicator-type="[[getIndicatorTypeForSourceMojo(
+                      item.source)]]">
               </cr-policy-indicator>
             </template>
           </cr-link-row>
@@ -60,13 +59,12 @@
       <template is="dom-repeat" items="[[networkStateList_]]"
           filter="networkIsNotPreferred_">
         <div class="list-item">
-          <cr-link-row
-              embedded
-              label="[[item.Name]]"
+          <cr-link-row embedded label="[[getNetworkDisplayName_(item)]]"
               on-click="fireShowDetails_">
-            <template is="dom-if" if="[[isPolicySource(item.Source))]]">
+            <template is="dom-if" if="[[isPolicySourceMojo(item.source))]]">
               <cr-policy-indicator on-click="doNothing_"
-                  indicator-type="[[getIndicatorTypeForSource(item.Source)]]">
+                  indicator-type="[[getIndicatorTypeForSourceMojo(
+                      item.source)]]">
               </cr-policy-indicator>
             </template>
           </cr-link-row>
diff --git a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
index b52f897d3..6d613efe 100644
--- a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
@@ -33,7 +33,7 @@
 
     /**
      * List of all network state data for the network type.
-     * @private {!Array<!CrOnc.NetworkStateProperties>}
+     * @private {!Array<!OncMojo.NetworkStateProperties>}
      */
     networkStateList_: {
       type: Array,
@@ -60,6 +60,21 @@
   /** @private {string} */
   selectedGuid_: '',
 
+  /**
+   * This UI will use both the networkingPrivate extension API and the
+   * networkConfig mojo API until we provide all of the required functionality
+   * in networkConfig. TODO(stevenjb): Remove use of networkingPrivate api.
+   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy}
+   */
+  networkConfigProxy_: null,
+
+  /** @override */
+  created: function() {
+    this.networkConfigProxy_ =
+        network_config.MojoInterfaceProviderImpl.getInstance()
+            .getMojoServiceProxy();
+  },
+
   /** CrosNetworkConfigObserver impl */
   onNetworkStateListChanged: function() {
     this.refreshNetworks_();
@@ -80,32 +95,32 @@
       return;
     }
     const filter = {
-      networkType: this.networkType,
-      visible: false,
-      configured: true
+      filter: chromeos.networkConfig.mojom.FilterType.kConfigured,
+      limit: chromeos.networkConfig.mojom.kNoLimit,
+      networkType: OncMojo.getNetworkTypeFromString(this.networkType),
     };
-    this.networkingPrivate.getNetworks(filter, states => {
-      this.networkStateList_ = states;
+    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+      this.networkStateList_ = response.result;
     });
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties} state
+   * @param {!OncMojo.NetworkStateProperties} networkState
    * @return {boolean}
    * @private
    */
-  networkIsPreferred_: function(state) {
+  networkIsPreferred_: function(networkState) {
     // Currently we treat NetworkStateProperties.Priority as a boolean.
-    return state.Priority > 0;
+    return networkState.priority > 0;
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties} networkState
+   * @param {!OncMojo.NetworkStateProperties} networkState
    * @return {boolean}
    * @private
    */
   networkIsNotPreferred_: function(networkState) {
-    return networkState.Priority == 0;
+    return networkState.priority == 0;
   },
 
   /**
@@ -127,14 +142,23 @@
   },
 
   /**
+   * @param {!OncMojo.NetworkStateProperties} networkState
+   * @return {string}
+   * @private
+   */
+  getNetworkDisplayName_: function(networkState) {
+    return OncMojo.getNetworkDisplayName(networkState);
+  },
+
+  /**
    * @param {!Event} event
    * @private
    */
   onMenuButtonTap_: function(event) {
     const button = event.target;
     const networkState =
-        /** @type {!CrOnc.NetworkStateProperties} */ (event.model.item);
-    this.selectedGuid_ = networkState.GUID;
+        /** @type {!OncMojo.NetworkStateProperties} */ (event.model.item);
+    this.selectedGuid_ = networkState.guid;
     // We need to make a round trip to Chrome in order to retrieve the managed
     // properties for the network. The delay is not noticeable (~5ms) and is
     // preferable to initiating a query for every known network at load time.
@@ -179,16 +203,15 @@
   },
 
   /**
-   * Fires a 'show-details' event with an item containing a |networkStateList_|
+   * Fires a 'show-detail' event with an item containing a |networkStateList_|
    * entry in the event model.
    * @param {!Event} event
    * @private
    */
   fireShowDetails_: function(event) {
-    const state =
-        /** @type {!{model: !{item: !CrOnc.NetworkStateProperties}}} */ (event)
-            .model.item;
-    this.fire('show-detail', state);
+    const networkState =
+        /** @type {!OncMojo.NetworkStateProperties} */ (event.model.item);
+    this.fire('show-detail', networkState);
     event.stopPropagation();
   },
 
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.html b/chrome/browser/resources/settings/internet_page/internet_page.html
index 063144d..2412485 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_page.html
@@ -35,8 +35,7 @@
         focus-config="[[focusConfig_]]">
       <div route-path="default">
         <network-summary default-network="{{defaultNetwork}}"
-            device-states="{{deviceStates}}"
-            networking-private="[[networkingPrivate]]">
+            device-states="{{deviceStates}}">
         </network-summary>
         <template is="dom-if" if="[[allowAddConnection_(globalPolicy_,
             managedNetworkAvailable)]]">
@@ -50,7 +49,7 @@
           <template is="dom-if" if="[[addConnectionExpanded_]]">
             <div class="list-frame vertical-list">
               <template is="dom-if"
-                  if="[[deviceIsEnabled_(deviceStates.WiFi)]]">
+                  if="[[deviceIsEnabled_(deviceStates, 'WiFi')]]">
                 <div actionable class="list-item" on-click="onAddWiFiTap_">
                   <div class="start settings-box-text">
                     $i18n{internetAddWiFi}
@@ -127,11 +126,10 @@
           <settings-internet-subpage
               default-network="[[defaultNetwork]]"
               device-state="[[getDeviceState_(subpageType_, deviceStates)]]"
-              tether-device-state="[[get('Tether', deviceStates)]]"
+              tether-device-state="[[getTetherDeviceState_(deviceStates)]]"
               global-policy="[[globalPolicy_]]"
               third-party-vpn-providers="[[thirdPartyVpnProviders_]]"
               arc-vpn-providers="[[arcVpnProviders_]]"
-              networking-private="[[networkingPrivate]]"
               show-spinner="{{showSpinner_}}">
           </settings-internet-subpage>
         </settings-subpage>
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.js b/chrome/browser/resources/settings/internet_page/internet_page.js
index 8284287..bb18ded6 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_page.js
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+(function() {
+
+const mojom = chromeos.networkConfig.mojom;
+
 /**
  * @fileoverview
  * 'settings-internet-page' is the settings page containing internet
@@ -33,8 +37,9 @@
     },
 
     /**
-     * The device state for each network device type. Set by network-summary.
-     * @type {!Object<!CrOnc.DeviceStateProperties>|undefined}
+     * The device state for each network device type, keyed by NetworkType. Set
+     * by network-summary.
+     * @type {!Object<!OncMojo.DeviceStateProperties>|undefined}
      * @private
      */
     deviceStates: {
@@ -45,7 +50,7 @@
 
     /**
      * Highest priority connected network or null. Set by network-summary.
-     * @type {?CrOnc.NetworkStateProperties|undefined}
+     * @type {?OncMojo.NetworkStateProperties|undefined}
      */
     defaultNetwork: {
       type: Object,
@@ -277,72 +282,70 @@
    * Event triggered by a device state enabled toggle.
    * @param {!CustomEvent<!{
    *     enabled: boolean,
-   *     type: chrome.networkingPrivate.NetworkType
+   *     type: chromeos.networkConfig.mojom.NetworkType
    * }>} event
    * @private
    */
   onDeviceEnabledToggled_: function(event) {
     this.networkConfigProxy_.setNetworkTypeEnabledState(
-        OncMojo.getNetworkTypeFromString(event.detail.type),
-        event.detail.enabled);
+        event.detail.type, event.detail.enabled);
   },
 
   /**
-   * @param {!CustomEvent<!CrOnc.NetworkProperties>} event
+   * @param {!CustomEvent<!{type: string, guid: ?string, name: ?string}>} event
    * @private
    */
   onShowConfig_: function(event) {
-    const properties = event.detail;
-    if (!properties.GUID) {
+    if (!event.detail.guid) {
       // New configuration
-      this.showConfig_(true /* configAndConnect */, properties.Type);
+      this.showConfig_(true /* configAndConnect */, event.detail.type);
     } else {
       this.showConfig_(
-          false /* configAndConnect */, properties.Type, properties.GUID,
-          CrOnc.getNetworkName(properties));
+          false /* configAndConnect */, event.detail.type, event.detail.guid,
+          event.detail.name);
     }
   },
 
   /**
    * @param {boolean} configAndConnect
    * @param {string} type
-   * @param {string=} guid
-   * @param {string=} name
+   * @param {?string=} opt_guid
+   * @param {?string=} opt_name
    * @private
    */
-  showConfig_: function(configAndConnect, type, guid, name) {
+  showConfig_: function(configAndConnect, type, opt_guid, opt_name) {
     assert(type != CrOnc.Type.CELLULAR && type != CrOnc.Type.TETHER);
     const configDialog =
         /** @type {!InternetConfigElement} */ (this.$.configDialog);
     configDialog.type =
         /** @type {chrome.networkingPrivate.NetworkType} */ (type);
-    configDialog.guid = guid || '';
-    configDialog.name = name || '';
+    configDialog.guid = opt_guid || '';
+    configDialog.name = opt_name || '';
     configDialog.showConnect = configAndConnect;
     configDialog.open();
   },
 
   /**
-   * @param {!CustomEvent<!CrOnc.NetworkStateProperties>} event
+   * @param {!CustomEvent<!OncMojo.NetworkStateProperties>} event
    * @private
    */
   onShowDetail_: function(event) {
-    this.detailType_ = event.detail.Type;
+    const networkState = event.detail;
+    const oncType = OncMojo.getNetworkTypeString(networkState.type);
+    this.detailType_ = oncType;
     const params = new URLSearchParams;
-    params.append('guid', event.detail.GUID);
-    params.append('type', event.detail.Type);
-    if (event.detail.Name) {
-      params.append('name', event.detail.Name);
-    }
+    params.append('guid', networkState.guid);
+    params.append('type', oncType);
+    params.append('name', OncMojo.getNetworkDisplayName(networkState));
     settings.navigateTo(settings.routes.NETWORK_DETAIL, params);
   },
 
   /**
-   * @param {!CustomEvent<!{type: string}>} event
+   * @param {!CustomEvent<chromeos.networkConfig.mojom.NetworkType>} event
    * @private
    */
   onShowNetworks_: function(event) {
-    this.showNetworksSubpage_(event.detail.type);
+    this.showNetworksSubpage_(event.detail);
   },
 
   /**
@@ -371,37 +374,51 @@
 
   /**
    * @param {string} subpageType
-   * @param {!Object<!CrOnc.DeviceStateProperties>|undefined} deviceStates
-   * @return {!CrOnc.DeviceStateProperties|undefined}
+   * @param {!Object<!OncMojo.DeviceStateProperties>|undefined} deviceStates
+   * @return {!OncMojo.DeviceStateProperties|undefined}
    * @private
    */
   getDeviceState_: function(subpageType, deviceStates) {
+    if (!subpageType) {
+      return undefined;
+    }
     // If both Tether and Cellular are enabled, use the Cellular device state
     // when directly navigating to the Tether page.
     if (subpageType == CrOnc.Type.TETHER &&
-        this.deviceStates[CrOnc.Type.CELLULAR]) {
+        this.deviceStates[mojom.NetworkType.kCellular]) {
       subpageType = CrOnc.Type.CELLULAR;
     }
-    return deviceStates[subpageType];
+    return deviceStates[OncMojo.getNetworkTypeFromString(subpageType)];
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} newValue
-   * @param {!CrOnc.DeviceStateProperties|undefined} oldValue
+   * @param {!Object<!OncMojo.DeviceStateProperties>|undefined} deviceStates
+   * @return {!OncMojo.DeviceStateProperties|undefined}
+   * @private
+   */
+  getTetherDeviceState_: function(deviceStates) {
+    return deviceStates[mojom.NetworkType.kTether];
+  },
+
+  /**
+   * @param {!OncMojo.DeviceStateProperties|undefined} newValue
+   * @param {!OncMojo.DeviceStateProperties|undefined} oldValue
    * @private
    */
   onDeviceStatesChanged_: function(newValue, oldValue) {
     const wifiDeviceState = this.getDeviceState_(CrOnc.Type.WI_FI, newValue);
     let managedNetworkAvailable = false;
     if (wifiDeviceState) {
-      managedNetworkAvailable = !!wifiDeviceState.ManagedNetworkAvailable;
+      managedNetworkAvailable = !!wifiDeviceState.managedNetworkAvailable;
     }
 
     if (this.managedNetworkAvailable != managedNetworkAvailable) {
       this.managedNetworkAvailable = managedNetworkAvailable;
     }
 
-    if (this.detailType_ && !this.deviceStates[this.detailType_]) {
+    if (this.detailType_ &&
+        !this.deviceStates[OncMojo.getNetworkTypeFromString(
+            this.detailType_)]) {
       // If the device type associated with the current network has been
       // removed (e.g., due to unplugging a Cellular dongle), the details page,
       // if visible, displays controls which are no longer functional. If this
@@ -414,14 +431,15 @@
   },
 
   /**
-   * @param {!CustomEvent<!{type: string}>} event
+   * @param {!CustomEvent<chromeos.networkConfig.mojom.NetworkType>} event
    * @private
    */
   onShowKnownNetworks_: function(event) {
-    this.detailType_ = event.detail.type;
+    const oncType = OncMojo.getNetworkTypeString(event.detail);
+    this.detailType_ = oncType;
+    this.knownNetworksType_ = oncType;
     const params = new URLSearchParams;
-    params.append('type', event.detail.type);
-    this.knownNetworksType_ = event.detail.type;
+    params.append('type', oncType);
     settings.navigateTo(settings.routes.KNOWN_NETWORKS, params);
   },
 
@@ -448,18 +466,19 @@
 
   /** @private */
   onAddArcVpnTap_: function() {
-    this.showNetworksSubpage_(CrOnc.Type.VPN);
+    this.showNetworksSubpage_(mojom.NetworkType.kVPN);
   },
 
   /**
-   * @param {string} type
+   * @param {chromeos.networkConfig.mojom.NetworkType} type
    * @private
    */
   showNetworksSubpage_: function(type) {
-    this.detailType_ = type;
+    const oncType = OncMojo.getNetworkTypeString(type);
+    this.detailType_ = oncType;
     const params = new URLSearchParams;
-    params.append('type', type);
-    this.subpageType_ = type;
+    params.append('type', oncType);
+    this.subpageType_ = oncType;
     settings.navigateTo(settings.routes.INTERNET_NETWORKS, params);
   },
 
@@ -559,12 +578,16 @@
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties} deviceState
+   * @param {!Array<!OncMojo.DeviceStateProperties>} deviceStates
+   * @param {string} type
    * @return {boolean}
    * @private
    */
-  deviceIsEnabled_: function(deviceState) {
-    return !!deviceState && deviceState.State == CrOnc.DeviceState.ENABLED;
+  deviceIsEnabled_: function(deviceStates, type) {
+    const device = deviceStates[OncMojo.getNetworkTypeFromString(type)];
+    return !!device &&
+        device.deviceState ==
+        chromeos.networkConfig.mojom.DeviceStateType.kEnabled;
   },
 
   /**
@@ -594,36 +617,37 @@
    * Handles UI requests to connect to a network.
    * TODO(stevenjb): Handle Cellular activation, etc.
    * @param {!CustomEvent<!{
-   *     networkProperties: (!CrOnc.NetworkProperties|
-   *         !CrOnc.NetworkStateProperties),
+   *     networkState: !OncMojo.NetworkStateProperties,
    *     bypassConnectionDialog: (boolean|undefined)
    * }>} event
    * @private
    */
   onNetworkConnect_: function(event) {
-    const properties = event.detail.networkProperties;
-    const name = CrOnc.getNetworkName(properties);
-    const networkType = properties.Type;
+    const networkState = event.detail.networkState;
+    const oncType = OncMojo.getNetworkTypeString(networkState.type);
+    const displayName = OncMojo.getNetworkDisplayName(networkState);
+
     if (!event.detail.bypassConnectionDialog &&
-        CrOnc.shouldShowTetherDialogBeforeConnection(properties)) {
+        networkState.type == mojom.NetworkType.kTether &&
+        !networkState.tether.hasConnectedToHost) {
       const params = new URLSearchParams;
-      params.append('guid', properties.GUID);
-      params.append('type', networkType);
-      params.append('name', name);
+      params.append('guid', networkState.guid);
+      params.append('type', oncType);
+      params.append('name', displayName);
       params.append('showConfigure', true.toString());
 
       settings.navigateTo(settings.routes.NETWORK_DETAIL, params);
       return;
     }
 
-    if (!CrOnc.isMobileNetwork(properties) &&
-        (properties.Connectable === false || !!properties.ErrorState)) {
+    const isMobile = OncMojo.networkTypeIsMobile(networkState.type);
+    if (!isMobile && (!networkState.connectable || !!networkState.errorState)) {
       this.showConfig_(
-          true /* configAndConnect */, networkType, properties.GUID, name);
+          true /* configAndConnect */, oncType, networkState.guid, displayName);
       return;
     }
 
-    this.networkingPrivate.startConnect(properties.GUID, () => {
+    this.networkingPrivate.startConnect(networkState.guid, () => {
       if (chrome.runtime.lastError) {
         const message = chrome.runtime.lastError.message;
         if (message == 'connecting' || message == 'connect-canceled' ||
@@ -632,14 +656,16 @@
         }
         console.error(
             'networkingPrivate.startConnect error: ' + message +
-            ' For: ' + properties.GUID);
+            ' For: ' + networkState.guid);
 
         // There is no configuration flow for Mobile Networks.
-        if (!CrOnc.isMobileNetwork(properties)) {
+        if (!isMobile) {
           this.showConfig_(
-              true /* configAndConnect */, networkType, properties.GUID, name);
+              true /* configAndConnect */, oncType, networkState.guid,
+              displayName);
         }
       }
     });
   },
 });
+})();
diff --git a/chrome/browser/resources/settings/internet_page/internet_subpage.html b/chrome/browser/resources/settings/internet_page/internet_subpage.html
index 0cc871e..3516ce9 100644
--- a/chrome/browser/resources/settings/internet_page/internet_subpage.html
+++ b/chrome/browser/resources/settings/internet_page/internet_subpage.html
@@ -6,6 +6,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/chromeos/onc_mojo.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
@@ -111,7 +112,7 @@
     <template is="dom-if" if="[[deviceIsEnabled_(deviceState)]]">
       <div id="networkListDiv" class="layout vertical flex">
         <!-- VPN only header for built-in VPNs. -->
-        <template is="dom-if" if="[[isEqual_('VPN', deviceState.Type)]]">
+        <template is="dom-if" if="[[matchesType_('VPN', deviceState)]]">
           <div class="vpn-header layout horizontal center">
             <div class="flex settings-box-text">$i18n{networkVpnBuiltin}</div>
             <cr-icon-button class="icon-add-circle"
@@ -157,7 +158,7 @@
           class="no-networks">
         </div>
 
-        <template is="dom-if" if="[[isEqual_('VPN', deviceState.Type)]]">
+        <template is="dom-if" if="[[matchesType_('VPN', deviceState)]]">
           <!-- Third party VPNs. -->
           <template is="dom-repeat" items="[[thirdPartyVpnProviders]]">
             <div id="[[item.ProviderName]]"
diff --git a/chrome/browser/resources/settings/internet_page/internet_subpage.js b/chrome/browser/resources/settings/internet_page/internet_subpage.js
index 1e47c0dd..0b5b7863 100644
--- a/chrome/browser/resources/settings/internet_page/internet_subpage.js
+++ b/chrome/browser/resources/settings/internet_page/internet_subpage.js
@@ -7,6 +7,10 @@
  * WiMAX, or virtual networks.
  */
 
+(function() {
+
+const mojom = chromeos.networkConfig.mojom;
+
 Polymer({
   is: 'settings-internet-subpage',
 
@@ -21,7 +25,7 @@
     /**
      * Highest priority connected network or null. Provided by
      * settings-internet-page (but set in network-summary).
-     * @type {?CrOnc.NetworkStateProperties|undefined}
+     * @type {?OncMojo.NetworkStateProperties|undefined}
      */
     defaultNetwork: Object,
 
@@ -29,14 +33,14 @@
      * Device state for the network type. Note: when both Cellular and Tether
      * are available this will always be set to the Cellular device state and
      * |tetherDeviceState| will be set to the Tether device state.
-     * @type {!CrOnc.DeviceStateProperties|undefined}
+     * @type {!OncMojo.DeviceStateProperties|undefined}
      */
     deviceState: Object,
 
     /**
      * If both Cellular and Tether technologies exist, we combine the subpages
      * and set this to the device state for Tether.
-     * @type {!CrOnc.DeviceStateProperties|undefined}
+     * @type {!OncMojo.DeviceStateProperties|undefined}
      */
     tetherDeviceState: Object,
 
@@ -56,12 +60,6 @@
      */
     arcVpnProviders: Array,
 
-    /**
-     * Interface for networkingPrivate calls, passed from internet_page.
-     * @type {!NetworkingPrivate}
-     */
-    networkingPrivate: Object,
-
     showSpinner: {
       type: Boolean,
       notify: true,
@@ -70,7 +68,7 @@
 
     /**
      * List of all network state data for the network type.
-     * @private {!Array<!CrOnc.NetworkStateProperties>}
+     * @private {!Array<!OncMojo.NetworkStateProperties>}
      */
     networkStateList_: {
       type: Array,
@@ -81,7 +79,7 @@
 
     /**
      * Dictionary of lists of network states for third party VPNs.
-     * @private {!Object<!Array<!CrOnc.NetworkStateProperties>>}
+     * @private {!Object<!Array<!OncMojo.NetworkStateProperties>>}
      */
     thirdPartyVpns_: {
       type: Object,
@@ -92,7 +90,7 @@
 
     /**
      * Dictionary of lists of network states for Arc VPNs.
-     * @private {!Object<!Array<!CrOnc.NetworkStateProperties>>}
+     * @private {!Object<!Array<!OncMojo.NetworkStateProperties>>}
      */
     arcVpns_: {
       type: Object,
@@ -126,7 +124,7 @@
     },
   },
 
-  observers: ['deviceStateChanged_(networkingPrivate, deviceState)'],
+  observers: ['deviceStateChanged_(deviceState)'],
 
   /** @private {number|null} */
   scanIntervalId_: null,
@@ -134,9 +132,20 @@
   /** @private  {settings.InternetPageBrowserProxy} */
   browserProxy_: null,
 
+  /**
+   * This UI will use both the networkingPrivate extension API and the
+   * networkConfig mojo API until we provide all of the required functionality
+   * in networkConfig. TODO(stevenjb): Remove use of networkingPrivate api.
+   * @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy}
+   */
+  networkConfigProxy_: null,
+
   /** @override */
   created: function() {
     this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance();
+    this.networkConfigProxy_ =
+        network_config.MojoInterfaceProviderImpl.getInstance()
+            .getMojoServiceProxy();
   },
 
   /** @override */
@@ -172,8 +181,7 @@
 
   /**
    * CrosNetworkConfigObserver impl
-   * @param {!Array<chromeos.networkConfig.mojom.NetworkStateProperties>}
-   *     networks
+   * @param {!Array<OncMojo.NetworkStateProperties>} networks
    */
   onActiveNetworksChanged: function(networks) {
     this.getNetworkStateList_();
@@ -187,7 +195,7 @@
   /** @private */
   deviceStateChanged_: function() {
     this.showSpinner =
-        this.deviceState !== undefined && !!this.deviceState.Scanning;
+        this.deviceState !== undefined && !!this.deviceState.scanning;
 
     // Scans should only be triggered by the "networks" subpage.
     if (settings.getCurrentRoute() != settings.routes.INTERNET_NETWORKS) {
@@ -195,6 +203,7 @@
       return;
     }
 
+    this.getNetworkStateList_();
     this.updateScanning_();
   },
 
@@ -208,9 +217,6 @@
       this.startScanning_();
       return;
     }
-
-    // deviceState probably changed, re-request networks.
-    this.getNetworkStateList_();
   },
 
   /**
@@ -219,14 +225,14 @@
    */
   shouldStartScan_: function() {
     // Scans should be kicked off from the Wi-Fi networks subpage.
-    if (this.deviceState.Type == CrOnc.Type.WI_FI) {
+    if (this.deviceState.type == mojom.NetworkType.kWiFi) {
       return true;
     }
 
     // Scans should be kicked off from the Mobile data subpage, as long as it
     // includes Tether networks.
-    if (this.deviceState.Type == CrOnc.Type.TETHER ||
-        (this.deviceState.Type == CrOnc.Type.CELLULAR &&
+    if (this.deviceState.type == mojom.NetworkType.kTether ||
+        (this.deviceState.type == mojom.NetworkType.kCellular &&
          this.tetherDeviceState)) {
       return true;
     }
@@ -240,9 +246,9 @@
       return;
     }
     const INTERVAL_MS = 10 * 1000;
-    this.networkingPrivate.requestNetworkScan(this.deviceState.Type);
+    this.networkConfigProxy_.requestNetworkScan(this.deviceState.type);
     this.scanIntervalId_ = window.setInterval(() => {
-      this.networkingPrivate.requestNetworkScan(this.deviceState.Type);
+      this.networkConfigProxy_.requestNetworkScan(this.deviceState.type);
     }, INTERVAL_MS);
   },
 
@@ -261,58 +267,66 @@
       return;
     }
     const filter = {
-      networkType: this.deviceState.Type,
-      visible: true,
-      configured: false
+      filter: chromeos.networkConfig.mojom.FilterType.kVisible,
+      limit: chromeos.networkConfig.mojom.kNoLimit,
+      networkType: this.deviceState.type,
     };
-    this.networkingPrivate.getNetworks(filter, this.onGetNetworks_.bind(this));
+    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+      this.onGetNetworks_(response.result);
+    });
   },
 
   /**
-   * @param {!Array<!CrOnc.NetworkStateProperties>} networkStates
+   * @param {!Array<!OncMojo.NetworkStateProperties>} networkStates
    * @private
    */
   onGetNetworks_: function(networkStates) {
     if (!this.deviceState) {
+      // Edge case when device states change before this callback.
       return;
-    }  // Edge case when device states change before this callback.
+    }
 
-    // For the Cellular/Mobile subpage, request Tether networks if available.
-    if (this.deviceState.Type == CrOnc.Type.CELLULAR &&
+    // For the Cellular/Mobile subpage, also request Tether networks.
+    if (this.deviceState.type == mojom.NetworkType.kCellular &&
         this.tetherDeviceState) {
       const filter = {
-        networkType: CrOnc.Type.TETHER,
-        visible: true,
-        configured: false
+        filter: chromeos.networkConfig.mojom.FilterType.kVisible,
+        limit: chromeos.networkConfig.mojom.kNoLimit,
+        networkType: mojom.NetworkType.kTether,
       };
-      this.networkingPrivate.getNetworks(filter, tetherNetworkStates => {
+      this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+        const tetherNetworkStates = response.result;
         this.networkStateList_ = networkStates.concat(tetherNetworkStates);
       });
       return;
     }
 
     // For VPNs, separate out third party VPNs and Arc VPNs.
-    if (this.deviceState.Type == CrOnc.Type.VPN) {
+    if (this.deviceState.type == mojom.NetworkType.kVPN) {
       const builtinNetworkStates = [];
       const thirdPartyVpns = {};
       const arcVpns = {};
-      for (let i = 0; i < networkStates.length; ++i) {
-        const state = networkStates[i];
-        const providerType = this.get('VPN.ThirdPartyVPN.ProviderName', state);
-        if (providerType) {
-          thirdPartyVpns[providerType] = thirdPartyVpns[providerType] || [];
-          thirdPartyVpns[providerType].push(state);
-        } else if (this.get('VPN.Type', state) == 'ARCVPN') {
-          const arcProviderName = this.get('VPN.Host', state);
-          if (state.ConnectionState != CrOnc.ConnectionState.CONNECTED) {
-            continue;
-          }
-          arcVpns[arcProviderName] = arcVpns[arcProviderName] || [];
-          arcVpns[arcProviderName].push(state);
-        } else {
-          builtinNetworkStates.push(state);
+      networkStates.forEach(state => {
+        assert(state.type == mojom.NetworkType.kVPN);
+        switch (state.vpn.type) {
+          case mojom.VPNType.kL2TPIPsec:
+          case mojom.VPNType.kOpenVPN:
+            builtinNetworkStates.push(state);
+            break;
+          case mojom.VPNType.kThirdPartyVPN:
+            const providerName = state.vpn.providerName;
+            thirdPartyVpns[providerName] = thirdPartyVpns[providerName] || [];
+            thirdPartyVpns[providerName].push(state);
+            break;
+          case mojom.VPNType.kArcVPN:
+            const arcProviderName = this.get('VPN.Host', state);
+            if (OncMojo.connectionStateIsConnected(state.connectionState)) {
+              arcVpns[arcProviderName] = arcVpns[arcProviderName] || [];
+              arcVpns[arcProviderName].push(state);
+            }
+            break;
         }
-      }
+      });
       networkStates = builtinNetworkStates;
       this.thirdPartyVpns_ = thirdPartyVpns;
       this.arcVpns_ = arcVpns;
@@ -331,16 +345,18 @@
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {boolean} Whether or not the device state is enabled.
    * @private
    */
   deviceIsEnabled_: function(deviceState) {
-    return !!deviceState && deviceState.State == CrOnc.DeviceState.ENABLED;
+    return !!deviceState &&
+        deviceState.deviceState ==
+        chromeos.networkConfig.mojom.DeviceStateType.kEnabled;
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @param {string} onstr
    * @param {string} offstr
    * @return {string}
@@ -351,26 +367,28 @@
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {boolean}
    * @private
    */
   enableToggleIsVisible_: function(deviceState) {
-    return !!deviceState && deviceState.Type != CrOnc.Type.ETHERNET &&
-        deviceState.Type != CrOnc.Type.VPN;
+    return !!deviceState && deviceState.type != mojom.NetworkType.kEthernet &&
+        deviceState.type != mojom.NetworkType.kVPN;
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {boolean}
    * @private
    */
   enableToggleIsEnabled_: function(deviceState) {
-    return !!deviceState && deviceState.State != CrOnc.DeviceState.PROHIBITED;
+    return !!deviceState &&
+        deviceState.deviceState !=
+        chromeos.networkConfig.mojom.DeviceStateType.kProhibited;
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {string}
    * @private
    */
@@ -378,13 +396,13 @@
     if (!this.enableToggleIsVisible_(deviceState)) {
       return '';
     }
-    switch (deviceState.Type) {
-      case CrOnc.Type.TETHER:
-      case CrOnc.Type.CELLULAR:
+    switch (deviceState.type) {
+      case mojom.NetworkType.kTether:
+      case mojom.NetworkType.kCellular:
         return this.i18n('internetToggleMobileA11yLabel');
-      case CrOnc.Type.WI_FI:
+      case mojom.NetworkType.kWiFi:
         return this.i18n('internetToggleWiFiA11yLabel');
-      case CrOnc.Type.WI_MAX:
+      case mojom.NetworkType.kWiMAX:
         return this.i18n('internetToggleWiMAXA11yLabel');
     }
     assertNotReached();
@@ -397,8 +415,7 @@
    * @private
    */
   getAddThirdPartyVpnA11yString_: function(vpnState) {
-    return this.i18n(
-        'internetAddThirdPartyVPN', vpnState.ProviderName || '');
+    return this.i18n('internetAddThirdPartyVPN', vpnState.ProviderName || '');
   },
 
   /**
@@ -420,13 +437,13 @@
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy
    * @return {boolean}
    * @private
    */
   showAddButton_: function(deviceState, globalPolicy) {
-    if (!deviceState || deviceState.Type != CrOnc.Type.WI_FI) {
+    if (!deviceState || deviceState.type != mojom.NetworkType.kWiFi) {
       return false;
     }
     if (!this.deviceIsEnabled_(deviceState)) {
@@ -438,15 +455,14 @@
   /** @private */
   onAddButtonTap_: function() {
     assert(this.deviceState);
-    const type = this.deviceState.Type;
-    assert(type != CrOnc.Type.CELLULAR);
-    this.fire('show-config', {GUID: '', Type: type});
+    const type = this.deviceState.type;
+    assert(type != mojom.NetworkType.kCellular);
+    this.fire('show-config', {type: OncMojo.getNetworkTypeString(type)});
   },
 
   /**
-   * @param {!{model:
-   *              !{item: !chrome.networkingPrivate.ThirdPartyVPNProperties},
-   *        }} event
+   * @param {!{model: !{item:
+   *     !chrome.networkingPrivate.ThirdPartyVPNProperties}}} event
    * @private
    */
   onAddThirdPartyVpnTap_: function(event) {
@@ -464,12 +480,12 @@
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {boolean}
    * @private
    */
   knownNetworksIsVisible_: function(deviceState) {
-    return !!deviceState && deviceState.Type == CrOnc.Type.WI_FI;
+    return !!deviceState && deviceState.type == mojom.NetworkType.kWiFi;
   },
 
   /**
@@ -477,8 +493,8 @@
    * @private
    */
   onKnownNetworksTap_: function() {
-    assert(this.deviceState.Type == CrOnc.Type.WI_FI);
-    this.fire('show-known-networks', {type: this.deviceState.Type});
+    assert(this.deviceState.type == mojom.NetworkType.kWiFi);
+    this.fire('show-known-networks', this.deviceState.type);
   },
 
   /**
@@ -490,14 +506,14 @@
     assert(this.deviceState);
     this.fire('device-enabled-toggled', {
       enabled: !this.deviceIsEnabled_(this.deviceState),
-      type: this.deviceState.Type
+      type: this.deviceState.type
     });
   },
 
   /**
-   * @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} thirdPartyVpns
+   * @param {!Object<!Array<!OncMojo.NetworkStateProperties>>} thirdPartyVpns
    * @param {!chrome.networkingPrivate.ThirdPartyVPNProperties} vpnState
-   * @return {!Array<!CrOnc.NetworkStateProperties>}
+   * @return {!Array<!OncMojo.NetworkStateProperties>}
    * @private
    */
   getThirdPartyVpnNetworks_: function(thirdPartyVpns, vpnState) {
@@ -505,7 +521,7 @@
   },
 
   /**
-   * @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} thirdPartyVpns
+   * @param {!Object<!Array<!OncMojo.NetworkStateProperties>>} thirdPartyVpns
    * @param {!chrome.networkingPrivate.ThirdPartyVPNProperties} vpnState
    * @return {boolean}
    * @private
@@ -516,9 +532,9 @@
   },
 
   /**
-   * @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} arcVpns
+   * @param {!Object<!Array<!OncMojo.NetworkStateProperties>>} arcVpns
    * @param {!settings.ArcVpnProvider} arcVpnProvider
-   * @return {!Array<!CrOnc.NetworkStateProperties>}
+   * @return {!Array<!OncMojo.NetworkStateProperties>}
    * @private
    */
   getArcVpnNetworks_: function(arcVpns, arcVpnProvider) {
@@ -526,7 +542,7 @@
   },
 
   /**
-   * @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} arcVpns
+   * @param {!Object<!Array<!OncMojo.NetworkStateProperties>>} arcVpns
    * @param {!settings.ArcVpnProvider} arcVpnProvider
    * @return {boolean}
    * @private
@@ -538,81 +554,81 @@
 
   /**
    * Event triggered when a network list item is selected.
-   * @param {!{target: HTMLElement, detail: !CrOnc.NetworkStateProperties}} e
+   * @param {!{target: HTMLElement, detail: !OncMojo.NetworkStateProperties}} e
    * @private
    */
   onNetworkSelected_: function(e) {
     assert(this.globalPolicy);
     assert(this.defaultNetwork !== undefined);
-    const state = e.detail;
+    const networkState = e.detail;
     e.target.blur();
-    if (this.canConnect_(state)) {
-      this.fire('network-connect', {networkProperties: state});
+    if (this.canConnect_(networkState)) {
+      this.fire('network-connect', {networkState: networkState});
       return;
     }
-    this.fire('show-detail', state);
+    this.fire('show-detail', networkState);
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties} state The network state.
+   * @param {!OncMojo.NetworkStateProperties} state The network state.
    * @return {boolean}
    * @private
    */
   isBlockedByPolicy_: function(state) {
-    if (state.Type != CrOnc.Type.WI_FI || this.isPolicySource(state.Source) ||
-        !this.globalPolicy) {
+    if (state.type != mojom.NetworkType.kWiFi ||
+        this.isPolicySourceMojo(state.source) || !this.globalPolicy) {
       return false;
     }
     return !!this.globalPolicy.AllowOnlyPolicyNetworksToConnect ||
         (!!this.globalPolicy.AllowOnlyPolicyNetworksToConnectIfAvailable &&
-         !!this.deviceState && !!this.deviceState.ManagedNetworkAvailable) ||
-        (!!state.WiFi && !!state.WiFi.HexSSID &&
-         !!this.globalPolicy.BlacklistedHexSSIDs &&
-         this.globalPolicy.BlacklistedHexSSIDs.includes(state.WiFi.HexSSID));
+         !!this.deviceState && !!this.deviceState.managedNetworkAvailable) ||
+        (!!this.globalPolicy.BlacklistedHexSSIDs &&
+         this.globalPolicy.BlacklistedHexSSIDs.includes(state.wifi.hexSsid));
   },
 
   /**
    * Determines whether or not a network state can be connected to.
-   * @param {!CrOnc.NetworkStateProperties} state The network state.
+   * @param {!OncMojo.NetworkStateProperties} state The network state.
    * @private
    */
   canConnect_: function(state) {
-    if (state.ConnectionState != CrOnc.ConnectionState.NOT_CONNECTED) {
+    if (state.connectionState != mojom.ConnectionStateType.kNotConnected) {
       return false;
     }
     if (this.isBlockedByPolicy_(state)) {
       return false;
     }
-    if (state.Type == CrOnc.Type.VPN &&
+    if (state.type == mojom.NetworkType.kVPN &&
         (!this.defaultNetwork ||
-         this.defaultNetwork.ConnectionState !=
-             CrOnc.ConnectionState.CONNECTED)) {
+         !OncMojo.connectionStateIsConnected(
+             this.defaultNetwork.connectionState))) {
       return false;
     }
     return true;
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
-   * @param {!CrOnc.DeviceStateProperties|undefined} tetherDeviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} tetherDeviceState
    * @return {boolean}
    * @private
    */
   tetherToggleIsVisible_: function(deviceState, tetherDeviceState) {
-    return !!deviceState && deviceState.Type == CrOnc.Type.CELLULAR &&
+    return !!deviceState && deviceState.type == mojom.NetworkType.kCellular &&
         !!tetherDeviceState;
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
-   * @param {!CrOnc.DeviceStateProperties|undefined} tetherDeviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} tetherDeviceState
    * @return {boolean}
    * @private
    */
   tetherToggleIsEnabled_: function(deviceState, tetherDeviceState) {
     return this.tetherToggleIsVisible_(deviceState, tetherDeviceState) &&
         this.enableToggleIsEnabled_(tetherDeviceState) &&
-        tetherDeviceState.State != CrOnc.DeviceState.UNINITIALIZED;
+        tetherDeviceState.deviceState !=
+        chromeos.networkConfig.mojom.DeviceStateType.kUninitialized;
   },
 
   /**
@@ -622,23 +638,24 @@
   onTetherEnabledChange_: function(event) {
     this.fire('device-enabled-toggled', {
       enabled: !this.deviceIsEnabled_(this.tetherDeviceState),
-      type: CrOnc.Type.TETHER,
+      type: mojom.NetworkType.kTether,
     });
     event.stopPropagation();
   },
 
   /**
-   * @param {*} lhs
-   * @param {*} rhs
+   * @param {string} typeString
+   * @param {OncMojo.DeviceStateProperties} device
    * @return {boolean}
    * @private
    */
-  isEqual_: function(lhs, rhs) {
-    return lhs === rhs;
+  matchesType_: function(typeString, device) {
+    return device &&
+        device.type == OncMojo.getNetworkTypeFromString(typeString);
   },
 
   /**
-   * @param {!Array<!CrOnc.NetworkStateProperties>} networkStateList
+   * @param {!Array<!OncMojo.NetworkStateProperties>} networkStateList
    * @return {boolean}
    * @private
    */
@@ -647,15 +664,15 @@
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
-   * @param {!CrOnc.DeviceStateProperties|undefined} tetherDeviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} tetherDeviceState
    * @return {string}
    * @private
    */
   getNoNetworksString_: function(deviceState, tetherDeviceState) {
-    const type = deviceState.Type;
-    if (type == CrOnc.Type.TETHER ||
-        (type == CrOnc.Type.CELLULAR && this.tetherDeviceState)) {
+    const type = deviceState.type;
+    if (type == mojom.NetworkType.kTether ||
+        (type == mojom.NetworkType.kCellular && this.tetherDeviceState)) {
       return this.i18nAdvanced('internetNoNetworksMobileData');
     }
 
@@ -694,3 +711,4 @@
     return this.i18n('gmscoreNotificationsManyDevicesSubtitle');
   },
 });
+})();
diff --git a/chrome/browser/resources/settings/internet_page/network_summary.html b/chrome/browser/resources/settings/internet_page/network_summary.html
index dcac95b..1dfcd0b 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary.html
+++ b/chrome/browser/resources/settings/internet_page/network_summary.html
@@ -1,18 +1,18 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_listener_behavior.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html">
+<link rel="import" href="chrome://resources/html/chromeos/onc_mojo.html">
 <link rel="import" href="network_summary_item.html">
 
 <dom-module id="network-summary">
   <template>
     <div id="summary">
       <template is="dom-repeat" items="[[activeNetworkStates_]]">
-        <network-summary-item id="[[item.Type]]"
+        <network-summary-item id="[[getTypeString_(item)]]"
             active-network-state="[[item]]"
-            device-state="[[get(item.Type, deviceStates)]]"
-            network-state-list="[[get(item.Type, networkStateLists_)]]"
+            device-state="[[get(item.type, deviceStates)]]"
+            network-state-list="[[get(item.type, networkStateLists_)]]"
             tether-device-state="[[get('Tether', deviceStates)]]">
         </network-summary-item>
       </template>
diff --git a/chrome/browser/resources/settings/internet_page/network_summary.js b/chrome/browser/resources/settings/internet_page/network_summary.js
index e28764d..70b08bd 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary.js
+++ b/chrome/browser/resources/settings/internet_page/network_summary.js
@@ -7,27 +7,9 @@
  * by type: Ethernet, WiFi, Cellular, WiMAX, and VPN.
  */
 
-/**
- * @typedef {{
- *   Ethernet: (!CrOnc.DeviceStateProperties|undefined),
- *   WiFi: (!CrOnc.DeviceStateProperties|undefined),
- *   Cellular: (!CrOnc.DeviceStateProperties|undefined),
- *   WiMAX: (!CrOnc.DeviceStateProperties|undefined),
- *   VPN: (!CrOnc.DeviceStateProperties|undefined)
- * }}
- */
-let DeviceStateObject;
+(function() {
 
-/**
- * @typedef {{
- *   Ethernet: (Array<!CrOnc.NetworkStateProperties>|undefined),
- *   WiFi: (Array<!CrOnc.NetworkStateProperties>|undefined),
- *   Cellular: (Array<!CrOnc.NetworkStateProperties>|undefined),
- *   WiMAX: (Array<!CrOnc.NetworkStateProperties>|undefined),
- *   VPN: (Array<!CrOnc.NetworkStateProperties>|undefined)
- * }}
- */
-let NetworkStateListObject;
+const mojom = chromeos.networkConfig.mojom;
 
 Polymer({
   is: 'network-summary',
@@ -39,8 +21,9 @@
 
   properties: {
     /**
-     * Highest priority connected network or null.
-     * @type {?CrOnc.NetworkStateProperties}
+     * Highest priority connected network or null. Set here to update
+     * internet-page which updates internet-subpage and internet-detail-page.
+     * @type {?OncMojo.NetworkStateProperties}
      */
     defaultNetwork: {
       type: Object,
@@ -49,23 +32,20 @@
     },
 
     /**
-     * Interface for networkingPrivate calls, passed from internet_page.
-     * @type {!NetworkingPrivate}
-     */
-    networkingPrivate: Object,
-
-    /**
      * The device state for each network device type. We initialize this to
      * include a disabled WiFi type since WiFi is always present. This reduces
      * the amount of visual change on first load.
-     * @private {!DeviceStateObject}
+     * @private {!Object<!OncMojo.DeviceStateProperties>}
      */
     deviceStates: {
       type: Object,
       value: function() {
-        return {
-          WiFi: {Type: CrOnc.Type.WI_FI, State: CrOnc.DeviceState.DISABLED},
+        const result = {};
+        result[chromeos.networkConfig.mojom.NetworkType.kWiFi] = {
+          deviceState: chromeos.networkConfig.mojom.DeviceStateType.kDisabled,
+          type: chromeos.networkConfig.mojom.NetworkType.kWiFi,
         };
+        return result;
       },
       notify: true,
     },
@@ -73,27 +53,32 @@
     /**
      * Array of active network states, one per device type. Initialized to
      * include a default WiFi state (see deviceStates comment).
-     * @private {!Array<!CrOnc.NetworkStateProperties>}
+     * @private {!Array<!OncMojo.NetworkStateProperties>}
      */
     activeNetworkStates_: {
       type: Array,
       value: function() {
-        return [{GUID: '', Type: CrOnc.Type.WI_FI}];
+        return [OncMojo.getDefaultNetworkState(mojom.NetworkType.kWiFi)];
       },
     },
 
     /**
      * List of network state data for each network type.
-     * @private {!NetworkStateListObject}
+     * @private {!Object<!Array<!OncMojo.NetworkStateProperties>>}
      */
     networkStateLists_: {
       type: Object,
       value: function() {
-        return {WiFi: []};
+        const result = {};
+        result[mojom.NetworkType.kWiFi] = [];
+        return result;
       },
     },
   },
 
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
+  networkConfigProxy_: null,
+
   /**
    * Set of GUIDs identifying active networks, one for each type.
    * @private {?Set<string>}
@@ -101,14 +86,22 @@
   activeNetworkIds_: null,
 
   /** @override */
+  created: function() {
+    this.networkConfigProxy_ =
+        network_config.MojoInterfaceProviderImpl.getInstance()
+            .getMojoServiceProxy();
+  },
+
+  /** @override */
   attached: function() {
     this.getNetworkLists_();
   },
 
   /**
    * CrosNetworkConfigObserver impl
-   * @param {!Array<chromeos.networkConfig.mojom.NetworkStateProperties>}
-   *     networks
+   * Updates any matching existing active networks. Note: newly active networks
+   * will trigger onNetworkStateListChanged which triggers getNetworkLists_.
+   * @param {!Array<OncMojo.NetworkStateProperties>} networks
    */
   onActiveNetworksChanged: function(networks) {
     if (!this.activeNetworkIds_) {
@@ -116,10 +109,10 @@
       return;
     }
     networks.forEach(network => {
-      const id = network.guid;
-      if (this.activeNetworkIds_.has(id)) {
-        this.networkingPrivate.getState(
-            id, this.getActiveStateCallback_.bind(this, id));
+      const index = this.activeNetworkStates_.findIndex(
+          state => state.guid == network.guid);
+      if (index != -1) {
+        this.set(['activeNetworkStates_', index], network);
       }
     });
   },
@@ -135,41 +128,6 @@
   },
 
   /**
-   * networkingPrivate.getState event callback for an active state.
-   * @param {string} id The id of the requested state.
-   * @param {!CrOnc.NetworkStateProperties} state
-   * @private
-   */
-  getActiveStateCallback_: function(id, state) {
-    if (chrome.runtime.lastError) {
-      const message = chrome.runtime.lastError.message;
-      if (message != 'Error.NetworkUnavailable' &&
-          message != 'Error.InvalidNetworkGuid') {
-        console.error(
-            'Unexpected networkingPrivate.getState error: ' + message +
-            ' For: ' + id);
-        return;
-      }
-    }
-    // Async call, ensure id still exists.
-    if (!this.activeNetworkIds_.has(id)) {
-      return;
-    }
-    if (!state) {
-      this.activeNetworkIds_.delete(id);
-      return;
-    }
-    // Find the active state for the type and update it.
-    const idx =
-        this.activeNetworkStates_.findIndex((s) => s.Type == state.Type);
-    if (idx == -1) {
-      console.error('Active state not found: ' + state.Name);
-      return;
-    }
-    this.set(['activeNetworkStates_', idx], state);
-  },
-
-  /**
    * Requests the list of device states and network states from Chrome.
    * Updates deviceStates, activeNetworkStates, and networkStateLists once the
    * results are returned from Chrome.
@@ -177,9 +135,9 @@
    */
   getNetworkLists_: function() {
     // First get the device states.
-    this.networkingPrivate.getDeviceStates(deviceStates => {
+    this.networkConfigProxy_.getDeviceStateList().then(response => {
       // Second get the network states.
-      this.getNetworkStates_(deviceStates);
+      this.getNetworkStates_(response.result);
     });
   },
 
@@ -187,54 +145,61 @@
    * Requests the list of network states from Chrome. Updates
    * activeNetworkStates and networkStateLists once the results are returned
    * from Chrome.
-   * @param {!Array<!CrOnc.DeviceStateProperties>} deviceStates
+   * @param {!Array<!OncMojo.DeviceStateProperties>} deviceStateList
    * @private
    */
-  getNetworkStates_: function(deviceStates) {
+  getNetworkStates_: function(deviceStateList) {
     const filter = {
-      networkType: CrOnc.Type.ALL,
-      visible: true,
-      configured: false
+      filter: chromeos.networkConfig.mojom.FilterType.kVisible,
+      limit: chromeos.networkConfig.mojom.kNoLimit,
+      networkType: mojom.NetworkType.kAll,
     };
-    this.networkingPrivate.getNetworks(filter, networkStates => {
-      this.updateNetworkStates_(networkStates, deviceStates);
+    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+      this.updateNetworkStates_(response.result, deviceStateList);
     });
   },
 
   /**
    * Called after network states are received from getNetworks.
-   * @param {!Array<!CrOnc.NetworkStateProperties>} networkStates The state
+   * @param {!Array<!OncMojo.NetworkStateProperties>} networkStates The state
    *     properties for all visible networks.
-   * @param {!Array<!CrOnc.DeviceStateProperties>} deviceStates
+   * @param {!Array<!OncMojo.DeviceStateProperties>} deviceStateList
    * @private
    */
-  updateNetworkStates_: function(networkStates, deviceStates) {
-    const newDeviceStates = /** @type {!DeviceStateObject} */ ({});
-    for (const state of deviceStates) {
-      newDeviceStates[state.Type] = state;
+  updateNetworkStates_: function(networkStates, deviceStateList) {
+    const newDeviceStates = {};
+    for (const device of deviceStateList) {
+      newDeviceStates[device.type] = device;
     }
 
+    const orderedNetworkTypes = [
+      mojom.NetworkType.kEthernet,
+      mojom.NetworkType.kWiFi,
+      mojom.NetworkType.kCellular,
+      mojom.NetworkType.kTether,
+      mojom.NetworkType.kWiMAX,
+      mojom.NetworkType.kVPN,
+    ];
+
     // Clear any current networks.
     const activeNetworkStatesByType =
-        /** @type {!Map<string, !CrOnc.NetworkStateProperties>} */ (new Map);
+        /** @type {!Map<mojom.NetworkType, !OncMojo.NetworkStateProperties>} */
+        (new Map);
 
     // Complete list of states by type.
-    /** @type {!NetworkStateListObject} */ const newNetworkStateLists = {
-      Ethernet: [],
-      Tether: [],
-      WiFi: [],
-      Cellular: [],
-      WiMAX: [],
-      VPN: [],
-    };
+    const newNetworkStateLists = {};
+    for (const type of orderedNetworkTypes) {
+      newNetworkStateLists[type] = [];
+    }
 
     let firstConnectedNetwork = null;
     networkStates.forEach(function(networkState) {
-      const type = networkState.Type;
+      const type = networkState.type;
       if (!activeNetworkStatesByType.has(type)) {
         activeNetworkStatesByType.set(type, networkState);
-        if (!firstConnectedNetwork && networkState.Type != CrOnc.Type.VPN &&
-            networkState.ConnectionState == CrOnc.ConnectionState.CONNECTED) {
+        if (!firstConnectedNetwork &&
+            networkState.type != mojom.NetworkType.kVPN &&
+            OncMojo.connectionStateIsConnected(networkState.connectionState)) {
           firstConnectedNetwork = networkState;
         }
       }
@@ -244,10 +209,10 @@
     this.defaultNetwork = firstConnectedNetwork;
 
     // Create a VPN entry in deviceStates if there are any VPN networks.
-    if (newNetworkStateLists.VPN && newNetworkStateLists.VPN.length > 0) {
-      newDeviceStates.VPN = {
-        Type: CrOnc.Type.VPN,
-        State: CrOnc.DeviceState.ENABLED
+    if (newNetworkStateLists[mojom.NetworkType.kVPN].length > 0) {
+      newDeviceStates[mojom.NetworkType.kVPN] = {
+        type: mojom.NetworkType.kVPN,
+        deviceState: chromeos.networkConfig.mojom.DeviceStateType.kEnabled,
       };
     }
 
@@ -255,11 +220,7 @@
     // device priority, creating an empty state for devices with no networks.
     const newActiveNetworkStates = [];
     this.activeNetworkIds_ = new Set;
-    const orderedDeviceTypes = [
-      CrOnc.Type.ETHERNET, CrOnc.Type.WI_FI, CrOnc.Type.CELLULAR,
-      CrOnc.Type.TETHER, CrOnc.Type.WI_MAX, CrOnc.Type.VPN
-    ];
-    for (const type of orderedDeviceTypes) {
+    for (const type of orderedNetworkTypes) {
       const device = newDeviceStates[type];
       if (!device) {
         continue;
@@ -268,24 +229,26 @@
       // If both 'Tether' and 'Cellular' technologies exist, merge the network
       // lists and do not add an active network for 'Tether' so that there is
       // only one 'Mobile data' section / subpage.
-      if (type == CrOnc.Type.TETHER && newDeviceStates[CrOnc.Type.CELLULAR]) {
-        newNetworkStateLists[CrOnc.Type.CELLULAR] =
-            newNetworkStateLists[CrOnc.Type.CELLULAR].concat(
-                newNetworkStateLists[CrOnc.Type.TETHER]);
+      if (type == mojom.NetworkType.kTether &&
+          newDeviceStates[mojom.NetworkType.kCellular]) {
+        newNetworkStateLists[mojom.NetworkType.kCellular] =
+            newNetworkStateLists[mojom.NetworkType.kCellular].concat(
+                newNetworkStateLists[mojom.NetworkType.kTether]);
         continue;
       }
 
       // Note: The active state for 'Cellular' may be a Tether network if both
       // types are enabled but no Cellular network exists (edge case).
-      const state =
+      const networkState =
           this.getActiveStateForType_(activeNetworkStatesByType, type);
-      if (state.Source === undefined &&
-          device.State == CrOnc.DeviceState.PROHIBITED) {
+      if (networkState.source === undefined &&
+          device.deviceState == mojom.DeviceStateType.kProhibited) {
         // Prohibited technologies are enforced by the device policy.
-        state.Source = CrOnc.Source.DEVICE_POLICY;
+        networkState.source =
+            chromeos.networkConfig.mojom.OncSource.kDevicePolicy;
       }
-      newActiveNetworkStates.push(state);
-      this.activeNetworkIds_.add(state.GUID);
+      newActiveNetworkStates.push(networkState);
+      this.activeNetworkIds_.add(networkState.guid);
     }
 
     this.deviceStates = newDeviceStates;
@@ -298,15 +261,37 @@
    * Returns the active network state for |type| or a default network state.
    * If there is no 'Cellular' network, return the active 'Tether' network if
    * any since the two types are represented by the same section / subpage.
-   * @param {!Map<string, !CrOnc.NetworkStateProperties>} activeStatesByType
-   * @param {string} type
-   * @return {!CrOnc.NetworkStateProperties|undefined}
+   * @param {!Map<mojom.NetworkType, !OncMojo.NetworkStateProperties>}
+   *     activeStatesByType
+   * @param {!mojom.NetworkType} type
+   * @return {!OncMojo.NetworkStateProperties|undefined}
+   * @private
    */
   getActiveStateForType_: function(activeStatesByType, type) {
     let activeState = activeStatesByType.get(type);
-    if (!activeState && type == CrOnc.Type.CELLULAR) {
-      activeState = activeStatesByType.get(CrOnc.Type.TETHER);
+    if (!activeState && type == mojom.NetworkType.kCellular) {
+      activeState = activeStatesByType.get(mojom.NetworkType.kTether);
     }
-    return activeState || {GUID: '', Type: type};
+    return activeState || OncMojo.getDefaultNetworkState(type);
+  },
+
+  /**
+   * Provides an id string for summary items. Used in tests.
+   * @param {!OncMojo.NetworkStateProperties} network
+   * @return {string}
+   * @private
+   */
+  getTypeString_: function(network) {
+    return OncMojo.getNetworkTypeString(network.type);
+  },
+
+  /**
+   * @param {!Object<!OncMojo.DeviceStateProperties>} deviceStates
+   * @return {!OncMojo.DeviceStateProperties|undefined}
+   * @private
+   */
+  getTetherDeviceState_: function(deviceStates) {
+    return this.deviceStates[mojom.NetworkType.kTether];
   },
 });
+})();
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.html b/chrome/browser/resources/settings/internet_page/network_summary_item.html
index 6ba4b77..81af6a2e 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.html
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.html
@@ -7,6 +7,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/chromeos/onc_mojo.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
 <link rel="import" href="../settings_page/settings_subpage.html">
@@ -66,8 +67,8 @@
         </template>
 
         <template is="dom-if" if="[[showPolicyIndicator_(activeNetworkState)]]">
-          <cr-policy-indicator indicator-type="[[getIndicatorTypeForSource(
-              activeNetworkState.Source)]]" on-click="doNothing_">
+          <cr-policy-indicator indicator-type="[[getIndicatorTypeForSourceMojo(
+              activeNetworkState.source)]]" on-click="doNothing_">
           </cr-policy-indicator>
         </template>
 
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chrome/browser/resources/settings/internet_page/network_summary_item.js
index 44ec90f..4c55a87 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.js
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.js
@@ -9,6 +9,10 @@
  * section. See crbug.com/726380.
  */
 
+(function() {
+
+const mojom = chromeos.networkConfig.mojom;
+
 Polymer({
   is: 'network-summary-item',
 
@@ -16,28 +20,28 @@
 
   properties: {
     /**
-     * Device state for the network type. This might briefly be undefined if a
-     * device becomes unavailable.
-     * @type {!CrOnc.DeviceStateProperties|undefined}
+     * Device state for the network type. This might briefly be undefined if
+     * a device becomes unavailable.
+     * @type {!OncMojo.DeviceStateProperties|undefined}
      */
     deviceState: Object,
 
     /**
-     * If both Cellular and Tether technologies exist, we combine the sections
-     * and set this to the device state for Tether.
-     * @type {!CrOnc.DeviceStateProperties|undefined}
+     * If both Cellular and Tether technologies exist, we combine the
+     * sections and set this to the device state for Tether.
+     * @type {!OncMojo.DeviceStateProperties|undefined}
      */
     tetherDeviceState: Object,
 
     /**
      * Network state for the active network.
-     * @type {!CrOnc.NetworkStateProperties|undefined}
+     * @type {!OncMojo.NetworkStateProperties|undefined}
      */
     activeNetworkState: Object,
 
     /**
      * List of all network state data for the network type.
-     * @type {!Array<!CrOnc.NetworkStateProperties>}
+     * @type {!Array<!OncMojo.NetworkStateProperties>}
      */
     networkStateList: {
       type: Array,
@@ -47,9 +51,9 @@
     },
 
     /**
-     * Title line describing the network type to appear in the row's top line.
-     * If it is undefined, the title text is a default from CrOncStrings (see
-     * this.getTitleText_() below).
+     * Title line describing the network type to appear in the row's top
+     * line. If it is undefined, the title text is a default from
+     * CrOncStrings (see this.getTitleText_() below).
      * @type {string|undefined}
      */
     networkTitleText: String,
@@ -68,39 +72,40 @@
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties} activeNetworkState
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.NetworkStateProperties} activeNetworkState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {string}
    * @private
    */
   getNetworkStateText_: function(activeNetworkState, deviceState) {
-    const stateText = this.getConnectionStateText_(activeNetworkState);
+    const stateText =
+        this.getConnectionStateText_(activeNetworkState, deviceState);
     if (stateText) {
       return stateText;
     }
     // No network state, use device state.
     if (deviceState) {
       // Type specific scanning or initialization states.
-      if (deviceState.Type == CrOnc.Type.CELLULAR) {
-        if (deviceState.Scanning) {
+      if (deviceState.type == mojom.NetworkType.kCellular) {
+        if (deviceState.scanning) {
           return this.i18n('internetMobileSearching');
         }
-        if (deviceState.State == CrOnc.DeviceState.UNINITIALIZED) {
+        if (deviceState.deviceState == mojom.DeviceStateType.kUninitialized) {
           return this.i18n('internetDeviceInitializing');
         }
-      } else if (deviceState.Type == CrOnc.Type.TETHER) {
-        if (deviceState.State == CrOnc.DeviceState.UNINITIALIZED) {
+      } else if (deviceState.type == mojom.NetworkType.kTether) {
+        if (deviceState.deviceState == mojom.DeviceStateType.kUninitialized) {
           return this.i18n('tetherEnableBluetooth');
         }
       }
       // Enabled or enabling states.
-      if (deviceState.State == CrOnc.DeviceState.ENABLED) {
+      if (deviceState.deviceState == mojom.DeviceStateType.kEnabled) {
         if (this.networkStateList.length > 0) {
           return CrOncStrings.networkListItemNotConnected;
         }
         return CrOncStrings.networkListItemNoNetwork;
       }
-      if (deviceState.State == CrOnc.DeviceState.ENABLING) {
+      if (deviceState.deviceState == mojom.DeviceStateType.kEnabling) {
         return this.i18n('internetDeviceEnabling');
       }
     }
@@ -109,61 +114,59 @@
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties} networkState
+   * @param {!OncMojo.NetworkStateProperties} networkState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {string}
    * @private
    */
-  getConnectionStateText_: function(networkState) {
-    const state = networkState ? networkState.ConnectionState : null;
-    if (!state) {
+  getConnectionStateText_: function(networkState, deviceState) {
+    const connectionState = networkState ? networkState.connectionState : null;
+    if (!connectionState) {
       return '';
     }
-    const name = CrOnc.getNetworkName(networkState);
-    switch (state) {
-      case CrOnc.ConnectionState.CONNECTED:
-        return name;
-      case CrOnc.ConnectionState.CONNECTING:
-        if (name) {
-          return CrOncStrings.networkListItemConnectingTo.replace('$1', name);
-        }
-        return CrOncStrings.networkListItemConnecting;
-      case CrOnc.ConnectionState.NOT_CONNECTED:
-        if (networkState.Type == CrOnc.Type.CELLULAR && networkState.Cellular &&
-            networkState.Cellular.Scanning) {
-          return this.i18n('internetMobileSearching');
-        }
-        return CrOncStrings.networkListItemNotConnected;
+    const name =
+        networkState ? OncMojo.getNetworkDisplayName(networkState) : '';
+    if (OncMojo.connectionStateIsConnected(connectionState)) {
+      return name;
     }
-    assertNotReached();
-    return state;
+    if (connectionState == mojom.ConnectionStateType.kConnecting) {
+      return name ?
+          CrOncStrings.networkListItemConnectingTo.replace('$1', name) :
+          CrOncStrings.networkListItemConnecting;
+    }
+    if (networkState.type == mojom.NetworkType.kCellular && deviceState &&
+        deviceState.scanning) {
+      return this.i18n('internetMobileSearching');
+    }
+    return CrOncStrings.networkListItemNotConnected;
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties} activeNetworkState
+   * @param {!OncMojo.NetworkStateProperties} activeNetworkState
    * @return {boolean}
    * @private
    */
   showPolicyIndicator_: function(activeNetworkState) {
     return (activeNetworkState !== undefined &&
-            activeNetworkState.ConnectionState ==
-                CrOnc.ConnectionState.CONNECTED) ||
-        this.isPolicySource(activeNetworkState.Source);
+            OncMojo.connectionStateIsConnected(
+                activeNetworkState.connectionState)) ||
+        this.isPolicySourceMojo(activeNetworkState.source);
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {boolean}
    * @private
    */
   showSimInfo_: function(deviceState) {
-    if (!deviceState || deviceState.Type != CrOnc.Type.CELLULAR) {
+    if (!deviceState || deviceState.type != mojom.NetworkType.kCellular) {
       return false;
     }
     return this.simLockedOrAbsent_(deviceState);
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties} deviceState
+   * @param {!OncMojo.DeviceStateProperties} deviceState
    * @return {boolean}
    * @private
    */
@@ -171,26 +174,29 @@
     if (this.deviceIsEnabled_(deviceState)) {
       return false;
     }
-    if (deviceState.SIMPresent === false) {
+    if (!deviceState.simLockStatus) {
+      return false;
+    }
+    if (deviceState.simLockStatus.simAbsent) {
       return true;
     }
-    const simLockType =
-        deviceState.SIMLockStatus ? deviceState.SIMLockStatus.LockType : '';
+    const simLockType = deviceState.simLockStatus.lockType;
     return simLockType == CrOnc.LockType.PIN ||
         simLockType == CrOnc.LockType.PUK;
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {boolean} Whether or not the device state is enabled.
    * @private
    */
   deviceIsEnabled_: function(deviceState) {
-    return !!deviceState && deviceState.State == CrOnc.DeviceState.ENABLED;
+    return !!deviceState &&
+        deviceState.deviceState == mojom.DeviceStateType.kEnabled;
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {boolean}
    * @private
    */
@@ -198,17 +204,18 @@
     if (!deviceState) {
       return false;
     }
-    switch (deviceState.Type) {
-      case CrOnc.Type.ETHERNET:
-      case CrOnc.Type.VPN:
+    switch (deviceState.type) {
+      case mojom.NetworkType.kEthernet:
+      case mojom.NetworkType.kVPN:
         return false;
-      case CrOnc.Type.TETHER:
+      case mojom.NetworkType.kTether:
         return true;
-      case CrOnc.Type.WI_FI:
-      case CrOnc.Type.WI_MAX:
-        return deviceState.State != CrOnc.DeviceState.UNINITIALIZED;
-      case CrOnc.Type.CELLULAR:
-        return deviceState.State != CrOnc.DeviceState.UNINITIALIZED &&
+      case mojom.NetworkType.kWiFi:
+      case mojom.NetworkType.kWiMAX:
+        return deviceState.deviceState != mojom.DeviceStateType.kUninitialized;
+      case mojom.NetworkType.kCellular:
+        return deviceState.deviceState !=
+            mojom.DeviceStateType.kUninitialized &&
             !this.simLockedOrAbsent_(deviceState);
     }
     assertNotReached();
@@ -216,18 +223,18 @@
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {boolean}
    * @private
    */
   enableToggleIsEnabled_: function(deviceState) {
     return this.enableToggleIsVisible_(deviceState) &&
-        deviceState.State != CrOnc.DeviceState.PROHIBITED &&
-        deviceState.State != CrOnc.DeviceState.UNINITIALIZED;
+        deviceState.deviceState != mojom.DeviceStateType.kProhibited &&
+        deviceState.deviceState != mojom.DeviceStateType.kUninitialized;
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
    * @return {string}
    * @private
    */
@@ -235,13 +242,13 @@
     if (!this.enableToggleIsVisible_(deviceState)) {
       return '';
     }
-    switch (deviceState.Type) {
-      case CrOnc.Type.TETHER:
-      case CrOnc.Type.CELLULAR:
+    switch (deviceState.type) {
+      case mojom.NetworkType.kTether:
+      case mojom.NetworkType.kCellular:
         return this.i18n('internetToggleMobileA11yLabel');
-      case CrOnc.Type.WI_FI:
+      case mojom.NetworkType.kWiFi:
         return this.i18n('internetToggleWiFiA11yLabel');
-      case CrOnc.Type.WI_MAX:
+      case mojom.NetworkType.kWiMAX:
         return this.i18n('internetToggleWiMAXA11yLabel');
     }
     assertNotReached();
@@ -249,21 +256,21 @@
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties} activeNetworkState
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
-   * @param {!Array<!CrOnc.NetworkStateProperties>} networkStateList
+   * @param {!OncMojo.NetworkStateProperties} activeNetworkState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
+   * @param {!Array<!OncMojo.NetworkStateProperties>} networkStateList
    * @return {boolean}
    * @private
    */
   showDetailsIsVisible_: function(
       activeNetworkState, deviceState, networkStateList) {
     return this.deviceIsEnabled_(deviceState) &&
-        (!!activeNetworkState.GUID || networkStateList.length > 0);
+        (!!activeNetworkState.guid || networkStateList.length > 0);
   },
 
   /**
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
-   * @param {!Array<!CrOnc.NetworkStateProperties>} networkStateList
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
+   * @param {!Array<!OncMojo.NetworkStateProperties>} networkStateList
    * @return {boolean}
    * @private
    */
@@ -271,14 +278,17 @@
     if (!deviceState) {
       return false;
     }
-    const type = deviceState.Type;
-    if (type == CrOnc.Type.TETHER ||
-        (type == CrOnc.Type.CELLULAR && this.tetherDeviceState)) {
+    const type = deviceState.type;
+    if (type == mojom.NetworkType.kTether ||
+        (type == mojom.NetworkType.kCellular && this.tetherDeviceState)) {
       // The "Mobile data" subpage should always be shown if Tether networks are
       // available, even if there are currently no associated networks.
       return true;
     }
-    const minlen = (type == CrOnc.Type.WI_FI || type == CrOnc.Type.VPN) ? 1 : 2;
+    const minlen =
+        (type == mojom.NetworkType.kWiFi || type == mojom.NetworkType.kVPN) ?
+        1 :
+        2;
     return networkStateList.length >= minlen;
   },
 
@@ -289,14 +299,13 @@
   onShowDetailsTap_: function(event) {
     if (!this.deviceIsEnabled_(this.deviceState)) {
       if (this.enableToggleIsEnabled_(this.deviceState)) {
-        this.fire(
-            'device-enabled-toggled',
-            {enabled: true, type: this.deviceState.Type});
+        const type = this.deviceState.type;
+        this.fire('device-enabled-toggled', {enabled: true, type: type});
       }
     } else if (this.shouldShowSubpage_(
                    this.deviceState, this.networkStateList)) {
-      this.fire('show-networks', {type: this.deviceState.Type});
-    } else if (this.activeNetworkState.GUID) {
+      this.fire('show-networks', this.deviceState.type);
+    } else if (this.activeNetworkState.guid) {
       this.fire('show-detail', this.activeNetworkState);
     } else if (this.networkStateList.length > 0) {
       this.fire('show-detail', this.networkStateList[0]);
@@ -305,22 +314,22 @@
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties} activeNetworkState
-   * @param {!CrOnc.DeviceStateProperties|undefined} deviceState
-   * @param {!Array<!CrOnc.NetworkStateProperties>} networkStateList
+   * @param {!OncMojo.NetworkStateProperties} activeNetworkState
+   * @param {!OncMojo.DeviceStateProperties|undefined} deviceState
+   * @param {!Array<!OncMojo.NetworkStateProperties>} networkStateList
    * @return {string}
    * @private
    */
   getDetailsA11yString_: function(
       activeNetworkState, deviceState, networkStateList) {
     if (!this.shouldShowSubpage_(deviceState, networkStateList)) {
-      if (activeNetworkState.GUID) {
-        return CrOnc.getNetworkName(activeNetworkState);
+      if (activeNetworkState.guid) {
+        return OncMojo.getNetworkDisplayName(activeNetworkState);
       } else if (networkStateList.length > 0) {
-        return CrOnc.getNetworkName(networkStateList[0]);
+        return OncMojo.getNetworkDisplayName(networkStateList[0]);
       }
     }
-    return this.getNetworkTypeString_(deviceState.Type);
+    return this.getNetworkTypeString_(deviceState.type);
   },
 
   /**
@@ -329,10 +338,11 @@
    * @private
    */
   onDeviceEnabledChange_: function(event) {
+    assert(this.deviceState);
     const deviceIsEnabled = this.deviceIsEnabled_(this.deviceState);
-    const type = this.deviceState ? this.deviceState.Type : '';
     this.fire(
-        'device-enabled-toggled', {enabled: !deviceIsEnabled, type: type});
+        'device-enabled-toggled',
+        {enabled: !deviceIsEnabled, type: this.deviceState.type});
   },
 
   /**
@@ -342,7 +352,7 @@
   getTitleText_: function() {
     assert(CrOncStrings);
     return this.networkTitleText ||
-        this.getNetworkTypeString_(this.activeNetworkState.Type);
+        this.getNetworkTypeString_(this.activeNetworkState.type);
   },
 
   /**
@@ -355,7 +365,7 @@
   },
 
   /**
-   * @param {!chrome.networkingPrivate.NetworkType} type
+   * @param {!mojom.NetworkType} type
    * @return {string}
    * @private
    */
@@ -363,9 +373,11 @@
     // The shared Cellular/Tether subpage is referred to as "Mobile".
     // TODO(khorimoto): Remove once Cellular/Tether are split into their own
     // sections.
-    if (type == CrOnc.Type.CELLULAR || type == CrOnc.Type.TETHER) {
-      return this.i18n('OncTypeMobile');
+    if (type == mojom.NetworkType.kCellular ||
+        type == mojom.NetworkType.kTether) {
+      type = mojom.NetworkType.kMobile;
     }
-    return this.i18n('OncType' + type);
+    return this.i18n('OncType' + OncMojo.getNetworkTypeString(type));
   },
 });
+})();
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.html b/chrome/browser/resources/settings/languages_page/languages_page.html
index 4456949..4546af40 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.html
+++ b/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -12,7 +12,7 @@
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
diff --git a/chrome/browser/resources/settings/multidevice_page/BUILD.gn b/chrome/browser/resources/settings/multidevice_page/BUILD.gn
index 6f6bd02..7818234 100644
--- a/chrome/browser/resources/settings/multidevice_page/BUILD.gn
+++ b/chrome/browser/resources/settings/multidevice_page/BUILD.gn
@@ -110,6 +110,7 @@
       "..:route",
       "//ui/webui/resources/cr_elements/chromeos/network:cr_network_listener_behavior",
       "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types",
+      "//ui/webui/resources/js/chromeos:onc_mojo",
     ]
     externs_list = [ "$externs_path/networking_private.js" ]
     extra_sources = [ "$interfaces_path/networking_private_interface.js" ]
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js b/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js
index 11bea12e..808af92 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js
@@ -23,12 +23,6 @@
       type: Object,
       value: settings.routes,
     },
-
-    /** Overridden from NetworkListenerBehavior. */
-    networkingPrivate: {
-      type: Object,
-      value: chrome.networkingPrivate,
-    },
   },
 
   /** @private {?settings.MultiDeviceBrowserProxy} */
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html
index 6475b01..326a4c5 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html
@@ -3,6 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_icon.html">
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_listener_behavior.html">
+<link rel="import" href="chrome://resources/html/chromeos/onc_mojo.html">
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="../route.html">
 <link rel="import" href="../settings_shared_css.html">
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js
index 210705b..8dc4cf2 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js
@@ -7,7 +7,7 @@
  * This element provides a layer between the settings-multidevice-subpage
  * element and the internet_page folder's network-summary-item. It is
  * responsible for loading initial tethering network data from the
- * chrome.networkingPrivate API as well as updating the data in real time. It
+ * networkConfig mojo API as well as updating the data in real time. It
  * serves a role comparable to the internet_page's network-summary element.
  */
 
@@ -21,17 +21,8 @@
 
   properties: {
     /**
-     * Interface for networkingPrivate calls.
-     * @private {!NetworkingPrivate}
-     */
-    networkingPrivate_: {
-      type: Object,
-      value: chrome.networkingPrivate,
-    },
-
-    /**
      * The device state for tethering.
-     * @private {?CrOnc.DeviceStateProperties|undefined}
+     * @private {?OncMojo.DeviceStateProperties|undefined}
      */
     deviceState_: Object,
 
@@ -39,7 +30,7 @@
      * The network state for a potential tethering host phone. Note that there
      * is at most one because only one MultiDevice host phone is allowed on an
      * account at a given time.
-     * @private {?CrOnc.NetworkStateProperties|undefined}
+     * @private {?OncMojo.NetworkStateProperties|undefined}
      */
     activeNetworkState_: Object,
 
@@ -65,6 +56,16 @@
     },
   },
 
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
+  networkConfigProxy_: null,
+
+  /** @override */
+  created: function() {
+    this.networkConfigProxy_ =
+        network_config.MojoInterfaceProviderImpl.getInstance()
+            .getMojoServiceProxy();
+  },
+
   /** @override */
   attached: function() {
     this.updateTetherDeviceState_();
@@ -82,22 +83,14 @@
    * @private
    */
   onActiveNetworksChanged: function(networks) {
-    const id = this.activeNetworkState_.GUID;
-    if (!networks.find(network => network.guid == id)) {
+    const guid = this.activeNetworkState_.guid;
+    if (!networks.find(network => network.guid == guid)) {
       return;
     }
-    this.networkingPrivate_.getState(id, newNetworkState => {
-      if (chrome.runtime.lastError) {
-        const message = chrome.runtime.lastError.message;
-        if (message != 'Error.NetworkUnavailable' &&
-            message != 'Error.InvalidNetworkGuid') {
-          console.error(
-              'Unexpected networkingPrivate.getState error: ' + message +
-              ' For: ' + id);
-          return;
-        }
+    this.networkConfigProxy_.getNetworkState(guid).then(response => {
+      if (response.result) {
+        this.activeNetworkState_ = response.result;
       }
-      this.activeNetworkState_ = newNetworkState;
     });
   },
 
@@ -112,48 +105,60 @@
   },
 
   /**
-   * Retrieves device states (CrOnc.DeviceStateProperties) and sets
-   * this.deviceState_ to the retrieved Instant Tethering state (or undefined if
-   * there is none) in its callback. Note that the function
-   * chrome.networkingPrivate.getDevicePolicy() retrieves at most one object per
-   * network type (CrOnc.Type) so, in particular there will be at most one state
-   * for Instant Tethering.
+   * Retrieves device states (OncMojo.DeviceStateProperties) and sets
+   * this.deviceState_ to the retrieved Tether device state (or undefined if
+   * there is none). Note that crosNetworkConfig.getDeviceStateList retrieves at
+   * most one device per NetworkType so there will be at most one Tether device
+   * state.
    * @private
    */
   updateTetherDeviceState_: function() {
-    this.networkingPrivate_.getDeviceStates(deviceStates => {
-      this.deviceState_ =
-          deviceStates.find(
-              deviceState => deviceState.Type == CrOnc.Type.TETHER) ||
-          {Type: CrOnc.Type.TETHER, State: CrOnc.DeviceState.DISABLED};
+    this.networkConfigProxy_.getDeviceStateList().then(response => {
+      const kTether = chromeos.networkConfig.mojom.NetworkType.kTether;
+      const deviceStates = response.result;
+      const deviceState =
+          deviceStates.find(deviceState => deviceState.type == kTether);
+      this.deviceState_ = deviceState || {
+        deviceState: chromeos.networkConfig.mojom.DeviceStateType.kDisabled,
+        managedNetworkAvailable: false,
+        scanning: false,
+        simAbsent: false,
+        type: kTether,
+      };
     });
   },
 
   /**
    * Retrieves all Instant Tethering network states
-   * (CrOnc.NetworkStateProperties). Note that there is at most one because
+   * (OncMojo.NetworkStateProperties). Note that there is at most one because
    * only one host is allowed on an account at a given time. Then it sets
    * this.activeNetworkState_ to that network if there is one or a dummy object
    * with an empty string for a GUID otherwise.
    * @private
    */
   updateTetherNetworkState_: function() {
-    this.networkingPrivate_.getNetworks(
-        {networkType: CrOnc.Type.TETHER}, networkStates => {
-          this.activeNetworkState_ =
-              networkStates[0] || {GUID: '', Type: CrOnc.Type.TETHER};
-        });
+    const kTether = chromeos.networkConfig.mojom.NetworkType.kTether;
+    const filter = {
+      filter: chromeos.networkConfig.mojom.FilterType.kVisible,
+      limit: 1,
+      networkType: kTether,
+    };
+    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+      const networks = response.result;
+      this.activeNetworkState_ =
+          networks[0] || OncMojo.getDefaultNetworkState(kTether);
+    });
   },
 
   /**
    * Returns an array containing the active network state if there is one
    * (note that if there is not GUID will be falsy).  Returns an empty array
    * otherwise.
-   * @return {!Array<CrOnc.NetworkStateProperties>}
+   * @return {!Array<chromeos.networkConfig.mojom.NetworkStateProperties>}
    * @private
    */
   getNetworkStateList_: function() {
-    return this.activeNetworkState_.GUID ? [this.activeNetworkState_] : [];
+    return this.activeNetworkState_.guid ? [this.activeNetworkState_] : [];
   },
 
   /**
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
index 040d1e8..cb74a00 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
+++ b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
diff --git a/chrome/browser/resources/settings/people_page/change_picture.html b/chrome/browser/resources/settings/people_page/change_picture.html
index d83319e..1dc7a5d0 100644
--- a/chrome/browser/resources/settings/people_page/change_picture.html
+++ b/chrome/browser/resources/settings/people_page/change_picture.html
@@ -88,7 +88,8 @@
             capture-video-label="$i18n{captureVideo}"
             switch-mode-to-camera-label="$i18n{switchModeToCamera}"
             switch-mode-to-video-label="$i18n{switchModeToVideo}"
-            camera-video-mode-enabled="[[cameraVideoModeEnabled_]]">
+            camera-video-mode-enabled="[[cameraVideoModeEnabled_]]"
+            on-keys-pressed="onCameraPaneKeysPressed_">
         </cr-picture-pane>
         <div id="authorCredit"
             hidden="[[!isAuthorCreditShown_(selectedItem_)]]">
diff --git a/chrome/browser/resources/settings/people_page/change_picture.js b/chrome/browser/resources/settings/people_page/change_picture.js
index f97aa38e..8dd7052 100644
--- a/chrome/browser/resources/settings/people_page/change_picture.js
+++ b/chrome/browser/resources/settings/people_page/change_picture.js
@@ -237,6 +237,17 @@
         videomode ? 'videoModeAccessibleText' : 'photoModeAccessibleText'));
   },
 
+  /**
+   * Callback the iron-a11y-keys "keys-pressed" event bubbles up from the
+   * cr-camera-pane.
+   * @param {!CustomEvent<!{key: string, keyboardEvent: Object}>} event
+   * @private
+   */
+  onCameraPaneKeysPressed_(event) {
+    this.$.pictureList.focus();
+    this.$.pictureList.onKeysPressed(event);
+  },
+
   /** @private */
   onDiscardImage_: function() {
     // Prevent image from being discarded if old image is pending.
diff --git a/chrome/browser/resources/settings/people_page/import_data_dialog.html b/chrome/browser/resources/settings/people_page/import_data_dialog.html
index 1dedfd5..9eb241a 100644
--- a/chrome/browser/resources/settings/people_page/import_data_dialog.html
+++ b/chrome/browser/resources/settings/people_page/import_data_dialog.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
diff --git a/chrome/browser/resources/settings/people_page/user_list.html b/chrome/browser/resources/settings/people_page/user_list.html
index bc59dbd..f6ba5eb 100644
--- a/chrome/browser/resources/settings/people_page/user_list.html
+++ b/chrome/browser/resources/settings/people_page/user_list.html
@@ -39,6 +39,7 @@
 
       :host([disabled]) .user-list {
         opacity: var(--cr-disabled-opacity);
+        overflow: auto;
       }
     </style>
     <div class="user-list" scrollable>
diff --git a/chrome/browser/resources/settings/people_page/users_page.html b/chrome/browser/resources/settings/people_page/users_page.html
index 7cb7af9..7bfa99c 100644
--- a/chrome/browser/resources/settings/people_page/users_page.html
+++ b/chrome/browser/resources/settings/people_page/users_page.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
diff --git a/chrome/browser/resources/settings/printing_page/BUILD.gn b/chrome/browser/resources/settings/printing_page/BUILD.gn
index 0a750d1..f7e22d6 100644
--- a/chrome/browser/resources/settings/printing_page/BUILD.gn
+++ b/chrome/browser/resources/settings/printing_page/BUILD.gn
@@ -66,7 +66,10 @@
 
   js_library("cups_edit_printer_dialog") {
     deps = [
+      "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider",
       "//ui/webui/resources/cr_elements:cr_scrollable_behavior",
+      "//ui/webui/resources/cr_elements/chromeos/network:cr_network_listener_behavior",
+      "//ui/webui/resources/js/chromeos:onc_mojo",
     ]
   }
 
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
index 8d50621..5900c7f 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
index 70c6c2d..311d400 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
@@ -1,9 +1,12 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_components/chromeos/network/mojo_interface_provider.html">
+<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_listener_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html">
+<link rel="import" href="chrome://resources/html/chromeos/onc_mojo.html">
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="cups_add_printer_dialog_elements.html">
 <link rel="import" href="cups_printer_dialog_util.html">
@@ -31,7 +34,8 @@
               on-input="onPrinterInfoChange_"
               value="{{pendingPrinter_.printerAddress}}"
               disabled="[[!networkProtocolActive_]]"
-              maxlength=63>
+              maxlength=63
+              readonly="[[!isOnline_]]">
           </cr-input>
         </div>
         <div class="settings-box two-line">
@@ -41,7 +45,8 @@
               <select class="md-select" aria-labelledby="printerProtocol"
                   value="[[pendingPrinter_.printerProtocol]]"
                   on-change="onProtocolChange_"
-                  disabled="[[!networkProtocolActive_]]">
+                  disabled="[[!protocolSelectEnabled(isOnline_,
+                      networkProtocolActive_)]]">
                 <option value="ipp" disabled="[[!networkProtocolActive_]]">
                   $i18n{printerProtocolIpp}
                 </option>
@@ -74,8 +79,9 @@
           <cr-input id="printerQueue" label="$i18n{printerQueue}"
               value="{{pendingPrinter_.printerQueue}}"
               on-input="onPrinterInfoChange_"
+              maxlength=64
               disabled="[[!networkProtocolActive_]]"
-              maxlength=64>
+              readonly="[[!isOnline_]]">
           </cr-input>
         </div>
         <div class="settings-box two-line">
@@ -87,14 +93,16 @@
           <cr-searchable-drop-down items="[[manufacturerList]]"
               id="printerPPDManufacturer"
               label="$i18n{printerManufacturer}"
-              value="{{pendingPrinter_.ppdManufacturer}}">
+              value="{{pendingPrinter_.ppdManufacturer}}"
+              readonly="[[!isOnline_]]">
           </cr-searchable-drop-down>
         </div>
         <div class="settings-box two-line">
           <cr-searchable-drop-down items="[[modelList]]"
               id="printerPPDModel"
               label="$i18n{printerModel}"
-              value="{{pendingPrinter_.ppdModel}}">
+              value="{{pendingPrinter_.ppdModel}}"
+              readonly="[[!isOnline_]]">
           </cr-searchable-drop-down>
         </div>
         <div id="ppdLabel" class="field-label">
@@ -109,7 +117,8 @@
               error-message="$i18n{selectDriverErrorMessage}"
               invalid="[[invalidPPD_]]">
           </cr-input>
-          <cr-button class="browse-button" on-click="onBrowseFile_">
+          <cr-button class="browse-button" on-click="onBrowseFile_"
+          disabled="[[!isOnline_]]">
             $i18n{selectDriverButtonText}
           </cr-button>
         </div>
@@ -123,7 +132,7 @@
         </cr-button>
         <cr-button class="action-button" on-click="onSaveTap_"
             disabled="[[!canSavePrinter_(pendingPrinter_.*,
-                                         printerInfoChanged_)]]">
+                printerInfoChanged_, isOnline_)]]">
           $i18n{editPrinterButtonText}
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
index 903fa018..fd67d724 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
@@ -12,6 +12,7 @@
 
   behaviors: [
     CrScrollableBehavior,
+    CrNetworkListenerBehavior,
   ],
 
   properties: {
@@ -101,6 +102,12 @@
       type: String,
       value: '',
     },
+
+    /** @private */
+    isOnline_: {
+      type: Boolean,
+      value: true,
+    },
   },
 
   observers: [
@@ -109,11 +116,24 @@
     'onModelChanged_(pendingPrinter_.ppdModel)',
   ],
 
+  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigProxy} */
+  networkConfigProxy_: null,
+
+  /** @override */
+  created: function() {
+    this.networkConfigProxy_ =
+        network_config.MojoInterfaceProviderImpl.getInstance()
+            .getMojoServiceProxy();
+  },
+
   /** @override */
   attached: function() {
     // Create a copy of activePrinter so that we can modify its fields.
     this.pendingPrinter_ = /** @type{CupsPrinterInfo} */
         (Object.assign({}, this.activePrinter));
+
+    this.refreshNetworks_();
+
     settings.CupsPrintersBrowserProxyImpl.getInstance()
         .getPrinterPpdManufacturerAndModel(this.pendingPrinter_.printerId)
         .then(
@@ -127,6 +147,18 @@
   },
 
   /**
+   * CrosNetworkConfigObserver impl
+   * @param {!Array<chromeos.networkConfig.mojom.NetworkStateProperties>}
+   *     networks
+   * @private
+   */
+  onActiveNetworksChanged: function(networks) {
+    this.isOnline_ = networks.some(function(network) {
+      return OncMojo.connectionStateIsConnected(network.connectionState);
+    });
+  },
+
+  /**
    * @param {!{path: string, value: string}} change
    * @private
    */
@@ -169,16 +201,20 @@
   /** @private */
   onSaveTap_: function() {
     this.updateActivePrinter_();
-    if (this.needsReconfigured_) {
-      settings.CupsPrintersBrowserProxyImpl.getInstance()
-          .reconfigureCupsPrinter(this.activePrinter)
-          .then(this.onPrinterEdited_.bind(this));
-    } else {
+    if (!this.needsReconfigured_ || !this.isOnline_) {
+      // If we don't need to reconfigure or we are offline, just update the
+      // printer name.
       settings.CupsPrintersBrowserProxyImpl.getInstance()
           .updateCupsPrinter(
               this.activePrinter.printerId, this.activePrinter.printerName)
           .then(this.onPrinterEdited_.bind(this));
+    } else {
+      settings.CupsPrintersBrowserProxyImpl.getInstance()
+          .reconfigureCupsPrinter(this.activePrinter)
+          .then(this.onPrinterEdited_.bind(this));
     }
+
+    this.$$('add-printer-dialog').close();
   },
 
   /**
@@ -237,7 +273,8 @@
    * @private
    */
   canSavePrinter_: function() {
-    return this.printerInfoChanged_ && this.isPrinterValid();
+    return this.printerInfoChanged_ &&
+        (this.isPrinterValid() || !this.isOnline_);
   },
 
   /**
@@ -337,7 +374,7 @@
     this.userPPD_ = settings.printing.getBaseName(path);
   },
 
-  /*
+  /**
    * Returns true if the printer has valid name, address, and PPD.
    * @return {boolean}
    */
@@ -348,15 +385,48 @@
             this.pendingPrinter_.printerPPDPath);
   },
 
-  /*
+
+  /**
    * Helper function to copy over modified fields to activePrinter.
    * @private
    */
   updateActivePrinter_: function() {
+    if (!this.isOnline_) {
+      // If we are not online, only copy over the printerName.
+      this.activePrinter.printerName = this.pendingPrinter_.printerName;
+      return;
+    }
+
     this.activePrinter =
         /** @type{CupsPrinterInfo} */ (Object.assign({}, this.pendingPrinter_));
     // Set ppdModel since there is an observer that clears ppdmodel's value when
     // ppdManufacturer changes.
     this.activePrinter.ppdModel = this.pendingPrinter_.ppdModel;
   },
+
+  /**
+   * Callback function when networks change.
+   * @private
+   */
+  refreshNetworks_: function() {
+    this.networkConfigProxy_
+        .getNetworkStateList({
+          filter: chromeos.networkConfig.mojom.FilterType.kActive,
+          networkType: chromeos.networkConfig.mojom.NetworkType.kAll,
+          limit: chromeos.networkConfig.mojom.kNoLimit,
+        })
+        .then((responseParams) => {
+          this.onActiveNetworksChanged(responseParams.result);
+        });
+  },
+
+  /**
+   * Returns true if the printer protocol select field should be enabled.
+   * @return {boolean}
+   * @private
+   */
+  protocolSelectEnabled: function() {
+    return this.isOnline_ && this.networkProtocolActive_;
+  },
+
 });
diff --git a/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html b/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
index bee85419..e125bb36 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
+++ b/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../settings_shared_css.html">
 
 <!-- Common styles for CUPS printer settings. -->
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers.html b/chrome/browser/resources/settings/printing_page/cups_printers.html
index 7bb83da..29455d3 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers.html
+++ b/chrome/browser/resources/settings/printing_page/cups_printers.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="cups_add_printer_dialog.html">
diff --git a/chrome/browser/resources/settings/privacy_page/BUILD.gn b/chrome/browser/resources/settings/privacy_page/BUILD.gn
index dca9d34..a77c34a 100644
--- a/chrome/browser/resources/settings/privacy_page/BUILD.gn
+++ b/chrome/browser/resources/settings/privacy_page/BUILD.gn
@@ -92,6 +92,7 @@
     ":security_keys_browser_proxy",
     ":security_keys_pin_field",
     "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
 
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_credential_management_dialog.html b/chrome/browser/resources/settings/privacy_page/security_keys_credential_management_dialog.html
index 5e6ff75..1bf6a09 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_credential_management_dialog.html
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_credential_management_dialog.html
@@ -3,6 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
diff --git a/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html b/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
index 59552af8..5c5863d8 100644
--- a/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
+++ b/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="reset_browser_proxy.html">
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index ba363d6..8511441 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -26,6 +26,7 @@
  *   BASIC: (undefined|!settings.Route),
  *   BLUETOOTH: (undefined|!settings.Route),
  *   BLUETOOTH_DEVICES: (undefined|!settings.Route),
+ *   CAPTIONS: (undefined|!settings.Route),
  *   CERTIFICATES: (undefined|!settings.Route),
  *   CHANGE_PICTURE: (undefined|!settings.Route),
  *   CHROME_CLEANUP: (undefined|!settings.Route),
@@ -263,8 +264,7 @@
     }
 
     // TODO(hsuregan): Remove once this file is forked.
-    if (loadTimeData.valueExists('showOSSettings') &&
-        loadTimeData.getBoolean('showOSSettings')) {
+    if (loadTimeData.getBoolean('showOSSettings')) {
       r.PERSONALIZATION =
           r.BASIC.createSection('/personalization', 'personalization');
     }
@@ -463,6 +463,20 @@
       // </if>
 
       r.ACCESSIBILITY = r.ADVANCED.createSection('/accessibility', 'a11y');
+
+      // <if expr="chromeos or is_linux">
+      if (loadTimeData.getBoolean('enableCaptionSettings')) {
+        r.CAPTIONS = r.ACCESSIBILITY.createChild('/captions');
+      }
+      // </if>
+
+      // <if expr="is_win">
+      if (loadTimeData.getBoolean('enableCaptionSettings') &&
+          !loadTimeData.getBoolean('isWindows10OrNewer')) {
+        r.CAPTIONS = r.ACCESSIBILITY.createChild('/captions');
+      }
+      // </if>
+
       // <if expr="chromeos">
       r.MANAGE_ACCESSIBILITY =
           r.ACCESSIBILITY.createChild('/manageAccessibility');
@@ -495,7 +509,10 @@
     // "About" is the only section in About, but we still need to create the
     // route in order to show the subpage on Chrome OS.
     r.ABOUT_ABOUT = r.ABOUT.createSection('/help/about', 'about');
-    r.DETAILED_BUILD_INFO = r.ABOUT_ABOUT.createChild('/help/details');
+    // TODO(aee): Remove once this file is forked.
+    if (loadTimeData.getBoolean('showOSSettings')) {
+      r.DETAILED_BUILD_INFO = r.ABOUT_ABOUT.createChild('/help/details');
+    }
     // </if>
 
     return r;
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
index b275f05..6dc16d5 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
diff --git a/chrome/browser/resources/settings/search_page/search_page.html b/chrome/browser/resources/settings/search_page/search_page.html
index 1f488de7..b98d0a8 100644
--- a/chrome/browser/resources/settings/search_page/search_page.html
+++ b/chrome/browser/resources/settings/search_page/search_page.html
@@ -5,7 +5,7 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../controls/extension_controlled_indicator.html">
 <link rel="import" href="../controls/settings_toggle_button.html">
 <link rel="import" href="../icons.html">
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 6e6b2c12..beae2b9 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -21,6 +21,18 @@
                  type="chrome_html"
                  preprocess="true"
                  allowexternalscript="true" />
+      <structure name="IDR_SETTINGS_CAPTIONS_SUBPAGE_HTML"
+                 file="a11y_page/captions_subpage.html"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_CAPTIONS_SUBPAGE_JS"
+                 file="a11y_page/captions_subpage.js"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_CAPTIONS_BROWSER_PROXY_HTML"
+                 file="a11y_page/captions_browser_proxy.html"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_CAPTIONS_BROWSER_PROXY_JS"
+                 file="a11y_page/captions_browser_proxy.js"
+                 type="chrome_html" />
       <if expr="chromeos">
         <structure name="IDR_SETTINGS_MANAGE_A11Y_PAGE_JS"
                    file="a11y_page/manage_a11y_page.js"
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.html b/chrome/browser/resources/settings/site_settings/all_sites.html
index 2b9d1a5c..53a046e 100644
--- a/chrome/browser/resources/settings/site_settings/all_sites.html
+++ b/chrome/browser/resources/settings/site_settings/all_sites.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
 <link rel="import" href="../global_scroll_target_behavior.html">
 <link rel="import" href="../route.html">
diff --git a/chrome/browser/resources/settings/site_settings/media_picker.html b/chrome/browser/resources/settings/site_settings/media_picker.html
index deba343..92f8c15f 100644
--- a/chrome/browser/resources/settings/site_settings/media_picker.html
+++ b/chrome/browser/resources/settings/site_settings/media_picker.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="../settings_shared_css.html">
 <link rel="import" href="../settings_vars_css.html">
 
diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b/chrome/browser/resources/settings/site_settings/site_details.html
index 7c8025fc..d9e9115 100644
--- a/chrome/browser/resources/settings/site_settings/site_details.html
+++ b/chrome/browser/resources/settings/site_settings/site_details.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.html b/chrome/browser/resources/settings/site_settings/site_details_permission.html
index 55250bf..c5a1e00 100644
--- a/chrome/browser/resources/settings/site_settings/site_details_permission.html
+++ b/chrome/browser/resources/settings/site_settings/site_details_permission.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
diff --git a/chrome/browser/resources/usb_internals/descriptor_panel.js b/chrome/browser/resources/usb_internals/descriptor_panel.js
index ac18374b..2bf5efb 100644
--- a/chrome/browser/resources/usb_internals/descriptor_panel.js
+++ b/chrome/browser/resources/usb_internals/descriptor_panel.js
@@ -24,7 +24,7 @@
   const ENDPOINT_DESCRIPTOR_TYPE = 0x05;
   const BOS_DESCRIPTOR_TYPE = 0x0F;
 
-  const DEVICE_CAPABILITY_DESCRIPTOR_TYPE_PLATFORM = 0x05;
+  const DEVICE_CAPABILITY_DESCRIPTOR_TYPE_PLATFORM_TYPE = 0x05;
 
   const DEVICE_DESCRIPTOR_LENGTH = 18;
   const CONFIGURATION_DESCRIPTOR_LENGTH = 9;
@@ -114,10 +114,10 @@
      * @param {!HTMLElement} rootElement
      */
     constructor(usbDeviceProxy, rootElement) {
-      /** @private {!device.mojom.UsbDeviceInterface} */
+      /** @type {!device.mojom.UsbDeviceInterface} */
       this.usbDeviceProxy_ = usbDeviceProxy;
 
-      /** @private {!HTMLElement} */
+      /** @type {!HTMLElement} */
       this.rootElement_ = rootElement;
     }
 
@@ -127,7 +127,7 @@
      * @param {!DescriptorPanel} stringDescriptorPanel
      */
     setStringDescriptorPanel(stringDescriptorPanel) {
-      /** @private {!DescriptorPanel} */
+      /** @type {!DescriptorPanel} */
       this.stringDescriptorPanel_ = stringDescriptorPanel;
     }
 
@@ -138,7 +138,6 @@
       this.rootElement_.querySelectorAll('descriptorpanel')
           .forEach(el => el.remove());
       this.rootElement_.querySelectorAll('error').forEach(el => el.remove());
-      this.rootElement_.querySelectorAll('warn').forEach(el => el.remove());
       this.rootElement_.querySelectorAll('descriptorpaneltitle')
           .forEach(el => el.remove());
     }
@@ -175,7 +174,7 @@
           // Clear the previous string descriptors.
           item.querySelector('.tree-children').textContent = '';
           this.stringDescriptorPanel_.clearView();
-          this.stringDescriptorPanel_.renderStringDescriptorForAllLanguages(
+          this.stringDescriptorPanel_.getStringDescriptorForAllLanguages_(
               index, item);
         });
       } else if (index < 0) {
@@ -189,28 +188,29 @@
     }
 
     /**
-     * Renders a URL descriptor item for the URL Descriptor Index and
-     * adds it to the URL descriptor index item.
+     * Adds a button for getting URL descriptor.
      * @param {!Uint8Array} rawData
-     * @param {number} offset The offset of the URL descriptor index field.
+     * @param {number} offset The offset of the URL descriptor index.
      * @param {!cr.ui.TreeItem} item
      * @param {string} fieldLabel Not used in this function, but used to match
      *     other extraTreeItemFormatter.
      * @private
      */
-    async renderLandingPageItem_(rawData, offset, item, fieldLabel) {
-      // The second to last byte is the vendor code used to query URL
-      // descriptor. Last byte is index of url descriptor. These are defined by
-      // the WebUSB specification: http://wicg.github.io/webusb/
-      const vendorCode = rawData[offset + WEB_USB_VENDOR_CODE_OFFSET];
-      const urlIndex = rawData[offset + WEB_USB_URL_DESCRIPTOR_INDEX_OFFSET];
-      const url = await this.getUrlDescriptor_(vendorCode, urlIndex);
-
-      const landingPageItem = customTreeItem(url);
-      item.add(landingPageItem);
-
-      landingPageItem.querySelector('.tree-label')
-          .addEventListener('click', () => window.open(url, '_blank'));
+    renderUrlDescriptorIndexItem_(rawData, offset, item, fieldLabel) {
+      const index = rawData[offset];
+      if (index > 0) {
+        const buttonTemplate = document.querySelector('#raw-data-tree-button');
+        const button = document.importNode(buttonTemplate.content, true)
+                           .querySelector('button');
+        item.labelElement.appendChild(button);
+        button.addEventListener('click', (event) => {
+          event.stopPropagation();
+          // Clear the previous URL descriptors.
+          item.querySelector('.tree-children').textContent = '';
+          this.getUrlDescriptor_(
+              rawData, offset - WEB_USB_URL_DESCRIPTOR_INDEX_OFFSET, item);
+        });
+      }
     }
 
     /**
@@ -331,11 +331,9 @@
     }
 
     /**
-     * Gets device descriptor of current device.
-     * @return {!Uint8Array}
-     * @private
+     * Gets device descriptor of current device, and display it.
      */
-    async getDeviceDescriptor_() {
+    async getDeviceDescriptor() {
       /** @type {!device.mojom.UsbControlTransferParams} */
       const usbControlTransferParams = {};
       usbControlTransferParams.type =
@@ -346,37 +344,32 @@
       usbControlTransferParams.value = (DEVICE_DESCRIPTOR_TYPE << 8);
       usbControlTransferParams.index = 0;
 
-      const response = await this.usbDeviceProxy_.controlTransferIn(
-          usbControlTransferParams, DEVICE_DESCRIPTOR_LENGTH,
-          CONTROL_TRANSFER_TIMEOUT_MS);
-
-      checkTransferSuccess(
-          response.status, 'Failed to read the device descriptor.',
-          this.rootElement_);
-
-      return new Uint8Array(response.data);
+      try {
+        await this.usbDeviceProxy_.open();
+        const response = await this.usbDeviceProxy_.controlTransferIn(
+            usbControlTransferParams, DEVICE_DESCRIPTOR_LENGTH,
+            CONTROL_TRANSFER_TIMEOUT_MS);
+        checkTransferSuccess(
+            response.status, 'Failed to read the device descriptor.',
+            this.rootElement_);
+        this.renderDeviceDescriptor_(new Uint8Array(response.data));
+      } catch (e) {
+        showError(e.message, this.rootElement_);
+      } finally {
+        await this.usbDeviceProxy_.close();
+      }
     }
 
     /**
      * Renders a view to display device descriptor hex data in both tree view
      * and raw form.
+     * @param {!Uint8Array} rawData
+     * @private
      */
-    async renderDeviceDescriptor() {
-      let rawData;
-      try {
-        await this.usbDeviceProxy_.open();
-        rawData = await this.getDeviceDescriptor_();
-      } catch (e) {
-        showError(e.message, this.rootElement_);
-        // Stop rendering if failed to read the device descriptor.
-        return;
-      } finally {
-        await this.usbDeviceProxy_.close();
-      }
-
+    async renderDeviceDescriptor_(rawData) {
       const fields = [
         {
-          label: 'Length (should be 18): ',
+          label: `Length (should be ${DEVICE_DESCRIPTOR_LENGTH}): `,
           size: 1,
           formatter: formatByte,
         },
@@ -470,11 +463,9 @@
     }
 
     /**
-     * Gets configuration descriptor of current device.
-     * @return {!Uint8Array}
-     * @private
+     * Gets configuration descriptor of current device, and display it.
      */
-    async getConfigurationDescriptor_() {
+    async getConfigurationDescriptor() {
       /** @type {!device.mojom.UsbControlTransferParams} */
       const usbControlTransferParams = {};
       usbControlTransferParams.type =
@@ -485,51 +476,44 @@
       usbControlTransferParams.value = (CONFIGURATION_DESCRIPTOR_TYPE << 8);
       usbControlTransferParams.index = 0;
 
-      let response = await this.usbDeviceProxy_.controlTransferIn(
-          usbControlTransferParams, CONFIGURATION_DESCRIPTOR_LENGTH,
-          CONTROL_TRANSFER_TIMEOUT_MS);
-
-      checkTransferSuccess(
-          response.status,
-          'Failed to read the device configuration descriptor to determine ' +
-              'the total descriptor length.',
-          this.rootElement_);
-
-      const data = new DataView(new Uint8Array(response.data).buffer);
-      const length =
-          data.getUint16(CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_OFFSET, true);
-      // Re-gets the data use the full length.
-      response = await this.usbDeviceProxy_.controlTransferIn(
-          usbControlTransferParams, length, CONTROL_TRANSFER_TIMEOUT_MS);
-
-      checkTransferSuccess(
-          response.status,
-          'Failed to read the complete configuration descriptor.',
-          this.rootElement_);
-
-      return new Uint8Array(response.data);
+      try {
+        await this.usbDeviceProxy_.open();
+        let response = await this.usbDeviceProxy_.controlTransferIn(
+            usbControlTransferParams, CONFIGURATION_DESCRIPTOR_LENGTH,
+            CONTROL_TRANSFER_TIMEOUT_MS);
+        checkTransferSuccess(
+            response.status,
+            'Failed to read the device configuration descriptor to determine ' +
+                'the total descriptor length.',
+            this.rootElement_);
+        const dataView = new DataView(new Uint8Array(response.data).buffer);
+        const length = dataView.getUint16(
+            CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_OFFSET, true);
+        // Re-gets the data using the full length.
+        response = await this.usbDeviceProxy_.controlTransferIn(
+            usbControlTransferParams, length, CONTROL_TRANSFER_TIMEOUT_MS);
+        checkTransferSuccess(
+            response.status,
+            'Failed to read the complete configuration descriptor.',
+            this.rootElement_);
+        this.renderConfigurationDescriptor_(new Uint8Array(response.data));
+      } catch (e) {
+        showError(e.message, this.rootElement_);
+      } finally {
+        await this.usbDeviceProxy_.close();
+      }
     }
 
     /**
      * Renders a view to display configuration descriptor hex data in both tree
      * view and raw form.
+     * @param {!Uint8Array} rawData
+     * @private
      */
-    async renderConfigurationDescriptor() {
-      let rawData;
-      try {
-        await this.usbDeviceProxy_.open();
-        rawData = await this.getConfigurationDescriptor_();
-      } catch (e) {
-        showError(e.message, this.rootElement_);
-        // Stop rendering if failed to read the configuration descriptor.
-        return;
-      } finally {
-        await this.usbDeviceProxy_.close();
-      }
-
+    async renderConfigurationDescriptor_(rawData) {
       const fields = [
         {
-          label: 'Length (should be 9): ',
+          label: `Length (should be ${CONFIGURATION_DESCRIPTOR_LENGTH}): `,
           size: 1,
           formatter: formatByte,
         },
@@ -580,7 +564,7 @@
       renderRawDataBytes(rawDataByteElement, rawData);
 
       const expectNumInterfaces =
-          rawData[CONFIGURATION_DESCRIPTOR_NUM_INTERFACES_OFFSET];
+          rawData[CONFIGURATION_DESCRIPTOR_NUM_INTERFACES_OFFSET] || 0;
 
       let offset = renderRawDataTree(
           rawDataTreeRoot, rawDataByteElement, fields, rawData, 0,
@@ -656,13 +640,6 @@
     renderInterfaceDescriptor_(
         rawDataTreeRoot, rawDataByteElement, rawData, originalOffset,
         indexInterface, expectNumEndpoints) {
-      if (originalOffset + INTERFACE_DESCRIPTOR_LENGTH > rawData.length) {
-        showError(
-            `Failed to read the interface descriptor at index ${
-                indexInterface}.`,
-            this.rootElement_);
-      }
-
       const parentClassName = `descriptor-interface-${indexInterface}`;
       const interfaceItem =
           customTreeItem(`Interface ${indexInterface}`, parentClassName);
@@ -670,7 +647,7 @@
 
       const fields = [
         {
-          label: 'Length (should be 7): ',
+          label: `Length (should be ${INTERFACE_DESCRIPTOR_LENGTH}): `,
           size: 1,
           formatter: formatByte,
         },
@@ -717,20 +694,15 @@
         },
       ];
 
-      expectNumEndpoints +=
-          rawData[originalOffset + INTERFACE_DESCRIPTOR_NUM_ENDPOINTS_OFFSET];
-
+      const numEndpointsOffset =
+          originalOffset + INTERFACE_DESCRIPTOR_NUM_ENDPOINTS_OFFSET;
+      if (numEndpointsOffset < rawData.length) {
+        expectNumEndpoints += rawData[numEndpointsOffset];
+      }
       const offset = renderRawDataTree(
           interfaceItem, rawDataByteElement, fields, rawData, originalOffset,
           this.rootElement_, parentClassName);
 
-      if (offset !== originalOffset + INTERFACE_DESCRIPTOR_LENGTH) {
-        showError(
-            `An error occurred while rendering interface descriptor at index ${
-                indexInterface}.`,
-            this.rootElement_);
-      }
-
       return [offset, expectNumEndpoints];
     }
 
@@ -749,12 +721,6 @@
     renderEndpointDescriptor_(
         rawDataTreeRoot, rawDataByteElement, rawData, originalOffset,
         indexEndpoint) {
-      if (originalOffset + ENDPOINT_DESCRIPTOR_LENGTH > rawData.length) {
-        showError(
-            `Failed to read the endpoint descriptor at index ${indexEndpoint}.`,
-            this.rootElement_);
-      }
-
       const parentClassName = `descriptor-endpoint-${indexEndpoint}`;
       const endpointItem =
           customTreeItem(`Endpoint ${indexEndpoint}`, parentClassName);
@@ -762,7 +728,7 @@
 
       const fields = [
         {
-          label: 'Length (should be 7): ',
+          label: `Length (should be ${ENDPOINT_DESCRIPTOR_LENGTH}): `,
           size: 1,
           formatter: formatByte,
         },
@@ -797,13 +763,6 @@
           endpointItem, rawDataByteElement, fields, rawData, originalOffset,
           this.rootElement_, parentClassName);
 
-      if (offset !== originalOffset + ENDPOINT_DESCRIPTOR_LENGTH) {
-        showError(
-            `An error occurred while rendering endpoint descriptor at index ${
-                indexEndpoint}.`,
-            this.rootElement_);
-      }
-
       return offset;
     }
 
@@ -823,14 +782,6 @@
         indexUnknown) {
       const length =
           rawData[originalOffset + STANDARD_DESCRIPTOR_LENGTH_OFFSET];
-
-      if (originalOffset + length > rawData.length) {
-        showError(
-            `Failed to read the unknown descriptor at index ${indexUnknown}.`,
-            this.rootElement_);
-        return;
-      }
-
       const parentClassName = `descriptor-unknown-${indexUnknown}`;
       const unknownItem =
           customTreeItem(`Unknown Descriptor ${indexUnknown}`, parentClassName);
@@ -851,7 +802,7 @@
 
       let offset = renderRawDataTree(
           unknownItem, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       const rawDataByteElements = rawDataByteElement.querySelectorAll('span');
 
@@ -928,13 +879,15 @@
 
     /**
      * Gets the string descriptor for the current device with the given index
-     * and language code.
+     * and language code, and display it.
      * @param {number} index
      * @param {number} languageCode
+     * @param {!cr.ui.TreeItem=} treeItem
      * @return {{languageCode:string,rawData:!Uint8Array}}
      * @private
      */
-    async getStringDescriptorForLanguageCode_(index, languageCode) {
+    async getStringDescriptorForLanguageCode_(
+        index, languageCode, treeItem = undefined) {
       /** @type {!device.mojom.UsbControlTransferParams} */
       const usbControlTransferParams = {};
       usbControlTransferParams.type =
@@ -942,24 +895,28 @@
       usbControlTransferParams.recipient =
           device.mojom.UsbControlTransferRecipient.DEVICE;
       usbControlTransferParams.request = GET_DESCRIPTOR_REQUEST;
-
       usbControlTransferParams.index = languageCode;
-
       usbControlTransferParams.value = (STRING_DESCRIPTOR_TYPE << 8) | index;
 
-      const response = await this.usbDeviceProxy_.controlTransferIn(
-          usbControlTransferParams, MAX_STRING_DESCRIPTOR_LENGTH,
-          CONTROL_TRANSFER_TIMEOUT_MS);
+      try {
+        await this.usbDeviceProxy_.open();
+        const response = await this.usbDeviceProxy_.controlTransferIn(
+            usbControlTransferParams, MAX_STRING_DESCRIPTOR_LENGTH,
+            CONTROL_TRANSFER_TIMEOUT_MS);
+        checkTransferSuccess(
+            response.status,
+            `Failed to read the device string descriptor of index: ${
+                index}, language: ${parseLanguageCode(languageCode)}.`,
+            this.rootElement_);
 
-      const languageCodeStr = parseLanguageCode(languageCode);
-      checkTransferSuccess(
-          response.status,
-          `Failed to read the device string descriptor of index: ${
-              index}, language: ${languageCodeStr}.`,
-          this.rootElement_);
-
-      const rawData = new Uint8Array(response.data);
-      return {languageCodeStr, rawData};
+        this.indexInput_.value = index;
+        this.renderStringDescriptorForLanguageCode_(
+            new Uint8Array(response.data), languageCode, treeItem);
+      } catch (e) {
+        showError(e.message, this.rootElement_);
+      } finally {
+        await this.usbDeviceProxy_.close();
+      }
     }
 
     /**
@@ -968,37 +925,13 @@
      * @param {number} index
      * @param {number} languageCode
      * @param {cr.ui.TreeItem=} treeItem
+     * @private
      */
-    async renderStringDescriptorForLanguageCode(
-        index, languageCode, treeItem = undefined) {
+    renderStringDescriptorForLanguageCode_(
+        rawData, languageCode, treeItem = undefined) {
       this.rootElement_.hidden = false;
 
-      this.indexInput_.value = index;
-
-      let rawDataMap;
-      try {
-        await this.usbDeviceProxy_.open();
-        rawDataMap =
-            await this.getStringDescriptorForLanguageCode_(index, languageCode);
-      } catch (e) {
-        showError(e.message, this.rootElement_);
-        // Stop rendering if failed to read the string descriptor.
-        return;
-      } finally {
-        await this.usbDeviceProxy_.close();
-      }
-
-      const languageStr = rawDataMap.languageCodeStr;
-      const rawData = rawDataMap.rawData;
-
-      const length = rawData[STANDARD_DESCRIPTOR_LENGTH_OFFSET];
-      if (length > rawData.length) {
-        showError(
-            `Failed to read the string descriptor at index ${index} in ${
-                languageStr}.`,
-            this.rootElement_);
-        return;
-      }
+      const languageStr = parseLanguageCode(languageCode);
 
       const fields = [
         {
@@ -1031,8 +964,7 @@
 
       // The first two elements of rawData are length and descriptor type.
       const stringDescriptor = decodeUtf16Array(rawData.slice(2), true);
-      const parentClassName =
-          `descriptor-string-${index}-language-code-${languageStr}`;
+      const parentClassName = `descriptor-string-language-code-${languageStr}`;
       const stringDescriptorItem = customTreeItem(
           `${languageStr}: ${stringDescriptor}`, parentClassName);
       rawDataTreeRoot.add(stringDescriptorItem);
@@ -1055,8 +987,9 @@
      * given index.
      * @param {number} index
      * @param {cr.ui.TreeItem=} treeItem
+     * @private
      */
-    async renderStringDescriptorForAllLanguages(index, treeItem = undefined) {
+    async getStringDescriptorForAllLanguages_(index, treeItem = undefined) {
       this.rootElement_.hidden = false;
 
       this.indexInput_.value = index;
@@ -1065,7 +998,7 @@
       const languageCodesList = await this.getAllLanguageCodes();
 
       for (const languageCode of languageCodesList) {
-        await this.renderStringDescriptorForLanguageCode(
+        await this.getStringDescriptorForLanguageCode_(
             index, languageCode, treeItem);
       }
     }
@@ -1089,17 +1022,18 @@
       const languageCodeInput =
           this.rootElement_.querySelector('#language-code-input');
 
-      button.addEventListener('click', () => {
+      button.addEventListener('click', async () => {
         this.clearView();
         const index = Number.parseInt(this.indexInput_.value);
         if (this.checkParamValid_(index, 'Index', 1, 255)) {
           if (languageCodeInput.value === 'All') {
-            this.renderStringDescriptorForAllLanguages(index);
+            await this.getStringDescriptorForAllLanguages_(index);
           } else {
             const languageCode = Number.parseInt(languageCodeInput.value);
             if (this.checkParamValid_(
                     languageCode, 'Language Code', 0, 65535)) {
-              this.renderStringDescriptorForLanguageCode(index, languageCode);
+              await this.getStringDescriptorForLanguageCode_(
+                  index, languageCode);
             }
           }
         }
@@ -1118,11 +1052,9 @@
     /**
      * Gets the Binary device Object Store (BOS) descriptor of the current
      * device, which contains the WebUSB descriptor and Microsoft OS 2.0
-     * descriptor.
-     * @return {!Uint8Array}
-     * @private
+     * descriptor, and display it.
      */
-    async getBosDescriptor_() {
+    async getBosDescriptor() {
       /** @type {!device.mojom.UsbControlTransferParams} */
       const usbControlTransferParams = {};
       usbControlTransferParams.type =
@@ -1133,47 +1065,40 @@
       usbControlTransferParams.value = (BOS_DESCRIPTOR_TYPE << 8);
       usbControlTransferParams.index = 0;
 
-      let response = await this.usbDeviceProxy_.controlTransferIn(
-          usbControlTransferParams, BOS_DESCRIPTOR_HEADER_LENGTH,
-          CONTROL_TRANSFER_TIMEOUT_MS);
-
-      checkTransferSuccess(
-          response.status,
-          'Failed to read the device BOS descriptor to determine ' +
-              'the total descriptor length.',
-          this.rootElement_);
-
-      const data = new DataView(new Uint8Array(response.data).buffer);
-      const length = data.getUint16(BOS_DESCRIPTOR_TOTAL_LENGTH_OFFSET, true);
-
-      // Re-gets the data use the full length.
-      response = await this.usbDeviceProxy_.controlTransferIn(
-          usbControlTransferParams, length, CONTROL_TRANSFER_TIMEOUT_MS);
-
-      checkTransferSuccess(
-          response.status, 'Failed to read the complete BOS descriptor.',
-          this.rootElement_);
-
-      return new Uint8Array(response.data);
+      try {
+        await this.usbDeviceProxy_.open();
+        let response = await this.usbDeviceProxy_.controlTransferIn(
+            usbControlTransferParams, BOS_DESCRIPTOR_HEADER_LENGTH,
+            CONTROL_TRANSFER_TIMEOUT_MS);
+        checkTransferSuccess(
+            response.status,
+            'Failed to read the device BOS descriptor to determine ' +
+                'the total descriptor length.',
+            this.rootElement_);
+        const dataView = new DataView(new Uint8Array(response.data).buffer);
+        const length =
+            dataView.getUint16(BOS_DESCRIPTOR_TOTAL_LENGTH_OFFSET, true);
+        // Re-gets the data using the full length.
+        response = await this.usbDeviceProxy_.controlTransferIn(
+            usbControlTransferParams, length, CONTROL_TRANSFER_TIMEOUT_MS);
+        checkTransferSuccess(
+            response.status, 'Failed to read the complete BOS descriptor.',
+            this.rootElement_);
+        await this.renderBosDescriptor_(new Uint8Array(response.data));
+      } catch (e) {
+        showError(e.message, this.rootElement_);
+      } finally {
+        await this.usbDeviceProxy_.close();
+      }
     }
 
     /**
      * Renders a view to display BOS descriptor hex data in both tree view
      * and raw form.
+     * @param {!Uint8Array} rawData
+     * @private
      */
-    async renderBosDescriptor() {
-      let rawData;
-      try {
-        await this.usbDeviceProxy_.open();
-        rawData = await this.getBosDescriptor_();
-      } catch (e) {
-        showError(e.message, this.rootElement_);
-        // Stop rendering if failed to read the BOS descriptor.
-        return;
-      } finally {
-        await this.usbDeviceProxy_.close();
-      }
-
+    async renderBosDescriptor_(rawData) {
       const fields = [
         {
           'label': 'Length (should be 5): ',
@@ -1206,7 +1131,8 @@
       renderRawDataBytes(rawDataByteElement, rawData);
 
       let offset = renderRawDataTree(
-          rawDataTreeRoot, rawDataByteElement, fields, rawData, 0);
+          rawDataTreeRoot, rawDataByteElement, fields, rawData, 0,
+          this.rootElement_);
 
       if (offset !== BOS_DESCRIPTOR_HEADER_LENGTH) {
         showError(
@@ -1224,32 +1150,35 @@
              rawData.length) {
         switch (
             rawData[offset + BOS_DESCRIPTOR_DEVICE_CAPABILITY_TYPE_OFFSET]) {
-          case DEVICE_CAPABILITY_DESCRIPTOR_TYPE_PLATFORM:
+          case DEVICE_CAPABILITY_DESCRIPTOR_TYPE_PLATFORM_TYPE:
             if (isSameUuid(rawData, offset, WEB_USB_CAPABILITY_UUID)) {
-              offset = this.renderWebUsbPlatformDescriptor_(
+              this.renderWebUsbPlatformDescriptor_(
                   rawDataTreeRoot, rawDataByteElement, rawData, offset,
                   indexWebUsb);
               indexWebUsb++;
+              offset += rawData[offset + STANDARD_DESCRIPTOR_LENGTH_OFFSET];
               break;
             } else if (isSameUuid(
                            rawData, offset,
                            MS_OS_20_PLATFORM_CAPABILITY_UUID)) {
-              offset = this.renderMsOs20PlatformDescriptor_(
+              this.renderMsOs20PlatformDescriptor_(
                   rawDataTreeRoot, rawDataByteElement, rawData, offset,
                   indexMsOs20);
               indexMsOs20++;
+              offset += rawData[offset + STANDARD_DESCRIPTOR_LENGTH_OFFSET];
               break;
             }
           default:
-            offset = this.renderUnknownBosDescriptor_(
+            this.renderUnknownBosDescriptor_(
                 rawDataTreeRoot, rawDataByteElement, rawData, offset,
                 indexUnknownDevCapability);
             indexUnknownDevCapability++;
+            offset += rawData[offset + STANDARD_DESCRIPTOR_LENGTH_OFFSET];
         }
       }
 
       const expectNumDevCapabilityDescriptors =
-          rawData[BOS_DESCRIPTOR_NUM_DEVICE_CAPABILITIES_OFFSET];
+          rawData[BOS_DESCRIPTOR_NUM_DEVICE_CAPABILITIES_OFFSET] || 0;
       const encounteredNumBosDescriptors =
           indexWebUsb + indexMsOs20 + indexUnknownDevCapability;
       if (encounteredNumBosDescriptors !== expectNumDevCapabilityDescriptors) {
@@ -1272,7 +1201,6 @@
      * @param {number} originalOffset The start offset of the WebUSB platform
      *     capability descriptor.
      * @param {number} indexWebUsb
-     * @return {number}
      * @private
      */
     renderWebUsbPlatformDescriptor_(
@@ -1328,10 +1256,7 @@
           label: 'Landing Page: ',
           size: 1,
           formatter: formatByte,
-          extraTreeItemFormatter: (rawData, offset, item, fieldLabel) =>
-              this.renderLandingPageItem_(
-                  rawData, offset - WEB_USB_URL_DESCRIPTOR_INDEX_OFFSET, item,
-                  fieldLabel)
+          extraTreeItemFormatter: this.renderUrlDescriptorIndexItem_.bind(this),
         },
       ];
 
@@ -1345,8 +1270,6 @@
                 indexWebUsb}.`,
             this.rootElement_);
       }
-
-      return offset;
     }
 
     /**
@@ -1358,7 +1281,6 @@
      * @param {number} originalOffset The start offset of the Microsoft OS 2.0
      *     platform capability descriptor.
      * @param {number} indexMsOs20
-     * @return {number}
      * @private
      */
     renderMsOs20PlatformDescriptor_(
@@ -1422,8 +1344,7 @@
       // descriptor set information structures. Stop if accessing the descriptor
       // set information structure would cause us to read past the end of the
       // buffer.
-      while ((offset + MS_OS_20_DESCRIPTOR_SET_INFORMATION_LENGTH) <=
-             rawData.length) {
+      while (offset < rawData.length) {
         offset = this.renderMsOs20DescriptorSetInfo_(
             msOs20Item, rawDataByteElement, rawData, offset,
             indexMsOs20DescriptorSetInfo, indexMsOs20);
@@ -1436,8 +1357,6 @@
                 ` index ${indexMsOs20}.`,
             this.rootElement_);
       }
-
-      return offset;
     }
 
     /**
@@ -1526,7 +1445,6 @@
      * @param {number} originalOffset The start offset of the unknown BOS
      *     descriptor.
      * @param {number} indexUnknownDevCapability
-     * @return {number}
      * @private
      */
     renderUnknownBosDescriptor_(
@@ -1569,18 +1487,23 @@
         rawDataByteElements[offset].classList.add(`field-offset-${offset}`);
         rawDataByteElements[offset].classList.add(parentClassName);
       }
-
-      return offset;
     }
 
     /**
-     * Gets the URL Descriptor, and returns the parsed URL.
-     * @param {number} vendorCode
-     * @param {number} urlIndex
-     * @return {string}
+     * Gets the URL Descriptor, renders a URL descriptor item and adds it to
+     * the URL descriptor index item.
+     * @param {!Uint8Array} rawData
+     * @param {number} offset The offset of the WebUSB descriptor.
+     * @param {!cr.ui.TreeItem} item The URL descriptor index item.
      * @private
      */
-    async getUrlDescriptor_(vendorCode, urlIndex) {
+    async getUrlDescriptor_(rawData, offset, item) {
+      // The second to last byte is the vendor code used to query URL
+      // descriptor. Last byte is index of url descriptor. These are defined by
+      // the WebUSB specification: http://wicg.github.io/webusb/
+      const vendorCode = rawData[offset + WEB_USB_VENDOR_CODE_OFFSET];
+      const urlIndex = rawData[offset + WEB_USB_URL_DESCRIPTOR_INDEX_OFFSET];
+
       /** @type {!device.mojom.UsbControlTransferParams} */
       const usbControlTransferParams = {};
       usbControlTransferParams.recipient =
@@ -1593,17 +1516,40 @@
       usbControlTransferParams.value = urlIndex;
       usbControlTransferParams.index = GET_URL_REQUEST;
 
-      let urlResponse;
       try {
         await this.usbDeviceProxy_.open();
         // Gets the URL descriptor.
-        urlResponse = await this.usbDeviceProxy_.controlTransferIn(
+        const urlResponse = await this.usbDeviceProxy_.controlTransferIn(
             usbControlTransferParams, MAX_URL_DESCRIPTOR_LENGTH,
             CONTROL_TRANSFER_TIMEOUT_MS);
 
         checkTransferSuccess(
             urlResponse.status, 'Failed to read the device URL descriptor.',
             this.rootElement_);
+
+        let url;
+        // URL Prefixes are defined by Chapter 4.3.1 of the WebUSB
+        // specification: http://wicg.github.io/webusb/
+        switch (urlResponse.data[2]) {
+          case 0:
+            url = 'http://';
+            break;
+          case 1:
+            url = 'https://';
+            break;
+          case 255:
+          default:
+            url = '';
+        }
+        // The first three elements of urlResponse.data are length, descriptor
+        // type and URL scheme prefix.
+        url += decodeUtf8Array(new Uint8Array(urlResponse.data.slice(3)));
+
+        const landingPageItem = customTreeItem(url, 'descriptor-url');
+        landingPageItem.labelElement.addEventListener(
+            'click', () => window.open(url, '_blank'));
+        item.add(landingPageItem);
+        item.expanded = true;
       } catch (e) {
         showError(e.message, this.rootElement_);
         // Stops parsing to string format URL if failed to read the URL
@@ -1612,26 +1558,6 @@
       } finally {
         await this.usbDeviceProxy_.close();
       }
-
-      let urlDescriptor;
-      // URL Prefixes are defined by Chapter 4.3.1 of the WebUSB specification:
-      // http://wicg.github.io/webusb/
-      switch (urlResponse.data[2]) {
-        case 0:
-          urlDescriptor = 'http://';
-          break;
-        case 1:
-          urlDescriptor = 'https://';
-          break;
-        case 255:
-        default:
-          urlDescriptor = '';
-      }
-      // The first three elements of urlResponse.data are length, descriptor
-      // type and URL scheme prefix.
-      urlDescriptor +=
-          decodeUtf8Array(new Uint8Array(urlResponse.data.slice(3)));
-      return urlDescriptor;
     }
 
     /**
@@ -1915,7 +1841,7 @@
 
       const offset = renderRawDataTree(
           item, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       if (offset !== originalOffset + length) {
         showError(
@@ -1980,7 +1906,7 @@
 
       const offset = renderRawDataTree(
           item, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       if (offset !== originalOffset + length) {
         showError(
@@ -2040,7 +1966,7 @@
 
       const offset = renderRawDataTree(
           item, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       if (offset !== originalOffset + length) {
         showError(
@@ -2202,7 +2128,7 @@
 
       const offset = renderRawDataTree(
           item, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       if (offset !== originalOffset + length) {
         showError(
@@ -2257,7 +2183,7 @@
 
       const offset = renderRawDataTree(
           item, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       if (offset !== originalOffset + length) {
         showError(
@@ -2310,7 +2236,7 @@
 
       const offset = renderRawDataTree(
           item, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       if (offset !== originalOffset + length) {
         showError(
@@ -2366,7 +2292,7 @@
 
       const offset = renderRawDataTree(
           item, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       if (offset !== originalOffset + length) {
         showError(
@@ -2416,7 +2342,7 @@
 
       let offset = renderRawDataTree(
           item, rawDataByteElement, fields, rawData, originalOffset,
-          parentClassName);
+          this.rootElement_, parentClassName);
 
       const rawDataByteElements = rawDataByteElement.querySelectorAll('span');
 
@@ -2467,7 +2393,7 @@
               response.status, 'Failed to send request.', this.rootElement_);
         }
       } catch (e) {
-        this.showError_(e.message);
+        showError(e.message, this.rootElement_);
         return;
       } finally {
         await this.usbDeviceProxy_.close();
diff --git a/chrome/browser/resources/usb_internals/devices_page.js b/chrome/browser/resources/usb_internals/devices_page.js
index 700759d6..e7743ac 100644
--- a/chrome/browser/resources/usb_internals/devices_page.js
+++ b/chrome/browser/resources/usb_internals/devices_page.js
@@ -374,7 +374,7 @@
         break;
     }
 
-    button.addEventListener('click', () => {
+    button.addEventListener('click', async () => {
       displayElement.hidden = !displayElement.hidden;
       // Clear the panel before rendering new data.
       descriptorPanel.clearView();
@@ -382,16 +382,16 @@
       if (!displayElement.hidden) {
         switch (panelType) {
           case 'device-descriptor':
-            descriptorPanel.renderDeviceDescriptor();
+            await descriptorPanel.getDeviceDescriptor();
             break;
           case 'configuration-descriptor':
-            descriptorPanel.renderConfigurationDescriptor();
+            await descriptorPanel.getConfigurationDescriptor();
             break;
           case 'string-descriptor':
-            descriptorPanel.getAllLanguageCodes();
+            await descriptorPanel.getAllLanguageCodes();
             break;
           case 'bos-descriptor':
-            descriptorPanel.renderBosDescriptor();
+            await descriptorPanel.getBosDescriptor();
             break;
         }
       }
@@ -431,7 +431,6 @@
   }
 
   return {
-    DevicePage,
     DevicesPage,
   };
 });
diff --git a/chrome/browser/resources/user_manager/create_profile.html b/chrome/browser/resources/user_manager/create_profile.html
index 61ab879..151db114 100644
--- a/chrome/browser/resources/user_manager/create_profile.html
+++ b/chrome/browser/resources/user_manager/create_profile.html
@@ -10,7 +10,7 @@
 <link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
@@ -84,9 +84,7 @@
 
       #nameInput {
         --cr-input-color: var(--cr-primary-text-color);
-        --cr-input-input: {
-          border-bottom: 1px solid var(--cr-secondary-text-color);
-        }
+        --cr-input-border-bottom: 1px solid var(--cr-secondary-text-color);
         margin-bottom: 24px;
         margin-top: 32px;
         width: 300px;
@@ -129,6 +127,7 @@
     <div class="container">
       <div id="title-bar">$i18n{createProfileTitle}</div>
       <cr-input id="nameInput" value="{{profileName_}}" pattern=".*\S.*"
+          placeholder="$i18n{createProfileNamePlaceholder}"
           auto-validate spellcheck="false">
       </cr-input>
       <cr-profile-avatar-selector avatars="[[availableIcons_]]"
diff --git a/chrome/browser/resources/user_manager/create_profile.js b/chrome/browser/resources/user_manager/create_profile.js
index 3ddd8cec..24aa9d51 100644
--- a/chrome/browser/resources/user_manager/create_profile.js
+++ b/chrome/browser/resources/user_manager/create_profile.js
@@ -103,8 +103,6 @@
     this.addWebUIListener('profile-icons-received', icons => {
       this.availableIcons_ = icons;
     });
-    this.addWebUIListener(
-        'profile-defaults-received', this.handleProfileDefaults_.bind(this));
 
     this.browserProxy_.getAvailableIcons();
   },
@@ -136,15 +134,6 @@
   },
 
   /**
-   * Handler for when the profile defaults are pushed from the browser.
-   * @param {!ProfileInfo} profileInfo Default Info for the new profile.
-   * @private
-   */
-  handleProfileDefaults_: function(profileInfo) {
-    this.profileName_ = profileInfo.name;
-  },
-
-  /**
    * Handler for the 'Save' button tap event.
    * @param {!Event} event
    * @private
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html
index 97449a2..52b75a13 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="navi_colors_css.html">
 
 <dom-module id="chooser-shared-css">
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/navi_colors_css.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/navi_colors_css.html
index 45b22f1..99846f3 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/navi_colors_css.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/navi_colors_css.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 
 <dom-module id="navi-colors-css">
   <template>
diff --git a/chrome/browser/resources/welcome/welcome_win10.html b/chrome/browser/resources/welcome/welcome_win10.html
index 5321eb7..3a4266cf 100644
--- a/chrome/browser/resources/welcome/welcome_win10.html
+++ b/chrome/browser/resources/welcome/welcome_win10.html
@@ -13,7 +13,7 @@
   <link rel="import" href="chrome://resources/html/util.html">
 
   <link rel="import" href="chrome://resources/html/action_link.html">
-  <link rel="import" href="chrome://resources/html/action_link_css.html">
+  <link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 
   <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
diff --git a/chrome/browser/safe_browsing/download_protection/download_feedback.cc b/chrome/browser/safe_browsing/download_protection/download_feedback.cc
index 421dcb5..7d8a7b060 100644
--- a/chrome/browser/safe_browsing/download_protection/download_feedback.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_feedback.cc
@@ -8,8 +8,10 @@
 #include "base/files/file_util.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task_runner.h"
+#include "chrome/browser/safe_browsing/download_protection/two_phase_uploader.h"
 #include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/net_errors.h"
@@ -65,6 +67,9 @@
                       const std::string& response);
 
   void RecordUploadResult(UploadResultType result);
+  void RecordErrorCodes(TwoPhaseUploader::State state,
+                        int net_error,
+                        int response_code);
 
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   scoped_refptr<base::TaskRunner> file_task_runner_;
@@ -78,6 +83,9 @@
 
   std::unique_ptr<TwoPhaseUploader> uploader_;
 
+  // The time at which we started uploading. Used for metrics.
+  base::Time uploader_start_time_;
+
   DISALLOW_COPY_AND_ASSIGN(DownloadFeedbackImpl);
 };
 
@@ -172,6 +180,7 @@
                  finish_callback),
       traffic_annotation);
   uploader_->Start();
+  uploader_start_time_ = base::Time::Now();
 }
 
 void DownloadFeedbackImpl::FinishedUpload(base::Closure finish_callback,
@@ -201,11 +210,15 @@
       RecordUploadResult(net_error != net::OK ? UPLOAD_METADATA_NET_ERROR
                                               : UPLOAD_METADATA_RESPONSE_ERROR);
       break;
-    default:
+    case TwoPhaseUploader::STATE_NONE:
       NOTREACHED();
       break;
   }
 
+  RecordErrorCodes(state, net_error, response_code);
+  UMA_HISTOGRAM_LONG_TIMES("SBDownloadFeedback.UploadDuration",
+                           base::Time::Now() - uploader_start_time_);
+
   uploader_.reset();
 
   finish_callback.Run();
@@ -224,6 +237,18 @@
                             UPLOAD_RESULT_MAX);
 }
 
+void DownloadFeedbackImpl::RecordErrorCodes(TwoPhaseUploader::State state,
+                                            int net_error,
+                                            int response_code) {
+  if (state == TwoPhaseUploader::UPLOAD_FILE) {
+    base::UmaHistogramSparse("SBDownloadFeedback.FileErrors",
+                             net_error == net::OK ? response_code : net_error);
+  } else if (state == TwoPhaseUploader::UPLOAD_METADATA) {
+    base::UmaHistogramSparse("SBDownloadFeedback.MetadataErrors",
+                             net_error == net::OK ? response_code : net_error);
+  }
+}
+
 }  // namespace
 
 // static
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
index b0110e2c..793bb904 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
@@ -303,6 +303,8 @@
     Profile* profile =
         Profile::FromBrowserContext(web_contents()->GetBrowserContext());
     safe_browsing_service->AddPrefService(profile->GetPrefs());
+    content::BrowserThread::RunAllPendingTasksOnThreadForTesting(
+        content::BrowserThread::IO);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     // EventRouterFactory redirects incognito context to original profile.
     test_event_router_ =
@@ -1111,6 +1113,8 @@
     TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(
         safe_browsing_service);
     g_browser_process->safe_browsing_service()->Initialize();
+    content::BrowserThread::RunAllPendingTasksOnThreadForTesting(
+        content::BrowserThread::IO);
   }
 
   void TearDown() override {
diff --git a/chrome/browser/safe_browsing/ui_manager_unittest.cc b/chrome/browser/safe_browsing/ui_manager_unittest.cc
index 49b3165..696512a6 100644
--- a/chrome/browser/safe_browsing/ui_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/ui_manager_unittest.cc
@@ -99,6 +99,8 @@
     safe_browsing_service->AddPrefService(
         Profile::FromBrowserContext(web_contents()->GetBrowserContext())
             ->GetPrefs());
+    content::BrowserThread::RunAllPendingTasksOnThreadForTesting(
+        content::BrowserThread::IO);
   }
 
   void TearDown() override {
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 35d3a27b..89c94a2ee 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -40,6 +40,7 @@
 #include "chrome/common/search.mojom.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/favicon_base/favicon_url_parser.h"
 #include "components/ntp_tiles/constants.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -133,18 +134,6 @@
                      chrome::kChromeSearchLocalNtpBackgroundFilename));
 }
 
-void DoDeleteThumbnailDataIfExists(
-    const base::FilePath& database_dir,
-    base::Optional<base::OnceCallback<void(bool)>> callback) {
-  bool result = false;
-  if (base::PathExists(database_dir)) {
-    base::DeleteFile(database_dir, true);
-    result = true;
-  }
-  if (callback.has_value())
-    std::move(*callback).Run(result);
-}
-
 // |GetBitmapMainColor| just wraps |CalculateKMeanColorOfBitmap|.
 // As |CalculateKMeanColorOfBitmap| is overloaded, it cannot be bind for async
 // call.
@@ -239,15 +228,11 @@
   most_visited_info_->is_visible =
       pref_service_->GetBoolean(prefs::kNtpShortcutsVisible);
 
-  if (profile_) {
-    DeleteThumbnailDataIfExists(profile_->GetPath(), base::nullopt);
-
-    if (profile_->GetResourceContext()) {
-      base::PostTaskWithTraits(
-          FROM_HERE, {content::BrowserThread::IO},
-          base::BindOnce(&InstantIOContext::SetUserDataOnIO,
-                         profile->GetResourceContext(), instant_io_context_));
-    }
+  if (profile_ && profile_->GetResourceContext()) {
+    base::PostTaskWithTraits(
+        FROM_HERE, {content::BrowserThread::IO},
+        base::BindOnce(&InstantIOContext::SetUserDataOnIO,
+                       profile->GetResourceContext(), instant_io_context_));
   }
 
   CreateDarkModeObserver(ui::NativeTheme::GetInstanceForNativeUi());
@@ -266,8 +251,9 @@
                               std::make_unique<LocalNtpSource>(profile_));
   content::URLDataSource::Add(profile_,
                               std::make_unique<NtpIconSource>(profile_));
-  content::URLDataSource::Add(profile_,
-                              std::make_unique<FaviconSource>(profile_));
+  content::URLDataSource::Add(
+      profile_, std::make_unique<FaviconSource>(
+                    profile_, chrome::FaviconUrlFormat::kFaviconLegacy));
   content::URLDataSource::Add(profile_,
                               std::make_unique<MostVisitedIframeSource>());
 
@@ -856,17 +842,6 @@
       base::BindOnce(IgnoreResult(&base::DeleteFile), path, false));
 }
 
-void InstantService::DeleteThumbnailDataIfExists(
-    const base::FilePath& profile_path,
-    base::Optional<base::OnceCallback<void(bool)>> callback) {
-  base::FilePath database_dir(
-      profile_path.Append(FILE_PATH_LITERAL("Thumbnails")));
-  base::PostTaskWithTraits(FROM_HERE,
-                           {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
-                           base::BindOnce(&DoDeleteThumbnailDataIfExists,
-                                          database_dir, std::move(callback)));
-}
-
 void InstantService::AddValidBackdropUrlForTesting(const GURL& url) const {
   background_service_->AddValidBackdropUrlForTesting(url);
 }
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index 08f47d6..e2f4cde 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -178,7 +178,6 @@
   friend class TestInstantService;
 
   FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ProcessIsolation);
-  FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, DeleteThumbnailDataIfExists);
   FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, GetNTPTileSuggestion);
   FRIEND_TEST_ALL_PREFIXES(InstantServiceTest,
                            DoesToggleMostVisitedOrCustomLinks);
@@ -231,14 +230,6 @@
 
   void RemoveLocalBackgroundImageCopy();
 
-  // Remove old user thumbnail data if it exists. If |callback| is provided,
-  // calls back true if the thumbnail data was deleted. Thumbnails have been
-  // deprecated as of M69.
-  // TODO(crbug.com/893362): Remove after M75.
-  void DeleteThumbnailDataIfExists(
-      const base::FilePath& profile_path,
-      base::Optional<base::OnceCallback<void(bool)>> callback);
-
   // Returns false if the custom background pref cannot be parsed, otherwise
   // returns true and sets custom_background_url to the value in the pref.
   bool IsCustomBackgroundPrefValid(GURL& custom_background_url);
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index 478dee3..2ff95f0 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -87,36 +87,6 @@
   EXPECT_EQ(ntp_tiles::TileTitleSource::TITLE_TAG, items[0].title_source);
 }
 
-TEST_F(InstantServiceTest, DeleteThumbnailDataIfExists) {
-  const std::string kTestData("test");
-  base::FilePath database_dir =
-      profile()->GetPath().Append(FILE_PATH_LITERAL("Thumbnails"));
-
-  if (!base::PathExists(database_dir))
-    ASSERT_TRUE(base::CreateDirectory(database_dir));
-  ASSERT_NE(-1, base::WriteFile(
-                    database_dir.Append(FILE_PATH_LITERAL("test_thumbnail")),
-                    kTestData.c_str(), kTestData.length()));
-
-  // Delete the thumbnail directory.
-  base::MockCallback<base::OnceCallback<void(bool)>> result;
-  EXPECT_CALL(result, Run(true));
-  instant_service_->DeleteThumbnailDataIfExists(
-      profile()->GetPath(),
-      base::Optional<base::OnceCallback<void(bool)>>(result.Get()));
-  thread_bundle()->RunUntilIdle();
-  EXPECT_FALSE(base::PathExists(database_dir));
-
-  // Delete should fail since the path does not exist.
-  base::MockCallback<base::OnceCallback<void(bool)>> result2;
-  EXPECT_CALL(result2, Run(false));
-  instant_service_->DeleteThumbnailDataIfExists(
-      profile()->GetPath(),
-      base::Optional<base::OnceCallback<void(bool)>>(result2.Get()));
-  thread_bundle()->RunUntilIdle();
-  EXPECT_FALSE(base::PathExists(database_dir));
-}
-
 TEST_F(InstantServiceTest, DoesToggleMostVisitedOrCustomLinks) {
   sync_preferences::TestingPrefServiceSyncable* pref_service =
       profile()->GetTestingPrefService();
diff --git a/chrome/browser/service_process/service_process_control_browsertest.cc b/chrome/browser/service_process/service_process_control_browsertest.cc
index 06a5af3..8ff6e0ce 100644
--- a/chrome/browser/service_process/service_process_control_browsertest.cc
+++ b/chrome/browser/service_process/service_process_control_browsertest.cc
@@ -218,7 +218,14 @@
   EXPECT_TRUE(ServiceProcessControl::GetInstance()->Shutdown());
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, LaunchAndReconnect) {
+// Flaky on macOS: https://crbug.com/978948
+#if defined(OS_MACOSX)
+#define MAYBE_LaunchAndReconnect DISABLED_LaunchAndReconnect
+#else
+#define MAYBE_LaunchAndReconnect LaunchAndReconnect
+#endif
+IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest,
+                       MAYBE_LaunchAndReconnect) {
   LaunchServiceProcessControlAndWait();
 
   // Make sure we are connected to the service process.
diff --git a/chrome/browser/sessions/tab_restore_browsertest.cc b/chrome/browser/sessions/tab_restore_browsertest.cc
index fcd6381..c9df3f1 100644
--- a/chrome/browser/sessions/tab_restore_browsertest.cc
+++ b/chrome/browser/sessions/tab_restore_browsertest.cc
@@ -895,3 +895,58 @@
   ASSERT_EQ(chrome::GetRestoreTabType(browser()),
             TabStripModelDelegate::RESTORE_TAB);
 }
+
+IN_PROC_BROWSER_TEST_F(TabRestoreTest, RestoreGroupedTab) {
+  const int tab_count = AddSomeTabs(browser(), 1);
+  ASSERT_LE(2, tab_count);
+
+  const int grouped_tab_index = tab_count - 1;
+  browser()->tab_strip_model()->AddToNewGroup({grouped_tab_index});
+  CloseTab(grouped_tab_index);
+
+  ASSERT_NO_FATAL_FAILURE(RestoreTab(0, grouped_tab_index));
+  ASSERT_EQ(tab_count, browser()->tab_strip_model()->count());
+
+  // Make sure the tab is *not* grouped when restored. We have not yet decided
+  // how to handle groups with the same group ID in different browser
+  // windows. Until we figure this out, we don't have a good way to handle
+  // restoring individual grouped tabs. TODO(crbug.com/930991): resolve this and
+  // change this expectation.
+  EXPECT_EQ(base::nullopt,
+            browser()->tab_strip_model()->GetTabGroupForTab(grouped_tab_index));
+}
+
+IN_PROC_BROWSER_TEST_F(TabRestoreTest, RestoreWindowWithGroupedTabs) {
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL(chrome::kChromeUINewTabURL),
+      WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
+  ASSERT_EQ(2u, active_browser_list_->size());
+
+  const int tab_count = AddSomeTabs(browser(), 3);
+  TabGroupId group1 = browser()->tab_strip_model()->AddToNewGroup(
+      {tab_count - 3, tab_count - 2});
+  TabGroupId group2 =
+      browser()->tab_strip_model()->AddToNewGroup({tab_count - 1});
+  CloseBrowserSynchronously(browser());
+  ASSERT_EQ(1u, active_browser_list_->size());
+
+  content::WindowedNotificationObserver open_window_observer(
+      chrome::NOTIFICATION_BROWSER_OPENED,
+      content::NotificationService::AllSources());
+  chrome::RestoreTab(GetBrowser(0));
+  open_window_observer.Wait();
+  ASSERT_EQ(2u, active_browser_list_->size());
+
+  Browser* restored_window = GetBrowser(1);
+  ASSERT_EQ(tab_count, restored_window->tab_strip_model()->count());
+  EXPECT_EQ(
+      base::make_optional(group1),
+      restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 3));
+  EXPECT_EQ(
+      base::make_optional(group1),
+      restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 2));
+  EXPECT_EQ(
+      base::make_optional(group2),
+      restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 1));
+}
diff --git a/chrome/browser/ssl/ssl_blocking_page.cc b/chrome/browser/ssl/ssl_blocking_page.cc
index 6f77a27..7392b89f 100644
--- a/chrome/browser/ssl/ssl_blocking_page.cc
+++ b/chrome/browser/ssl/ssl_blocking_page.cc
@@ -171,8 +171,6 @@
       callback_(callback),
       ssl_info_(ssl_info),
       overridable_(overridable),
-      expired_but_previously_allowed_(
-          (options_mask & SSLErrorUI::EXPIRED_BUT_PREVIOUSLY_ALLOWED) != 0),
       ssl_error_ui_(std::make_unique<SSLErrorUI>(request_url,
                                                  cert_error,
                                                  ssl_info,
diff --git a/chrome/browser/ssl/ssl_blocking_page.h b/chrome/browser/ssl/ssl_blocking_page.h
index 2ea50da..da775b0 100644
--- a/chrome/browser/ssl/ssl_blocking_page.h
+++ b/chrome/browser/ssl/ssl_blocking_page.h
@@ -113,10 +113,6 @@
   const net::SSLInfo ssl_info_;
   const bool overridable_;  // The UI allows the user to override the error.
 
-  // The user previously allowed a bad certificate, but the decision has now
-  // expired.
-  const bool expired_but_previously_allowed_;
-
   const std::unique_ptr<security_interstitials::SSLErrorUI> ssl_error_ui_;
 
   DISALLOW_COPY_AND_ASSIGN(SSLBlockingPage);
diff --git a/chrome/browser/ssl/ssl_error_handler.cc b/chrome/browser/ssl/ssl_error_handler.cc
index 50a0eac..51d57e00 100644
--- a/chrome/browser/ssl/ssl_error_handler.cc
+++ b/chrome/browser/ssl/ssl_error_handler.cc
@@ -623,8 +623,7 @@
   bool hard_override_disabled =
       !profile->GetPrefs()->GetBoolean(prefs::kSSLErrorOverrideAllowed);
   int options_mask = CalculateOptionsMask(cert_error, hard_override_disabled,
-                                          ssl_info.is_fatal_cert_error,
-                                          expired_previous_decision);
+                                          ssl_info.is_fatal_cert_error);
 
   SSLErrorHandler* error_handler = new SSLErrorHandler(
       std::unique_ptr<SSLErrorHandler::Delegate>(
@@ -1061,8 +1060,7 @@
 // static
 int SSLErrorHandler::CalculateOptionsMask(int cert_error,
                                           bool hard_override_disabled,
-                                          bool should_ssl_errors_be_fatal,
-                                          bool expired_previous_decision) {
+                                          bool should_ssl_errors_be_fatal) {
   int options_mask = 0;
   if (!IsCertErrorFatal(cert_error) && !hard_override_disabled &&
       !should_ssl_errors_be_fatal) {
@@ -1074,10 +1072,6 @@
   if (should_ssl_errors_be_fatal) {
     options_mask |= security_interstitials::SSLErrorUI::STRICT_ENFORCEMENT;
   }
-  if (expired_previous_decision) {
-    options_mask |=
-        security_interstitials::SSLErrorUI::EXPIRED_BUT_PREVIOUSLY_ALLOWED;
-  }
   return options_mask;
 }
 
diff --git a/chrome/browser/ssl/ssl_error_handler.h b/chrome/browser/ssl/ssl_error_handler.h
index 1da4cd0..9eb19ee 100644
--- a/chrome/browser/ssl/ssl_error_handler.h
+++ b/chrome/browser/ssl/ssl_error_handler.h
@@ -226,8 +226,7 @@
   // Calculates a mask encoded using flags in SSLErrorUI::SSLErrorOptionsMask.
   static int CalculateOptionsMask(int cert_error,
                                   bool hard_override_disabled,
-                                  bool should_ssl_errors_be_fatal,
-                                  bool expired_previous_decision);
+                                  bool should_ssl_errors_be_fatal);
 
   std::unique_ptr<Delegate> delegate_;
   content::WebContents* const web_contents_;
diff --git a/chrome/browser/ssl/ssl_error_handler_unittest.cc b/chrome/browser/ssl/ssl_error_handler_unittest.cc
index e96660ec..05f9008 100644
--- a/chrome/browser/ssl/ssl_error_handler_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_handler_unittest.cc
@@ -1507,55 +1507,39 @@
   mask = SSLErrorHandler::CalculateOptionsMask(
       net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, /* cert_error */
       false,                                     /* hard_override_disabled */
-      false, /* should_ssl_errors_be_fatal */
-      false /* expired_previous_decision */);
+      false /* should_ssl_errors_be_fatal */
+  );
   EXPECT_EQ(0, mask);
   mask = SSLErrorHandler::CalculateOptionsMask(
       net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, /* cert_error */
       true,                                      /* hard_override_disabled */
-      false, /* should_ssl_errors_be_fatal */
-      false /* expired_previous_decision */);
+      false /* should_ssl_errors_be_fatal */
+  );
   EXPECT_EQ(security_interstitials::SSLErrorUI::HARD_OVERRIDE_DISABLED, mask);
   mask = SSLErrorHandler::CalculateOptionsMask(
       net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, /* cert_error */
       false,                                     /* hard_override_disabled */
-      true,  /* should_ssl_errors_be_fatal */
-      false /* expired_previous_decision */);
+      true /* should_ssl_errors_be_fatal */
+  );
   EXPECT_EQ(security_interstitials::SSLErrorUI::STRICT_ENFORCEMENT, mask);
-  mask = SSLErrorHandler::CalculateOptionsMask(
-      net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, /* cert_error */
-      false,                                     /* hard_override_disabled */
-      false, /* should_ssl_errors_be_fatal */
-      true /* expired_previous_decision */);
-  EXPECT_EQ(security_interstitials::SSLErrorUI::EXPIRED_BUT_PREVIOUSLY_ALLOWED,
-            mask);
 
   // Overridable cert error.
   mask = SSLErrorHandler::CalculateOptionsMask(
       net::ERR_CERT_DATE_INVALID, /* cert_error */
       false,                      /* hard_override_disabled */
-      false,                      /* should_ssl_errors_be_fatal */
-      false /* expired_previous_decision */);
+      false                       /* should_ssl_errors_be_fatal */
+  );
   EXPECT_EQ(security_interstitials::SSLErrorUI::SOFT_OVERRIDE_ENABLED, mask);
   mask = SSLErrorHandler::CalculateOptionsMask(
       net::ERR_CERT_DATE_INVALID, /* cert_error */
       true,                       /* hard_override_disabled */
-      false,                      /* should_ssl_errors_be_fatal */
-      false /* expired_previous_decision */);
+      false                       /* should_ssl_errors_be_fatal */
+  );
   EXPECT_EQ(security_interstitials::SSLErrorUI::HARD_OVERRIDE_DISABLED, mask);
   mask = SSLErrorHandler::CalculateOptionsMask(
       net::ERR_CERT_DATE_INVALID, /* cert_error */
       false,                      /* hard_override_disabled */
-      true,                       /* should_ssl_errors_be_fatal */
-      false /* expired_previous_decision */);
+      true                        /* should_ssl_errors_be_fatal */
+  );
   EXPECT_EQ(security_interstitials::SSLErrorUI::STRICT_ENFORCEMENT, mask);
-  mask = SSLErrorHandler::CalculateOptionsMask(
-      net::ERR_CERT_DATE_INVALID, /* cert_error */
-      false,                      /* hard_override_disabled */
-      false,                      /* should_ssl_errors_be_fatal */
-      true /* expired_previous_decision */);
-  EXPECT_EQ(
-      security_interstitials::SSLErrorUI::SOFT_OVERRIDE_ENABLED |
-          security_interstitials::SSLErrorUI::EXPIRED_BUT_PREVIOUSLY_ALLOWED,
-      mask);
 }
diff --git a/chrome/browser/supervised_user/supervised_user_service.h b/chrome/browser/supervised_user/supervised_user_service.h
index b9b37fa..f84c581 100644
--- a/chrome/browser/supervised_user/supervised_user_service.h
+++ b/chrome/browser/supervised_user/supervised_user_service.h
@@ -184,6 +184,15 @@
   // SupervisedUserURLFilter::Observer implementation:
   void OnSiteListUpdated() override;
 
+#if !defined(OS_ANDROID)
+  bool signout_required_after_supervision_enabled() {
+    return signout_required_after_supervision_enabled_;
+  }
+  void set_signout_required_after_supervision_enabled() {
+    signout_required_after_supervision_enabled_ = true;
+  }
+#endif  // !defined(OS_ANDROID)
+
  private:
   friend class SupervisedUserServiceExtensionTestBase;
   friend class SupervisedUserServiceFactory;
@@ -351,6 +360,10 @@
 
   base::ObserverList<SupervisedUserServiceObserver>::Unchecked observer_list_;
 
+#if !defined(OS_ANDROID)
+  bool signout_required_after_supervision_enabled_ = false;
+#endif
+
   base::WeakPtrFactory<SupervisedUserService> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SupervisedUserService);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c34eab4b..b933563 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -335,6 +335,13 @@
     "webui/webui_util.h",
   ]
 
+  if (is_win || is_mac) {
+    sources += [
+      "webui/settings/captions_handler.cc",
+      "webui/settings/captions_handler.h",
+    ]
+  }
+
   if (enable_vr) {
     if (is_win) {
       sources += [
@@ -904,6 +911,10 @@
       "global_error/global_error_service.h",
       "global_error/global_error_service_factory.cc",
       "global_error/global_error_service_factory.h",
+      "global_media_controls/media_dialog_controller.cc",
+      "global_media_controls/media_dialog_controller.h",
+      "global_media_controls/media_dialog_controller_delegate.cc",
+      "global_media_controls/media_dialog_controller_delegate.h",
       "global_media_controls/media_toolbar_button_controller.cc",
       "global_media_controls/media_toolbar_button_controller.h",
       "global_media_controls/media_toolbar_button_controller_delegate.cc",
@@ -1590,6 +1601,8 @@
       "webui/chromeos/add_supervision/add_supervision_handler_utils.h",
       "webui/chromeos/add_supervision/add_supervision_ui.cc",
       "webui/chromeos/add_supervision/add_supervision_ui.h",
+      "webui/chromeos/add_supervision/confirm_signout_dialog.cc",
+      "webui/chromeos/add_supervision/confirm_signout_dialog.h",
       "webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc",
       "webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h",
       "webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.cc",
@@ -2723,6 +2736,10 @@
       "views/fullscreen_control/fullscreen_control_view.h",
       "views/global_error_bubble_view.cc",
       "views/global_error_bubble_view.h",
+      "views/global_media_controls/media_dialog_view.cc",
+      "views/global_media_controls/media_dialog_view.h",
+      "views/global_media_controls/media_notification_container_impl.cc",
+      "views/global_media_controls/media_notification_container_impl.h",
       "views/global_media_controls/media_toolbar_button_view.cc",
       "views/global_media_controls/media_toolbar_button_view.h",
       "views/hover_button.cc",
@@ -3089,6 +3106,7 @@
     deps += [
       "//chrome/browser/ui/views",
       "//components/constrained_window",
+      "//components/media_message_center",
       "//components/payments/content",
       "//components/payments/core",
       "//components/ui_devtools/views",
@@ -3394,6 +3412,8 @@
       "app_list/search/search_result_ranker/recurrence_predictor.h",
       "app_list/search/search_result_ranker/recurrence_ranker.cc",
       "app_list/search/search_result_ranker/recurrence_ranker.h",
+      "app_list/search/search_result_ranker/recurrence_ranker_util.cc",
+      "app_list/search/search_result_ranker/recurrence_ranker_util.h",
       "app_list/search/search_result_ranker/search_result_ranker.cc",
       "app_list/search/search_result_ranker/search_result_ranker.h",
       "app_list/search/settings_shortcut/settings_shortcut_metadata.cc",
diff --git a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
index 466a0186..5ee2501 100644
--- a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
+++ b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
@@ -38,7 +38,9 @@
 using autofill::UserInfo;
 using autofill::password_generation::PasswordGenerationUIData;
 using base::android::ConvertJavaStringToUTF16;
+using base::android::ConvertJavaStringToUTF8;
 using base::android::ConvertUTF16ToJavaString;
+using base::android::ConvertUTF8ToJavaString;
 using base::android::JavaRef;
 using base::android::ScopedJavaLocalRef;
 
@@ -162,7 +164,8 @@
           static_cast<int>(tab_data.get_sheet_type()),
           ConvertUTF16ToJavaString(env, field.display_text()),
           ConvertUTF16ToJavaString(env, field.a11y_description()),
-          field.is_obfuscated(), field.selectable());
+          ConvertUTF8ToJavaString(env, field.id()), field.is_obfuscated(),
+          field.selectable());
     }
   }
 
@@ -182,9 +185,11 @@
       env, Java_UserInfoField_getDisplayText(env, j_field_to_convert));
   base::string16 a11y_description = ConvertJavaStringToUTF16(
       env, Java_UserInfoField_getA11yDescription(env, j_field_to_convert));
+  std::string id = ConvertJavaStringToUTF8(
+      env, Java_UserInfoField_getId(env, j_field_to_convert));
   bool is_obfuscated = Java_UserInfoField_isObfuscated(env, j_field_to_convert);
   bool selectable = Java_UserInfoField_isSelectable(env, j_field_to_convert);
-  return UserInfo::Field(display_text, a11y_description, is_obfuscated,
+  return UserInfo::Field(display_text, a11y_description, id, is_obfuscated,
                          selectable);
 }
 
diff --git a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
index 0223dab..f13db59 100644
--- a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
+++ b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/ui/android/tab_model/android_live_tab_context.h"
+
+#include "base/token.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/android/tab_model/android_live_tab_context.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "components/sessions/content/content_live_tab.h"
@@ -59,6 +61,12 @@
   return false;
 }
 
+base::Optional<base::Token> AndroidLiveTabContext::GetTabGroupForTab(
+    int index) const {
+  // Not applicable to android.
+  return base::Optional<base::Token>();
+}
+
 const gfx::Rect AndroidLiveTabContext::GetRestoredBounds() const {
   // Not applicable to android.
   return gfx::Rect();
@@ -79,6 +87,7 @@
     int tab_index,
     int selected_navigation,
     const std::string& extension_app_id,
+    base::Optional<base::Token> group,
     bool select,
     bool pin,
     bool from_last_session,
@@ -108,6 +117,7 @@
 // Currently does nothing.
 sessions::LiveTab* AndroidLiveTabContext::ReplaceRestoredTab(
     const std::vector<sessions::SerializedNavigationEntry>& navigations,
+    base::Optional<base::Token> group,
     int selected_navigation,
     bool from_last_session,
     const std::string& extension_app_id,
diff --git a/chrome/browser/ui/android/tab_model/android_live_tab_context.h b/chrome/browser/ui/android/tab_model/android_live_tab_context.h
index f5e12139..5b516b1 100644
--- a/chrome/browser/ui/android/tab_model/android_live_tab_context.h
+++ b/chrome/browser/ui/android/tab_model/android_live_tab_context.h
@@ -33,6 +33,7 @@
   sessions::LiveTab* GetLiveTabAt(int index) const override;
   sessions::LiveTab* GetActiveLiveTab() const override;
   bool IsTabPinned(int index) const override;
+  base::Optional<base::Token> GetTabGroupForTab(int index) const override;
   const gfx::Rect GetRestoredBounds() const override;
   ui::WindowShowState GetRestoredState() const override;
   std::string GetWorkspace() const override;
@@ -41,6 +42,7 @@
       int tab_index,
       int selected_navigation,
       const std::string& extension_app_id,
+      base::Optional<base::Token> group,
       bool select,
       bool pin,
       bool from_last_session,
@@ -48,6 +50,7 @@
       const std::string& user_agent_override) override;
   sessions::LiveTab* ReplaceRestoredTab(
       const std::vector<sessions::SerializedNavigationEntry>& navigations,
+      base::Optional<base::Token> group,
       int selected_navigation,
       bool from_last_session,
       const std::string& extension_app_id,
diff --git a/chrome/browser/ui/app_list/crostini/OWNERS b/chrome/browser/ui/app_list/crostini/OWNERS
index 9d9bf0c4..dc48383c 100644
--- a/chrome/browser/ui/app_list/crostini/OWNERS
+++ b/chrome/browser/ui/app_list/crostini/OWNERS
@@ -1 +1 @@
-file://chrome/browser/chromeos/crostini/OWNERS
+file://chrome/browser/chromeos/guest_os/OWNERS
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.cc b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.cc
index 587b3d2..9a863b7 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.h"
+
 #include "base/time/time.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/frecency_store.pb.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/histogram_util.h"
@@ -74,6 +75,77 @@
 
 void DefaultPredictor::FromProto(const RecurrencePredictorProto& proto) {}
 
+ConditionalFrequencyPredictor::ConditionalFrequencyPredictor() = default;
+ConditionalFrequencyPredictor::ConditionalFrequencyPredictor(
+    const ConditionalFrequencyPredictorConfig& config) {}
+ConditionalFrequencyPredictor::~ConditionalFrequencyPredictor() = default;
+
+ConditionalFrequencyPredictor::Events::Events() = default;
+ConditionalFrequencyPredictor::Events::~Events() = default;
+ConditionalFrequencyPredictor::Events::Events(const Events& other) = default;
+
+const char ConditionalFrequencyPredictor::kPredictorName[] =
+    "ConditionalFrequencyPredictor";
+const char* ConditionalFrequencyPredictor::GetPredictorName() const {
+  return kPredictorName;
+}
+
+void ConditionalFrequencyPredictor::Train(unsigned int target,
+                                          unsigned int condition) {
+  TrainWithDelta(target, condition, 1.0f);
+}
+
+void ConditionalFrequencyPredictor::TrainWithDelta(unsigned int target,
+                                                   unsigned int condition,
+                                                   float delta) {
+  DCHECK_NE(delta, 0.0f);
+  auto& events = table_[condition];
+  events.freqs[target] += delta;
+  events.total += delta;
+}
+
+base::flat_map<unsigned int, float> ConditionalFrequencyPredictor::Rank(
+    unsigned int condition) {
+  const auto& it = table_.find(condition);
+  // If the total frequency is zero, we can't return any meaningful results, so
+  // return empty.
+  if (it == table_.end() || it->second.total == 0.0f)
+    return {};
+
+  base::flat_map<unsigned int, float> result;
+  const auto& events = it->second;
+  for (const auto& target_freq : events.freqs)
+    result[target_freq.first] = target_freq.second / events.total;
+  return result;
+}
+
+void ConditionalFrequencyPredictor::ToProto(
+    RecurrencePredictorProto* proto) const {
+  auto* predictor = proto->mutable_conditional_frequency_predictor();
+  for (const auto& condition_events : table_) {
+    for (const auto& event_freq : condition_events.second.freqs) {
+      auto* event = predictor->add_events();
+      event->set_condition(condition_events.first);
+      event->set_event(event_freq.first);
+      event->set_freq(event_freq.second);
+    }
+  }
+}
+
+void ConditionalFrequencyPredictor::FromProto(
+    const RecurrencePredictorProto& proto) {
+  if (!proto.has_conditional_frequency_predictor()) {
+    // TODO(921444): Add error metrics for new predictors.
+    return;
+  }
+
+  for (const auto& event : proto.conditional_frequency_predictor().events()) {
+    auto& events = table_[event.condition()];
+    events.freqs[event.event()] = event.freq();
+    events.total += event.freq();
+  }
+}
+
 FrecencyPredictor::FrecencyPredictor(const FrecencyPredictorConfig& config)
     : decay_coeff_(config.decay_coeff()) {}
 FrecencyPredictor::~FrecencyPredictor() = default;
@@ -261,4 +333,40 @@
   }
 }
 
+MarkovPredictor::MarkovPredictor(const MarkovPredictorConfig& config) {}
+MarkovPredictor::~MarkovPredictor() = default;
+
+const char MarkovPredictor::kPredictorName[] = "MarkovPredictor";
+const char* MarkovPredictor::GetPredictorName() const {
+  return kPredictorName;
+}
+
+void MarkovPredictor::Train(unsigned int target, unsigned int condition) {
+  if (previous_target_)
+    frequencies_.Train(target, previous_target_.value());
+  previous_target_ = target;
+}
+
+base::flat_map<unsigned int, float> MarkovPredictor::Rank(
+    unsigned int condition) {
+  if (previous_target_)
+    return frequencies_.Rank(previous_target_.value());
+  return base::flat_map<unsigned int, float>();
+}
+
+void MarkovPredictor::ToProto(RecurrencePredictorProto* proto) const {
+  auto* predictor = proto->mutable_markov_predictor();
+  frequencies_.ToProto(predictor->mutable_frequencies());
+}
+
+void MarkovPredictor::FromProto(const RecurrencePredictorProto& proto) {
+  if (!proto.has_markov_predictor()) {
+    // TODO(921444): Update error metrics for new predictors and add reporting
+    // here.
+    return;
+  }
+
+  frequencies_.FromProto(proto.markov_predictor().frequencies());
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.h b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.h
index cd0a1f4..1960ba37 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.h
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.h
@@ -16,13 +16,17 @@
 
 namespace app_list {
 
-using FakePredictorConfig = RecurrenceRankerConfigProto::FakePredictorConfig;
+using FakePredictorConfig = RecurrencePredictorConfigProto::FakePredictorConfig;
 using DefaultPredictorConfig =
-    RecurrenceRankerConfigProto::DefaultPredictorConfig;
+    RecurrencePredictorConfigProto::DefaultPredictorConfig;
+using ConditionalFrequencyPredictorConfig =
+    RecurrencePredictorConfigProto::ConditionalFrequencyPredictorConfig;
 using FrecencyPredictorConfig =
-    RecurrenceRankerConfigProto::FrecencyPredictorConfig;
+    RecurrencePredictorConfigProto::FrecencyPredictorConfig;
 using HourBinPredictorConfig =
-    RecurrenceRankerConfigProto::HourBinPredictorConfig;
+    RecurrencePredictorConfigProto::HourBinPredictorConfig;
+using MarkovPredictorConfig =
+    RecurrencePredictorConfigProto::MarkovPredictorConfig;
 
 // |RecurrencePredictor| is the interface for all predictors used by
 // |RecurrenceRanker| to drive rankings. If a predictor has some form of
@@ -93,6 +97,53 @@
   DISALLOW_COPY_AND_ASSIGN(DefaultPredictor);
 };
 
+// Represents a conditional probability table which stores the frequency of
+// targets given a condition. Conditions can be client-provided, or could be the
+// output of a hash of other contextual features. This allows for an arbitrary
+// number of conditions to be used.
+class ConditionalFrequencyPredictor : public RecurrencePredictor {
+ public:
+  // The predictor doesn't use any configuration values, so a zero-argument
+  // constructor is also provided for convenience when this is used within other
+  // predictors.
+  explicit ConditionalFrequencyPredictor(
+      const ConditionalFrequencyPredictorConfig& config);
+  ConditionalFrequencyPredictor();
+  ~ConditionalFrequencyPredictor() override;
+
+  // Stores a mapping from events to frequencies, along with the total frequency
+  // of all events.
+  class Events {
+   public:
+    Events();
+    Events(const Events& other);
+    ~Events();
+
+    base::flat_map<unsigned int, float> freqs;
+    float total = 0.0f;
+  };
+
+  // RecurrencePredictor:
+  // Add 1.0f to the relative frequency of |target| given |condition|.
+  void Train(unsigned int target, unsigned int condition) override;
+  // The scores in the returned map sum to 1 if the map is non-empty.
+  base::flat_map<unsigned int, float> Rank(unsigned int condition) override;
+  void ToProto(RecurrencePredictorProto* proto) const override;
+  void FromProto(const RecurrencePredictorProto& proto) override;
+  const char* GetPredictorName() const override;
+
+  static const char kPredictorName[];
+
+  // Add |delta| to the relative frequency of |target| given |condition|.
+  void TrainWithDelta(unsigned int target, unsigned int condition, float delta);
+
+ private:
+  // Stores a mapping from conditions to events to frequencies.
+  base::flat_map<unsigned int, ConditionalFrequencyPredictor::Events> table_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConditionalFrequencyPredictor);
+};
+
 // FrecencyPredictor ranks targets according to their frecency, and
 // can only be used for zero-state predictions. This predictor allows for
 // frecency-based rankings with different configuration to that of the ranker's
@@ -190,6 +241,34 @@
   DISALLOW_COPY_AND_ASSIGN(HourBinPredictor);
 };
 
+// A first-order Markov chain that predicts the next target from the previous.
+// It does not use the condition.
+class MarkovPredictor : public RecurrencePredictor {
+ public:
+  explicit MarkovPredictor(const MarkovPredictorConfig& config);
+  ~MarkovPredictor() override;
+
+  // RecurrencePredictor:
+  void Train(unsigned int target, unsigned int condition) override;
+  base::flat_map<unsigned int, float> Rank(unsigned int condition) override;
+  void ToProto(RecurrencePredictorProto* proto) const override;
+  void FromProto(const RecurrencePredictorProto& proto) override;
+  const char* GetPredictorName() const override;
+
+  static const char kPredictorName[];
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(MarkovPredictorTest, ToAndFromProto);
+
+  // Stores transition probabilities: P(target | previous_target).
+  ConditionalFrequencyPredictor frequencies_;
+
+  // The most recently observed target.
+  base::Optional<unsigned int> previous_target_;
+
+  DISALLOW_COPY_AND_ASSIGN(MarkovPredictor);
+};
+
 }  // namespace app_list
 
 #endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_RECURRENCE_PREDICTOR_H_
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.proto b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.proto
index 7d9bfe5a..410086c0 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.proto
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.proto
@@ -47,11 +47,30 @@
   optional uint32 last_decay_timestamp = 4;
 }
 
+// Conditional frequency predictor.
+message ConditionalFrequencyPredictorProto {
+  message Event {
+    required fixed64 condition = 1;
+    required uint32 event = 2;
+    required float freq = 3;
+  }
+
+  repeated Event events = 1;
+}
+
+// First-order markov chain predictor
+message MarkovPredictorProto {
+  // Stores a conditional frequency predictor.
+  required RecurrencePredictorProto frequencies = 1;
+}
+
 // Represents the serialisation of one particular predictor.
 message RecurrencePredictorProto {
   oneof predictor {
     FakePredictorProto fake_predictor = 1;
     FrecencyPredictorProto frecency_predictor = 2;
     HourBinPredictorProto hour_bin_predictor = 3;
+    ConditionalFrequencyPredictorProto conditional_frequency_predictor = 4;
+    MarkovPredictorProto markov_predictor = 5;
   }
 }
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor_unittest.cc
index fad8ce95..418b295 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor_unittest.cc
@@ -87,6 +87,54 @@
   EXPECT_EQ(predictor_->Rank(0u), new_predictor.Rank(0u));
 }
 
+class ConditionalFrequencyPredictorTest : public testing::Test {};
+
+TEST_F(ConditionalFrequencyPredictorTest, TrainAndRank) {
+  ConditionalFrequencyPredictor cfp;
+
+  cfp.TrainWithDelta(0u, 1u, 5.0f);
+  cfp.TrainWithDelta(1u, 1u, 1.0f);
+  cfp.TrainWithDelta(1u, 1u, 1.0f);
+  cfp.TrainWithDelta(1u, 50u, 1.0f);
+  cfp.TrainWithDelta(1u, 50u, 2.0f);
+  cfp.TrainWithDelta(2u, 50u, 1.0f);
+  cfp.TrainWithDelta(2u, 50u, 1.0f);
+
+  EXPECT_THAT(cfp.Rank(1u),
+              UnorderedElementsAre(Pair(0u, FloatEq(5.0f / 7.0f)),
+                                   Pair(1u, FloatEq(2.0f / 7.0f))));
+  EXPECT_THAT(cfp.Rank(50u),
+              UnorderedElementsAre(Pair(1u, FloatEq(3.0f / 5.0f)),
+                                   Pair(2u, FloatEq(2.0f / 5.0f))));
+}
+
+TEST_F(ConditionalFrequencyPredictorTest, ToFromProto) {
+  ConditionalFrequencyPredictor cfp1;
+
+  cfp1.Train(1u, 1u);
+  cfp1.Train(2u, 1u);
+  cfp1.Train(3u, 1u);
+  cfp1.Train(4u, 1u);
+  cfp1.Train(1u, 2u);
+  cfp1.Train(1u, 2u);
+  cfp1.TrainWithDelta(2u, 3u, 3.0f);
+  cfp1.Train(3u, 3u);
+
+  RecurrencePredictorProto proto;
+  cfp1.ToProto(&proto);
+
+  ConditionalFrequencyPredictor cfp2;
+  cfp2.FromProto(proto);
+
+  EXPECT_THAT(
+      cfp2.Rank(1u),
+      UnorderedElementsAre(Pair(1u, FloatEq(0.25f)), Pair(2u, FloatEq(0.25f)),
+                           Pair(3u, FloatEq(0.25f)), Pair(4u, FloatEq(0.25f))));
+  EXPECT_THAT(cfp2.Rank(2u), UnorderedElementsAre(Pair(1u, FloatEq(1.0f))));
+  EXPECT_THAT(cfp2.Rank(3u), UnorderedElementsAre(Pair(2u, FloatEq(0.75f)),
+                                                  Pair(3u, FloatEq(0.25f))));
+}
+
 class HourBinPredictorTest : public testing::Test {
  protected:
   void SetUp() override {
@@ -322,4 +370,79 @@
   EXPECT_TRUE(EquivToProtoLite(proto.hour_bin_predictor(),
                                target_proto.hour_bin_predictor()));
 }
+
+class MarkovPredictorTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    predictor_ = std::make_unique<MarkovPredictor>(config_);
+  }
+
+  MarkovPredictorConfig config_;
+  std::unique_ptr<MarkovPredictor> predictor_;
+};
+
+TEST_F(MarkovPredictorTest, RankWithNoTargets) {
+  // This should ignore the condition.
+  EXPECT_TRUE(predictor_->Rank(0u).empty());
+  EXPECT_TRUE(predictor_->Rank(1u).empty());
+}
+
+TEST_F(MarkovPredictorTest, RecordAndRank) {
+  // Transitions 1 -> 2 -> 3. Condition should be ignored.
+  predictor_->Train(1u, 0u);
+  predictor_->Train(2u, 2u);
+  predictor_->Train(3u, 4u);
+  predictor_->Train(1u, 6u);
+
+  // Last target is 1, we've only seen 1 -> 2.
+  EXPECT_THAT(predictor_->Rank(0u),
+              UnorderedElementsAre(Pair(2u, FloatEq(1.0f))));
+
+  predictor_->Train(3u, 8u);
+  predictor_->Train(1u, 6u);
+  predictor_->Train(1u, 4u);
+  predictor_->Train(1u, 2u);
+
+  // Last target is 1, now we've seen 1 -> {1, 2, 3}.
+  EXPECT_THAT(
+      predictor_->Rank(0u),
+      UnorderedElementsAre(Pair(1u, FloatEq(0.5f)), Pair(2u, FloatEq(0.25f)),
+                           Pair(3u, FloatEq(0.25f))));
+
+  predictor_->Train(3u, 8u);
+  predictor_->Train(3u, 8u);
+
+  // Last target is 3, we have 3 -> {1, 3}.
+  EXPECT_THAT(predictor_->Rank(0u),
+              UnorderedElementsAre(Pair(1u, FloatEq(2.0f / 3.0f)),
+                                   Pair(3u, FloatEq(1.0f / 3.0f))));
+}
+
+TEST_F(MarkovPredictorTest, ToAndFromProto) {
+  // Some complicated transitions.
+  for (int i = 0; i < 10; ++i) {
+    for (int j = 10; j < 10 + i; ++j) {
+      for (int trains = 0; trains < j; ++trains) {
+        predictor_->Train(i, 0u);
+        predictor_->Train(j, 0u);
+      }
+    }
+  }
+
+  RecurrencePredictorProto proto;
+  predictor_->ToProto(&proto);
+
+  MarkovPredictor new_predictor(config_);
+  new_predictor.FromProto(proto);
+
+  EXPECT_TRUE(proto.has_markov_predictor());
+  for (int i = 0; i < 10; ++i) {
+    // Set the last target without modifying the transition frequencies.
+    predictor_->previous_target_ = i;
+    new_predictor.previous_target_ = i;
+
+    EXPECT_EQ(predictor_->Rank(5u), new_predictor.Rank(5u));
+  }
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.cc
index 0d384b4..953766e 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.pb.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.pb.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_config.pb.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.h"
 
 namespace app_list {
 namespace {
@@ -73,23 +74,6 @@
   return proto;
 }
 
-// Returns a new, configured instance of the predictor defined in |config|.
-std::unique_ptr<RecurrencePredictor> MakePredictor(
-    const RecurrenceRankerConfigProto& config) {
-  if (config.has_fake_predictor())
-    return std::make_unique<FakePredictor>(config.fake_predictor());
-  if (config.has_default_predictor())
-    return std::make_unique<DefaultPredictor>(config.default_predictor());
-  if (config.has_frecency_predictor())
-    return std::make_unique<FrecencyPredictor>(config.frecency_predictor());
-  if (config.has_hour_bin_predictor())
-    return std::make_unique<HourBinPredictor>(config.hour_bin_predictor());
-
-  LogConfigurationError(ConfigurationError::kInvalidPredictor);
-  NOTREACHED();
-  return nullptr;
-}
-
 std::vector<std::pair<std::string, float>> SortAndTruncateRanks(
     int n,
     const base::flat_map<std::string, float>& ranks) {
@@ -160,9 +144,10 @@
     // Ephemeral users have no persistent storage, so we don't try and load the
     // proto from disk. Instead, we fall back on using a default (frecency)
     // predictor, which is still useful with only data from the current session.
-    predictor_ = std::make_unique<DefaultPredictor>(config.default_predictor());
+    predictor_ = std::make_unique<DefaultPredictor>(
+        RecurrencePredictorConfigProto::DefaultPredictorConfig());
   } else {
-    predictor_ = MakePredictor(config);
+    predictor_ = MakePredictor(config.predictor());
 
     // Load the proto from disk and finish initialisation in
     // |OnLoadProtoFromDiskComplete|.
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_config.proto b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_config.proto
index e91d571..0b1133e 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_config.proto
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_config.proto
@@ -8,18 +8,11 @@
 
 package app_list;
 
-// Configuration for an instance of |RecurrenceRanker|.
+// Configuration for a subclass of RecurrencePredictor.
 //
 // Warning: this cannot contain any map fields, as they cannot be relied upon
 // for a consistent hash.
-message RecurrenceRankerConfigProto {
-  required uint32 min_seconds_between_saves = 1;
-
-  required uint32 target_limit = 2;
-  required float target_decay = 3;
-  required uint32 condition_limit = 4;
-  required float condition_decay = 5;
-
+message RecurrencePredictorConfigProto {
   // Config for a fake predictor, used for testing.
   message FakePredictorConfig {}
 
@@ -27,6 +20,10 @@
   // as its ranks. As a result, it has no configuration of its own.
   message DefaultPredictorConfig {}
 
+  // Config for a conditional frequency predictor, which returns normalized
+  // frequencies for targets given a condition.
+  message ConditionalFrequencyPredictorConfig {}
+
   // Config for a frecency predictor.
   message FrecencyPredictorConfig {
     // The frecency parameter used to control the frequency-recency tradeoff
@@ -49,11 +46,32 @@
     repeated BinWeight bin_weights = 2;
   }
 
+  // A first-order Markov chain predictor.
+  message MarkovPredictorConfig {}
+
   // The choice of which kind of predictor to use, and its configuration.
-  oneof predictor_config {
-    FakePredictorConfig fake_predictor = 10001;
-    FrecencyPredictorConfig frecency_predictor = 10002;
-    DefaultPredictorConfig default_predictor = 10003;
-    HourBinPredictorConfig hour_bin_predictor = 10004;
+  oneof predictor {
+    FakePredictorConfig fake_predictor = 1;
+    FrecencyPredictorConfig frecency_predictor = 2;
+    DefaultPredictorConfig default_predictor = 3;
+    HourBinPredictorConfig hour_bin_predictor = 4;
+    ConditionalFrequencyPredictorConfig conditional_frequency_predictor = 5;
+    MarkovPredictorConfig markov_predictor = 6;
   }
 }
+
+// Configuration for an instance of RecurrenceRanker.
+//
+// Warning: this cannot contain any map fields, as they cannot be relied upon
+// for a consistent hash.
+message RecurrenceRankerConfigProto {
+  required uint32 min_seconds_between_saves = 1;
+
+  required uint32 target_limit = 2;
+  required float target_decay = 3;
+  required uint32 condition_limit = 4;
+  required float condition_decay = 5;
+
+  // The predictor to use for this ranker, and its configuration.
+  required RecurrencePredictorConfigProto predictor = 6;
+}
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc
index c187700..43e6fd0 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc
@@ -61,7 +61,7 @@
     // Even if empty, the setting of the oneof |predictor_config| in
     // RecurrenceRankerConfigProto is used to determine which predictor is
     // constructed.
-    config.mutable_fake_predictor();
+    config.mutable_predictor()->mutable_fake_predictor();
     return config;
   }
 
@@ -326,7 +326,7 @@
   // Construct a second ranker with a slightly different config.
   RecurrenceRankerConfigProto other_config;
   PartiallyPopulateConfig(&other_config);
-  other_config.mutable_fake_predictor();
+  other_config.mutable_predictor()->mutable_fake_predictor();
   other_config.set_min_seconds_between_saves(1234);
 
   RecurrenceRanker other_ranker(ranker_filepath_, other_config, false);
@@ -355,7 +355,7 @@
 TEST_F(RecurrenceRankerTest, IntegrationWithDefaultPredictor) {
   RecurrenceRankerConfigProto config;
   PartiallyPopulateConfig(&config);
-  config.mutable_default_predictor();
+  config.mutable_predictor()->mutable_default_predictor();
 
   RecurrenceRanker ranker(ranker_filepath_, config, false);
   Wait();
@@ -375,7 +375,7 @@
 TEST_F(RecurrenceRankerTest, IntegrationWithZeroStateFrecencyPredictor) {
   RecurrenceRankerConfigProto config;
   PartiallyPopulateConfig(&config);
-  auto* predictor = config.mutable_frecency_predictor();
+  auto* predictor = config.mutable_predictor()->mutable_frecency_predictor();
   predictor->set_decay_coeff(0.5f);
 
   RecurrenceRanker ranker(ranker_filepath_, config, false);
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.cc b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.cc
new file mode 100644
index 0000000..ec60128
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium 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/search_result_ranker/recurrence_ranker_util.h"
+
+#include "chrome/browser/ui/app_list/search/search_result_ranker/histogram_util.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.pb.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.pb.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_config.pb.h"
+
+namespace app_list {
+
+std::unique_ptr<RecurrencePredictor> MakePredictor(
+    const RecurrencePredictorConfigProto& config) {
+  if (config.has_fake_predictor())
+    return std::make_unique<FakePredictor>(config.fake_predictor());
+  if (config.has_default_predictor())
+    return std::make_unique<DefaultPredictor>(config.default_predictor());
+  if (config.has_conditional_frequency_predictor())
+    return std::make_unique<ConditionalFrequencyPredictor>(
+        config.conditional_frequency_predictor());
+  if (config.has_frecency_predictor())
+    return std::make_unique<FrecencyPredictor>(config.frecency_predictor());
+  if (config.has_hour_bin_predictor())
+    return std::make_unique<HourBinPredictor>(config.hour_bin_predictor());
+  if (config.has_markov_predictor())
+    return std::make_unique<MarkovPredictor>(config.markov_predictor());
+
+  LogConfigurationError(ConfigurationError::kInvalidPredictor);
+  NOTREACHED();
+  return nullptr;
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.h b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.h
new file mode 100644
index 0000000..8b38757
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_RECURRENCE_RANKER_UTIL_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_RECURRENCE_RANKER_UTIL_H_
+
+#include <memory>
+
+#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_config.pb.h"
+
+namespace app_list {
+
+// Returns a new, configured instance of the predictor defined in |config|.
+std::unique_ptr<RecurrencePredictor> MakePredictor(
+    const RecurrencePredictorConfigProto& config);
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_RECURRENCE_RANKER_UTIL_H_
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
index 6e057dd..9bb1fe1 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -126,7 +126,7 @@
         0.8f));
     // TODO(931149): Replace this with a more sophisticated model if the
     // query-based mixed type model is being used.
-    config.mutable_default_predictor();
+    config.mutable_predictor()->mutable_default_predictor();
 
     if (GetFieldTrialParamByFeatureAsBool(
             app_list_features::kEnableQueryBasedMixedTypesRanker,
@@ -163,7 +163,7 @@
 
     // Despite not changing any fields, this sets the predictor to the default
     // predictor.
-    config.mutable_default_predictor();
+    config.mutable_predictor()->mutable_default_predictor();
 
     zero_state_mixed_types_ranker_ = std::make_unique<RecurrenceRanker>(
         profile->GetPath().AppendASCII("zero_state_mixed_types_ranker.proto"),
diff --git a/chrome/browser/ui/ash/DEPS b/chrome/browser/ui/ash/DEPS
index fbb5c43..30ceec1 100644
--- a/chrome/browser/ui/ash/DEPS
+++ b/chrome/browser/ui/ash/DEPS
@@ -11,6 +11,7 @@
   ".*test.*": [
    "!ash",
    "+ash/components/shortcut_viewer",
+   "+ash/components/strings/grit/ash_components_strings.h",
    "+ash/keyboard/ui",
    "+ash/public",
    "+ash/shelf/shelf_constants.h",
diff --git a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
index 18822fc..e11608a 100644
--- a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
+++ b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "ash/components/shortcut_viewer/keyboard_shortcut_item.h"
 #include "ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
+#include "ash/components/strings/grit/ash_components_strings.h"
 #include "ash/public/cpp/accelerators.h"
 #include "base/hash/md5.h"
 #include "base/macros.h"
@@ -20,9 +21,9 @@
 namespace {
 
 // The total number of Ash accelerators.
-constexpr int kAshAcceleratorsTotalNum = 103;
+constexpr int kAshAcceleratorsTotalNum = 109;
 // The hash of Ash accelerators.
-constexpr char kAshAcceleratorsHash[] = "8abbed9538aa112612cc81c223027545";
+constexpr char kAshAcceleratorsHash[] = "34dbed14c8d49b0aecea0cce70d4b502";
 #if defined(GOOGLE_CHROME_BUILD)
 // Internal builds add an extra accelerator for the Feedback app.
 // The total number of Chrome accelerators (available on Chrome OS).
@@ -156,6 +157,18 @@
 TEST_F(KeyboardShortcutViewerMetadataTest, CheckAcceleratorIdHasAccelerator) {
   for (const auto& shortcut_item :
        keyboard_shortcut_viewer::GetKeyboardShortcutItemList()) {
+    if (shortcut_item.description_message_id ==
+            IDS_KSV_DESCRIPTION_DESKS_NEW_DESK ||
+        shortcut_item.description_message_id ==
+            IDS_KSV_DESCRIPTION_DESKS_REMOVE_CURRENT_DESK) {
+      // Ignore these for now until https://crbug.com/976487 is fixed.
+      // These two accelerators have to be listed differently in the keyboard
+      // shortcut viewer than how they are actually defined in the accelerator
+      // table due to the SEARCH + "=" and "-" remapping to F11 and F12
+      // respectively.
+      continue;
+    }
+
     for (const auto& accelerator_id : shortcut_item.accelerator_ids) {
       int number_of_accelerator = ash_accelerator_ids_.count(accelerator_id) +
                                   chrome_accelerator_ids_.count(accelerator_id);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index a10ca99..91e3293 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -12,7 +12,7 @@
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/window_properties.h"
-#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/home_button.h"
 #include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_app_button.h"
diff --git a/chrome/browser/ui/ash/launcher_animations_interactive_uitest.cc b/chrome/browser/ui/ash/launcher_animations_interactive_uitest.cc
index 2e9c2c7d..2ac9571 100644
--- a/chrome/browser/ui/ash/launcher_animations_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/launcher_animations_interactive_uitest.cc
@@ -232,7 +232,7 @@
  private:
   // LauncherAnimationsTestBase:
   std::string GetAnimationSmoothnessMetricsName() const override {
-    return "Half.ClamshellMode";
+    return "FullscreenSearch.ClamshellMode";
   }
 
   DISALLOW_COPY_AND_ASSIGN(LauncherAnimationsFullscreenSearchTest);
diff --git a/chrome/browser/ui/autofill/autofill_popup_layout_model.cc b/chrome/browser/ui/autofill/autofill_popup_layout_model.cc
index 4235e540..f77a73e 100644
--- a/chrome/browser/ui/autofill/autofill_popup_layout_model.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_layout_model.cc
@@ -20,7 +20,6 @@
 #include "components/autofill/core/common/autofill_util.h"
 #include "components/grit/components_scaled_resources.h"
 #include "components/strings/grit/components_strings.h"
-#include "components/vector_icons/vector_icons.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/color_palette.h"
@@ -216,7 +215,6 @@
     return gfx::ImageSkia();
 
   constexpr int kIconSize = 16;
-  constexpr int kAccountBoxIconSize = 20;
 
   // For http warning message, get icon images from VectorIcon, which is the
   // same as security indicator icons in location bar.
@@ -243,11 +241,6 @@
 #endif
   }
 
-  if (icon_str == "accountBoxIcon") {
-    return gfx::CreateVectorIcon(kAccountBoxIcon, kAccountBoxIconSize,
-                                 gfx::kChromeIconGrey);
-  }
-
 #if !defined(GOOGLE_CHROME_BUILD)
   if (icon_str == "googlePay" || icon_str == "googlePayDark") {
     return gfx::ImageSkia();
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils.cc b/chrome/browser/ui/bookmarks/bookmark_utils.cc
index da93cf8..2970b08 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils.cc
@@ -34,8 +34,11 @@
 #endif
 
 #if defined(TOOLKIT_VIEWS)
+#include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
+#include "ui/gfx/image/image_skia_source.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/scoped_canvas.h"
 #endif
 
 #if defined(OS_WIN) || defined(OS_MACOSX)
@@ -94,12 +97,33 @@
   return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED;
 }
 
-#if defined(TOOLKIT_VIEWS) && !defined(OS_WIN) && !defined(OS_MACOSX)
+#if defined(TOOLKIT_VIEWS)
+// Image source that flips the supplied source image in RTL.
+class RTLFlipSource : public gfx::ImageSkiaSource {
+ public:
+  explicit RTLFlipSource(gfx::ImageSkia source) : source_(std::move(source)) {}
+  ~RTLFlipSource() override = default;
+
+  // gfx::ImageSkiaSource:
+  gfx::ImageSkiaRep GetImageForScale(float scale) override {
+    gfx::Canvas canvas(source_.size(), scale, false);
+    gfx::ScopedCanvas scoped_canvas(&canvas);
+    scoped_canvas.FlipIfRTL(source_.width());
+    canvas.DrawImageInt(source_, 0, 0);
+    return gfx::ImageSkiaRep(canvas.GetBitmap(), scale);
+  }
+
+ private:
+  const gfx::ImageSkia source_;
+};
+
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
 gfx::ImageSkia GetFolderIcon(const gfx::VectorIcon& icon, SkColor text_color) {
   return gfx::CreateVectorIcon(icon,
                                color_utils::DeriveDefaultIconColor(text_color));
 }
-#endif
+#endif  // !defined(OS_WIN) && !defined(OS_MACOSX)
+#endif  // defined(TOOLKIT_VIEWS)
 
 }  // namespace
 
@@ -295,41 +319,45 @@
 #if defined(TOOLKIT_VIEWS)
 // TODO(bsep): vectorize the Windows versions: crbug.com/564112
 gfx::ImageSkia GetBookmarkFolderIcon(SkColor text_color) {
+  gfx::ImageSkia folder;
 #if defined(OS_WIN)
-  return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+  folder = *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
       IDR_BOOKMARK_BAR_FOLDER);
 #elif defined(OS_MACOSX)
   int resource_id = color_utils::IsDark(text_color)
                         ? IDR_BOOKMARK_BAR_FOLDER
                         : IDR_BOOKMARK_BAR_FOLDER_WHITE;
-  return *ui::ResourceBundle::GetSharedInstance()
-              .GetNativeImageNamed(resource_id)
-              .ToImageSkia();
+  folder = *ui::ResourceBundle::GetSharedInstance()
+                .GetNativeImageNamed(resource_id)
+                .ToImageSkia();
 #else
-  return GetFolderIcon(ui::MaterialDesignController::touch_ui()
-                           ? vector_icons::kFolderTouchIcon
-                           : vector_icons::kFolderIcon,
-                       text_color);
+  folder = GetFolderIcon(ui::MaterialDesignController::touch_ui()
+                             ? vector_icons::kFolderTouchIcon
+                             : vector_icons::kFolderIcon,
+                         text_color);
 #endif
+  return gfx::ImageSkia(std::make_unique<RTLFlipSource>(folder), folder.size());
 }
 
 gfx::ImageSkia GetBookmarkManagedFolderIcon(SkColor text_color) {
+  gfx::ImageSkia folder;
 #if defined(OS_WIN)
-  return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+  folder = *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
       IDR_BOOKMARK_BAR_FOLDER_MANAGED);
 #elif defined(OS_MACOSX)
   int resource_id = color_utils::IsDark(text_color)
                         ? IDR_BOOKMARK_BAR_FOLDER_MANAGED
                         : IDR_BOOKMARK_BAR_FOLDER_MANAGED_WHITE;
-  return *ui::ResourceBundle::GetSharedInstance()
-              .GetNativeImageNamed(resource_id)
-              .ToImageSkia();
+  folder = *ui::ResourceBundle::GetSharedInstance()
+                .GetNativeImageNamed(resource_id)
+                .ToImageSkia();
 #else
-  return GetFolderIcon(ui::MaterialDesignController::touch_ui()
-                           ? vector_icons::kFolderManagedTouchIcon
-                           : vector_icons::kFolderManagedIcon,
-                       text_color);
+  folder = GetFolderIcon(ui::MaterialDesignController::touch_ui()
+                             ? vector_icons::kFolderManagedTouchIcon
+                             : vector_icons::kFolderManagedIcon,
+                         text_color);
 #endif
+  return gfx::ImageSkia(std::make_unique<RTLFlipSource>(folder), folder.size());
 }
 #endif
 
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 19661ffc..ef5b143f 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -222,6 +222,8 @@
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/shell_dialogs/selected_file_info.h"
+#include "url/origin.h"
+#include "url/scheme_host_port.h"
 
 #if defined(OS_WIN)
 #include <shellapi.h>
@@ -1522,10 +1524,6 @@
 }
 
 bool Browser::TakeFocus(content::WebContents* source, bool reverse) {
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_FOCUS_RETURNED_TO_BROWSER,
-      content::Source<Browser>(this),
-      content::NotificationService::NoDetails());
   return false;
 }
 
@@ -2041,24 +2039,45 @@
   // in which case let the sad tabs remain.
   // Also, if tab is muted and the cause is the unloaded extension, unmute it.
   if (reason != extensions::UnloadedExtensionReason::TERMINATE) {
+    auto should_close_tab = [](const extensions::ExtensionId& extension_id,
+                               content::WebContents* web_contents) {
+      // Case 1: A "regular" extension page, e.g.
+      // chrome-extension://<id>/page.html.
+      // Note: we check the tuple or precursor tuple in order to close any
+      // windows with opaque origins that were opened by extensions, and may
+      // still be running code.
+      const url::SchemeHostPort& tuple_or_precursor_tuple =
+          web_contents->GetMainFrame()
+              ->GetLastCommittedOrigin()
+              .GetTupleOrPrecursorTupleIfOpaque();
+      if (tuple_or_precursor_tuple.scheme() == extensions::kExtensionScheme &&
+          tuple_or_precursor_tuple.host() == extension_id) {
+        // Edge-case: Chrome URL overrides (such as NTP overrides) are handled
+        // differently (reloaded), and managed by ExtensionWebUI. Ignore them.
+        if (!web_contents->GetLastCommittedURL().SchemeIs(
+                content::kChromeUIScheme)) {
+          return true;
+        }
+      }
+
+      // Case 2: Check if the page is a page associated with a hosted app, which
+      // can have non-extension schemes. For example, the Gmail hosted app would
+      // have a URL of https://mail.google.com.
+      if (extensions::TabHelper::FromWebContents(web_contents)->GetAppId() ==
+          extension_id) {
+        return true;
+      }
+
+      return false;
+    };
+
     // Iterate backwards as we may remove items while iterating.
     for (int i = tab_strip_model_->count() - 1; i >= 0; --i) {
       WebContents* web_contents = tab_strip_model_->GetWebContentsAt(i);
-      // Two cases are handled here:
-
-      // - The scheme check is for when an extension page is loaded in a
-      // tab, e.g. chrome-extension://id/page.html.
-      // - The extension_app check is for apps, which can have non-extension
-      // schemes, e.g. https://mail.google.com if you have the Gmail app
-      // installed.
-      if ((web_contents->GetURL().SchemeIs(extensions::kExtensionScheme) &&
-           web_contents->GetURL().host_piece() == extension->id()) ||
-          (extensions::TabHelper::FromWebContents(web_contents)->GetAppId() ==
-           extension->id())) {
+      if (should_close_tab(extension->id(), web_contents))
         tab_strip_model_->CloseWebContentsAt(i, TabStripModel::CLOSE_NONE);
-      } else {
+      else
         UnmuteIfMutedByExtension(web_contents, extension->id());
-      }
     }
   }
 
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 7a99984..3b0b7635 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -983,6 +983,10 @@
          !(GetContentRestrictions(browser) & CONTENT_RESTRICTION_SAVE);
 }
 
+void ShowFindBar(Browser* browser) {
+  browser->GetFindBarController()->Show();
+}
+
 void Print(Browser* browser) {
 #if BUILDFLAG(ENABLE_PRINTING)
   auto* web_contents = browser->tab_strip_model()->GetActiveWebContents();
@@ -1077,7 +1081,7 @@
 
 void Find(Browser* browser) {
   base::RecordAction(UserMetricsAction("Find"));
-  FindInPage(browser, false, true);
+  FindInPage(browser, false, false);
 }
 
 void FindNext(Browser* browser) {
@@ -1091,7 +1095,21 @@
 }
 
 void FindInPage(Browser* browser, bool find_next, bool forward_direction) {
-  browser->GetFindBarController()->Show(find_next, forward_direction);
+  ShowFindBar(browser);
+  if (find_next) {
+    base::string16 find_text;
+    FindTabHelper* find_helper = FindTabHelper::FromWebContents(
+        browser->tab_strip_model()->GetActiveWebContents());
+#if defined(OS_MACOSX)
+    // We always want to search for the current contents of the find bar on
+    // OS X. For regular profile it's always the current find pboard. For
+    // Incognito window it's the newest value of the find pboard content and
+    // user-typed text.
+    FindBar* find_bar = browser->GetFindBarController()->find_bar();
+    find_text = find_bar->GetFindText();
+#endif
+    find_helper->StartFinding(find_text, forward_direction, false);
+  }
 }
 
 void Zoom(Browser* browser, content::PageZoom zoom) {
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h
index 897d6fc..c9a80095 100644
--- a/chrome/browser/ui/browser_commands.h
+++ b/chrome/browser/ui/browser_commands.h
@@ -122,6 +122,7 @@
 void SendTabToSelfFromPageAction(Browser* browser);
 void SavePage(Browser* browser);
 bool CanSavePage(const Browser* browser);
+void ShowFindBar(Browser* browser);
 void Print(Browser* browser);
 bool CanPrint(Browser* browser);
 #if BUILDFLAG(ENABLE_PRINTING)
diff --git a/chrome/browser/ui/browser_focus_uitest.cc b/chrome/browser/ui/browser_focus_uitest.cc
index 5c03fb9..0946acb 100644
--- a/chrome/browser/ui/browser_focus_uitest.cc
+++ b/chrome/browser/ui/browser_focus_uitest.cc
@@ -18,7 +18,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
@@ -41,6 +40,7 @@
 #include "content/public/browser/interstitial_page.h"
 #include "content/public/browser/interstitial_page_delegate.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
@@ -50,17 +50,10 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
-using content::RenderViewHost;
-using content::WebContents;
-
 namespace {
 
-bool PressTabAndWait(const Browser* browser,
-                     int type,
-                     const content::NotificationSource& source) {
-  return ui_test_utils::SendKeyPressAndWait(browser, ui::VKEY_TAB, false, false,
-                                            false, false, type, source);
-}
+using content::RenderViewHost;
+using content::WebContents;
 
 #if defined(OS_POSIX)
 // The delay waited in some cases where we don't have a notifications for an
@@ -106,23 +99,21 @@
       // iff "Full Keyboard Access" is enabled. In reverse, four Tab key presses
       // are required to traverse the back/forward buttons and the tab strip.
 #if defined(OS_MACOSX)
+      constexpr int kFocusableElementsBeforeOmnibox = 4;
+      constexpr int kFocusableElementsAfterOmnibox = 1;
       if (ui_controls::IsFullKeyboardAccessEnabled()) {
-        ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                    reverse, false, false));
-        if (reverse) {
-          for (int j = 0; j < 3; ++j) {
-            ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                        reverse, false, false));
-          }
+        for (int j = 0; j < (reverse ? kFocusableElementsBeforeOmnibox
+                                     : kFocusableElementsAfterOmnibox);
+             ++j) {
+          ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
+                                                      reverse, false, false));
         }
       }
 #endif
 
       if (reverse) {
-        ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-            browser(), key, false, reverse, false, false,
-            content::NOTIFICATION_ALL,
-            content::NotificationService::AllSources()));
+        ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
+                                                    reverse, false, false));
       }
 
       for (size_t j = 0; j < base::size(kExpectedIDs); ++j) {
@@ -144,32 +135,33 @@
         EXPECT_STREQ(kExpectedIDs[index], focused_id.c_str());
       }
 
-#if defined(OS_MACOSX)
-      // TODO(msw): Mac doesn't post NOTIFICATION_FOCUS_RETURNED_TO_BROWSER and
-      // would also apparently require extra Tab key presses here. Sigh...
-      chrome::FocusLocationBar(browser());
-#else
       // On the last Tab key press, focus returns to the browser.
-      ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-          browser(), key, false, reverse, false, false,
-          chrome::NOTIFICATION_FOCUS_RETURNED_TO_BROWSER,
-          content::Source<Browser>(browser())));
-      EXPECT_TRUE(
-          IsViewFocused(reverse ? VIEW_ID_OMNIBOX : VIEW_ID_LOCATION_ICON));
+      ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
+                                                  reverse, false, false));
 
-      ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-          browser(), key, false, reverse, false, false,
-          content::NOTIFICATION_ALL,
-          content::NotificationService::AllSources()));
+      // Except on Mac, where extra tabs are once again required to traverse the
+      // other top chrome elements.
+#if defined(OS_MACOSX)
+      if (ui_controls::IsFullKeyboardAccessEnabled()) {
+        for (int j = 0; j < (reverse ? kFocusableElementsAfterOmnibox
+                                     : kFocusableElementsBeforeOmnibox);
+             ++j) {
+          ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
+                                                      reverse, false, false));
+        }
+      }
 #endif
-      content::RunAllPendingInMessageLoop();
-      EXPECT_TRUE(
-          IsViewFocused(reverse ? VIEW_ID_LOCATION_ICON : VIEW_ID_OMNIBOX));
+
+      ui_test_utils::WaitForViewFocus(
+          browser(), reverse ? VIEW_ID_OMNIBOX : VIEW_ID_LOCATION_ICON, true);
+
+      ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
+                                                  reverse, false, false));
+      ui_test_utils::WaitForViewFocus(
+          browser(), reverse ? VIEW_ID_LOCATION_ICON : VIEW_ID_OMNIBOX, true);
       if (reverse) {
-        ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-            browser(), key, false, false, false, false,
-            content::NOTIFICATION_ALL,
-            content::NotificationService::AllSources()));
+        ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
+                                                    false, false, false));
       }
     }
   }
@@ -501,19 +493,19 @@
   ui_test_utils::NavigateToURL(browser(), url);
   EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
 
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
   EXPECT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
 
   chrome::FocusLocationBar(browser());
   EXPECT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
 
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
   EXPECT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
 
   ClickOnView(VIEW_ID_TAB_CONTAINER);
   EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
 
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
   EXPECT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
 }
 
@@ -790,22 +782,17 @@
   EXPECT_TRUE(
       ui_test_utils::IsViewFocused(popup_browser, VIEW_ID_TAB_CONTAINER));
 
-  ASSERT_TRUE(PressTabAndWait(popup_browser,
-                              chrome::NOTIFICATION_FOCUS_RETURNED_TO_BROWSER,
-                              content::Source<Browser>(popup_browser)));
-  EXPECT_TRUE(
-      ui_test_utils::IsViewFocused(popup_browser, VIEW_ID_LOCATION_ICON));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(popup_browser, ui::VKEY_TAB,
+                                              false, false, false, false));
+  ui_test_utils::WaitForViewFocus(popup_browser, VIEW_ID_LOCATION_ICON, true);
 
-  ASSERT_TRUE(PressTabAndWait(popup_browser,
-                              chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
-                              content::NotificationService::AllSources()));
-  EXPECT_TRUE(ui_test_utils::IsViewFocused(popup_browser, VIEW_ID_OMNIBOX));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(popup_browser, ui::VKEY_TAB,
+                                              false, false, false, false));
+  ui_test_utils::WaitForViewFocus(popup_browser, VIEW_ID_OMNIBOX, true);
 
-  ASSERT_TRUE(PressTabAndWait(popup_browser,
-                              chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
-                              content::NotificationService::AllSources()));
-  EXPECT_TRUE(
-      ui_test_utils::IsViewFocused(popup_browser, VIEW_ID_TAB_CONTAINER));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(popup_browser, ui::VKEY_TAB,
+                                              false, false, false, false));
+  ui_test_utils::WaitForViewFocus(popup_browser, VIEW_ID_TAB_CONTAINER, true);
 }
 
 // Tests that the location bar is not focusable when hidden, which is the case
@@ -819,10 +806,10 @@
   ui_test_utils::FocusView(app_browser, VIEW_ID_TAB_CONTAINER);
   EXPECT_TRUE(ui_test_utils::IsViewFocused(app_browser, VIEW_ID_TAB_CONTAINER));
 
-  ASSERT_TRUE(PressTabAndWait(app_browser,
-                              chrome::NOTIFICATION_FOCUS_RETURNED_TO_BROWSER,
-                              content::Source<Browser>(app_browser)));
-  EXPECT_TRUE(ui_test_utils::IsViewFocused(app_browser, VIEW_ID_TAB_CONTAINER));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(app_browser, ui::VKEY_TAB, false,
+                                              false, false, false));
+  base::RunLoop().RunUntilIdle();
+  ui_test_utils::WaitForViewFocus(app_browser, VIEW_ID_TAB_CONTAINER, true);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/browser_live_tab_context.cc b/chrome/browser/ui/browser_live_tab_context.cc
index 2242d63..048e2d4 100644
--- a/chrome/browser/ui/browser_live_tab_context.cc
+++ b/chrome/browser/ui/browser_live_tab_context.cc
@@ -6,12 +6,14 @@
 
 #include <memory>
 
+#include "base/token.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_tabrestore.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_group_id.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "components/sessions/content/content_live_tab.h"
@@ -87,6 +89,14 @@
   return browser_->tab_strip_model()->IsTabPinned(index);
 }
 
+base::Optional<base::Token> BrowserLiveTabContext::GetTabGroupForTab(
+    int index) const {
+  const base::Optional<TabGroupId> group_id =
+      browser_->tab_strip_model()->GetTabGroupForTab(index);
+  return group_id.has_value() ? base::make_optional(group_id.value().token())
+                              : base::nullopt;
+}
+
 const gfx::Rect BrowserLiveTabContext::GetRestoredBounds() const {
   return browser_->window()->GetRestoredBounds();
 }
@@ -104,6 +114,7 @@
     int tab_index,
     int selected_navigation,
     const std::string& extension_app_id,
+    base::Optional<base::Token> group,
     bool select,
     bool pin,
     bool from_last_session,
@@ -118,7 +129,7 @@
 
   WebContents* web_contents = chrome::AddRestoredTab(
       browser_, navigations, tab_index, selected_navigation, extension_app_id,
-      base::nullopt, select, pin, from_last_session, base::TimeTicks(),
+      group, select, pin, from_last_session, base::TimeTicks(),
       storage_namespace, user_agent_override, false /* from_session_restore */);
 
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
@@ -132,7 +143,7 @@
   }
   std::vector<TabLoader::RestoredTab> restored_tabs;
   restored_tabs.emplace_back(web_contents, select, !extension_app_id.empty(),
-                             pin, base::nullopt);
+                             pin, group);
   TabLoader::RestoreTabs(restored_tabs, base::TimeTicks::Now());
 #else   // BUILDFLAG(ENABLE_SESSION_SERVICE)
   // Load the tab manually if there is no TabLoader.
@@ -144,6 +155,7 @@
 
 sessions::LiveTab* BrowserLiveTabContext::ReplaceRestoredTab(
     const std::vector<sessions::SerializedNavigationEntry>& navigations,
+    base::Optional<base::Token> group,
     int selected_navigation,
     bool from_last_session,
     const std::string& extension_app_id,
diff --git a/chrome/browser/ui/browser_live_tab_context.h b/chrome/browser/ui/browser_live_tab_context.h
index f49729a..cc907d7 100644
--- a/chrome/browser/ui/browser_live_tab_context.h
+++ b/chrome/browser/ui/browser_live_tab_context.h
@@ -40,6 +40,7 @@
   sessions::LiveTab* GetLiveTabAt(int index) const override;
   sessions::LiveTab* GetActiveLiveTab() const override;
   bool IsTabPinned(int index) const override;
+  base::Optional<base::Token> GetTabGroupForTab(int index) const override;
   const gfx::Rect GetRestoredBounds() const override;
   ui::WindowShowState GetRestoredState() const override;
   std::string GetWorkspace() const override;
@@ -49,6 +50,7 @@
       int tab_index,
       int selected_navigation,
       const std::string& extension_app_id,
+      base::Optional<base::Token> group,
       bool select,
       bool pin,
       bool from_last_session,
@@ -56,6 +58,7 @@
       const std::string& user_agent_override) override;
   sessions::LiveTab* ReplaceRestoredTab(
       const std::vector<sessions::SerializedNavigationEntry>& navigations,
+      base::Optional<base::Token> group,
       int selected_navigation,
       bool from_last_session,
       const std::string& extension_app_id,
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index df5b034..e05e4b2 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -891,11 +891,6 @@
 }
 
 void ContentSettingCookiesBubbleModel::OnCustomLinkClicked() {
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
-      content::Source<TabSpecificContentSettings>(
-          TabSpecificContentSettings::FromWebContents(web_contents())),
-      content::NotificationService::NoDetails());
   delegate()->ShowCollectedCookiesDialog(web_contents());
 }
 
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index 738b00a..13cece9f 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -1002,7 +1002,8 @@
 
 // Regression test for https://crbug.com/955408
 // See also: ContentSettingImageModelTest.SensorAccessPermissionsChanged
-TEST_F(ContentSettingBubbleModelTest, SensorAccessPermissionsChanged) {
+// TODO(https://crbug.com/978882) Disabled due to flakiness.
+TEST_F(ContentSettingBubbleModelTest, DISABLED_SensorAccessPermissionsChanged) {
   // Enable all sensors just to avoid hardcoding the expected messages to the
   // motion sensor-specific ones.
   base::test::ScopedFeatureList feature_list;
diff --git a/chrome/browser/ui/find_bar/find_bar_controller.cc b/chrome/browser/ui/find_bar/find_bar_controller.cc
index 7ac1245..355ada72 100644
--- a/chrome/browser/ui/find_bar/find_bar_controller.cc
+++ b/chrome/browser/ui/find_bar/find_bar_controller.cc
@@ -27,7 +27,6 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_source.h"
-#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/range/range.h"
@@ -105,7 +104,7 @@
   DCHECK(!web_contents_);
 }
 
-void FindBarController::Show(bool find_next, bool forward_direction) {
+void FindBarController::Show() {
   FindTabHelper* find_tab_helper =
       FindTabHelper::FromWebContents(web_contents_);
 
@@ -118,24 +117,6 @@
     find_bar_->Show(true);
   }
   find_bar_->SetFocusAndSelection();
-
-  base::string16 find_text = GetSelectedText();
-
-#if defined(OS_MACOSX)
-  // We always want to search for the current contents of the find bar on
-  // OS X. For regular profile it's always the current find pboard. For
-  // Incognito window it's the newest value of the find pboard content and
-  // user-typed text.
-  find_text = find_bar_->GetFindText();
-#endif
-
-  if (!find_text.empty() || find_next) {
-    // Don't update the local input if we're using the global pasteboard.
-    if (!find_bar_->HasGlobalFindPasteboard())
-      find_bar_->SetFindTextAndSelectedRange(find_text,
-                                             find_tab_helper->selected_range());
-    find_tab_helper->StartFinding(find_text, forward_direction, false);
-  }
 }
 
 void FindBarController::EndFindSession(SelectionAction selection_action,
@@ -370,10 +351,3 @@
   find_bar_->SetFindTextAndSelectedRange(find_string,
                                          find_tab_helper->selected_range());
 }
-
-base::string16 FindBarController::GetSelectedText() {
-  auto* host_view = web_contents_->GetRenderWidgetHostView();
-  return host_view
-             ? base::CollapseWhitespace(host_view->GetSelectedText(), false)
-             : base::string16();
-}
diff --git a/chrome/browser/ui/find_bar/find_bar_controller.h b/chrome/browser/ui/find_bar/find_bar_controller.h
index 967cfc6f..f6c5692c 100644
--- a/chrome/browser/ui/find_bar/find_bar_controller.h
+++ b/chrome/browser/ui/find_bar/find_bar_controller.h
@@ -48,11 +48,7 @@
   ~FindBarController() override;
 
   // Shows the find bar. Any previous search string will again be visible.
-  // The find operation will also be started depending on |find_next| and
-  // if there is currently a text selection. |find_next| means the user
-  // used a command to advance the search and |forward_direction| indicates if
-  // the find should be forward or backwards.
-  void Show(bool find_next = false, bool forward_direction = true);
+  void Show();
 
   // Ends the current session. |selection_action| specifies what to do with the
   // selection on the page created by the find operation. |results_action|
@@ -108,9 +104,6 @@
   // Mac.
   void MaybeSetPrepopulateText();
 
-  // Gets the text that is selected in the current tab, or an empty string.
-  base::string16 GetSelectedText();
-
   content::NotificationRegistrar registrar_;
 
   std::unique_ptr<FindBar> find_bar_;
diff --git a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
index fd12ab8d..b6f8904 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
+++ b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
@@ -130,7 +130,7 @@
   }
 
   void EnsureFindBoxOpenForBrowser(Browser* browser) {
-    chrome::Find(browser);
+    chrome::ShowFindBar(browser);
     gfx::Point position;
     bool fully_visible = false;
     EXPECT_TRUE(GetFindBarWindowInfoForBrowser(
@@ -840,7 +840,7 @@
   GURL url2 = GetURL(kFramePage);
   ui_test_utils::NavigateToURL(browser(), url);
 
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
 
   gfx::Point position;
   bool fully_visible = false;
@@ -861,7 +861,7 @@
   EXPECT_FALSE(fully_visible);
 
   // Open the find bar again.
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
 
   // Make sure it is open.
   EXPECT_TRUE(GetFindBarWindowInfo(&position, &fully_visible));
@@ -879,7 +879,7 @@
   GURL url = GetURL(kAnchorPage);
   ui_test_utils::NavigateToURL(browser(), url);
 
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
 
   gfx::Point position;
   bool fully_visible = false;
@@ -906,7 +906,7 @@
   GURL url = GetURL(kSimple);
   ui_test_utils::NavigateToURL(browser(), url);
 
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
 
   gfx::Point position;
   bool fully_visible = false;
@@ -942,7 +942,7 @@
   GURL url = GetURL(kMoveIfOver);
   ui_test_utils::NavigateToURL(browser(), url);
 
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
 
   // This is needed on GTK because the reposition operation is asynchronous.
   base::RunLoop().RunUntilIdle();
@@ -1034,7 +1034,7 @@
   GURL url = GetURL(kSimple);
   ui_test_utils::NavigateToURL(browser(), url);
 
-  chrome::Find(browser());
+  chrome::ShowFindBar(browser());
 
   // Simulate a user clearing the search string. Ideally, we should be
   // simulating keypresses here for searching for something and pressing
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller.cc b/chrome/browser/ui/global_media_controls/media_dialog_controller.cc
new file mode 100644
index 0000000..ad0a04cd
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/media_dialog_controller.cc
@@ -0,0 +1,103 @@
+// Copyright 2019 The Chromium 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/global_media_controls/media_dialog_controller.h"
+
+#include "base/bind.h"
+#include "base/containers/adapters.h"
+#include "chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h"
+#include "components/media_message_center/media_notification_item.h"
+#include "services/media_session/public/mojom/constants.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+using media_session::mojom::MediaSessionAction;
+
+MediaDialogController::MediaDialogController(
+    service_manager::Connector* connector,
+    MediaDialogControllerDelegate* delegate)
+    : connector_(connector), delegate_(delegate) {
+  DCHECK(delegate_);
+}
+
+MediaDialogController::~MediaDialogController() = default;
+
+void MediaDialogController::Initialize() {
+  // |connector| can be null in tests.
+  if (!connector_)
+    return;
+
+  // Connect to the controller manager so we can create media controllers for
+  // media sessions.
+  connector_->BindInterface(media_session::mojom::kServiceName,
+                            mojo::MakeRequest(&controller_manager_ptr_));
+
+  // Connect to receive audio focus events.
+  connector_->BindInterface(media_session::mojom::kServiceName,
+                            mojo::MakeRequest(&audio_focus_ptr_));
+  media_session::mojom::AudioFocusObserverPtr audio_focus_observer;
+  audio_focus_observer_binding_.Bind(mojo::MakeRequest(&audio_focus_observer));
+  audio_focus_ptr_->AddObserver(std::move(audio_focus_observer));
+
+  audio_focus_ptr_->GetFocusRequests(
+      base::BindOnce(&MediaDialogController::OnReceivedAudioFocusRequests,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void MediaDialogController::OnFocusGained(
+    media_session::mojom::AudioFocusRequestStatePtr session) {
+  const std::string id = session->request_id->ToString();
+
+  if (base::Contains(sessions_, id))
+    return;
+
+  media_session::mojom::MediaControllerPtr controller;
+
+  // |controller_manager_ptr_| may be null in tests where connector is
+  // unavailable.
+  if (controller_manager_ptr_) {
+    controller_manager_ptr_->CreateMediaControllerForSession(
+        mojo::MakeRequest(&controller), *session->request_id);
+  }
+
+  sessions_.emplace(
+      std::piecewise_construct, std::forward_as_tuple(id),
+      std::forward_as_tuple(
+          this, id, session->source_name.value_or(std::string()),
+          std::move(controller), std::move(session->session_info)));
+}
+
+void MediaDialogController::OnFocusLost(
+    media_session::mojom::AudioFocusRequestStatePtr session) {
+  sessions_.erase(session->request_id->ToString());
+}
+
+void MediaDialogController::ShowNotification(const std::string& id) {
+  base::WeakPtr<media_message_center::MediaNotificationItem> item;
+
+  auto it = sessions_.find(id);
+  if (it != sessions_.end())
+    item = it->second.GetWeakPtr();
+
+  delegate_->ShowMediaSession(id, item);
+}
+
+void MediaDialogController::HideNotification(const std::string& id) {
+  delegate_->HideMediaSession(id);
+}
+
+void MediaDialogController::OnReceivedAudioFocusRequests(
+    std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions) {
+  // TODO(steimel): Show all controllable sessions.
+  media_session::mojom::AudioFocusRequestStatePtr active_session;
+  for (auto& session : base::Reversed(sessions)) {
+    if (!session->session_info->is_controllable)
+      continue;
+
+    active_session = session.Clone();
+    break;
+  }
+
+  if (active_session)
+    OnFocusGained(std::move(active_session));
+}
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller.h b/chrome/browser/ui/global_media_controls/media_dialog_controller.h
new file mode 100644
index 0000000..d6d886cc
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/media_dialog_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_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_H_
+#define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/media_message_center/media_notification_controller.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/media_session/public/mojom/audio_focus.mojom.h"
+#include "services/media_session/public/mojom/media_controller.mojom.h"
+
+namespace media_message_center {
+class MediaNotificationItem;
+}  // namespace media_message_center
+
+namespace service_manager {
+class Connector;
+}  // namespace service_manager
+
+class MediaDialogControllerDelegate;
+
+// Controller for the MediaDialogView that updates the view when the active
+// media session changes.
+class MediaDialogController
+    : public media_session::mojom::AudioFocusObserver,
+      public media_message_center::MediaNotificationController {
+ public:
+  MediaDialogController(service_manager::Connector* connector,
+                        MediaDialogControllerDelegate* delegate);
+  ~MediaDialogController() override;
+
+  void Initialize();
+
+  // media_session::mojom::AudioFocusObserver implementation.
+  void OnFocusGained(
+      media_session::mojom::AudioFocusRequestStatePtr session) override;
+  void OnFocusLost(
+      media_session::mojom::AudioFocusRequestStatePtr session) override;
+
+  // media_message_center::MediaNotificationController implementation.
+  void ShowNotification(const std::string& id) override;
+  void HideNotification(const std::string& id) override;
+
+ private:
+  friend class MediaDialogControllerTest;
+
+  // Called when we receive all currently active media sessions for the audio
+  // focus manager. Used to initialize the list of sessions.
+  void OnReceivedAudioFocusRequests(
+      std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions);
+
+  service_manager::Connector* const connector_;
+  MediaDialogControllerDelegate* const delegate_;
+
+  // Stores a |media_message_center::MediaNotificationItem| for each media
+  // session keyed by its |request_id| in string format.
+  std::map<const std::string, media_message_center::MediaNotificationItem>
+      sessions_;
+
+  media_session::mojom::AudioFocusManagerPtr audio_focus_ptr_;
+  media_session::mojom::MediaControllerManagerPtr controller_manager_ptr_;
+
+  // Used to receive updates to the active media controller.
+  mojo::Binding<media_session::mojom::AudioFocusObserver>
+      audio_focus_observer_binding_{this};
+
+  base::WeakPtrFactory<MediaDialogController> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(MediaDialogController);
+};
+
+#endif  // CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_H_
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.cc b/chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.cc
new file mode 100644
index 0000000..97d12fe
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.cc
@@ -0,0 +1,7 @@
+// Copyright 2019 The Chromium 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/global_media_controls/media_dialog_controller_delegate.h"
+
+MediaDialogControllerDelegate::~MediaDialogControllerDelegate() = default;
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h b/chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h
new file mode 100644
index 0000000..3b3eca3
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_DELEGATE_H_
+#define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_DELEGATE_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+
+namespace media_message_center {
+class MediaNotificationItem;
+}  // namespace media_message_center
+
+// Delegate for MediaDialogController that is told when to display or hide a
+// media session.
+class MediaDialogControllerDelegate {
+ public:
+  virtual void ShowMediaSession(
+      const std::string& id,
+      base::WeakPtr<media_message_center::MediaNotificationItem> item) = 0;
+  virtual void HideMediaSession(const std::string& id) = 0;
+
+ protected:
+  virtual ~MediaDialogControllerDelegate();
+};
+
+#endif  // CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_DELEGATE_H_
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc b/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc
new file mode 100644
index 0000000..d2c366b
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright 2019 The Chromium 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/global_media_controls/media_dialog_controller.h"
+
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/unguessable_token.h"
+#include "chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h"
+#include "components/media_message_center/media_notification_item.h"
+#include "services/media_session/public/mojom/audio_focus.mojom.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using media_session::mojom::AudioFocusRequestState;
+using media_session::mojom::AudioFocusRequestStatePtr;
+using media_session::mojom::MediaSessionInfo;
+using media_session::mojom::MediaSessionInfoPtr;
+using testing::_;
+
+namespace {
+
+class MockMediaDialogControllerDelegate : public MediaDialogControllerDelegate {
+ public:
+  MockMediaDialogControllerDelegate() = default;
+  ~MockMediaDialogControllerDelegate() override = default;
+
+  // MediaDialogControllerDelegate implementation.
+  MOCK_METHOD2(
+      ShowMediaSession,
+      void(const std::string& id,
+           base::WeakPtr<media_message_center::MediaNotificationItem> item));
+  MOCK_METHOD1(HideMediaSession, void(const std::string& id));
+};
+
+}  // anonymous namespace
+
+class MediaDialogControllerTest : public testing::Test {
+ public:
+  MediaDialogControllerTest() = default;
+  ~MediaDialogControllerTest() override = default;
+
+  void SetUp() override {
+    controller_ = std::make_unique<MediaDialogController>(nullptr, &delegate_);
+  }
+
+ protected:
+  void SimulateFocusGained(const base::UnguessableToken& id,
+                           bool controllable) {
+    MediaSessionInfoPtr session_info(MediaSessionInfo::New());
+    session_info->is_controllable = controllable;
+
+    AudioFocusRequestStatePtr focus(AudioFocusRequestState::New());
+    focus->request_id = id;
+    focus->session_info = std::move(session_info);
+    controller_->OnFocusGained(std::move(focus));
+  }
+
+  void SimulateFocusLost(const base::UnguessableToken& id) {
+    AudioFocusRequestStatePtr focus(AudioFocusRequestState::New());
+    focus->request_id = id;
+    controller_->OnFocusLost(std::move(focus));
+  }
+
+  void SimulateNecessaryMetadata(const base::UnguessableToken& id) {
+    // In order for the MediaNotificationItem to tell the MediaDialogController
+    // to show a media session, that session needs a title and artist. Typically
+    // this would happen through the media session service, but since the
+    // service doesn't run for this test, we'll manually grab the
+    // MediaNotificationItem from the MediaDialogController and set the
+    // metadata.
+    auto item_itr = controller_->sessions_.find(id.ToString());
+    ASSERT_NE(controller_->sessions_.end(), item_itr);
+
+    media_session::MediaMetadata metadata;
+    metadata.title = base::ASCIIToUTF16("title");
+    metadata.artist = base::ASCIIToUTF16("artist");
+    item_itr->second.MediaSessionMetadataChanged(std::move(metadata));
+  }
+
+  MockMediaDialogControllerDelegate& delegate() { return delegate_; }
+
+ private:
+  MockMediaDialogControllerDelegate delegate_;
+  std::unique_ptr<MediaDialogController> controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaDialogControllerTest);
+};
+
+TEST_F(MediaDialogControllerTest, ShowControllableOnGainAndHideOnLoss) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  EXPECT_CALL(delegate(), ShowMediaSession(id.ToString(), _));
+
+  SimulateFocusGained(id, true);
+  SimulateNecessaryMetadata(id);
+
+  // Ensure that the session was shown.
+  testing::Mock::VerifyAndClearExpectations(&delegate());
+
+  EXPECT_CALL(delegate(), HideMediaSession(id.ToString()));
+  SimulateFocusLost(id);
+}
+
+TEST_F(MediaDialogControllerTest, DoesNotShowUncontrollableSession) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  EXPECT_CALL(delegate(), ShowMediaSession(_, _)).Times(0);
+
+  SimulateFocusGained(id, false);
+  SimulateNecessaryMetadata(id);
+}
diff --git a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
index e328f4e..27ab3ec0 100644
--- a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
+++ b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
@@ -39,6 +39,11 @@
 
 void MediaToolbarButtonController::MediaSessionInfoChanged(
     media_session::mojom::MediaSessionInfoPtr session_info) {
-  if (session_info && session_info->is_controllable)
+  if (session_info) {
+    // We only want to show if there's a controllable media session. However, as
+    // a MediaControllerObserver we should only receive a
+    // |MediaSessionInfoChanged()| call for a controllable session.
+    DCHECK(session_info->is_controllable);
     delegate_->Show();
+  }
 }
diff --git a/chrome/browser/ui/login/OWNERS b/chrome/browser/ui/login/OWNERS
index 4802f3e..f99ede4f 100644
--- a/chrome/browser/ui/login/OWNERS
+++ b/chrome/browser/ui/login/OWNERS
@@ -1,4 +1,5 @@
 carlosil@chromium.org
 davidben@chromium.org
+estark@chromium.org
 
 # COMPONENT: Internals>Network>Auth
diff --git a/chrome/browser/ui/login/login_handler_browsertest.cc b/chrome/browser/ui/login/login_handler_browsertest.cc
index 30ee6c5b..ce1ae50 100644
--- a/chrome/browser/ui/login/login_handler_browsertest.cc
+++ b/chrome/browser/ui/login/login_handler_browsertest.cc
@@ -1628,14 +1628,41 @@
   EXPECT_EQ(expected_title, auth_supplied_title_watcher.WaitAndGetTitle());
 }
 
+// Tests that FTP auth challenges appear over a blank committed interstitial.
+IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, FtpAuth) {
+  net::SpawnedTestServer ftp_server(
+      net::SpawnedTestServer::TYPE_FTP,
+      base::FilePath(FILE_PATH_LITERAL("chrome/test/data/ftp")));
+  ftp_server.set_no_anonymous_ftp_user(true);
+  ASSERT_TRUE(ftp_server.Start());
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  NavigationController* controller = &contents->GetController();
+  LoginPromptBrowserTestObserver observer;
+  observer.Register(content::Source<NavigationController>(controller));
+
+  // Navigate to an FTP server and wait for the auth prompt to appear.
+  WindowedAuthNeededObserver auth_needed_waiter(controller);
+  ui_test_utils::NavigateToURL(browser(), ftp_server.GetURL(""));
+  auth_needed_waiter.Wait();
+  ASSERT_EQ(1u, observer.handlers().size());
+  EXPECT_EQ("<head></head><body></body>",
+            content::EvalJs(contents, "document.documentElement.innerHTML"));
+
+  // Supply credentials and wait for the page to successfully load.
+  LoginHandler* handler = *observer.handlers().begin();
+  WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
+  handler->SetAuth(base::ASCIIToUTF16("chrome"), base::ASCIIToUTF16("chrome"));
+  auth_supplied_waiter.Wait();
+  const base::string16 kExpectedTitle = base::ASCIIToUTF16("Index of /");
+  content::TitleWatcher title_watcher(contents, kExpectedTitle);
+  EXPECT_EQ(kExpectedTitle, title_watcher.WaitAndGetTitle());
+}
+
 // Tests that FTP auth prompts do not appear when credentials have been
 // previously entered and cached.
 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, FtpAuthWithCache) {
-  // TODO(https://crbug.com/972188): support FTP auth with committed
-  // interstitials.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kHTTPAuthCommittedInterstitials);
   net::SpawnedTestServer ftp_server(
       net::SpawnedTestServer::TYPE_FTP,
       base::FilePath(FILE_PATH_LITERAL("chrome/test/data/ftp")));
diff --git a/chrome/browser/ui/login/login_tab_helper.cc b/chrome/browser/ui/login/login_tab_helper.cc
index 4172184..b3765b867 100644
--- a/chrome/browser/ui/login/login_tab_helper.cc
+++ b/chrome/browser/ui/login/login_tab_helper.cc
@@ -36,11 +36,16 @@
   // TODO(https://crbug.com/969097): handle auth challenges that lead to
   // downloads (i.e. don't commit).
 
-  int response_code = navigation_handle->GetResponseHeaders()->response_code();
-  if (response_code !=
-          net::HttpStatusCode::HTTP_PROXY_AUTHENTICATION_REQUIRED &&
-      response_code != net::HttpStatusCode::HTTP_UNAUTHORIZED) {
-    return;
+  // Show a login prompt with the navigation's AuthChallengeInfo on FTP
+  // navigations and on HTTP 401/407 responses.
+  if (!navigation_handle->GetURL().SchemeIs(url::kFtpScheme)) {
+    int response_code =
+        navigation_handle->GetResponseHeaders()->response_code();
+    if (response_code !=
+            net::HttpStatusCode::HTTP_PROXY_AUTHENTICATION_REQUIRED &&
+        response_code != net::HttpStatusCode::HTTP_UNAUTHORIZED) {
+      return;
+    }
   }
 
   challenge_ = navigation_handle->GetAuthChallengeInfo().value();
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index 270d75c3..e70fcfe 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -323,6 +323,7 @@
       show_info_bar_(false),
       site_url_(url),
       site_identity_status_(SITE_IDENTITY_STATUS_UNKNOWN),
+      safe_browsing_status_(SAFE_BROWSING_STATUS_NONE),
       site_connection_status_(SITE_CONNECTION_STATUS_UNKNOWN),
       show_ssl_decision_revoke_button_(false),
       content_settings_(HostContentSettingsMapFactory::GetForProfile(profile)),
@@ -555,12 +556,12 @@
     content::WebContents* web_contents) {
 #if defined(FULL_SAFE_BROWSING)
   DCHECK(password_protection_service_);
-  DCHECK(site_identity_status_ == SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE ||
-         site_identity_status_ ==
-             SITE_IDENTITY_STATUS_ENTERPRISE_PASSWORD_REUSE);
+  DCHECK(safe_browsing_status_ == SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE ||
+         safe_browsing_status_ ==
+             SAFE_BROWSING_STATUS_ENTERPRISE_PASSWORD_REUSE);
   password_protection_service_->OnUserAction(
       web_contents,
-      site_identity_status_ == SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE
+      safe_browsing_status_ == SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE
           ? PasswordReuseEvent::SIGN_IN_PASSWORD
           : PasswordReuseEvent::ENTERPRISE_PASSWORD,
       safe_browsing::WarningUIType::PAGE_INFO,
@@ -572,12 +573,12 @@
     content::WebContents* web_contents) {
 #if defined(FULL_SAFE_BROWSING)
   DCHECK(password_protection_service_);
-  DCHECK(site_identity_status_ == SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE ||
-         site_identity_status_ ==
-             SITE_IDENTITY_STATUS_ENTERPRISE_PASSWORD_REUSE);
+  DCHECK(safe_browsing_status_ == SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE ||
+         safe_browsing_status_ ==
+             SAFE_BROWSING_STATUS_ENTERPRISE_PASSWORD_REUSE);
   password_protection_service_->OnUserAction(
       web_contents,
-      site_identity_status_ == SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE
+      safe_browsing_status_ == SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE
           ? PasswordReuseEvent::SIGN_IN_PASSWORD
           : PasswordReuseEvent::ENTERPRISE_PASSWORD,
       safe_browsing::WarningUIType::PAGE_INFO,
@@ -609,7 +610,7 @@
     // All about: URLs except about:blank are redirected.
     DCHECK_EQ(url::kAboutBlankURL, url.spec());
     site_identity_status_ = SITE_IDENTITY_STATUS_NO_CERT;
-    site_identity_details_ =
+    site_details_message_ =
         l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY);
     site_connection_status_ = SITE_CONNECTION_STATUS_UNENCRYPTED;
     site_connection_details_ = l10n_util::GetStringFUTF16(
@@ -620,7 +621,7 @@
 
   if (url.SchemeIs(content::kChromeUIScheme) || is_chrome_ui_native_scheme) {
     site_identity_status_ = SITE_IDENTITY_STATUS_INTERNAL_PAGE;
-    site_identity_details_ =
+    site_details_message_ =
         l10n_util::GetStringUTF16(IDS_PAGE_INFO_INTERNAL_PAGE);
     site_connection_status_ = SITE_CONNECTION_STATUS_INTERNAL_PAGE;
     return;
@@ -629,36 +630,14 @@
   // Identity section.
   certificate_ = visible_security_state.certificate;
 
-  if (visible_security_state.malicious_content_status !=
-      security_state::MALICIOUS_CONTENT_STATUS_NONE) {
-    // The site has been flagged by Safe Browsing as dangerous.
-    GetSiteIdentityByMaliciousContentStatus(
-        visible_security_state.malicious_content_status, &site_identity_status_,
-        &site_identity_details_);
-#if defined(FULL_SAFE_BROWSING)
-    bool old_show_change_pw_buttons = show_change_password_buttons_;
-#endif
-    show_change_password_buttons_ =
-        (visible_security_state.malicious_content_status ==
-             security_state::MALICIOUS_CONTENT_STATUS_SIGN_IN_PASSWORD_REUSE ||
-         visible_security_state.malicious_content_status ==
-             security_state::
-                 MALICIOUS_CONTENT_STATUS_ENTERPRISE_PASSWORD_REUSE);
-#if defined(FULL_SAFE_BROWSING)
-    // Only record password reuse when adding the button, not on updates.
-    if (show_change_password_buttons_ && !old_show_change_pw_buttons) {
-      RecordPasswordReuseEvent();
-    }
-#endif
-  } else if (certificate_ &&
-             (!net::IsCertStatusError(visible_security_state.cert_status) ||
-              net::IsCertStatusMinorError(
-                  visible_security_state.cert_status))) {
+  if (certificate_ &&
+      (!net::IsCertStatusError(visible_security_state.cert_status) ||
+       net::IsCertStatusMinorError(visible_security_state.cert_status))) {
     // HTTPS with no or minor errors.
     if (security_level == security_state::SECURE_WITH_POLICY_INSTALLED_CERT) {
 #if defined(OS_CHROMEOS)
       site_identity_status_ = SITE_IDENTITY_STATUS_ADMIN_PROVIDED_CERT;
-      site_identity_details_ = l10n_util::GetStringFUTF16(
+      site_details_message_ = l10n_util::GetStringFUTF16(
           IDS_CERT_POLICY_PROVIDED_CERT_MESSAGE, UTF8ToUTF16(url.host()));
 #else
       DCHECK(false) << "Policy certificates exist only on ChromeOS";
@@ -673,17 +652,17 @@
             IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
       }
 
-      site_identity_details_.assign(l10n_util::GetStringFUTF16(
+      site_details_message_.assign(l10n_util::GetStringFUTF16(
           IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_VERIFIED, issuer_name));
 
-      site_identity_details_ += ASCIIToUTF16("\n\n");
+      site_details_message_ += ASCIIToUTF16("\n\n");
       if (visible_security_state.cert_status &
           net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION) {
-        site_identity_details_ += l10n_util::GetStringUTF16(
+        site_details_message_ += l10n_util::GetStringUTF16(
             IDS_PAGE_INFO_SECURITY_TAB_UNABLE_TO_CHECK_REVOCATION);
       } else if (visible_security_state.cert_status &
                  net::CERT_STATUS_NO_REVOCATION_MECHANISM) {
-        site_identity_details_ += l10n_util::GetStringUTF16(
+        site_details_message_ += l10n_util::GetStringUTF16(
             IDS_PAGE_INFO_SECURITY_TAB_NO_REVOCATION_MECHANISM);
       } else {
         NOTREACHED() << "Need to specify string for this warning";
@@ -700,7 +679,7 @@
         // state is "if any".
         DCHECK(!certificate_->subject().locality_name.empty());
         DCHECK(!certificate_->subject().country_name.empty());
-        site_identity_details_.assign(l10n_util::GetStringFUTF16(
+        site_details_message_.assign(l10n_util::GetStringFUTF16(
             IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV_VERIFIED,
             organization_name_,
             UTF8ToUTF16(certificate_->subject().country_name)));
@@ -714,13 +693,13 @@
               IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
         }
 
-        site_identity_details_.assign(l10n_util::GetStringFUTF16(
+        site_details_message_.assign(l10n_util::GetStringFUTF16(
             IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_VERIFIED, issuer_name));
       }
       if (security_state::IsSHA1InChain(visible_security_state)) {
         site_identity_status_ =
             SITE_IDENTITY_STATUS_DEPRECATED_SIGNATURE_ALGORITHM;
-        site_identity_details_ +=
+        site_details_message_ +=
             UTF8ToUTF16("\n\n") +
             l10n_util::GetStringUTF16(
                 IDS_PAGE_INFO_SECURITY_TAB_DEPRECATED_SIGNATURE_ALGORITHM);
@@ -728,7 +707,7 @@
     }
   } else {
     // HTTP or HTTPS with errors (not warnings).
-    site_identity_details_.assign(l10n_util::GetStringUTF16(
+    site_details_message_.assign(l10n_util::GetStringUTF16(
         IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY));
     if (!security_state::IsSchemeCryptographic(visible_security_state.url) ||
         !visible_security_state.certificate) {
@@ -742,17 +721,40 @@
     ssl_errors::ErrorInfo::GetErrorsForCertStatus(
         certificate_, visible_security_state.cert_status, url, &errors);
     for (size_t i = 0; i < errors.size(); ++i) {
-      site_identity_details_ += bullet;
-      site_identity_details_ += errors[i].short_description();
+      site_details_message_ += bullet;
+      site_details_message_ += errors[i].short_description();
     }
 
     if (visible_security_state.cert_status & net::CERT_STATUS_NON_UNIQUE_NAME) {
-      site_identity_details_ += ASCIIToUTF16("\n\n");
-      site_identity_details_ +=
+      site_details_message_ += ASCIIToUTF16("\n\n");
+      site_details_message_ +=
           l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_NON_UNIQUE_NAME);
     }
   }
 
+  if (visible_security_state.malicious_content_status !=
+      security_state::MALICIOUS_CONTENT_STATUS_NONE) {
+    // The site has been flagged by Safe Browsing. Takes precedence over TLS.
+    GetSafeBrowsingStatusByMaliciousContentStatus(
+        visible_security_state.malicious_content_status, &safe_browsing_status_,
+        &site_details_message_);
+#if defined(FULL_SAFE_BROWSING)
+    bool old_show_change_pw_buttons = show_change_password_buttons_;
+#endif
+    show_change_password_buttons_ =
+        (visible_security_state.malicious_content_status ==
+             security_state::MALICIOUS_CONTENT_STATUS_SIGN_IN_PASSWORD_REUSE ||
+         visible_security_state.malicious_content_status ==
+             security_state::
+                 MALICIOUS_CONTENT_STATUS_ENTERPRISE_PASSWORD_REUSE);
+#if defined(FULL_SAFE_BROWSING)
+    // Only record password reuse when adding the button, not on updates.
+    if (show_change_password_buttons_ && !old_show_change_pw_buttons) {
+      RecordPasswordReuseEvent();
+    }
+#endif
+  }
+
   // Site Connection
   // We consider anything less than 80 bits encryption to be weak encryption.
   // TODO(wtc): Bug 1198735: report mixed/unsafe content for unencrypted and
@@ -840,8 +842,13 @@
       ChromeSSLHostStateDelegateFactory::GetForProfile(profile_);
   DCHECK(delegate);
   // Only show an SSL decision revoke button if the user has chosen to bypass
-  // SSL host errors for this host in the past.
-  show_ssl_decision_revoke_button_ = delegate->HasAllowException(url.host());
+  // SSL host errors for this host in the past, and we're not presently on a
+  // Safe Browsing error (since otherwise it's confusing which warning you're
+  // re-enabling).
+  show_ssl_decision_revoke_button_ =
+      delegate->HasAllowException(url.host()) &&
+      visible_security_state.malicious_content_status ==
+          security_state::MALICIOUS_CONTENT_STATUS_NONE;
 }
 
 void PageInfo::PresentSitePermissions() {
@@ -955,7 +962,8 @@
   info.connection_status = site_connection_status_;
   info.connection_status_description = UTF16ToUTF8(site_connection_details_);
   info.identity_status = site_identity_status_;
-  info.identity_status_description = UTF16ToUTF8(site_identity_details_);
+  info.safe_browsing_status = safe_browsing_status_;
+  info.identity_status_description = UTF16ToUTF8(site_details_message_);
   info.certificate = certificate_;
   info.show_ssl_decision_revoke_button = show_ssl_decision_revoke_button_;
   info.show_change_password_buttons = show_change_password_buttons_;
@@ -976,7 +984,7 @@
     return;
   }
 
-  if (site_identity_status_ == SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE) {
+  if (safe_browsing_status_ == SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE) {
     safe_browsing::LogWarningAction(
         safe_browsing::WarningUIType::PAGE_INFO,
         safe_browsing::WarningAction::SHOWN,
@@ -1006,31 +1014,31 @@
   return permission_list;
 }
 
-void PageInfo::GetSiteIdentityByMaliciousContentStatus(
+void PageInfo::GetSafeBrowsingStatusByMaliciousContentStatus(
     security_state::MaliciousContentStatus malicious_content_status,
-    PageInfo::SiteIdentityStatus* status,
+    PageInfo::SafeBrowsingStatus* status,
     base::string16* details) {
   switch (malicious_content_status) {
     case security_state::MALICIOUS_CONTENT_STATUS_NONE:
       NOTREACHED();
       break;
     case security_state::MALICIOUS_CONTENT_STATUS_MALWARE:
-      *status = PageInfo::SITE_IDENTITY_STATUS_MALWARE;
+      *status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
       *details = l10n_util::GetStringUTF16(IDS_PAGE_INFO_MALWARE_DETAILS);
       break;
     case security_state::MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING:
-      *status = PageInfo::SITE_IDENTITY_STATUS_SOCIAL_ENGINEERING;
+      *status = PageInfo::SAFE_BROWSING_STATUS_SOCIAL_ENGINEERING;
       *details =
           l10n_util::GetStringUTF16(IDS_PAGE_INFO_SOCIAL_ENGINEERING_DETAILS);
       break;
     case security_state::MALICIOUS_CONTENT_STATUS_UNWANTED_SOFTWARE:
-      *status = PageInfo::SITE_IDENTITY_STATUS_UNWANTED_SOFTWARE;
+      *status = PageInfo::SAFE_BROWSING_STATUS_UNWANTED_SOFTWARE;
       *details =
           l10n_util::GetStringUTF16(IDS_PAGE_INFO_UNWANTED_SOFTWARE_DETAILS);
       break;
     case security_state::MALICIOUS_CONTENT_STATUS_SIGN_IN_PASSWORD_REUSE:
 #if defined(FULL_SAFE_BROWSING)
-      *status = PageInfo::SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE;
+      *status = PageInfo::SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE;
       // |password_protection_service_| may be null in test.
       *details = password_protection_service_
                      ? password_protection_service_->GetWarningDetailText(
@@ -1040,7 +1048,7 @@
       break;
     case security_state::MALICIOUS_CONTENT_STATUS_ENTERPRISE_PASSWORD_REUSE:
 #if defined(FULL_SAFE_BROWSING)
-      *status = PageInfo::SITE_IDENTITY_STATUS_ENTERPRISE_PASSWORD_REUSE;
+      *status = PageInfo::SAFE_BROWSING_STATUS_ENTERPRISE_PASSWORD_REUSE;
       // |password_protection_service_| maybe null in test.
       *details = password_protection_service_
                      ? password_protection_service_->GetWarningDetailText(
@@ -1049,7 +1057,7 @@
 #endif
       break;
     case security_state::MALICIOUS_CONTENT_STATUS_BILLING:
-      *status = PageInfo::SITE_IDENTITY_STATUS_BILLING;
+      *status = PageInfo::SAFE_BROWSING_STATUS_BILLING;
       *details = l10n_util::GetStringUTF16(IDS_PAGE_INFO_BILLING_DETAILS);
       break;
   }
diff --git a/chrome/browser/ui/page_info/page_info.h b/chrome/browser/ui/page_info/page_info.h
index 3e4fc1e..4af18b7 100644
--- a/chrome/browser/ui/page_info/page_info.h
+++ b/chrome/browser/ui/page_info/page_info.h
@@ -87,15 +87,20 @@
     // The website provided a valid certificate, but the certificate or chain
     // is using a deprecated signature algorithm.
     SITE_IDENTITY_STATUS_DEPRECATED_SIGNATURE_ALGORITHM,
+  };
+
+  // Safe Browsing status of a website.
+  enum SafeBrowsingStatus {
+    SAFE_BROWSING_STATUS_NONE = 0,
     // The website has been flagged by Safe Browsing as dangerous for
     // containing malware, social engineering, unwanted software, or password
     // reuse on a low reputation site.
-    SITE_IDENTITY_STATUS_MALWARE,
-    SITE_IDENTITY_STATUS_SOCIAL_ENGINEERING,
-    SITE_IDENTITY_STATUS_UNWANTED_SOFTWARE,
-    SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE,
-    SITE_IDENTITY_STATUS_ENTERPRISE_PASSWORD_REUSE,
-    SITE_IDENTITY_STATUS_BILLING,
+    SAFE_BROWSING_STATUS_MALWARE,
+    SAFE_BROWSING_STATUS_SOCIAL_ENGINEERING,
+    SAFE_BROWSING_STATUS_UNWANTED_SOFTWARE,
+    SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE,
+    SAFE_BROWSING_STATUS_ENTERPRISE_PASSWORD_REUSE,
+    SAFE_BROWSING_STATUS_BILLING,
   };
 
   // Events for UMA. Do not reorder or change! Exposed in header so enum is
@@ -192,8 +197,12 @@
     return site_identity_status_;
   }
 
-  const base::string16& site_identity_details() const {
-    return site_identity_details_;
+  const SafeBrowsingStatus& safe_browsing_status() const {
+    return safe_browsing_status_;
+  }
+
+  const base::string16& site_details_message() const {
+    return site_details_message_;
   }
 
   const base::string16& organization_name() const { return organization_name_; }
@@ -232,11 +241,12 @@
   void RecordPasswordReuseEvent();
 #endif
 
-  // Helper function to get the site identification status and details by
-  // malicious content status.
-  void GetSiteIdentityByMaliciousContentStatus(
+  // Helper function to get the Safe Browsing status and details by malicious
+  // content status.
+  // TODO(jdeblasio): Eliminate this and just use MaliciousContentStatus?
+  void GetSafeBrowsingStatusByMaliciousContentStatus(
       security_state::MaliciousContentStatus malicious_content_status,
-      PageInfo::SiteIdentityStatus* status,
+      PageInfo::SafeBrowsingStatus* status,
       base::string16* details);
 
   // Retrieves all the permissions that are shown in Page Info.
@@ -260,6 +270,9 @@
   // Status of the website's identity verification check.
   SiteIdentityStatus site_identity_status_;
 
+  // Safe Browsing status of the website.
+  SafeBrowsingStatus safe_browsing_status_;
+
   // For secure connection |certificate_| is set to the server certificate.
   scoped_refptr<net::X509Certificate> certificate_;
 
@@ -271,9 +284,9 @@
   // unnecessary UTF-8 string conversions.
 
   // Details about the website's identity. If the website's identity has been
-  // verified then |site_identity_details_| contains who verified the identity.
+  // verified then |site_details_message_| contains who verified the identity.
   // This string will be displayed in the UI.
-  base::string16 site_identity_details_;
+  base::string16 site_details_message_;
 
   // Set when the user has explicitly bypassed an SSL error for this host or
   // explicitly denied it (the latter of which is not currently possible in the
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index 11c54a4..70030d6 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -232,6 +232,7 @@
 
 PageInfoUI::IdentityInfo::IdentityInfo()
     : identity_status(PageInfo::SITE_IDENTITY_STATUS_UNKNOWN),
+      safe_browsing_status(PageInfo::SAFE_BROWSING_STATUS_NONE),
       connection_status(PageInfo::SITE_CONNECTION_STATUS_UNKNOWN),
       show_ssl_decision_revoke_button(false),
       show_change_password_buttons(false) {}
@@ -246,6 +247,41 @@
   std::unique_ptr<PageInfoUI::SecurityDescription> security_description(
       new PageInfoUI::SecurityDescription());
 
+  switch (identity_info.safe_browsing_status) {
+    case PageInfo::SAFE_BROWSING_STATUS_NONE:
+      break;
+    case PageInfo::SAFE_BROWSING_STATUS_MALWARE:
+      return CreateSecurityDescription(SecuritySummaryColor::RED,
+                                       IDS_PAGE_INFO_MALWARE_SUMMARY,
+                                       IDS_PAGE_INFO_MALWARE_DETAILS);
+    case PageInfo::SAFE_BROWSING_STATUS_SOCIAL_ENGINEERING:
+      return CreateSecurityDescription(
+          SecuritySummaryColor::RED, IDS_PAGE_INFO_SOCIAL_ENGINEERING_SUMMARY,
+          IDS_PAGE_INFO_SOCIAL_ENGINEERING_DETAILS);
+    case PageInfo::SAFE_BROWSING_STATUS_UNWANTED_SOFTWARE:
+      return CreateSecurityDescription(SecuritySummaryColor::RED,
+                                       IDS_PAGE_INFO_UNWANTED_SOFTWARE_SUMMARY,
+                                       IDS_PAGE_INFO_UNWANTED_SOFTWARE_DETAILS);
+    case PageInfo::SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE:
+#if defined(FULL_SAFE_BROWSING)
+      return CreateSecurityDescriptionForPasswordReuse(
+          /*is_enterprise_password=*/false);
+#endif
+      NOTREACHED();
+      break;
+    case PageInfo::SAFE_BROWSING_STATUS_ENTERPRISE_PASSWORD_REUSE:
+#if defined(FULL_SAFE_BROWSING)
+      return CreateSecurityDescriptionForPasswordReuse(
+          /*is_enterprise_password=*/true);
+#endif
+      NOTREACHED();
+      break;
+    case PageInfo::SAFE_BROWSING_STATUS_BILLING:
+      return CreateSecurityDescription(SecuritySummaryColor::RED,
+                                       IDS_PAGE_INFO_BILLING_SUMMARY,
+                                       IDS_PAGE_INFO_BILLING_DETAILS);
+  }
+
   switch (identity_info.identity_status) {
     case PageInfo::SITE_IDENTITY_STATUS_INTERNAL_PAGE:
 #if defined(OS_ANDROID)
@@ -285,32 +321,6 @@
                                            IDS_PAGE_INFO_SECURE_SUMMARY,
                                            IDS_PAGE_INFO_SECURE_DETAILS);
       }
-    case PageInfo::SITE_IDENTITY_STATUS_MALWARE:
-      return CreateSecurityDescription(SecuritySummaryColor::RED,
-                                       IDS_PAGE_INFO_MALWARE_SUMMARY,
-                                       IDS_PAGE_INFO_MALWARE_DETAILS);
-    case PageInfo::SITE_IDENTITY_STATUS_SOCIAL_ENGINEERING:
-      return CreateSecurityDescription(
-          SecuritySummaryColor::RED, IDS_PAGE_INFO_SOCIAL_ENGINEERING_SUMMARY,
-          IDS_PAGE_INFO_SOCIAL_ENGINEERING_DETAILS);
-    case PageInfo::SITE_IDENTITY_STATUS_UNWANTED_SOFTWARE:
-      return CreateSecurityDescription(SecuritySummaryColor::RED,
-                                       IDS_PAGE_INFO_UNWANTED_SOFTWARE_SUMMARY,
-                                       IDS_PAGE_INFO_UNWANTED_SOFTWARE_DETAILS);
-    case PageInfo::SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE:
-#if defined(FULL_SAFE_BROWSING)
-      return CreateSecurityDescriptionForPasswordReuse(
-          /*is_enterprise_password=*/false);
-#endif
-    case PageInfo::SITE_IDENTITY_STATUS_ENTERPRISE_PASSWORD_REUSE:
-#if defined(FULL_SAFE_BROWSING)
-      return CreateSecurityDescriptionForPasswordReuse(
-          /*is_enterprise_password=*/true);
-#endif
-    case PageInfo::SITE_IDENTITY_STATUS_BILLING:
-      return CreateSecurityDescription(SecuritySummaryColor::RED,
-                                       IDS_PAGE_INFO_BILLING_SUMMARY,
-                                       IDS_PAGE_INFO_BILLING_DETAILS);
     case PageInfo::SITE_IDENTITY_STATUS_DEPRECATED_SIGNATURE_ALGORITHM:
     case PageInfo::SITE_IDENTITY_STATUS_UNKNOWN:
     case PageInfo::SITE_IDENTITY_STATUS_NO_CERT:
diff --git a/chrome/browser/ui/page_info/page_info_ui.h b/chrome/browser/ui/page_info/page_info_ui.h
index 75d32d06..fb10769 100644
--- a/chrome/browser/ui/page_info/page_info_ui.h
+++ b/chrome/browser/ui/page_info/page_info_ui.h
@@ -117,6 +117,8 @@
     std::string site_identity;
     // Status of the site's identity.
     PageInfo::SiteIdentityStatus identity_status;
+    // Site's Safe Browsing status.
+    PageInfo::SafeBrowsingStatus safe_browsing_status;
     // Textual description of the site's identity status that is displayed to
     // the user.
     std::string identity_status_description;
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index 545296d..e4836b29 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -442,8 +442,8 @@
 
   EXPECT_EQ(PageInfo::SITE_CONNECTION_STATUS_UNENCRYPTED,
             page_info()->site_connection_status());
-  EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_MALWARE,
-            page_info()->site_identity_status());
+  EXPECT_EQ(PageInfo::SAFE_BROWSING_STATUS_MALWARE,
+            page_info()->safe_browsing_status());
 }
 
 TEST_F(PageInfoTest, SocialEngineering) {
@@ -454,8 +454,8 @@
 
   EXPECT_EQ(PageInfo::SITE_CONNECTION_STATUS_UNENCRYPTED,
             page_info()->site_connection_status());
-  EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_SOCIAL_ENGINEERING,
-            page_info()->site_identity_status());
+  EXPECT_EQ(PageInfo::SAFE_BROWSING_STATUS_SOCIAL_ENGINEERING,
+            page_info()->safe_browsing_status());
 }
 
 TEST_F(PageInfoTest, UnwantedSoftware) {
@@ -466,8 +466,8 @@
 
   EXPECT_EQ(PageInfo::SITE_CONNECTION_STATUS_UNENCRYPTED,
             page_info()->site_connection_status());
-  EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_UNWANTED_SOFTWARE,
-            page_info()->site_identity_status());
+  EXPECT_EQ(PageInfo::SAFE_BROWSING_STATUS_UNWANTED_SOFTWARE,
+            page_info()->safe_browsing_status());
 }
 
 #if defined(FULL_SAFE_BROWSING)
@@ -479,8 +479,8 @@
 
   EXPECT_EQ(PageInfo::SITE_CONNECTION_STATUS_UNENCRYPTED,
             page_info()->site_connection_status());
-  EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE,
-            page_info()->site_identity_status());
+  EXPECT_EQ(PageInfo::SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE,
+            page_info()->safe_browsing_status());
 }
 
 TEST_F(PageInfoTest, EnterprisePasswordReuse) {
@@ -491,8 +491,8 @@
 
   EXPECT_EQ(PageInfo::SITE_CONNECTION_STATUS_UNENCRYPTED,
             page_info()->site_connection_status());
-  EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_ENTERPRISE_PASSWORD_REUSE,
-            page_info()->site_identity_status());
+  EXPECT_EQ(PageInfo::SAFE_BROWSING_STATUS_ENTERPRISE_PASSWORD_REUSE,
+            page_info()->safe_browsing_status());
 }
 #endif
 
@@ -768,7 +768,7 @@
   EXPECT_EQ(
       base::UTF8ToUTF16(
           "This page has been identified as being owned by Google Inc [US]."),
-      page_info()->site_identity_details());
+      page_info()->site_details_message());
 }
 
 TEST_F(PageInfoTest, HTTPSRevocationError) {
diff --git a/chrome/browser/ui/prefs/pref_watcher.cc b/chrome/browser/ui/prefs/pref_watcher.cc
index adec473..44cffec 100644
--- a/chrome/browser/ui/prefs/pref_watcher.cc
+++ b/chrome/browser/ui/prefs/pref_watcher.cc
@@ -30,6 +30,13 @@
     prefs::kWebKitDefaultFixedFontSize,
     prefs::kWebKitDefaultFontSize,
     prefs::kWebKitDomPasteEnabled,
+    prefs::kAccessibilityCaptionsTextSize,
+    prefs::kAccessibilityCaptionsTextFont,
+    prefs::kAccessibilityCaptionsTextColor,
+    prefs::kAccessibilityCaptionsTextOpacity,
+    prefs::kAccessibilityCaptionsBackgroundColor,
+    prefs::kAccessibilityCaptionsTextShadow,
+    prefs::kAccessibilityCaptionsBackgroundOpacity,
 #if defined(OS_ANDROID)
     prefs::kWebKitFontScaleFactor,
     prefs::kWebKitForceDarkModeEnabled,
diff --git a/chrome/browser/ui/search/local_ntp_uitest.cc b/chrome/browser/ui/search/local_ntp_uitest.cc
index 2f23d68..951c9e9 100644
--- a/chrome/browser/ui/search/local_ntp_uitest.cc
+++ b/chrome/browser/ui/search/local_ntp_uitest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/search/ntp_features.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/ui/browser.h"
@@ -12,22 +11,22 @@
 #include "chrome/browser/ui/search/instant_test_utils.h"
 #include "chrome/browser/ui/search/local_ntp_test_utils.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/view_ids.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/omnibox/browser/omnibox_view.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/vector2d.h"
 
-class LocalNTPUITest : public InProcessBrowserTest {
+class LocalNtpUiTest : public InProcessBrowserTest {
  public:
-  LocalNTPUITest() {}
+  LocalNtpUiTest() {}
 
   OmniboxView* omnibox() {
     return browser()->window()->GetLocationBar()->GetOmniboxView();
@@ -44,7 +43,7 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(LocalNTPUITest, FakeboxRedirectsToOmnibox) {
+IN_PROC_BROWSER_TEST_F(LocalNtpUiTest, FakeboxRedirectsToOmnibox) {
   content::WebContents* active_tab =
       local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser(), 1000);
@@ -89,14 +88,11 @@
                                                gfx::Vector2d(1, 1)));
 
   // Click on the fakebox, and wait for the omnibox to receive invisible focus.
-  content::WindowedNotificationObserver focus_observer(
-      chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
-      content::NotificationService::AllSources());
   ASSERT_TRUE(
       ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
   ASSERT_TRUE(
       ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::UP));
-  focus_observer.Wait();
+  ui_test_utils::WaitForViewFocus(browser(), VIEW_ID_OMNIBOX, true);
   EXPECT_EQ(OMNIBOX_FOCUS_INVISIBLE, omnibox()->model()->focus_state());
 
   // The fakebox should now also have focus.
@@ -106,13 +102,10 @@
   EXPECT_TRUE(result);
 
   // Type "a" and wait for the omnibox to receive visible focus.
-  content::WindowedNotificationObserver focus_observer2(
-      chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
-      content::NotificationService::AllSources());
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
       browser(), ui::KeyboardCode::VKEY_A,
       /*control=*/false, /*shift=*/false, /*alt=*/false, /*command=*/false));
-  focus_observer2.Wait();
+  ui_test_utils::WaitForViewFocus(browser(), VIEW_ID_OMNIBOX, true);
   EXPECT_EQ(OMNIBOX_FOCUS_VISIBLE, omnibox()->model()->focus_state());
 
   // The typed text should have arrived in the omnibox.
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 6fc533e..03d7e58 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -9,7 +9,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/chrome_colors/chrome_colors_factory.h"
 #include "chrome/browser/search/instant_service.h"
@@ -42,8 +41,6 @@
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
@@ -128,11 +125,6 @@
 
 void SearchTabHelper::OmniboxFocusChanged(OmniboxFocusState state,
                                           OmniboxFocusChangeReason reason) {
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
-      content::Source<SearchTabHelper>(this),
-      content::NotificationService::NoDetails());
-
   ipc_router_.OmniboxFocusChanged(state, reason);
 
   // Don't send oninputstart/oninputend updates in response to focus changes
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index cdff187..04467bf 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -481,7 +481,6 @@
     PrefRegistrySimple* registry) {
 #if defined(OS_WIN)
   registry->RegisterBooleanPref(prefs::kWelcomePageOnOSUpgradeEnabled, true);
-  registry->RegisterBooleanPref(prefs::kHasSeenWin10PromoPage, false);
 #endif
 #if !defined(OS_CHROMEOS)
   registry->RegisterBooleanPref(prefs::kPromotionalTabsEnabled, true);
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index 0d406a97..fc1d12b 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -130,12 +130,6 @@
 void DisableWelcomePages(const std::vector<Profile*>& profiles) {
   for (Profile* profile : profiles)
     profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, true);
-
-#if defined(OS_WIN)
-  // TODO(hcarmona): deprecate this pref.
-  g_browser_process->local_state()->SetBoolean(prefs::kHasSeenWin10PromoPage,
-                                               true);
-#endif
 }
 
 Browser* OpenNewBrowser(Profile* profile) {
diff --git a/chrome/browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc b/chrome/browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc
index c51e56f3..6d4f4da4 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc
@@ -122,12 +122,6 @@
   // Avoid showing the Welcome page.
   profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, true);
 
-#if defined(OS_WIN)
-  // Do not show the Windows 10 promo page.
-  g_browser_process->local_state()->SetBoolean(prefs::kHasSeenWin10PromoPage,
-                                               true);
-#endif
-
   // Set the startup preference to open these URLs.
   SessionStartupPref pref(SessionStartupPref::URLS);
   pref.urls = urls;
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 42d58684..01a8068d 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -417,7 +417,6 @@
     return nullptr;
   DCHECK(ContainsIndex(index));
 
-  NotifyGroupChange(index, UngroupTab(index), base::nullopt);
   FixOpeners(index);
 
   // Ask the delegate to save an entry for this tab in the historical tab
@@ -426,6 +425,8 @@
   if (create_historical_tab)
     delegate_->CreateHistoricalTab(raw_web_contents);
 
+  NotifyGroupChange(index, UngroupTab(index), base::nullopt);
+
   int next_selected_index = order_controller_->DetermineNewSelectedIndex(index);
   std::unique_ptr<WebContentsData> old_data = std::move(contents_data_[index]);
   contents_data_.erase(contents_data_.begin() + index);
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
index e437715..196bf75 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -126,6 +126,11 @@
   // the order of deletion between the views and the ToolbarActionsBar.
   DCHECK(toolbar_actions_.empty())
       << "Must call DeleteActions() before destruction.";
+
+  // Make sure we don't listen to any more model changes during
+  // ToolbarActionsBar destruction.
+  model_observer_.RemoveAll();
+
   for (ToolbarActionsBarObserver& observer : observers_)
     observer.OnToolbarActionsBarDestroyed();
 }
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 5abd6754..d91eb01 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -44,4 +44,10 @@
     "EnableDbusAndX11StatusIcons", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
+#if defined(OS_CHROMEOS)
+// Enables a warning about connecting to hidden WiFi networks.
+// https://crbug.com/903908
+const base::Feature kHiddenNetworkWarning{"HiddenNetworkWarning",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+#endif  // defined(OS_CHROMEOS)
 }  // namespace features
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 5b8766c..9e062d8 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -35,6 +35,9 @@
 extern const base::Feature kEnableDbusAndX11StatusIcons;
 #endif
 
+#if defined(OS_CHROMEOS)
+extern const base::Feature kHiddenNetworkWarning;
+#endif  // defined(OS_CHROMEOS)
 }  // namespace features
 
 #endif  // CHROME_BROWSER_UI_UI_FEATURES_H_
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc
index fe4a4b29..7f98d40 100644
--- a/chrome/browser/ui/views/collected_cookies_views.cc
+++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
 #include "chrome/browser/browsing_data/cookies_tree_model.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/content_settings/local_shared_objects_container.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
@@ -31,8 +30,7 @@
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_source.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/web_contents.h"
 #include "net/cookies/canonical_cookie.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -265,25 +263,29 @@
 ///////////////////////////////////////////////////////////////////////////////
 // CollectedCookiesViews, public:
 
-CollectedCookiesViews::CollectedCookiesViews(content::WebContents* web_contents)
-    : web_contents_(web_contents),
-      allowed_label_(NULL),
-      blocked_label_(NULL),
-      allowed_cookies_tree_(NULL),
-      blocked_cookies_tree_(NULL),
-      block_allowed_button_(NULL),
-      delete_allowed_button_(NULL),
-      allow_blocked_button_(NULL),
-      for_session_blocked_button_(NULL),
-      cookie_info_view_(NULL),
-      infobar_(NULL),
-      status_changed_(false) {
-  TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
-  registrar_.Add(this, chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
-                 content::Source<TabSpecificContentSettings>(content_settings));
-  constrained_window::ShowWebModalDialogViews(this, web_contents);
-  chrome::RecordDialogCreation(chrome::DialogIdentifier::COLLECTED_COOKIES);
+CollectedCookiesViews::~CollectedCookiesViews() {
+  if (!destroying_) {
+    // The owning WebContents is being destroyed before the Widget. Close the
+    // widget pronto.
+    destroying_ = true;
+    GetWidget()->CloseNow();
+  }
+
+  allowed_cookies_tree_->SetModel(nullptr);
+  blocked_cookies_tree_->SetModel(nullptr);
+}
+
+// static
+void CollectedCookiesViews::CreateAndShowForWebContents(
+    content::WebContents* web_contents) {
+  CollectedCookiesViews* instance = FromWebContents(web_contents);
+  if (instance) {
+    web_modal::WebContentsModalDialogManager::FromWebContents(web_contents)
+        ->FocusTopmostDialog();
+    return;
+  }
+
+  CreateForWebContents(web_contents);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -324,6 +326,15 @@
   return false;
 }
 
+void CollectedCookiesViews::DeleteDelegate() {
+  if (!destroying_) {
+    // The associated Widget is being destroyed before the owning WebContents.
+    // Tell the owner to delete |this|.
+    destroying_ = true;
+    web_contents_->RemoveUserData(UserDataKey());
+  }
+}
+
 std::unique_ptr<views::View> CollectedCookiesViews::CreateExtraView() {
   // The code in |Init|, which runs before this does, needs the button pane to
   // already exist, so it is created there and this class holds ownership until
@@ -386,9 +397,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 // CollectedCookiesViews, private:
 
-CollectedCookiesViews::~CollectedCookiesViews() {
-  allowed_cookies_tree_->SetModel(NULL);
-  blocked_cookies_tree_->SetModel(NULL);
+CollectedCookiesViews::CollectedCookiesViews(content::WebContents* web_contents)
+    : web_contents_(web_contents) {
+  constrained_window::ShowWebModalDialogViews(this, web_contents);
+  chrome::RecordDialogCreation(chrome::DialogIdentifier::COLLECTED_COOKIES);
 }
 
 void CollectedCookiesViews::Init() {
@@ -686,13 +698,4 @@
   tree_view->SchedulePaint();
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// CollectedCookiesViews, content::NotificationObserver implementation:
-
-void CollectedCookiesViews::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  DCHECK_EQ(chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN, type);
-  GetWidget()->Close();
-}
+WEB_CONTENTS_USER_DATA_KEY_IMPL(CollectedCookiesViews)
diff --git a/chrome/browser/ui/views/collected_cookies_views.h b/chrome/browser/ui/views/collected_cookies_views.h
index 4df1ecbb..6071584e 100644
--- a/chrome/browser/ui/views/collected_cookies_views.h
+++ b/chrome/browser/ui/views/collected_cookies_views.h
@@ -10,8 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "components/content_settings/core/common/content_settings.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_user_data.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h"
 #include "ui/views/controls/tree/tree_view_controller.h"
@@ -38,14 +37,17 @@
 // cookies of the current tab contents. To display the dialog, invoke
 // ShowCollectedCookiesDialog() on the delegate of the WebContents's
 // content settings tab helper.
-class CollectedCookiesViews : public views::DialogDelegateView,
-                              public content::NotificationObserver,
-                              public views::ButtonListener,
-                              public views::TabbedPaneListener,
-                              public views::TreeViewController {
+class CollectedCookiesViews
+    : public views::DialogDelegateView,
+      public views::ButtonListener,
+      public views::TabbedPaneListener,
+      public views::TreeViewController,
+      public content::WebContentsUserData<CollectedCookiesViews> {
  public:
+  ~CollectedCookiesViews() override;
+
   // Use BrowserWindow::ShowCollectedCookiesDialog to show.
-  explicit CollectedCookiesViews(content::WebContents* web_contents);
+  static void CreateAndShowForWebContents(content::WebContents* web_contents);
 
   // views::DialogDelegate:
   base::string16 GetWindowTitle() const override;
@@ -54,6 +56,7 @@
   bool Accept() override;
   ui::ModalType GetModalType() const override;
   bool ShouldShowCloseButton() const override;
+  void DeleteDelegate() override;
   std::unique_ptr<views::View> CreateExtraView() override;
 
   // views::ButtonListener:
@@ -72,8 +75,9 @@
 
  private:
   friend class CollectedCookiesViewsTest;
+  friend class content::WebContentsUserData<CollectedCookiesViews>;
 
-  ~CollectedCookiesViews() override;
+  explicit CollectedCookiesViews(content::WebContents* web_contents);
 
   void Init();
 
@@ -96,37 +100,30 @@
 
   void AddContentException(views::TreeView* tree_view, ContentSetting setting);
 
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
-  content::NotificationRegistrar registrar_;
-
   // The web contents.
   content::WebContents* web_contents_;
 
   // Assorted views.
-  views::Label* allowed_label_;
-  views::Label* blocked_label_;
+  views::Label* allowed_label_ = nullptr;
+  views::Label* blocked_label_ = nullptr;
 
-  views::TreeView* allowed_cookies_tree_;
-  views::TreeView* blocked_cookies_tree_;
+  views::TreeView* allowed_cookies_tree_ = nullptr;
+  views::TreeView* blocked_cookies_tree_ = nullptr;
 
-  views::LabelButton* block_allowed_button_;
-  views::LabelButton* delete_allowed_button_;
-  views::LabelButton* allow_blocked_button_;
-  views::LabelButton* for_session_blocked_button_;
+  views::LabelButton* block_allowed_button_ = nullptr;
+  views::LabelButton* delete_allowed_button_ = nullptr;
+  views::LabelButton* allow_blocked_button_ = nullptr;
+  views::LabelButton* for_session_blocked_button_ = nullptr;
 
   std::unique_ptr<CookiesTreeModel> allowed_cookies_tree_model_;
   std::unique_ptr<CookiesTreeModel> blocked_cookies_tree_model_;
 
-  CookiesTreeViewDrawingProvider* allowed_cookies_drawing_provider_;
-  CookiesTreeViewDrawingProvider* blocked_cookies_drawing_provider_;
+  CookiesTreeViewDrawingProvider* allowed_cookies_drawing_provider_ = nullptr;
+  CookiesTreeViewDrawingProvider* blocked_cookies_drawing_provider_ = nullptr;
 
-  CookieInfoView* cookie_info_view_;
+  CookieInfoView* cookie_info_view_ = nullptr;
 
-  InfobarView* infobar_;
+  InfobarView* infobar_ = nullptr;
 
   // The buttons pane is owned by this class until the containing
   // DialogClientView requests it via |CreateExtraView|, at which point
@@ -135,10 +132,18 @@
 
   // Weak pointers to the allowed and blocked panes so that they can be
   // shown/hidden as needed.
-  views::View* allowed_buttons_pane_;
-  views::View* blocked_buttons_pane_;
+  views::View* allowed_buttons_pane_ = nullptr;
+  views::View* blocked_buttons_pane_ = nullptr;
 
-  bool status_changed_;
+  bool status_changed_ = false;
+
+  // This bit is set to true when the widget is shutting down or when |this|'s
+  // destructor has been called. Either the Widget or the WebContents may be the
+  // first to shut down, and this prevents double-destruction of this or
+  // double-closing of the widget.
+  bool destroying_ = false;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   DISALLOW_COPY_AND_ASSIGN(CollectedCookiesViews);
 };
diff --git a/chrome/browser/ui/views/collected_cookies_views_browsertest.cc b/chrome/browser/ui/views/collected_cookies_views_browsertest.cc
index 1d7368db..92f58ff 100644
--- a/chrome/browser/ui/views/collected_cookies_views_browsertest.cc
+++ b/chrome/browser/ui/views/collected_cookies_views_browsertest.cc
@@ -16,6 +16,10 @@
 
 class CollectedCookiesViewsTest : public InProcessBrowserTest {
  public:
+  CollectedCookiesViewsTest() = default;
+  ~CollectedCookiesViewsTest() override = default;
+
+  // InProcessBrowserTest:
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -27,10 +31,11 @@
     ui_test_utils::NavigateToURL(
         browser(), embedded_test_server()->GetURL("/cookie1.html"));
 
-    // Spawn a cookies dialog.  Note that |cookies_dialog_| will delete itself
-    // automatically when it closes.
-    cookies_dialog_ = new CollectedCookiesViews(
-        browser()->tab_strip_model()->GetActiveWebContents());
+    // Spawn a cookies dialog.
+    auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+    CollectedCookiesViews::CreateAndShowForWebContents(web_contents);
+    cookies_dialog_ = static_cast<CollectedCookiesViews*>(
+        web_contents->GetUserData(CollectedCookiesViews::UserDataKey()));
   }
 
   // Closing dialog with modified data will shows infobar.
@@ -47,6 +52,8 @@
 
  private:
   CollectedCookiesViews* cookies_dialog_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(CollectedCookiesViewsTest);
 };
 
 IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsTest, CloseDialog) {
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index 706f862..ae1c350 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -120,9 +120,9 @@
       blink::mojom::MediaStreamType stream_type = i->first;
       const ContentSettingBubbleModel::MediaMenu& menu = i->second;
 
-      views::Label* label = new views::Label(menu.label);
+      auto label = std::make_unique<views::Label>(menu.label);
       label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-      layout->AddView(label);
+      layout->AddView(std::move(label));
 
       auto combobox_model = std::make_unique<MediaComboboxModel>(stream_type);
       // Disable the device selection when the website is managing the devices
@@ -134,12 +134,12 @@
               ? 0
               : combobox_model->GetDeviceIndex(menu.selected_device);
       // The combobox takes ownership of the model.
-      views::Combobox* combobox =
-          new views::Combobox(std::move(combobox_model));
+      auto combobox =
+          std::make_unique<views::Combobox>(std::move(combobox_model));
       combobox->SetEnabled(combobox_enabled);
       combobox->set_listener(listener);
       combobox->SetSelectedIndex(combobox_selected_index);
-      layout->AddView(combobox);
+      layout->AddView(std::move(combobox));
     }
   }
 
@@ -205,14 +205,18 @@
 
  private:
   using Row = std::pair<views::ImageView*, views::Label*>;
+  using NewRow = std::pair<std::unique_ptr<views::ImageView>,
+                           std::unique_ptr<views::Label>>;
 
   void ResetLayout();
   void AddRowToLayout(const Row& row);
+  Row AddNewRowToLayout(NewRow row);
+  void UpdateScrollHeight(const Row& row);
 
   ContentSettingBubbleContents* parent_;
 
   // Our controls representing list items, so we can add or remove
-  // these dynamically. Each pair represetns one list item.
+  // these dynamically. Each pair represents one list item.
   std::vector<Row> list_item_views_;
 
   DISALLOW_COPY_AND_ASSIGN(ListItemContainer);
@@ -226,20 +230,21 @@
 
 void ContentSettingBubbleContents::ListItemContainer::AddItem(
     const ContentSettingBubbleModel::ListItem& item) {
-  views::ImageView* icon = new views::ImageView();
-  views::Label* label = nullptr;
+  std::unique_ptr<views::ImageView> icon = std::make_unique<views::ImageView>();
+  std::unique_ptr<views::Label> label;
   if (item.has_link) {
-    views::Link* link = new views::Link(item.title);
+    std::unique_ptr<views::Link> link =
+        std::make_unique<views::Link>(item.title);
     link->set_listener(parent_);
     link->SetElideBehavior(gfx::ELIDE_MIDDLE);
-    label = link;
+    label = std::move(link);
   } else {
     icon->SetImage(item.image.AsImageSkia());
-    label = new views::Label(item.title);
+    label = std::make_unique<views::Label>(item.title);
   }
   label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
-  list_item_views_.push_back(Row(icon, label));
-  AddRowToLayout(list_item_views_.back());
+  list_item_views_.push_back(
+      AddNewRowToLayout(NewRow(std::move(icon), std::move(label))));
 }
 
 void ContentSettingBubbleContents::ListItemContainer::RemoveRowAtIndex(
@@ -251,8 +256,8 @@
 
   // As GridLayout can't remove rows, we have to rebuild it entirely.
   ResetLayout();
-  for (size_t i = 0; i < list_item_views_.size(); i++)
-    AddRowToLayout(list_item_views_[i]);
+  for (auto& row : list_item_views_)
+    AddRowToLayout(row);
 }
 
 int ContentSettingBubbleContents::ListItemContainer::GetRowIndexOf(
@@ -293,9 +298,26 @@
       static_cast<views::GridLayout*>(GetLayoutManager());
   DCHECK(layout);
   layout->StartRow(views::GridLayout::kFixedSize, 0);
-  layout->AddView(row.first);
-  layout->AddView(row.second);
+  layout->AddExistingView(row.first);
+  layout->AddExistingView(row.second);
+  UpdateScrollHeight(row);
+}
 
+ContentSettingBubbleContents::ListItemContainer::Row
+ContentSettingBubbleContents::ListItemContainer::AddNewRowToLayout(NewRow row) {
+  views::GridLayout* layout =
+      static_cast<views::GridLayout*>(GetLayoutManager());
+  DCHECK(layout);
+  Row row_result;
+  layout->StartRow(views::GridLayout::kFixedSize, 0);
+  row_result.first = layout->AddView(std::move(row.first));
+  row_result.second = layout->AddView(std::move(row.second));
+  UpdateScrollHeight(row_result);
+  return row_result;
+}
+
+void ContentSettingBubbleContents::ListItemContainer::UpdateScrollHeight(
+    const Row& row) {
   auto* scroll_view = views::ScrollView::GetScrollViewForContents(this);
   DCHECK(scroll_view);
   if (!scroll_view->is_bounded()) {
@@ -315,12 +337,7 @@
     views::BubbleBorder::Arrow arrow)
     : content::WebContentsObserver(web_contents),
       BubbleDialogDelegateView(anchor_view, arrow),
-      content_setting_bubble_model_(std::move(content_setting_bubble_model)),
-      list_item_container_(nullptr),
-      custom_link_(nullptr),
-      manage_button_(nullptr),
-      manage_checkbox_(nullptr),
-      learn_more_button_(nullptr) {
+      content_setting_bubble_model_(std::move(content_setting_bubble_model)) {
   chrome::RecordDialogCreation(
       chrome::DialogIdentifier::CONTENT_SETTING_CONTENTS);
 }
@@ -483,7 +500,7 @@
       row.view->SetBorder(
           views::CreateEmptyBorder(0, row_left_margin, 0, right_margin));
     }
-    AddChildView(row.view.release());
+    AddChildView(std::move(row.view));
   }
 
   content_setting_bubble_model_->set_owner(this);
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.h b/chrome/browser/ui/views/content_setting_bubble_contents.h
index 6d97b39..d93e88d 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.h
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.h
@@ -98,14 +98,14 @@
   // Provides data for this bubble.
   std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model_;
 
-  ListItemContainer* list_item_container_;
+  ListItemContainer* list_item_container_ = nullptr;
 
   typedef std::vector<views::RadioButton*> RadioGroup;
   RadioGroup radio_group_;
-  views::Link* custom_link_;
-  views::LabelButton* manage_button_;
-  views::Checkbox* manage_checkbox_;
-  views::ImageButton* learn_more_button_;
+  views::Link* custom_link_ = nullptr;
+  views::LabelButton* manage_button_ = nullptr;
+  views::Checkbox* manage_checkbox_ = nullptr;
+  views::ImageButton* learn_more_button_ = nullptr;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(ContentSettingBubbleContents);
 };
diff --git a/chrome/browser/ui/views/crostini/OWNERS b/chrome/browser/ui/views/crostini/OWNERS
index 9d9bf0c4..dc48383c 100644
--- a/chrome/browser/ui/views/crostini/OWNERS
+++ b/chrome/browser/ui/views/crostini/OWNERS
@@ -1 +1 @@
-file://chrome/browser/chromeos/crostini/OWNERS
+file://chrome/browser/chromeos/guest_os/OWNERS
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view.cc b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
index 3a09a2a..db968bb 100644
--- a/chrome/browser/ui/views/crostini/crostini_installer_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
@@ -352,11 +352,9 @@
   UpdateState(State::START_CONCIERGE);
 }
 
-void CrostiniInstallerView::OnConciergeStarted(CrostiniResult result) {
+void CrostiniInstallerView::OnConciergeStarted(bool success) {
   DCHECK_EQ(state_, State::START_CONCIERGE);
-  if (result != CrostiniResult::SUCCESS) {
-    LOG(ERROR) << "Failed to install start Concierge with error code: "
-               << static_cast<int>(result);
+  if (!success) {
     HandleError(
         l10n_util::GetStringUTF16(IDS_CROSTINI_INSTALLER_START_CONCIERGE_ERROR),
         SetupResult::kErrorStartingConcierge);
@@ -367,20 +365,18 @@
 }
 
 void CrostiniInstallerView::OnDiskImageCreated(
-    CrostiniResult result,
+    bool success,
     vm_tools::concierge::DiskImageStatus status,
     int64_t disk_size_available) {
   DCHECK_EQ(state_, State::CREATE_DISK_IMAGE);
   if (status == vm_tools::concierge::DiskImageStatus::DISK_STATUS_EXISTS) {
     do_cleanup_ = false;
-  } else if (result == CrostiniResult::SUCCESS) {
+  } else if (success) {
     // Record the max space for the disk image at creation time, measured in GiB
     base::UmaHistogramCustomCounts(kCrostiniDiskImageSizeHistogram,
                                    disk_size_available >> 30, 1, 1024, 64);
   }
-  if (result != CrostiniResult::SUCCESS) {
-    LOG(ERROR) << "Failed to create disk imagewith error code: "
-               << static_cast<int>(result);
+  if (!success) {
     HandleError(l10n_util::GetStringUTF16(
                     IDS_CROSTINI_INSTALLER_CREATE_DISK_IMAGE_ERROR),
                 SetupResult::kErrorCreatingDiskImage);
@@ -390,11 +386,9 @@
   UpdateState(State::START_TERMINA_VM);
 }
 
-void CrostiniInstallerView::OnVmStarted(CrostiniResult result) {
+void CrostiniInstallerView::OnVmStarted(bool success) {
   DCHECK_EQ(state_, State::START_TERMINA_VM);
-  if (result != CrostiniResult::SUCCESS) {
-    LOG(ERROR) << "Failed to start Termina VM with error code: "
-               << static_cast<int>(result);
+  if (!success) {
     HandleError(l10n_util::GetStringUTF16(
                     IDS_CROSTINI_INSTALLER_START_TERMINA_VM_ERROR),
                 SetupResult::kErrorStartingTermina);
@@ -417,11 +411,11 @@
   UpdateState(State::SETUP_CONTAINER);
 }
 
-void CrostiniInstallerView::OnContainerSetup(CrostiniResult result) {
+void CrostiniInstallerView::OnContainerSetup(bool success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(state_, State::SETUP_CONTAINER);
 
-  if (result != CrostiniResult::SUCCESS) {
+  if (!success) {
     if (content::GetNetworkConnectionTracker()->IsOffline()) {
       LOG(ERROR) << "Network connection dropped while downloading container";
       const base::string16 device_type = ui::GetChromeOSDeviceName();
@@ -429,8 +423,6 @@
                       IDS_CROSTINI_INSTALLER_OFFLINE_ERROR, device_type),
                   SetupResult::kErrorOffline);
     } else {
-      LOG(ERROR) << "Failed to set up container with error code: "
-                 << static_cast<int>(result);
       HandleError(l10n_util::GetStringUTF16(
                       IDS_CROSTINI_INSTALLER_SETUP_CONTAINER_ERROR),
                   SetupResult::kErrorSettingUpContainer);
@@ -457,13 +449,11 @@
   UpdateState(State::FETCH_SSH_KEYS);
 }
 
-void CrostiniInstallerView::OnSshKeysFetched(CrostiniResult result) {
+void CrostiniInstallerView::OnSshKeysFetched(bool success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(state_, State::FETCH_SSH_KEYS);
 
-  if (result != CrostiniResult::SUCCESS) {
-    LOG(ERROR) << "Failed to fetch ssh keys with error code: "
-               << static_cast<int>(result);
+  if (!success) {
     HandleError(
         l10n_util::GetStringUTF16(IDS_CROSTINI_INSTALLER_FETCH_SSH_KEYS_ERROR),
         SetupResult::kErrorFetchingSshKeys);
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view.h b/chrome/browser/ui/views/crostini/crostini_installer_view.h
index 0c4e107..db41d71 100644
--- a/chrome/browser/ui/views/crostini/crostini_installer_view.h
+++ b/chrome/browser/ui/views/crostini/crostini_installer_view.h
@@ -85,16 +85,16 @@
 
   // crostini::CrostiniManager::RestartObserver
   void OnComponentLoaded(crostini::CrostiniResult result) override;
-  void OnConciergeStarted(crostini::CrostiniResult result) override;
-  void OnDiskImageCreated(crostini::CrostiniResult result,
+  void OnConciergeStarted(bool success) override;
+  void OnDiskImageCreated(bool success,
                           vm_tools::concierge::DiskImageStatus status,
                           int64_t disk_size_available) override;
-  void OnVmStarted(crostini::CrostiniResult result) override;
+  void OnVmStarted(bool success) override;
   void OnContainerDownloading(int32_t download_percent) override;
   void OnContainerCreated(crostini::CrostiniResult result) override;
-  void OnContainerSetup(crostini::CrostiniResult result) override;
+  void OnContainerSetup(bool success) override;
   void OnContainerStarted(crostini::CrostiniResult result) override;
-  void OnSshKeysFetched(crostini::CrostiniResult result) override;
+  void OnSshKeysFetched(bool success) override;
 
   static CrostiniInstallerView* GetActiveViewForTesting();
   void SetCloseCallbackForTesting(base::OnceClosure quit_closure);
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
index c2056ad..ca5b4343 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -19,7 +19,7 @@
       model_observer_(this),
       extensions_button_(new ExtensionsToolbarButton(browser_, this)) {
   model_observer_.Add(model_);
-  AddMainView(extensions_button_);
+  AddMainButton(extensions_button_);
   CreateActions();
 }
 
diff --git a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
index c54f8a0..8e521fed 100644
--- a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
+++ b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
@@ -33,7 +33,7 @@
 using ui_test_utils::IsViewFocused;
 
 namespace {
-const char kSimplePage[] = "/find_in_page/simple.html";
+static const char kSimplePage[] = "/find_in_page/simple.html";
 }  // namespace
 
 class FindInPageTest : public InProcessBrowserTest {
@@ -577,33 +577,3 @@
 
   observer.Wait();
 }
-
-// FindInPage on Mac doesn't use prepopulated values. Search there is global.
-#if !defined(OS_MACOSX)
-IN_PROC_BROWSER_TEST_F(FindInPageTest, SelectionDuringFind) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-  // Make sure Chrome is in the foreground, otherwise sending input
-  // won't do anything and the test will hang.
-  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
-
-  ui_test_utils::NavigateToURL(
-      browser(),
-      embedded_test_server()->GetURL("/find_in_page/find_from_selection.html"));
-
-  // Put focus in the input
-  ASSERT_TRUE(content::ExecuteScript(
-      browser()->tab_strip_model()->GetActiveWebContents(), "focusInput();"));
-
-  // Cause a selection with Ctrl+A
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_A, true,
-                                              false, false, false));
-
-  browser()->GetFindBarController()->Show();
-  EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
-
-  // verify the text matches the selection
-  EXPECT_EQ(ASCIIToUTF16("text"), GetFindBarText());
-  FindNotificationDetails details = WaitForFindResult();
-  EXPECT_TRUE(details.number_of_matches() > 0);
-}
-#endif
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
index 48a81b0..9df12f1 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -5,15 +5,17 @@
 #include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 
 #include "ui/views/layout/flex_layout.h"
+#include "ui/views/view_class_properties.h"
 
 TabStripRegionView::TabStripRegionView() {
   views::FlexLayout* layout_manager =
       SetLayoutManager(std::make_unique<views::FlexLayout>());
 
   layout_manager->SetOrientation(views::LayoutOrientation::kHorizontal)
-      .SetDefaultFlex(views::FlexSpecification::ForSizeRule(
-          views::MinimumFlexSizeRule::kScaleToZero,
-          views::MaximumFlexSizeRule::kUnbounded));
+      .SetDefault(views::kFlexBehaviorKey,
+                  views::FlexSpecification::ForSizeRule(
+                      views::MinimumFlexSizeRule::kScaleToZero,
+                      views::MaximumFlexSizeRule::kUnbounded));
 }
 
 TabStripRegionView::~TabStripRegionView() {}
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
new file mode 100644
index 0000000..2a8274ea
--- /dev/null
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
@@ -0,0 +1,123 @@
+// Copyright 2019 The Chromium 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/views/global_media_controls/media_dialog_view.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/fill_layout.h"
+
+using media_session::mojom::MediaSessionAction;
+
+// static
+MediaDialogView* MediaDialogView::instance_ = nullptr;
+
+// static
+void MediaDialogView::ShowDialog(views::View* anchor_view,
+                                 service_manager::Connector* connector) {
+  DCHECK(!instance_);
+  instance_ = new MediaDialogView(anchor_view, connector);
+
+  views::Widget* widget =
+      views::BubbleDialogDelegateView::CreateBubble(instance_);
+  widget->Show();
+}
+
+// static
+void MediaDialogView::HideDialog() {
+  if (IsShowing())
+    instance_->GetWidget()->Close();
+
+  // Set |instance_| to nullptr so that |IsShowing()| returns false immediately.
+  // We also set to nullptr in |WindowClosing()| (which happens asynchronously),
+  // since |HideDialog()| is not always called.
+  instance_ = nullptr;
+}
+
+// static
+bool MediaDialogView::IsShowing() {
+  return instance_ != nullptr;
+}
+
+void MediaDialogView::ShowMediaSession(
+    const std::string& id,
+    base::WeakPtr<media_message_center::MediaNotificationItem> item) {
+  // Do nothing if we're already showing this media session.
+  if (active_session_id_.has_value() && *active_session_id_ == id)
+    return;
+
+  active_session_id_ = id;
+
+  auto active_session_container =
+      std::make_unique<MediaNotificationContainerImpl>(this, item);
+  active_session_container_ = AddChildView(std::move(active_session_container));
+  OnAnchorBoundsChanged();
+}
+
+void MediaDialogView::HideMediaSession(const std::string& id) {
+  // Do nothing if we're not showing this media session.
+  if (!active_session_id_.has_value() || active_session_id_ != id)
+    return;
+
+  active_session_id_ = base::nullopt;
+
+  RemoveChildView(active_session_container_);
+  active_session_container_ = nullptr;
+  OnAnchorBoundsChanged();
+}
+
+bool MediaDialogView::ShouldShowCloseButton() const {
+  return true;
+}
+
+int MediaDialogView::GetDialogButtons() const {
+  return ui::DIALOG_BUTTON_NONE;
+}
+
+bool MediaDialogView::Close() {
+  return Cancel();
+}
+
+gfx::Size MediaDialogView::CalculatePreferredSize() const {
+  // If we have an active session, then fit to it.
+  if (active_session_container_)
+    return views::BubbleDialogDelegateView::CalculatePreferredSize();
+
+  // Otherwise, use a standard size for bubble dialogs.
+  const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
+      DISTANCE_BUBBLE_PREFERRED_WIDTH);
+  return gfx::Size(width, GetHeightForWidth(width));
+}
+
+MediaDialogView::MediaDialogView(views::View* anchor_view,
+                                 service_manager::Connector* connector)
+    : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
+      controller_(connector, this) {}
+
+MediaDialogView::~MediaDialogView() = default;
+
+void MediaDialogView::Init() {
+  auto* provider = ChromeLayoutProvider::Get();
+  set_margins(
+      gfx::Insets(provider->GetDistanceMetric(
+                      views::DISTANCE_DIALOG_CONTENT_MARGIN_TOP_CONTROL),
+                  0,
+                  provider->GetDistanceMetric(
+                      views::DISTANCE_DIALOG_CONTENT_MARGIN_BOTTOM_CONTROL),
+                  0));
+
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+
+  controller_.Initialize();
+}
+
+void MediaDialogView::WindowClosing() {
+  if (instance_ == this)
+    instance_ = nullptr;
+}
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.h b/chrome/browser/ui/views/global_media_controls/media_dialog_view.h
new file mode 100644
index 0000000..ae83d6d
--- /dev/null
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.h
@@ -0,0 +1,66 @@
+// Copyright 2019 The Chromium 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_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_VIEW_H_
+
+#include "base/optional.h"
+#include "chrome/browser/ui/global_media_controls/media_dialog_controller.h"
+#include "chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+
+namespace service_manager {
+class Connector;
+}  // namespace service_manager
+
+class MediaNotificationContainerImpl;
+
+// Dialog that shows media controls that control the active media session.
+class MediaDialogView : public views::BubbleDialogDelegateView,
+                        public MediaDialogControllerDelegate {
+ public:
+  static void ShowDialog(views::View* anchor_view,
+                         service_manager::Connector* connector);
+  static void HideDialog();
+  static bool IsShowing();
+
+  // MediaDialogControllerDelegate implementation.
+  void ShowMediaSession(
+      const std::string& id,
+      base::WeakPtr<media_message_center::MediaNotificationItem> item) override;
+  void HideMediaSession(const std::string& id) override;
+
+  // views::WidgetDelegateView implementation.
+  bool ShouldShowCloseButton() const override;
+
+  // views::DialogDelegate implementation.
+  int GetDialogButtons() const override;
+  bool Close() override;
+
+  // views::View implementation.
+  gfx::Size CalculatePreferredSize() const override;
+
+ private:
+  explicit MediaDialogView(views::View* anchor_view,
+                           service_manager::Connector* connector);
+  ~MediaDialogView() override;
+
+  static MediaDialogView* instance_;
+
+  // views::BubbleDialogDelegateView implementation.
+  void Init() override;
+  void WindowClosing() override;
+
+  MediaDialogController controller_;
+
+  // TODO(steimel): We should support showing multiple sessions instead of just
+  // the active one.
+  MediaNotificationContainerImpl* active_session_container_ = nullptr;
+
+  base::Optional<std::string> active_session_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaDialogView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl.cc b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl.cc
new file mode 100644
index 0000000..e5247eb7
--- /dev/null
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl.cc
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h"
+
+#include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h"
+#include "ui/views/layout/fill_layout.h"
+
+namespace {
+
+// TODO(steimel): We need to decide on the correct values here.
+constexpr int kWidth = 400;
+constexpr gfx::Size kNormalSize = gfx::Size(kWidth, 100);
+constexpr gfx::Size kExpandedSize = gfx::Size(kWidth, 150);
+
+}  // anonymous namespace
+
+MediaNotificationContainerImpl::MediaNotificationContainerImpl(
+    MediaDialogView* parent,
+    base::WeakPtr<media_message_center::MediaNotificationItem> item)
+    : parent_(parent), view_(this, std::move(item), nullptr, base::string16()) {
+  DCHECK(parent_);
+
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+
+  SetPreferredSize(kNormalSize);
+
+  view_.set_owned_by_client();
+
+  AddChildView(&view_);
+}
+
+MediaNotificationContainerImpl::~MediaNotificationContainerImpl() = default;
+
+void MediaNotificationContainerImpl::OnExpanded(bool expanded) {
+  SetPreferredSize(expanded ? kExpandedSize : kNormalSize);
+  PreferredSizeChanged();
+  parent_->OnAnchorBoundsChanged();
+}
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h
new file mode 100644
index 0000000..4ba9ce3
--- /dev/null
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_CONTAINER_IMPL_H_
+#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_CONTAINER_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/media_message_center/media_notification_container.h"
+#include "components/media_message_center/media_notification_view.h"
+#include "ui/views/view.h"
+
+namespace media_message_center {
+class MediaNotificationItem;
+}  // namespace media_message_center
+
+class MediaDialogView;
+
+// MediaNotificationContainerImpl holds a media notification for display within
+// the MediaDialogView. The media notification shows metadata for a media
+// session and can control playback.
+class MediaNotificationContainerImpl
+    : public views::View,
+      public media_message_center::MediaNotificationContainer {
+ public:
+  explicit MediaNotificationContainerImpl(
+      MediaDialogView* parent,
+      base::WeakPtr<media_message_center::MediaNotificationItem> item);
+  ~MediaNotificationContainerImpl() override;
+
+  // media_message_center::MediaNotificationContainer:
+  void OnExpanded(bool expanded) override;
+
+ private:
+  MediaDialogView* parent_;
+  media_message_center::MediaNotificationView view_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaNotificationContainerImpl);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_CONTAINER_IMPL_H_
diff --git a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
index abd3c8fc..457470b 100644
--- a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
 
 #include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/theme_provider.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -27,7 +28,10 @@
 
 void MediaToolbarButtonView::ButtonPressed(views::Button* sender,
                                            const ui::Event& event) {
-  // TODO(https://crbug.com/973491): Toggle the MediaDialogView.
+  if (MediaDialogView::IsShowing())
+    MediaDialogView::HideDialog();
+  else
+    MediaDialogView::ShowDialog(this, connector_);
 }
 
 void MediaToolbarButtonView::Show() {
diff --git a/chrome/browser/ui/views/hover_button.h b/chrome/browser/ui/views/hover_button.h
index a30368a79..820bf2a 100644
--- a/chrome/browser/ui/views/hover_button.h
+++ b/chrome/browser/ui/views/hover_button.h
@@ -28,6 +28,8 @@
 class View;
 }  // namespace views
 
+class PageInfoBubbleViewBrowserTest;
+
 // A button taking the full width of its parent that shows a background color
 // when hovered over.
 class HoverButton : public views::MenuButton, public views::MenuButtonListener {
@@ -120,6 +122,7 @@
                            SetStatusLabel);
   FRIEND_TEST_ALL_PREFIXES(ExtensionsMenuButtonTest,
                            UpdatesToDisplayCorrectActionTitle);
+  friend class PageInfoBubbleViewBrowserTest;
 
   views::StyledLabel* title_ = nullptr;
   views::Label* subtitle_ = nullptr;
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
index 0d058a7..ef7ec00 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
@@ -105,11 +105,20 @@
     title_label_->SetBackgroundColor(background_color);
     title_label_->SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL);
     title_label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
+    title_label_->SetProperty(views::kFlexBehaviorKey,
+                              views::FlexSpecification::ForSizeRule(
+                                  views::MinimumFlexSizeRule::kScaleToMinimum,
+                                  views::MaximumFlexSizeRule::kPreferred));
 
     location_label_->SetBackgroundColor(background_color);
     location_label_->SetElideBehavior(gfx::ElideBehavior::ELIDE_HEAD);
     location_label_->SetHorizontalAlignment(
         gfx::HorizontalAlignment::ALIGN_LEFT);
+    location_label_->SetProperty(
+        views::kFlexBehaviorKey,
+        views::FlexSpecification::ForSizeRule(
+            views::MinimumFlexSizeRule::kScaleToMinimum,
+            views::MaximumFlexSizeRule::kPreferred));
 
     AddChildView(title_label_);
     AddChildView(location_label_);
@@ -117,15 +126,7 @@
     auto* layout = SetLayoutManager(std::make_unique<views::FlexLayout>());
     layout->SetOrientation(views::LayoutOrientation::kVertical)
         .SetMainAxisAlignment(views::LayoutAlignment::kCenter)
-        .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
-        .SetFlexForView(title_label_,
-                        views::FlexSpecification::ForSizeRule(
-                            views::MinimumFlexSizeRule::kScaleToMinimum,
-                            views::MaximumFlexSizeRule::kPreferred))
-        .SetFlexForView(location_label_,
-                        views::FlexSpecification::ForSizeRule(
-                            views::MinimumFlexSizeRule::kScaleToMinimum,
-                            views::MaximumFlexSizeRule::kPreferred));
+        .SetCrossAxisAlignment(views::LayoutAlignment::kStart);
   }
 
   void Update(base::string16 title, base::string16 location) {
@@ -199,17 +200,17 @@
 
   title_origin_view_ =
       new CustomTabBarTitleOriginView(kCustomTabBarViewBackgroundColor);
+  title_origin_view_->SetProperty(
+      views::kFlexBehaviorKey, views::FlexSpecification::ForSizeRule(
+                                   views::MinimumFlexSizeRule::kScaleToMinimum,
+                                   views::MaximumFlexSizeRule::kPreferred));
   AddChildView(title_origin_view_);
 
   layout_manager_ = SetLayoutManager(std::make_unique<views::FlexLayout>());
   layout_manager_->SetOrientation(views::LayoutOrientation::kHorizontal)
       .SetMainAxisAlignment(views::LayoutAlignment::kStart)
       .SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
-      .SetInteriorMargin(GetLayoutInsets(LayoutInset::TOOLBAR_INTERIOR_MARGIN))
-      .SetFlexForView(title_origin_view_,
-                      views::FlexSpecification::ForSizeRule(
-                          views::MinimumFlexSizeRule::kScaleToMinimum,
-                          views::MaximumFlexSizeRule::kPreferred));
+      .SetInteriorMargin(GetLayoutInsets(LayoutInset::TOOLBAR_INTERIOR_MARGIN));
 
   tab_strip_model_observer_.Add(browser->tab_strip_model());
 }
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 62afe960..6eaed85 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -223,6 +223,10 @@
   // |browser_| may be null when LocationBarView is used for non-Browser windows
   // such as PresentationReceiverWindowView, which do not support page actions.
   if (browser_) {
+    // The send tab to self icon is intentionally the first one added so it is
+    // the left most icon.
+    if (send_tab_to_self::IsSendingEnabled())
+      params.types_enabled.push_back(PageActionIconType::kSendTabToSelf);
     if (!base::FeatureList::IsEnabled(
             autofill::features::kAutofillEnableToolbarStatusChip)) {
       params.types_enabled.push_back(PageActionIconType::kManagePasswords);
@@ -233,8 +237,6 @@
     params.types_enabled.push_back(PageActionIconType::kFind);
     params.types_enabled.push_back(PageActionIconType::kTranslate);
     params.types_enabled.push_back(PageActionIconType::kZoom);
-    if (send_tab_to_self::IsSendingEnabled())
-      params.types_enabled.push_back(PageActionIconType::kSendTabToSelf);
   }
   params.icon_size = GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
   params.icon_color = icon_color;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index 110147b..967e8711 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -24,10 +24,6 @@
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/widget/widget.h"
 
-#if defined(USE_AURA)
-#include "ui/wm/core/window_util.h"
-#endif  // defined(USE_AURA)
-
 class OmniboxPopupContentsView::AutocompletePopupWidget
     : public ThemeCopyingWidget,
       public base::SupportsWeakPtr<AutocompletePopupWidget> {
@@ -63,12 +59,6 @@
 
   void SetTargetBounds(const gfx::Rect& bounds) {
     SetBounds(bounds);
-
-#if defined(USE_AURA)
-    // TODO(malaykeshav): Remove this manual snap when we start snapping each
-    // window to its parent window. See https://crbug.com/863268 for more info.
-    wm::SnapWindowToPixelBoundary(GetNativeWindow());
-#endif  // defined(USE_AURA)
   }
 
   void ShowAnimated() {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
index 7f631ea..acd77b78 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
@@ -34,7 +34,6 @@
 
 #if defined(USE_AURA)
 #include "ui/aura/window.h"
-#include "ui/wm/core/window_properties.h"
 #endif
 
 namespace {
@@ -170,8 +169,6 @@
 
 #if defined(USE_AURA)
   popup_view()->UpdatePopupAppearance();
-  EXPECT_TRUE(
-      popup->GetNativeWindow()->GetProperty(wm::kSnapChildrenToPixelBoundary));
 #endif  // defined(USE_AURA)
 
   gfx::Rect alignment_rect = location_bar()->GetBoundsInScreen();
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index d27fded..d50fd90 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -282,8 +282,18 @@
     // appropriate; otherwise, a subsequent OnFocus() or OnBlur() call could
     // goof up the selection.  See comments on OnActiveTabChanged() call in
     // Browser::ActiveTabChanged().
-    SelectRange(state->selection);
-    saved_selection_for_focus_change_ = state->saved_selection_for_focus_change;
+    if (state->model_state.user_input_in_progress &&
+        state->model_state.user_text.empty() &&
+        state->model_state.keyword.empty()) {
+      // See comment in OmniboxEditModel::GetStateForTabSwitch() for details on
+      // this.
+      SelectAll(true);
+      saved_selection_for_focus_change_ = gfx::Range();
+    } else {
+      SelectRange(state->selection);
+      saved_selection_for_focus_change_ =
+          state->saved_selection_for_focus_change;
+    }
   }
 
   // TODO(msw|oshima): Consider saving/restoring edit history.
@@ -1549,13 +1559,16 @@
     case ui::VKEY_PRIOR:
       if (control || alt || shift || read_only())
         return false;
-      model()->OnUpOrDownKeyPressed(-1 * model()->result().size());
+      model()->OnUpOrDownKeyPressed(
+          -static_cast<int>(model()->popup_model()->selected_line()));
       return true;
 
     case ui::VKEY_NEXT:
       if (control || alt || shift || read_only())
         return false;
-      model()->OnUpOrDownKeyPressed(model()->result().size());
+      model()->OnUpOrDownKeyPressed(model()->result().size() -
+                                    model()->popup_model()->selected_line() -
+                                    1);
       return true;
 
     case ui::VKEY_RIGHT:
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
index a9b788a..a94c7cbe 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -28,6 +28,9 @@
 #include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/omnibox/browser/test_location_bar_model.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "ui/base/clipboard/clipboard.h"
@@ -170,6 +173,8 @@
       : ChromeOmniboxEditController(command_updater),
         location_bar_model_(location_bar_model) {}
 
+  void set_omnibox_view(OmniboxViewViews* view) { omnibox_view_ = view; }
+
  private:
   // ChromeOmniboxEditController:
   LocationBarModel* GetLocationBarModel() override {
@@ -178,8 +183,15 @@
   const LocationBarModel* GetLocationBarModel() const override {
     return location_bar_model_;
   }
+  void UpdateWithoutTabRestore() override {
+    // This is a minimal amount of what LocationBarView does. Not all tests
+    // set |omnibox_view_|.
+    if (omnibox_view_)
+      omnibox_view_->Update();
+  }
 
   LocationBarModel* location_bar_model_;
+  OmniboxViewViews* omnibox_view_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(TestingOmniboxEditController);
 };
@@ -229,6 +241,11 @@
   }
 
  protected:
+  Profile* profile() { return profile_.get(); }
+  TestingOmniboxEditController* omnibox_edit_controller() {
+    return &omnibox_edit_controller_;
+  }
+
   // testing::Test:
   void SetUp() override;
   void TearDown() override;
@@ -600,6 +617,52 @@
   EXPECT_EQ(expected_text, returned_text);
 }
 
+// Verifies |OmniboxEditModel::State::needs_revert_and_select_all|, and verifies
+// a recent regression in this logic (see https://crbug.com/923290).
+TEST_F(OmniboxViewViewsTest, SelectAllOnReactivateTabAfterDeleteAll) {
+  omnibox_edit_controller()->set_omnibox_view(omnibox_view());
+
+  content::RenderViewHostTestEnabler rvh_test_enabler;
+  auto web_contents1 =
+      content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+
+  // Simulate a new tab with "about:blank".
+  const GURL url_1("about:blank/");
+  location_bar_model()->set_url(url_1);
+  omnibox_view()->model()->ResetDisplayTexts();
+  omnibox_view()->RevertAll();
+  omnibox_view()->SaveStateToTab(web_contents1.get());
+
+  // Simulate creating another tab at "chrome://history". The second url should
+  // be longer than the first (to trigger the bug).
+  auto web_contents2 =
+      content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+  const GURL url_2("chrome://history/");
+  EXPECT_GT(url_2.spec().size(), url_1.spec().size());
+  // Notice the url is set before ResetDisplayTexts(), this matches what
+  // actually happens in code.
+  location_bar_model()->set_url(url_2);
+  omnibox_view()->model()->ResetDisplayTexts();
+  omnibox_view()->RevertAll();
+
+  // Delete all the text.
+  omnibox_view()->SetUserText(base::string16());
+  EXPECT_TRUE(omnibox_view()->model()->user_input_in_progress());
+
+  // Switch back to the first url.
+  location_bar_model()->set_url(url_1);
+  omnibox_view()->SaveStateToTab(web_contents2.get());
+  omnibox_view()->OnTabChanged(web_contents1.get());
+
+  // Switch back to the second url. Even though the text was deleted earlier,
+  // the previous text (|url_2|) should be restored *and* all the text selected.
+  location_bar_model()->set_url(url_2);
+  omnibox_view()->SaveStateToTab(web_contents1.get());
+  omnibox_view()->OnTabChanged(web_contents2.get());
+  EXPECT_EQ(url_2, GURL(base::UTF16ToUTF8(omnibox_view()->GetText())));
+  EXPECT_TRUE(omnibox_view()->IsSelectAll());
+}
+
 class OmniboxViewViewsClipboardTest
     : public OmniboxViewViewsTest,
       public ::testing::WithParamInterface<ui::TextEditCommand> {
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 2955f62..6216bc0b 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -1039,7 +1039,7 @@
       // Count how often the Collected Cookies dialog is opened.
       presenter_->RecordPageInfoAction(
           PageInfo::PAGE_INFO_COOKIES_DIALOG_OPENED);
-      new CollectedCookiesViews(web_contents());
+      CollectedCookiesViews::CreateAndShowForWebContents(web_contents());
       break;
     case PageInfoBubbleView::
         VIEW_ID_PAGE_INFO_LINK_OR_BUTTON_CERTIFICATE_VIEWER: {
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.h b/chrome/browser/ui/views/page_info/page_info_bubble_view.h
index d1f8f8a3..1606724 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.h
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/page_info/page_info_dialog.h"
 #include "chrome/browser/ui/page_info/page_info_ui.h"
 #include "chrome/browser/ui/views/bubble_anchor_util_views.h"
+#include "chrome/browser/ui/views/hover_button.h"
 #include "chrome/browser/ui/views/page_info/chosen_object_view_observer.h"
 #include "chrome/browser/ui/views/page_info/page_info_bubble_view_base.h"
 #include "chrome/browser/ui/views/page_info/permission_selector_row.h"
@@ -24,12 +25,12 @@
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/link_listener.h"
 #include "ui/views/controls/separator.h"
+#include "ui/views/controls/styled_label.h"
 #include "ui/views/controls/styled_label_listener.h"
 #include "ui/views/widget/widget.h"
 
 class BubbleHeaderView;
 class GURL;
-class HoverButton;
 class Profile;
 
 namespace content {
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
index 9121d7a5..8205e88 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
@@ -51,6 +51,8 @@
 
 namespace {
 
+constexpr char kExpiredCertificateFile[] = "expired_cert.pem";
+
 class ClickEvent : public ui::Event {
  public:
   ClickEvent() : ui::Event(ui::ET_UNKNOWN, base::TimeTicks(), 0) {}
@@ -141,6 +143,7 @@
     constexpr char kUnwantedSoftware[] = "UnwantedSoftware";
     constexpr char kSignInPasswordReuse[] = "SignInPasswordReuse";
     constexpr char kEnterprisePasswordReuse[] = "EnterprisePasswordReuse";
+    constexpr char kMalwareAndBadCert[] = "MalwareAndBadCert";
     constexpr char kMixedContentForm[] = "MixedContentForm";
     constexpr char kMixedContent[] = "MixedContent";
     constexpr char kAllowAllPermissions[] = "AllowAllPermissions";
@@ -158,7 +161,7 @@
     GURL url = http_url;
     if (name == kSecure || name == kEvSecure || name == kMixedContentForm ||
         name == kMixedContent || name == kAllowAllPermissions ||
-        name == kBlockAllPermissions) {
+        name == kBlockAllPermissions || name == kMalwareAndBadCert) {
       url = https_url;
     }
     if (name == kInternal) {
@@ -204,19 +207,24 @@
       expected_identifiers_.push_back(
           PageInfoBubbleView::VIEW_ID_PAGE_INFO_LABEL_EV_CERTIFICATE_DETAILS);
     } else if (name == kMalware) {
-      identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_MALWARE;
+      identity.safe_browsing_status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
     } else if (name == kDeceptive) {
-      identity.identity_status =
-          PageInfo::SITE_IDENTITY_STATUS_SOCIAL_ENGINEERING;
+      identity.safe_browsing_status =
+          PageInfo::SAFE_BROWSING_STATUS_SOCIAL_ENGINEERING;
     } else if (name == kUnwantedSoftware) {
-      identity.identity_status =
-          PageInfo::SITE_IDENTITY_STATUS_UNWANTED_SOFTWARE;
+      identity.safe_browsing_status =
+          PageInfo::SAFE_BROWSING_STATUS_UNWANTED_SOFTWARE;
     } else if (name == kSignInPasswordReuse) {
-      identity.identity_status =
-          PageInfo::SITE_IDENTITY_STATUS_SIGN_IN_PASSWORD_REUSE;
+      identity.safe_browsing_status =
+          PageInfo::SAFE_BROWSING_STATUS_SIGN_IN_PASSWORD_REUSE;
     } else if (name == kEnterprisePasswordReuse) {
-      identity.identity_status =
-          PageInfo::SITE_IDENTITY_STATUS_ENTERPRISE_PASSWORD_REUSE;
+      identity.safe_browsing_status =
+          PageInfo::SAFE_BROWSING_STATUS_ENTERPRISE_PASSWORD_REUSE;
+    } else if (name == kMalwareAndBadCert) {
+      identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_ERROR;
+      identity.certificate = net::ImportCertFromFile(
+          net::GetTestCertsDirectory(), kExpiredCertificateFile);
+      identity.safe_browsing_status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
     } else if (name == kMixedContentForm) {
       identity.identity_status =
           PageInfo::SITE_IDENTITY_STATUS_ADMIN_PROVIDED_CERT;
@@ -341,6 +349,15 @@
         ->SetIdentityInfo(identity_info);
   }
 
+  base::string16 GetCertificateButtonTitle() const {
+    // Only PageInfoBubbleViewBrowserTest can access certificate_button_ in
+    // PageInfoBubbleView, or title() in HoverButton.
+    PageInfoBubbleView* page_info_bubble_view =
+        static_cast<PageInfoBubbleView*>(
+            PageInfoBubbleView::GetPageInfoBubble());
+    return page_info_bubble_view->certificate_button_->title()->text();
+  }
+
  private:
   std::vector<PageInfoBubbleView::PageInfoBubbleViewID> expected_identifiers_;
 
@@ -622,6 +639,13 @@
   ShowAndVerifyUi();
 }
 
+// Shows the Page Info bubble for a site flagged for malware that also has a bad
+// certificate.
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
+                       InvokeUi_MalwareAndBadCert) {
+  ShowAndVerifyUi();
+}
+
 // Shows the Page Info bubble for an admin-provided cert when the page is
 // secure, but has a form that submits to an insecure url.
 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
@@ -715,6 +739,41 @@
             l10n_util::GetStringUTF16(IDS_PAGE_INFO_MIXED_CONTENT_SUMMARY));
 }
 
+// Ensure a page can both have an invalid certificate *and* be blocked by Safe
+// Browsing.  Regression test for bug 869925.
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, BlockedAndInvalidCert) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.AddDefaultHandlers(
+      base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+  ASSERT_TRUE(https_server.Start());
+
+  ui_test_utils::NavigateToURL(browser(), https_server.GetURL("/simple.html"));
+
+  // Setup the bogus identity with an expired cert and SB flagging.
+  PageInfoUI::IdentityInfo identity;
+  identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_ERROR;
+  identity.certificate = net::ImportCertFromFile(net::GetTestCertsDirectory(),
+                                                 kExpiredCertificateFile);
+  identity.safe_browsing_status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
+  OpenPageInfoBubble(browser());
+
+  SetPageInfoBubbleIdentityInfo(identity);
+
+  views::BubbleDialogDelegateView* page_info =
+      PageInfoBubbleView::GetPageInfoBubble();
+
+  // Verify bubble complains of malware...
+  EXPECT_EQ(page_info->GetWindowTitle(),
+            l10n_util::GetStringUTF16(IDS_PAGE_INFO_MALWARE_SUMMARY));
+
+  // ...and has a "Certificate (Invalid)" button.
+  const base::string16 invalid_parens = l10n_util::GetStringUTF16(
+      IDS_PAGE_INFO_CERTIFICATE_INVALID_PARENTHESIZED);
+  EXPECT_EQ(GetCertificateButtonTitle(),
+            l10n_util::GetStringFUTF16(IDS_PAGE_INFO_CERTIFICATE_BUTTON_TEXT,
+                                       invalid_parens));
+}
+
 namespace {
 
 // Tracks focus of an arbitrary UI element.
diff --git a/chrome/browser/ui/views/payments/payment_handler_change_payment_method_browsertest.cc b/chrome/browser/ui/views/payments/payment_handler_change_payment_method_browsertest.cc
index 13696f2..824f82e4 100644
--- a/chrome/browser/ui/views/payments/payment_handler_change_payment_method_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_change_payment_method_browsertest.cc
@@ -58,6 +58,9 @@
 };
 
 IN_PROC_BROWSER_TEST_P(PaymentHandlerChangePaymentMethodTest, Test) {
+  if (GetParam().method_identifier == MethodIdentifier::kBasicCard)
+    SetSkipUiForForBasicCard();
+
   NavigateTo("/change_payment_method.html");
 
   if (GetParam().method_identifier == MethodIdentifier::kBasicCard) {
@@ -72,9 +75,6 @@
   ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
                                      GetParam().init_test_code));
 
-  if (GetParam().method_identifier == MethodIdentifier::kBasicCard)
-    EnableSkipUIForForBasicCard();
-
   ASSERT_TRUE(content::ExecuteScriptAndExtractString(
       GetActiveWebContents(), "outputChangePaymentMethodReturnValue(request);",
       &actual_output));
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
index 168b055..bc3e8785 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
@@ -72,16 +72,11 @@
 
 }  // namespace
 
-PersonalDataLoadedObserverMock::PersonalDataLoadedObserverMock() {}
-PersonalDataLoadedObserverMock::~PersonalDataLoadedObserverMock() {}
+PersonalDataLoadedObserverMock::PersonalDataLoadedObserverMock() = default;
+PersonalDataLoadedObserverMock::~PersonalDataLoadedObserverMock() = default;
 
-PaymentRequestBrowserTestBase::PaymentRequestBrowserTestBase()
-    : delegate_(nullptr),
-      is_incognito_(false),
-      is_valid_ssl_(true),
-      is_browser_window_active_(true) {}
-
-PaymentRequestBrowserTestBase::~PaymentRequestBrowserTestBase() {}
+PaymentRequestBrowserTestBase::PaymentRequestBrowserTestBase() = default;
+PaymentRequestBrowserTestBase::~PaymentRequestBrowserTestBase() = default;
 
 void PaymentRequestBrowserTestBase::SetUpCommandLine(
     base::CommandLine* command_line) {
@@ -137,6 +132,10 @@
   is_browser_window_active_ = false;
 }
 
+void PaymentRequestBrowserTestBase::SetSkipUiForForBasicCard() {
+  skip_ui_for_basic_card_ = true;
+}
+
 void PaymentRequestBrowserTestBase::OnCanMakePaymentCalled() {
   if (event_waiter_)
     event_waiter_->OnEvent(DialogEvent::CAN_MAKE_PAYMENT_CALLED);
@@ -504,7 +503,7 @@
   std::unique_ptr<TestChromePaymentRequestDelegate> delegate =
       std::make_unique<TestChromePaymentRequestDelegate>(
           web_contents, this /* observer */, &prefs_, is_incognito_,
-          is_valid_ssl_, is_browser_window_active_);
+          is_valid_ssl_, is_browser_window_active_, skip_ui_for_basic_card_);
   delegate_ = delegate.get();
   PaymentRequestWebContentsManager::GetOrCreateForWebContents(web_contents)
       ->CreatePaymentRequest(web_contents->GetMainFrame(), web_contents,
@@ -836,14 +835,6 @@
   event_waiter_->Wait();
 }
 
-void PaymentRequestBrowserTestBase::EnableSkipUIForForBasicCard() {
-  std::vector<PaymentRequest*> requests =
-      GetPaymentRequests(GetActiveWebContents());
-  ASSERT_EQ(1U, requests.size());
-  requests.front()
-      ->set_skip_ui_for_non_url_payment_method_identifiers_for_test();
-}
-
 }  // namespace payments
 
 std::ostream& operator<<(
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.h b/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
index ea22e8e..a8eae5618 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
@@ -108,6 +108,7 @@
   void SetIncognito();
   void SetInvalidSsl();
   void SetBrowserWindowInactive();
+  void SetSkipUiForForBasicCard();
 
   // PaymentRequest::ObserverForTest:
   void OnCanMakePaymentCalled() override;
@@ -269,19 +270,17 @@
   // Wait for the event(s) passed to ResetEventWaiter*() to occur.
   void WaitForObservedEvent();
 
-  // Allows to skip UI into payment handler for "basic-card".
-  void EnableSkipUIForForBasicCard();
-
  private:
   std::unique_ptr<autofill::EventWaiter<DialogEvent>> event_waiter_;
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
   // Weak, owned by the PaymentRequest object.
-  TestChromePaymentRequestDelegate* delegate_;
+  TestChromePaymentRequestDelegate* delegate_ = nullptr;
   syncer::TestSyncService sync_service_;
   sync_preferences::TestingPrefServiceSyncable prefs_;
-  bool is_incognito_;
-  bool is_valid_ssl_;
-  bool is_browser_window_active_;
+  bool is_incognito_ = false;
+  bool is_valid_ssl_ = true;
+  bool is_browser_window_active_ = true;
+  bool skip_ui_for_basic_card_ = false;
 
   service_manager::BinderRegistryWithArgs<content::RenderFrameHost*> registry_;
 
diff --git a/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc
index d6c3b0f..82a86cd 100644
--- a/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc
@@ -34,9 +34,14 @@
   PaymentRequestPaymentAppTest()
       : alicepay_(net::EmbeddedTestServer::TYPE_HTTPS),
         bobpay_(net::EmbeddedTestServer::TYPE_HTTPS),
-        frankpay_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    scoped_feature_list_.InitAndEnableFeature(
-        ::features::kServiceWorkerPaymentApps);
+        frankpay_(net::EmbeddedTestServer::TYPE_HTTPS),
+        kylepay_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    scoped_feature_list_.InitWithFeatures(
+        // enabled features
+        {::features::kServiceWorkerPaymentApps,
+         features::kAlwaysAllowJustInTimePaymentApp},
+        // disabled features
+        {});
   }
 
   PermissionRequestManager* GetPermissionRequestManager() {
@@ -51,6 +56,7 @@
     ASSERT_TRUE(StartTestServer("alicepay.com", &alicepay_));
     ASSERT_TRUE(StartTestServer("bobpay.com", &bobpay_));
     ASSERT_TRUE(StartTestServer("frankpay.com", &frankpay_));
+    ASSERT_TRUE(StartTestServer("kylepay.com", &kylepay_));
 
     GetPermissionRequestManager()->set_auto_response_for_test(
         PermissionRequestManager::ACCEPT_ALL);
@@ -104,8 +110,8 @@
                                         std::string(), CONTENT_SETTING_BLOCK);
   }
 
-  // Sets a TestDownloader for alicepay.com, bobpay.com and frankpay.com to
-  // ServiceWorkerPaymentAppFactory, and ignores port in app scope.
+  // Sets a TestDownloader for ServiceWorkerPaymentAppFactory and ignores port
+  // in app scope.
   void SetDownloaderAndIgnorePortInOriginComparisonForTesting() {
     content::BrowserContext* context = browser()
                                            ->tab_strip_model()
@@ -120,6 +126,8 @@
                                  bobpay_.GetURL("bobpay.com", "/"));
     downloader->AddTestServerURL("https://frankpay.com/",
                                  frankpay_.GetURL("frankpay.com", "/"));
+    downloader->AddTestServerURL("https://kylepay.com/",
+                                 kylepay_.GetURL("kylepay.com", "/"));
     ServiceWorkerPaymentAppFactory::GetInstance()
         ->SetDownloaderAndIgnorePortInOriginComparisonForTesting(
             std::move(downloader));
@@ -148,6 +156,9 @@
   // https://frankpay.com/webpay supports payment apps from any origin.
   net::EmbeddedTestServer frankpay_;
 
+  // https://kylepay.com/webpay hosts a just-in-time installable payment app.
+  net::EmbeddedTestServer kylepay_;
+
   base::test::ScopedFeatureList scoped_feature_list_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestPaymentAppTest);
@@ -554,4 +565,23 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(PaymentRequestPaymentAppTest,
+                       AlwaysAllowJustInTimeInstall) {
+  SetDownloaderAndIgnorePortInOriginComparisonForTesting();
+
+  // Trigger a request that specifies both kylepay.com and basic-card.
+  NavigateTo("/payment_request_bobpay_and_cards_test.html");
+
+  ResetEventWaiterForDialogOpened();
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
+                                     "testInstallableAppAndCard();"));
+  WaitForObservedEvent();
+
+  ResetEventWaiterForSequence(
+      {DialogEvent::PROCESSING_SPINNER_SHOWN, DialogEvent::DIALOG_CLOSED});
+  ClickOnDialogViewAndWait(DialogViewID::PAY_BUTTON, dialog_view());
+
+  // kylepay should be installed just-in-time and used for testing.
+  ExpectBodyContains({"kylepay.com/webpay"});
+}
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_request_show_promise_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_show_promise_browsertest.cc
index de01ad79..35f38f6 100644
--- a/chrome/browser/ui/views/payments/payment_request_show_promise_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_show_promise_browsertest.cc
@@ -236,10 +236,10 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest, SkipUI) {
+  SetSkipUiForForBasicCard();
   NavigateTo("/show_promise/digital_goods.html");
   InstallEchoPaymentHandlerForBasicCard();
   ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "create();"));
-  EnableSkipUIForForBasicCard();
   ResetEventWaiterForSequence(
       {DialogEvent::PROCESSING_SPINNER_SHOWN,
        DialogEvent::PROCESSING_SPINNER_HIDDEN, DialogEvent::SPEC_DONE_UPDATING,
diff --git a/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.cc b/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.cc
index fd51c4fa..24989cc 100644
--- a/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.cc
+++ b/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.cc
@@ -14,14 +14,16 @@
     PrefService* pref_service,
     bool is_incognito,
     bool is_valid_ssl,
-    bool is_browser_window_active)
+    bool is_browser_window_active,
+    bool skip_ui_for_basic_card)
     : ChromePaymentRequestDelegate(web_contents),
       region_data_loader_(nullptr),
       observer_(observer),
       pref_service_(pref_service),
       is_incognito_(is_incognito),
       is_valid_ssl_(is_valid_ssl),
-      is_browser_window_active_(is_browser_window_active) {}
+      is_browser_window_active_(is_browser_window_active),
+      skip_ui_for_basic_card_(skip_ui_for_basic_card) {}
 
 void TestChromePaymentRequestDelegate::ShowDialog(PaymentRequest* request) {
   shown_dialog_ = new PaymentRequestDialogView(request, observer_);
@@ -52,4 +54,8 @@
   return is_valid_ssl_ ? "" : "Invalid SSL certificate";
 }
 
+bool TestChromePaymentRequestDelegate::SkipUiForBasicCard() const {
+  return skip_ui_for_basic_card_;
+}
+
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h b/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h
index c663565..3171cf5 100644
--- a/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h
+++ b/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h
@@ -31,7 +31,8 @@
       PrefService* pref_service,
       bool is_incognito,
       bool is_valid_ssl,
-      bool is_browser_window_active);
+      bool is_browser_window_active,
+      bool skip_ui_for_basic_card);
 
   void SetRegionDataLoader(autofill::RegionDataLoader* region_data_loader) {
     region_data_loader_ = region_data_loader;
@@ -44,6 +45,7 @@
   PrefService* GetPrefService() override;
   bool IsBrowserWindowActive() const override;
   std::string GetInvalidSslCertificateErrorMessage() override;
+  bool SkipUiForBasicCard() const override;
 
   PaymentRequestDialogView* dialog_view() {
     return static_cast<PaymentRequestDialogView*>(shown_dialog_);
@@ -58,6 +60,7 @@
   const bool is_incognito_;
   const bool is_valid_ssl_;
   const bool is_browser_window_active_;
+  const bool skip_ui_for_basic_card_;
 
   DISALLOW_COPY_AND_ASSIGN(TestChromePaymentRequestDelegate);
 };
diff --git a/chrome/browser/ui/views/tab_dialogs_views.cc b/chrome/browser/ui/views/tab_dialogs_views.cc
index 3e787f2c..051ddc53 100644
--- a/chrome/browser/ui/views/tab_dialogs_views.cc
+++ b/chrome/browser/ui/views/tab_dialogs_views.cc
@@ -40,8 +40,7 @@
 }
 
 void TabDialogsViews::ShowCollectedCookies() {
-  // Deletes itself on close.
-  new CollectedCookiesViews(web_contents_);
+  CollectedCookiesViews::CreateAndShowForWebContents(web_contents_);
 }
 
 void TabDialogsViews::ShowHungRendererDialog(
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
index d48fb152..f9b7b7b 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -19,11 +19,11 @@
 void FakeBaseTabStripController::AddTab(int index, bool is_active) {
   num_tabs_++;
   tab_strip_->AddTabAt(index, TabRendererData(), is_active);
-  ui::MouseEvent fake_event =
-      ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
-                     base::TimeTicks::Now(), 0, 0);
-  if (is_active)
-    SelectTab(index, fake_event);
+  if (is_active) {
+    SelectTab(index,
+              ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
+                             base::TimeTicks::Now(), 0, 0));
+  }
 }
 
 void FakeBaseTabStripController::AddPinnedTab(int index, bool is_active) {
@@ -35,6 +35,18 @@
     active_index_ = index;
 }
 
+void FakeBaseTabStripController::MoveTab(int from_index, int to_index) {
+  base::Optional<TabGroupId> prev_group;
+  if (from_index < int{tab_groups_.size()}) {
+    prev_group = tab_groups_[from_index];
+    tab_groups_.erase(tab_groups_.begin() + from_index);
+  }
+  if (to_index >= int{tab_groups_.size()})
+    tab_groups_.resize(to_index + 1);
+  tab_groups_.insert(tab_groups_.begin() + to_index, prev_group);
+  tab_strip_->MoveTab(from_index, to_index, TabRendererData());
+}
+
 void FakeBaseTabStripController::RemoveTab(int index) {
   num_tabs_--;
   // RemoveTabAt() expects the controller state to have been updated already.
@@ -50,8 +62,12 @@
 void FakeBaseTabStripController::MoveTabIntoGroup(
     int index,
     base::Optional<TabGroupId> new_group) {
-  const base::Optional<TabGroupId> old_group = tab_to_group_[index];
-  tab_to_group_[index] = new_group;
+  base::Optional<TabGroupId> old_group;
+  if (index >= int{tab_groups_.size()})
+    tab_groups_.resize(index + 1);
+  else
+    old_group = tab_groups_[index];
+  tab_groups_[index] = new_group;
   tab_strip_->ChangeTabGroup(index, old_group, new_group);
 }
 
@@ -63,9 +79,9 @@
 std::vector<int> FakeBaseTabStripController::ListTabsInGroup(
     TabGroupId group) const {
   std::vector<int> result;
-  for (auto const& tab_group_pair : tab_to_group_) {
-    if (tab_group_pair.second == group)
-      result.push_back(tab_group_pair.first);
+  for (size_t i = 0; i < tab_groups_.size(); i++) {
+    if (tab_groups_[i] == group)
+      result.push_back(i);
   }
   return result;
 }
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
index 467a19fb..351dea81 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_TABS_FAKE_BASE_TAB_STRIP_CONTROLLER_H_
 #define CHROME_BROWSER_UI_VIEWS_TABS_FAKE_BASE_TAB_STRIP_CONTROLLER_H_
 
-#include <map>
 #include <memory>
 #include <vector>
 
@@ -24,6 +23,7 @@
 
   void AddTab(int index, bool is_active);
   void AddPinnedTab(int index, bool is_active);
+  void MoveTab(int from_index, int to_index);
   void RemoveTab(int index);
 
   void MoveTabIntoGroup(int index, base::Optional<TabGroupId> new_group);
@@ -82,7 +82,7 @@
   int active_index_ = -1;
 
   TabGroupData fake_group_data_;
-  std::map<int, base::Optional<TabGroupId>> tab_to_group_;
+  std::vector<base::Optional<TabGroupId>> tab_groups_;
 
   ui::ListSelectionModel selection_model_;
 
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index df6035c..604f659b 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -536,12 +536,10 @@
 void Tab::MaybeUpdateHoverStatus(const ui::MouseEvent& event) {
 #if defined(OS_LINUX)
   // Move the hit test area for hovering up so that it is not overlapped by tab
-  // hover cards when they are shown. Also see the adjustment made in
-  // abHoverCardBubbleView::TabHoverCardBubbleView(); the two adjustments should
-  // add to a net six pixels.
+  // hover cards when they are shown.
   // TODO(crbug/978134): Once Linux/CrOS widget transparency is solved, remove
   // this case.
-  constexpr int kHoverCardOverlap = 4;
+  constexpr int kHoverCardOverlap = 6;
   if (event.location().y() >= height() - kHoverCardOverlap)
     return;
 #endif
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h
index b1155bc..9fd0a21c 100644
--- a/chrome/browser/ui/views/tabs/tab.h
+++ b/chrome/browser/ui/views/tabs/tab.h
@@ -118,6 +118,7 @@
   bool detached() const { return detached_; }
 
   void SetGroup(base::Optional<TabGroupId> group);
+  base::Optional<TabGroupId> group() { return group_; }
 
   // Returns the color for the tab's group, if any.
   base::Optional<SkColor> GetGroupColor() const;
diff --git a/chrome/browser/ui/views/tabs/tab_animation.cc b/chrome/browser/ui/views/tabs/tab_animation.cc
index d4e4e00..dcfa070 100644
--- a/chrome/browser/ui/views/tabs/tab_animation.cc
+++ b/chrome/browser/ui/views/tabs/tab_animation.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/tabs/tab_animation.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "base/numerics/ranges.h"
 #include "ui/gfx/animation/tween.h"
@@ -17,11 +18,13 @@
 
 constexpr base::TimeDelta TabAnimation::kAnimationDuration;
 
-TabAnimation::TabAnimation(TabAnimationState initial_state,
+TabAnimation::TabAnimation(ViewType view_type,
+                           TabAnimationState initial_state,
                            TabAnimationState target_state,
                            base::TimeDelta duration,
                            base::OnceClosure tab_removed_callback)
-    : initial_state_(initial_state),
+    : view_type_(view_type),
+      initial_state_(initial_state),
       target_state_(target_state),
       start_time_(base::TimeTicks::Now()),
       duration_(duration),
@@ -34,9 +37,10 @@
 
 // static
 TabAnimation TabAnimation::ForStaticState(
+    ViewType view_type,
     TabAnimationState static_state,
     base::OnceClosure tab_removed_callback) {
-  return TabAnimation(static_state, static_state, kZeroDuration,
+  return TabAnimation(view_type, static_state, static_state, kZeroDuration,
                       std::move(tab_removed_callback));
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_animation.h b/chrome/browser/ui/views/tabs/tab_animation.h
index 6133e53..dd513b3 100644
--- a/chrome/browser/ui/views/tabs/tab_animation.h
+++ b/chrome/browser/ui/views/tabs/tab_animation.h
@@ -16,15 +16,24 @@
   static constexpr base::TimeDelta kAnimationDuration =
       base::TimeDelta::FromMilliseconds(200);
 
+  // The types of Views that can be represented by TabAnimation.
+  enum class ViewType {
+    kTab,
+    kGroupHeader,
+  };
+
   ~TabAnimation();
 
   TabAnimation(TabAnimation&&) noexcept;
   TabAnimation& operator=(TabAnimation&&) noexcept;
 
   // Creates a TabAnimation for a tab with no active animations.
-  static TabAnimation ForStaticState(TabAnimationState static_state,
+  static TabAnimation ForStaticState(ViewType view_type,
+                                     TabAnimationState static_state,
                                      base::OnceClosure tab_removed_callback);
 
+  ViewType view_type() const { return view_type_; }
+
   // Animates this tab from its current state to |target_state|.
   // If an animation is already running, the duration is reset.
   void AnimateTo(TabAnimationState target_state);
@@ -44,11 +53,16 @@
   base::TimeDelta GetTimeRemaining() const;
 
  private:
-  TabAnimation(TabAnimationState initial_state,
+  TabAnimation(ViewType view_type,
+               TabAnimationState initial_state,
                TabAnimationState target_state,
                base::TimeDelta duration,
                base::OnceClosure tab_removed_callback);
 
+  // The type of View that this TabAnimation represents. Used for debug
+  // information only.
+  ViewType view_type_;
+
   TabAnimationState initial_state_;
   TabAnimationState target_state_;
   base::TimeTicks start_time_;
diff --git a/chrome/browser/ui/views/tabs/tab_animation_unittest.cc b/chrome/browser/ui/views/tabs/tab_animation_unittest.cc
index ce70460..755c7cf 100644
--- a/chrome/browser/ui/views/tabs/tab_animation_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_animation_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/tabs/tab_animation.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/time/time.h"
@@ -18,6 +20,14 @@
 
 class TabAnimationTest : public testing::Test {
  public:
+  static TabAnimation CreateAnimation(
+      TabAnimationState initial_state,
+      base::OnceClosure tab_removed_callback = base::BindOnce([]() {})) {
+    return TabAnimation::ForStaticState(TabAnimation::ViewType::kTab,
+                                        initial_state,
+                                        std::move(tab_removed_callback));
+  }
+
   TabAnimationTest()
       : env_(base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
              base::test::ScopedTaskEnvironment::NowSource::
@@ -33,8 +43,7 @@
       TabAnimationState::TabOpenness::kOpen,
       TabAnimationState::TabPinnedness::kUnpinned,
       TabAnimationState::TabActiveness::kInactive, 0);
-  TabAnimation static_animation =
-      TabAnimation::ForStaticState(static_state, base::BindOnce([]() {}));
+  TabAnimation static_animation = CreateAnimation(static_state);
 
   EXPECT_EQ(kZeroDuration, static_animation.GetTimeRemaining());
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(0),
@@ -54,8 +63,7 @@
       TabAnimationState::TabActiveness::kInactive, 0);
   TabAnimationState target_state =
       initial_state.WithPinnedness(TabAnimationState::TabPinnedness::kPinned);
-  TabAnimation animation =
-      TabAnimation::ForStaticState(initial_state, base::BindOnce([]() {}));
+  TabAnimation animation = CreateAnimation(initial_state);
   animation.AnimateTo(target_state);
 
   EXPECT_LT(kZeroDuration, animation.GetTimeRemaining());
@@ -83,8 +91,7 @@
       TabAnimationState::TabActiveness::kInactive, 0);
   TabAnimationState target_state =
       initial_state.WithPinnedness(TabAnimationState::TabPinnedness::kPinned);
-  TabAnimation animation =
-      TabAnimation::ForStaticState(initial_state, base::BindOnce([]() {}));
+  TabAnimation animation = CreateAnimation(initial_state);
   animation.AnimateTo(target_state);
 
   animation.CompleteAnimation();
@@ -102,8 +109,7 @@
       TabAnimationState::TabActiveness::kInactive, 0);
   TabAnimationState target_state =
       initial_state.WithPinnedness(TabAnimationState::TabPinnedness::kPinned);
-  TabAnimation animation =
-      TabAnimation::ForStaticState(initial_state, base::BindOnce([]() {}));
+  TabAnimation animation = CreateAnimation(initial_state);
   animation.AnimateTo(target_state);
 
   env_.FastForwardBy(TabAnimation::kAnimationDuration / 2.0);
@@ -123,8 +129,7 @@
       TabAnimationState::TabActiveness::kInactive, 0);
   TabAnimationState target_state =
       initial_state.WithPinnedness(TabAnimationState::TabPinnedness::kPinned);
-  TabAnimation animation =
-      TabAnimation::ForStaticState(initial_state, base::BindOnce([]() {}));
+  TabAnimation animation = CreateAnimation(initial_state);
   animation.AnimateTo(target_state);
 
   env_.FastForwardBy(TabAnimation::kAnimationDuration / 2.0);
@@ -152,12 +157,12 @@
       TabAnimationState::TabPinnedness::kUnpinned,
       TabAnimationState::TabActiveness::kInactive, 0);
   TabClosedDetector tab_closed_detector;
-  TabAnimation static_animation = TabAnimation::ForStaticState(
+  TabAnimation animation = CreateAnimation(
       static_state, base::BindOnce(&TabClosedDetector::NotifyTabClosed,
                                    base::Unretained(&tab_closed_detector)));
   EXPECT_FALSE(tab_closed_detector.was_closed_);
 
-  static_animation.NotifyCloseCompleted();
+  animation.NotifyCloseCompleted();
 
   EXPECT_TRUE(tab_closed_detector.was_closed_);
 }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 82a6874..f54d2d3 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/sad_tab_helper.h"
+#include "chrome/browser/ui/tabs/tab_group_id.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -994,6 +995,9 @@
             initial_move_);
         did_layout = true;
       }
+
+      UpdateGroupForDraggedTabs(to_index);
+
       attached_model->MoveSelectedTabsTo(to_index);
 
       // Move may do nothing in certain situations (such as when dragging pinned
@@ -2058,6 +2062,61 @@
 #endif
 }
 
+void TabDragController::UpdateGroupForDraggedTabs(int to_index) {
+  TabStripModel* attached_model = attached_context_->GetTabStripModel();
+  const ui::ListSelectionModel::SelectedIndices& selected =
+      attached_model->selection_model().selected_indices();
+
+  // TODO(crbug.com/978609): Support multi-select case.
+  if (selected.size() != 1)
+    return;
+
+  const int current_index = selected[0];
+
+  // If the tab hasn't moved, there is no need to update tab group membership.
+  if (current_index == to_index)
+    return;
+  if (!GetTabGroupForTargetIndex(current_index, to_index).has_value()) {
+    attached_model->RemoveFromGroup({current_index});
+  }
+}
+
+base::Optional<TabGroupId> TabDragController::GetTabGroupForTargetIndex(
+    int current_index,
+    int to_index) {
+  TabStripModel* attached_model = attached_context_->GetTabStripModel();
+  base::Optional<TabGroupId> current_group =
+      attached_model->GetTabGroupForTab(current_index);
+
+  // Keep tab in tab group if dragging all tabs in the tab group.
+  // TODO(crbug.com/978609): Handle multi-select drag case.
+  if ((current_group.has_value() &&
+       attached_model->ListTabsInGroup(current_group.value()).size() == 1)) {
+    return attached_model->GetTabGroupForTab(current_index);
+  }
+
+  // For the currently dragged tab, find the tab index of the tab to the left
+  // (|left_tab_index|) and right (|right_tab_index|)) after this current move
+  // has finished.
+  auto left_tab_index = [](int current_index, int to_index) {
+    return current_index < to_index ? to_index : to_index - 1;
+  };
+  auto right_tab_index = [](int current_index, int to_index) {
+    return current_index < to_index ? to_index + 1 : to_index;
+  };
+
+  base::Optional<TabGroupId> left_group = attached_model->GetTabGroupForTab(
+      left_tab_index(current_index, to_index));
+  base::Optional<TabGroupId> right_group = attached_model->GetTabGroupForTab(
+      right_tab_index(current_index, to_index));
+
+  // If the currently dragged tab will end up without either adjacent tab
+  // sharing its group, ungroup it.
+  return (left_group != current_group && right_group != current_group)
+             ? base::nullopt
+             : current_group;
+}
+
 bool TabDragController::ShouldDisallowDrag(gfx::NativeWindow window) {
 #if defined(USE_AURA)
   return wm::GetModalTransient(window) != nullptr;
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
index 960cca9f..d37d756 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -494,6 +494,18 @@
   // is showing a modal).
   bool ShouldDisallowDrag(gfx::NativeWindow window);
 
+  // Helper method for TabDragController::MoveAttached to update the tab group
+  // membership of selected tabs.
+  // TODO (cyan): Make this work for dragging into a tab group.
+  void UpdateGroupForDraggedTabs(int to_index);
+
+  // Helper method for TabDragController::UpdateGroupForDraggedTabs to decide if
+  // a dragged tab should stay in the tab group. Returns base::nullopt if the
+  // tab should not be in a group. Otherwise returns TabGroupId of the group
+  // being selected.
+  base::Optional<TabGroupId> GetTabGroupForTargetIndex(int index_of_selected,
+                                                       int to_index);
+
   EventSource event_source_;
 
   // The TabDragContext the drag originated from. This is set to null
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 f05ba587..2ddba38 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
@@ -23,6 +23,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -31,6 +32,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/native_browser_frame_factory.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
@@ -205,8 +207,10 @@
   tab_strip->StopAnimating(true);
 }
 
-void TabDragControllerTest::AddTabAndResetBrowser(Browser* browser) {
-  AddBlankTabAndShow(browser);
+void TabDragControllerTest::AddTabsAndResetBrowser(Browser* browser,
+                                                   int additional_tabs) {
+  for (int i = 0; i < additional_tabs; i++)
+    AddBlankTabAndShow(browser);
   StopAnimating(GetTabStripForBrowser(browser));
   ResetIDs(browser->tab_strip_model(), 0);
 }
@@ -341,7 +345,7 @@
 
 // See description above for details.
 IN_PROC_BROWSER_TEST_F(TabDragCaptureLostTest, ReleaseCaptureOnDrag) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
@@ -361,7 +365,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, GestureEndShouldEndDragTest) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
@@ -582,6 +586,8 @@
 
   Browser* browser() const { return InProcessBrowserTest::browser(); }
 
+  base::test::ScopedFeatureList scoped_feature_list_;
+
  private:
 #if defined(OS_CHROMEOS)
   // The root window for the event generator.
@@ -593,7 +599,7 @@
 
 // Creates a browser with two tabs, drags the second to the first.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, DragInSameWindow) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   TabStripModel* model = browser()->tab_strip_model();
@@ -614,11 +620,44 @@
   EXPECT_FALSE(tab_strip->GetWidget()->HasCapture());
 }
 
+// Creates a browser with three tabs. The first two belong in the same Tab
+// Group. Dragging the second tab to after the third tab will result in a
+// removal of the dragged tab from its group.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, DragToUngroupTab) {
+  scoped_feature_list_.InitAndEnableFeature(features::kTabGroups);
+
+  AddTabsAndResetBrowser(browser(), 2);
+
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  TabStripModel* model = browser()->tab_strip_model();
+
+  TabGroupId group = model->AddToNewGroup({0, 1});
+  StopAnimating(tab_strip);
+
+  EXPECT_EQ(3, model->count());
+  EXPECT_EQ(2u, model->ListTabsInGroup(group).size());
+
+  ASSERT_TRUE(PressInput(GetCenterInScreenCoordinates(tab_strip->tab_at(1))));
+
+  Tab* last_tab = tab_strip->tab_at(2);
+  gfx::Point tab_right_center = last_tab->GetLocalBounds().right_center();
+  views::View::ConvertPointToScreen(last_tab, &tab_right_center);
+
+  ASSERT_TRUE(DragInputTo(tab_right_center));
+  ASSERT_TRUE(ReleaseInput());
+
+  StopAnimating(tab_strip);
+
+  EXPECT_EQ("0 2 1", IDString(model));
+  EXPECT_EQ(1u, model->ListTabsInGroup(group).size());
+  EXPECT_EQ(base::nullopt, model->GetTabGroupForTab(2));
+}
+
 // Drags a tab within the window (without dragging the whole window) then
 // pressing a key ends the drag.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        KeyPressShouldEndDragTest) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   ASSERT_TRUE(PressInput(GetCenterInScreenCoordinates(tab_strip->tab_at(1))));
@@ -651,7 +690,7 @@
 // account. This test hangs without the fix. crbug.com/473080.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DragWithMaskedWindows) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   aura::Window* browser_window = browser()->window()->GetNativeWindow();
   const gfx::Rect bounds = browser_window->GetBoundsInScreen();
@@ -726,8 +765,7 @@
                        DragToSeparateWindow) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -798,8 +836,7 @@
                        CaptureLostDuringDrag) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Press on first tab so drag is active. Reset WindowFinder to one that causes
   // capture to be lost from within GetLocalProcessWindowAtPoint(), then
@@ -881,8 +918,7 @@
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DetachToOwnWindow) {
   const gfx::Rect initial_bounds(browser()->window()->GetBounds());
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
@@ -948,8 +984,7 @@
           .work_area();
   browser()->window()->SetBounds(work_area);
   const gfx::Rect initial_bounds(browser()->window()->GetBounds());
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
@@ -999,8 +1034,7 @@
   MaximizedBrowserWindowWaiter(browser()->window()).Wait();
   ASSERT_TRUE(browser()->window()->IsMaximized());
 
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
@@ -1057,8 +1091,7 @@
   std::unique_ptr<ImmersiveRevealedLock> lock(
       controller->GetRevealedLock(ImmersiveModeController::ANIMATE_REVEAL_NO));
 
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
@@ -1106,8 +1139,7 @@
 // Deletes a tab being dragged before the user moved enough to start a drag.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DeleteBeforeStartedDragging) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Click on the first tab, but don't move it.
@@ -1131,8 +1163,7 @@
 
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DragDoesntStartFromClick) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Click on the first tab, but don't move it.
@@ -1157,8 +1188,7 @@
 // Deletes a tab being dragged while still attached.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DeleteTabWhileAttached) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Click on the first tab and move it enough so that it starts dragging but is
@@ -1201,10 +1231,7 @@
 // while dragging tabs.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DeleteTabsWhileDetached) {
-  // Add 3 tabs for a total of 4 tabs.
-  AddTabAndResetBrowser(browser());
-  AddTabAndResetBrowser(browser());
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 3);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   EXPECT_EQ("0 1 2 3", IDString(browser()->tab_strip_model()));
 
@@ -1243,8 +1270,7 @@
 // Detaches a tab and while detached presses escape to revert the drag.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        PressEscapeWhileDetached) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
@@ -1282,8 +1308,7 @@
 
 // Selects multiple tabs and starts dragging the window.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, DragAll) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   browser()->tab_strip_model()->ToggleSelectionAt(0);
   const gfx::Rect initial_bounds = browser()->window()->GetBounds();
@@ -1339,8 +1364,7 @@
                        DragAllToSeparateWindow) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -1397,8 +1421,7 @@
                        DragAllToSeparateWindowAndCancel) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -1446,8 +1469,7 @@
 
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -1558,8 +1580,7 @@
                        CancelOnNewTabWhenDragging) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Move to the first tab and drag it enough so that it detaches.
   const gfx::Point tab_0_center =
@@ -1657,7 +1678,7 @@
 // TODO(960915): fix flakiness and re-enable this test on mac/linux.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DISABLED_OffsetForDraggingDetachedTab) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   DragWindowAndVerifyOffset(this, GetTabStripForBrowser(browser()), 1);
   ASSERT_FALSE(TabDragController::IsActive());
@@ -1693,7 +1714,7 @@
 // Creates a browser with two tabs, maximizes it, drags the tab out.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DragInMaximizedWindow) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   browser()->window()->Maximize();
 
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -1724,7 +1745,7 @@
 
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        OffsetForDraggingInMaximizedWindow) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   // Moves the browser window slightly to ensure that the browser's restored
   // bounds are different from the maximized bound's origin.
   browser()->window()->SetBounds(browser()->window()->GetBounds() +
@@ -1737,7 +1758,7 @@
 
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        OffsetForDraggingInTabletMode) {
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   // Moves the browser window slightly to ensure that the browser's restored
   // bounds are different from the maximized bound's origin.
   browser()->window()->SetBounds(browser()->window()->GetBounds() +
@@ -1799,7 +1820,7 @@
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -2006,8 +2027,7 @@
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DoNotAttachToOtherWindowTest) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -2068,8 +2088,7 @@
                        DeferredTargetTabStripTest) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -2125,8 +2144,7 @@
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        FastResizeDuringDragging) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -2182,8 +2200,9 @@
 
 // Test that the dragged tabs should be able to merge into an overview window
 // that represents a minimized window.
+// TODO(https://crbug.com/979013) Disabled due to flakiness.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
-                       DragToMinimizedOverviewWindow) {
+                       DISABLED_DragToMinimizedOverviewWindow) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Create another browser.
@@ -2246,8 +2265,7 @@
 // Drags from browser to a second display and releases input.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
                        DragSingleTabToSeparateWindowInSecondDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
@@ -2321,8 +2339,7 @@
 // Drags from browser to another browser on a second display and releases input.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
                        DragTabToWindowInSeparateDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Create another browser.
@@ -2431,8 +2448,7 @@
 // Drags from browser to another browser on a second display and releases input.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
                        DragTabToWindowOnSecondDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Create another browser.
@@ -2494,8 +2510,7 @@
 // display and releases input.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
                        DragMaxTabToNonMaxWindowInSeparateDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   browser()->window()->Maximize();
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
@@ -2559,8 +2574,7 @@
 // TODO(pkasting) https://crbug.com/910782 Hangs.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
                        DISABLED_DragTabToImmersiveBrowserOnSeparateDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Create another browser.
@@ -2714,8 +2728,7 @@
 // https://crbug.com/918732
 IN_PROC_BROWSER_TEST_P(DifferentDeviceScaleFactorDisplayTabDragControllerTest,
                        DISABLED_CursorDeviceScaleFactor) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move the second browser to the second display.
@@ -2786,8 +2799,7 @@
 IN_PROC_BROWSER_TEST_P(
     DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
     CancelDragTabToWindowIn2ndDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
@@ -2821,8 +2833,7 @@
   display::Screen* screen = display::Screen::GetScreen();
   const std::pair<Display, Display> displays = GetDisplays(screen);
 
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
@@ -2899,8 +2910,7 @@
 // Detaches a tab and while detached presses a second finger.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
                        PressSecondFingerWhileDetached) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
 
@@ -2971,8 +2981,7 @@
                        SecondFingerPressTest) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
   Browser* browser2 = CreateAnotherBrowserAndResize();
@@ -2992,7 +3001,7 @@
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
                        LeftSnapShouldntCauseMergeAtEnd) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
 
   // Set the last mouse location at the center of tab 0. This shouldn't affect
   // the touch behavior below. See https://crbug.com/914527#c1 for the details
@@ -3060,7 +3069,7 @@
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
                        FlingOnStartingDrag) {
   ui::GestureConfiguration::GetInstance()->set_min_fling_velocity(1);
-  AddTabAndResetBrowser(browser());
+  AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   const gfx::Point tab_0_center =
       GetCenterInScreenCoordinates(tab_strip->tab_at(0));
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h
index b83f427..bfc48e9 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h
@@ -36,7 +36,7 @@
 
   // Adds a new blank tab to |browser|, stops animations and resets the ids of
   // the tabs in |browser|.
-  void AddTabAndResetBrowser(Browser* browser);
+  void AddTabsAndResetBrowser(Browser* browser, int additional_tabs);
 
   // Creates a new Browser and resizes browser() and the new browser to be side
   // by side.
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.cc b/chrome/browser/ui/views/tabs/tab_group_header.cc
index db99a02..9ad9222 100644
--- a/chrome/browser/ui/views/tabs/tab_group_header.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_header.cc
@@ -25,6 +25,7 @@
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/layout/layout_provider.h"
+#include "ui/views/view_class_properties.h"
 
 TabGroupHeader::TabGroupHeader(TabController* controller, TabGroupId group)
     : controller_(controller), group_(group) {
@@ -48,10 +49,10 @@
       provider->GetInsetsMetric(INSETS_TAB_GROUP_TITLE_CHIP)));
   title_chip->SetLayoutManager(std::make_unique<views::FillLayout>());
   auto* title_chip_ptr = AddChildView(std::move(title_chip));
-  layout->SetFlexForView(title_chip_ptr,
-                         views::FlexSpecification::ForSizeRule(
-                             views::MinimumFlexSizeRule::kScaleToZero,
-                             views::MaximumFlexSizeRule::kPreferred));
+  title_chip_ptr->SetProperty(views::kFlexBehaviorKey,
+                              views::FlexSpecification::ForSizeRule(
+                                  views::MinimumFlexSizeRule::kScaleToZero,
+                                  views::MaximumFlexSizeRule::kPreferred));
 
   auto title = std::make_unique<views::Label>(data->title());
   title->SetAutoColorReadabilityEnabled(false);
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index a9e6d3f5..6e5de1dd 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -280,13 +280,7 @@
   set_margins(gfx::Insets());
 
   // Inset the tab hover cards anchor rect to bring the card closer to the tab.
-#if defined(OS_LINUX)
-  // TODO(crbug/978134): Once Linux/CrOS widget transparency is solved, remove
-  // this case.
-  constexpr gfx::Insets kTabHoverCardAnchorInsets(0, 0);
-#else
   constexpr gfx::Insets kTabHoverCardAnchorInsets(2, 0);
-#endif
   set_anchor_view_insets(kTabHoverCardAnchorInsets);
 
   // Set so that when hovering over a tab in a inactive window that window will
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 528215f..21554ad 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -809,8 +809,12 @@
 
 TabStrip::TabStrip(std::unique_ptr<TabStripController> controller)
     : controller_(std::move(controller)),
-      layout_helper_(std::make_unique<TabStripLayoutHelper>()),
-      animator_(std::make_unique<TabStripAnimator>(
+      layout_helper_(std::make_unique<TabStripLayoutHelper>(
+          controller_.get(),
+          base::BindRepeating(&TabStrip::tabs_view_model,
+                              base::Unretained(this)),
+          base::BindRepeating(&TabStrip::GetGroupHeaders,
+                              base::Unretained(this)),
           base::BindRepeating(&TabStrip::LayoutToCurrentBounds,
                               base::Unretained(this)))),
       drag_context_(std::make_unique<TabDragContextImpl>(this)) {
@@ -1005,7 +1009,7 @@
   if (tab_count() > 1 && GetWidget() && GetWidget()->IsVisible()) {
     StartInsertTabAnimation(model_index, activeness, pinnedness);
   } else {
-    animator_->InsertTabAtNoAnimation(
+    layout_helper_->InsertTabAtNoAnimation(
         model_index,
         base::BindOnce(&TabStrip::OnTabCloseAnimationCompleted,
                        base::Unretained(this), base::Unretained(tab)),
@@ -1046,7 +1050,9 @@
   DCHECK_GT(tabs_.view_size(), 0);
 
   const Tab* last_tab = GetLastVisibleTab();
-  tab_at(from_model_index)->SetData(std::move(data));
+
+  Tab* moving_tab = tab_at(from_model_index);
+  moving_tab->SetData(std::move(data));
 
   // Keep child views in same order as tab strip model.
   const int to_view_index = GetIndexOf(tab_at(to_model_index));
@@ -1064,8 +1070,9 @@
   }
   selected_tabs_.Move(from_model_index, to_model_index, /*length=*/1);
 
-  animator_->MoveTabNoAnimation(from_model_index, to_model_index);
-  animator_->SetPinnednessNoAnimation(
+  layout_helper_->MoveTab(moving_tab->group(), from_model_index,
+                          to_model_index);
+  layout_helper_->SetTabPinnedness(
       to_model_index, data.pinned
                           ? TabAnimationState::TabPinnedness::kPinned
                           : TabAnimationState::TabPinnedness::kUnpinned);
@@ -1132,7 +1139,7 @@
                              UpdateIdealBoundsForPinnedTabs(nullptr), old_x);
   }
 
-  animator_->RemoveTabNoAnimation(model_index);
+  layout_helper_->RemoveTabAt(model_index);
   UpdateIdealBounds();
   AnimateToIdealBounds();
 
@@ -1207,7 +1214,7 @@
       touch_layout_->SetXAndPinnedCount(start_x, pinned_tab_count);
     }
 
-    animator_->SetPinnednessNoAnimation(
+    layout_helper_->SetTabPinnedness(
         model_index, data.pinned ? TabAnimationState::TabPinnedness::kPinned
                                  : TabAnimationState::TabPinnedness::kUnpinned);
     if (GetWidget() && GetWidget()->IsVisible())
@@ -1227,9 +1234,14 @@
     header->set_owned_by_client();
     AddChildView(header.get());
     group_headers_[new_group.value()] = std::move(header);
+    layout_helper_->InsertGroupHeader(
+        new_group.value(),
+        base::BindOnce(&TabStrip::OnGroupCloseAnimationCompleted,
+                       base::Unretained(this), new_group.value()));
   }
   if (old_group.has_value() &&
       controller_->ListTabsInGroup(old_group.value()).size() == 0) {
+    layout_helper_->RemoveGroupHeader(old_group.value());
     group_headers_.erase(old_group.value());
   }
   UpdateIdealBounds();
@@ -1305,7 +1317,8 @@
       tab_at(selected_tabs_.active())->ActiveStateChanged();
     if (new_selection.active() >= 0)
       tab_at(new_selection.active())->ActiveStateChanged();
-    animator_->SetActiveTab(selected_tabs_.active(), new_selection.active());
+    layout_helper_->SetActiveTab(selected_tabs_.active(),
+                                 new_selection.active());
   }
 
   if (touch_layout_) {
@@ -1330,7 +1343,7 @@
       // |available_width_for_tabs_| already.
       UpdateIdealBounds();
       AnimateToIdealBounds();
-    } else if (!animator_->IsAnimating()) {
+    } else if (!layout_helper_->IsAnimating()) {
       // As in the animating case above, the selection change will have
       // affected the desired bounds of the tabs, but since we're not animating
       // we can just snap to the new bounds.
@@ -1392,11 +1405,11 @@
 }
 
 int TabStrip::GetPinnedTabCount() const {
-  return layout_helper_->GetPinnedTabCount(&tabs_);
+  return layout_helper_->GetPinnedTabCount();
 }
 
 bool TabStrip::IsAnimating() const {
-  return bounds_animator_.IsAnimating() || animator_->IsAnimating();
+  return bounds_animator_.IsAnimating() || layout_helper_->IsAnimating();
 }
 
 void TabStrip::StopAnimating(bool layout) {
@@ -1404,7 +1417,7 @@
     return;
 
   bounds_animator_.Cancel();
-  animator_->CompleteAnimations();
+  layout_helper_->CompleteAnimations();
 
   if (layout)
     CompleteAnimationAndLayout();
@@ -2096,21 +2109,31 @@
     bounds_animator_.SetAnimationDuration(0);
 }
 
+std::map<TabGroupId, TabGroupHeader*> TabStrip::GetGroupHeaders() {
+  // Transform |group_headers_| to raw pointers to avoid exposing unique_ptrs.
+  std::map<TabGroupId, TabGroupHeader*> group_headers;
+  for (const auto& header_pair : group_headers_) {
+    group_headers.insert(
+        std::make_pair(header_pair.first, header_pair.second.get()));
+  }
+  return group_headers;
+}
+
 void TabStrip::StartInsertTabAnimation(
     int model_index,
     TabAnimationState::TabActiveness activeness,
     TabAnimationState::TabPinnedness pinnedness) {
   if (!bounds_animator_.IsAnimating() && !in_tab_close_) {
-    animator_->InsertTabAt(
+    layout_helper_->InsertTabAt(
         model_index,
         base::BindOnce(&TabStrip::OnTabCloseAnimationCompleted,
                        base::Unretained(this),
                        base::Unretained(tab_at(model_index))),
         activeness, pinnedness);
   } else {
-    // TODO(958173): Delete this branch once |animator_| has taken over all
-    // animation responsibilities.
-    animator_->InsertTabAtNoAnimation(
+    // TODO(958173): Delete this branch once |TabStripLayoutHelper::animator_|
+    // has taken over all animation responsibilities.
+    layout_helper_->InsertTabAtNoAnimation(
         model_index,
         base::BindOnce(&TabStrip::OnTabCloseAnimationCompleted,
                        base::Unretained(this),
@@ -2150,11 +2173,12 @@
 }
 
 void TabStrip::AnimateToIdealBounds() {
-  // bounds_animator_ and animator_ should not run concurrently.
-  // bounds_animator_ takes precedence, and can finish what animator_ started.
-  if (animator_->IsAnimating()) {
+  // bounds_animator_ and TabStripLayoutHelper::animator_ should not run
+  // concurrently. bounds_animator_ takes precedence, and can finish what the
+  // other started.
+  if (layout_helper_->IsAnimating()) {
     LayoutToCurrentBounds();
-    animator_->CompleteAnimationsWithoutDestroyingTabs();
+    layout_helper_->CompleteAnimationsWithoutDestroyingTabs();
   }
 
   for (int i = 0; i < tab_count(); ++i) {
@@ -2216,7 +2240,7 @@
 }
 
 void TabStrip::CompleteAnimationAndLayout() {
-  animator_->CompleteAnimations();
+  layout_helper_->CompleteAnimations();
   LayoutToCurrentBounds();
 
   UpdateIdealBounds();
@@ -2238,11 +2262,7 @@
     const int available_width = (available_width_for_tabs_ < 0)
                                     ? GetTabAreaWidth()
                                     : available_width_for_tabs_;
-
-    int trailing_x = layout_helper_->LayoutTabs(
-        &tabs_, animator_->GetCurrentTabStates(), available_width,
-        controller_->GetActiveIndex());
-
+    int trailing_x = layout_helper_->LayoutTabs(available_width);
     new_tab_button_bounds_.set_origin(gfx::Point(
         std::min(available_width, trailing_x) + TabToNewTabButtonSpacing(), 0));
   }
@@ -2347,6 +2367,12 @@
   }
 }
 
+void TabStrip::OnGroupCloseAnimationCompleted(TabGroupId group) {
+  group_headers_.erase(group);
+  // TODO(crbug.com/905491): We might want to simulate a mouse move here, like
+  // we do in OnTabCloseAnimationCompleted.
+}
+
 void TabStrip::UpdateTabsClosingMap(int index, int delta) {
   if (tabs_closing_map_.empty())
     return;
@@ -2697,25 +2723,17 @@
     return;  // Should only happen during creation/destruction, ignore.
 
   if (!touch_layout_) {
-    // Transform |group_headers_| to raw pointers to avoid exposing unique_ptrs.
-    std::map<TabGroupId, TabGroupHeader*> group_headers;
-    for (const auto& header_pair : group_headers_) {
-      group_headers.insert(
-          std::make_pair(header_pair.first, header_pair.second.get()));
-    }
-
     const int available_width = (available_width_for_tabs_ < 0)
                                     ? GetTabAreaWidth()
                                     : available_width_for_tabs_;
-    layout_helper_->UpdateIdealBounds(
-        controller(), &tabs_, std::move(group_headers), available_width);
+    layout_helper_->UpdateIdealBounds(available_width);
   }
 
   new_tab_button_bounds_.set_origin(gfx::Point(GetNewTabButtonIdealX(), 0));
 }
 
 int TabStrip::UpdateIdealBoundsForPinnedTabs(int* first_non_pinned_index) {
-  layout_helper_->UpdateIdealBoundsForPinnedTabs(&tabs_);
+  layout_helper_->UpdateIdealBoundsForPinnedTabs();
   if (first_non_pinned_index)
     *first_non_pinned_index = layout_helper_->first_non_pinned_tab_index();
   return layout_helper_->first_non_pinned_tab_x();
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 7c56a2d..3d237cf 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -44,7 +44,6 @@
 class TabGroupHeader;
 class TabGroupId;
 class TabHoverCardBubbleView;
-class TabStripAnimator;
 class TabStripController;
 class TabStripObserver;
 class TabStripLayoutHelper;
@@ -336,6 +335,10 @@
 
   void Init();
 
+  views::ViewModelT<Tab>* tabs_view_model() { return &tabs_; }
+
+  std::map<TabGroupId, TabGroupHeader*> GetGroupHeaders();
+
   // Invoked from |AddTabAt| after the newly created tab has been inserted.
   void StartInsertTabAnimation(int model_index,
                                TabAnimationState::TabActiveness activeness,
@@ -405,6 +408,10 @@
   // code and is not a general-purpose method.
   void OnTabCloseAnimationCompleted(Tab* tab);
 
+  // Cleans up the TabGroupHeader for |group| from the TabStrip. This is called
+  // from the tab animation code and is not a general-purpose method.
+  void OnGroupCloseAnimationCompleted(TabGroupId group);
+
   // Adjusts the indices of all tabs in |tabs_closing_map_| whose index is
   // >= |index| to have a new index of |index + delta|.
   void UpdateTabsClosingMap(int index, int delta);
@@ -576,9 +583,8 @@
   std::unique_ptr<TabStripLayoutHelper> layout_helper_;
 
   // Responsible for animating tabs in response to model changes.
-  // https://crbug.com/958173 tracks migrating animations from
-  // |bounds_animator_| to |animator_|.
-  std::unique_ptr<TabStripAnimator> animator_;
+  // Deprecated; https://crbug.com/958173 tracks migrating animations from
+  // |bounds_animator_| to |TabStripLayoutHelper::animator_|.
   views::BoundsAnimator bounds_animator_{this};
 
   // The "New Tab" button.
diff --git a/chrome/browser/ui/views/tabs/tab_strip_animator.cc b/chrome/browser/ui/views/tabs/tab_strip_animator.cc
index 9ef791a..77bb6bf 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_animator.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_animator.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/tabs/tab_strip_animator.h"
 
+#include <utility>
+
 #include "base/time/time.h"
 
 // TODO(965227): align animation ticks to compositor events.
@@ -28,7 +30,13 @@
   return timer_.IsRunning();
 }
 
+TabAnimation::ViewType TabStripAnimator::GetAnimationViewTypeAt(
+    int index) const {
+  return animations_.at(index).view_type();
+}
+
 void TabStripAnimator::InsertTabAtNoAnimation(
+    TabAnimation::ViewType view_type,
     int index,
     base::OnceClosure tab_removed_callback,
     TabAnimationState::TabActiveness active,
@@ -36,18 +44,12 @@
   animations_.insert(
       animations_.begin() + index,
       TabAnimation::ForStaticState(
+          view_type,
           TabAnimationState::ForIdealTabState(
               TabAnimationState::TabOpenness::kOpen, pinned, active, 0),
           std::move(tab_removed_callback)));
 }
 
-void TabStripAnimator::MoveTabNoAnimation(int prev_index, int new_index) {
-  TabAnimation moved_animation = std::move(animations_[prev_index]);
-  animations_.erase(animations_.begin() + prev_index);
-  animations_.insert(animations_.begin() + new_index,
-                     std::move(moved_animation));
-}
-
 void TabStripAnimator::RemoveTabNoAnimation(int index) {
   animations_.erase(animations_.begin() + index);
 }
@@ -60,13 +62,34 @@
   animations_[index].CompleteAnimation();
 }
 
-void TabStripAnimator::InsertTabAt(int index,
+void TabStripAnimator::MoveTabsNoAnimation(std::vector<int> moving_tabs,
+                                           int new_index) {
+  DCHECK_GT(moving_tabs.size(), 0u);
+  std::vector<TabAnimation> moved_animations;
+  // Remove the rightmost tab first to avoid perturbing the indices of the other
+  // tabs in the list.
+  std::reverse(std::begin(moving_tabs), std::end(moving_tabs));
+  for (int tab : moving_tabs) {
+    moved_animations.push_back(std::move(animations_[tab]));
+    animations_.erase(animations_.begin() + tab);
+  }
+  // Insert the leftmost tab first, for the same reason.
+  std::reverse(std::begin(moved_animations), std::end(moved_animations));
+  for (size_t i = 0; i < moved_animations.size(); i++) {
+    animations_.insert(animations_.begin() + new_index + i,
+                       std::move(moved_animations[i]));
+  }
+}
+
+void TabStripAnimator::InsertTabAt(TabAnimation::ViewType view_type,
+                                   int index,
                                    base::OnceClosure tab_removed_callback,
                                    TabAnimationState::TabActiveness active,
                                    TabAnimationState::TabPinnedness pinned) {
   animations_.insert(
       animations_.begin() + index,
       TabAnimation::ForStaticState(
+          view_type,
           TabAnimationState::ForIdealTabState(
               TabAnimationState::TabOpenness::kClosed, pinned, active, 0),
           std::move(tab_removed_callback)));
diff --git a/chrome/browser/ui/views/tabs/tab_strip_animator.h b/chrome/browser/ui/views/tabs/tab_strip_animator.h
index ff065f4..aebf1fc 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_animator.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_animator.h
@@ -19,13 +19,20 @@
   explicit TabStripAnimator(base::RepeatingClosure on_animation_progressed);
   ~TabStripAnimator();
 
+  size_t animation_count() const { return animations_.size(); }
+
   std::vector<TabAnimationState> GetCurrentTabStates() const;
+
+  // Returns whether any animations in progress.
   bool IsAnimating() const;
 
+  TabAnimation::ViewType GetAnimationViewTypeAt(int index) const;
+
   // Inserts, without animation, a new tab at |index|.
   // |tab_removed_callback| will be invoked if the tab is removed
   // at the end of a remove animation.
-  void InsertTabAtNoAnimation(int index,
+  void InsertTabAtNoAnimation(TabAnimation::ViewType view_type,
+                              int index,
                               base::OnceClosure tab_removed_callback,
                               TabAnimationState::TabActiveness active,
                               TabAnimationState::TabPinnedness pinned);
@@ -34,16 +41,20 @@
   // Does not invoke the associated |tab_removed_callback|.
   void RemoveTabNoAnimation(int index);
 
-  // TODO(958173): Temporary methods to keep the animator's state in sync with
-  // changes it is not responsible for animating.
-  void MoveTabNoAnimation(int prev_index, int new_index);
+  // Sets the tab at |index|'s pinnedness to |pinnedness|. TODO(958173): Should
+  // be animated.
   void SetPinnednessNoAnimation(int index,
                                 TabAnimationState::TabPinnedness pinnedness);
 
+  // Moves the tabs in |moving_tabs| to |new_index|. The vector of tabs should
+  // be sorted by increasing index. TODO(958173): Should be animated.
+  void MoveTabsNoAnimation(std::vector<int> moving_tabs, int new_index);
+
   // Animates the insertion of a new tab at |index|.
   // |tab_removed_callback| will be invoked if the tab is removed
   // at the end of a remove animation.
-  void InsertTabAt(int index,
+  void InsertTabAt(TabAnimation::ViewType view_type,
+                   int index,
                    base::OnceClosure tab_removed_callback,
                    TabAnimationState::TabActiveness active,
                    TabAnimationState::TabPinnedness pinned);
@@ -52,10 +63,10 @@
   // invoke the associated |tab_removed_callback| when complete.
   void RemoveTab(int index);
 
+  // Changes the active tab from |prev_active_index| to |new_active_index|.
   void SetActiveTab(int prev_active_index, int new_active_index);
 
-  // TODO(958173): Implement move and pin animations.
-
+  // Finishes all in-progress animations.
   void CompleteAnimations();
 
   // TODO(958173): Temporary method that completes running animations,
diff --git a/chrome/browser/ui/views/tabs/tab_strip_animator_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_animator_unittest.cc
index d158fd5..e9d1ded 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_animator_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_animator_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/views/tabs/tab_strip_animator.h"
 
+#include <utility>
+
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/time/time.h"
 #include "chrome/browser/ui/views/tabs/tab_animation.h"
@@ -37,25 +40,32 @@
   float PinnednessOf(TabAnimationState state) { return state.pinnedness_; }
   float ActivenessOf(TabAnimationState state) { return state.activeness_; }
 
+  void AddTab(int index,
+              TabAnimationState::TabActiveness activeness,
+              TabAnimationState::TabPinnedness pinnedness =
+                  TabAnimationState::TabPinnedness::kUnpinned,
+              base::OnceClosure tab_removed_callback = base::BindOnce([]() {
+              })) {
+    animator_.InsertTabAtNoAnimation(TabAnimation::ViewType::kTab, index,
+                                     std::move(tab_removed_callback),
+                                     activeness, pinnedness);
+  }
+
   base::test::ScopedTaskEnvironment env_;
   TabStripAnimator animator_;
   bool has_animated_;
 };
 
 TEST_F(TabStripAnimatorTest, StaticStripIsNotAnimating) {
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kInactive,
-                                   TabAnimationState::TabPinnedness::kPinned);
+  AddTab(0, TabAnimationState::TabActiveness::kInactive);
   EXPECT_FALSE(animator_.IsAnimating());
-  EXPECT_EQ(2u, animator_.GetCurrentTabStates().size());
+  EXPECT_EQ(1u, animator_.GetCurrentTabStates().size());
   EXPECT_FALSE(has_animated_);
 }
 
 TEST_F(TabStripAnimatorTest, InsertTabAnimation) {
-  animator_.InsertTabAt(0, base::BindOnce([]() {}),
+  animator_.InsertTabAt(TabAnimation::ViewType::kTab, 0,
+                        base::BindOnce([]() {}),
                         TabAnimationState::TabActiveness::kActive,
                         TabAnimationState::TabPinnedness::kUnpinned);
   EXPECT_TRUE(animator_.IsAnimating());
@@ -71,12 +81,8 @@
 }
 
 TEST_F(TabStripAnimatorTest, ChangeActiveTab) {
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(1, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kInactive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
+  AddTab(0, TabAnimationState::TabActiveness::kActive);
+  AddTab(1, TabAnimationState::TabActiveness::kInactive);
   EXPECT_EQ(1.0f, ActivenessOf(animator_.GetCurrentTabStates()[0]));
   EXPECT_EQ(0.0f, ActivenessOf(animator_.GetCurrentTabStates()[1]));
 
@@ -88,9 +94,7 @@
 }
 
 TEST_F(TabStripAnimatorTest, PinAndUnpinTab) {
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
+  AddTab(0, TabAnimationState::TabActiveness::kActive);
   EXPECT_EQ(0.0f, PinnednessOf(animator_.GetCurrentTabStates()[0]));
 
   animator_.SetPinnednessNoAnimation(0,
@@ -106,12 +110,8 @@
 }
 
 TEST_F(TabStripAnimatorTest, RemoveTabNoAnimation) {
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(1, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kInactive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
+  AddTab(0, TabAnimationState::TabActiveness::kActive);
+  AddTab(1, TabAnimationState::TabActiveness::kInactive);
 
   animator_.RemoveTabNoAnimation(1);
 
@@ -121,16 +121,12 @@
 }
 
 TEST_F(TabStripAnimatorTest, RemoveTabAnimation) {
+  AddTab(0, TabAnimationState::TabActiveness::kActive);
   TabClosedDetector second_tab;
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(
-      1,
-      base::BindOnce(&TabClosedDetector::NotifyTabClosed,
-                     base::Unretained(&second_tab)),
-      TabAnimationState::TabActiveness::kInactive,
-      TabAnimationState::TabPinnedness::kUnpinned);
+  AddTab(1, TabAnimationState::TabActiveness::kInactive,
+         TabAnimationState::TabPinnedness::kUnpinned,
+         base::BindOnce(&TabClosedDetector::NotifyTabClosed,
+                        base::Unretained(&second_tab)));
 
   animator_.RemoveTab(1);
 
@@ -147,7 +143,8 @@
 }
 
 TEST_F(TabStripAnimatorTest, CompleteAnimations) {
-  animator_.InsertTabAt(0, base::BindOnce([]() {}),
+  animator_.InsertTabAt(TabAnimation::ViewType::kTab, 0,
+                        base::BindOnce([]() {}),
                         TabAnimationState::TabActiveness::kActive,
                         TabAnimationState::TabPinnedness::kUnpinned);
   EXPECT_TRUE(animator_.IsAnimating());
@@ -161,16 +158,12 @@
 }
 
 TEST_F(TabStripAnimatorTest, CompleteAnimationsRemovesClosedTabs) {
+  AddTab(0, TabAnimationState::TabActiveness::kActive);
   TabClosedDetector second_tab;
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(
-      1,
-      base::BindOnce(&TabClosedDetector::NotifyTabClosed,
-                     base::Unretained(&second_tab)),
-      TabAnimationState::TabActiveness::kInactive,
-      TabAnimationState::TabPinnedness::kUnpinned);
+  AddTab(1, TabAnimationState::TabActiveness::kInactive,
+         TabAnimationState::TabPinnedness::kUnpinned,
+         base::BindOnce(&TabClosedDetector::NotifyTabClosed,
+                        base::Unretained(&second_tab)));
 
   animator_.RemoveTab(1);
 
@@ -188,16 +181,12 @@
 
 TEST_F(TabStripAnimatorTest,
        CompleteAnimationsWithoutDestroyingTabsDoesNotRemoveClosedTabs) {
+  AddTab(0, TabAnimationState::TabActiveness::kActive);
   TabClosedDetector second_tab;
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(
-      1,
-      base::BindOnce(&TabClosedDetector::NotifyTabClosed,
-                     base::Unretained(&second_tab)),
-      TabAnimationState::TabActiveness::kInactive,
-      TabAnimationState::TabPinnedness::kUnpinned);
+  AddTab(1, TabAnimationState::TabActiveness::kInactive,
+         TabAnimationState::TabPinnedness::kUnpinned,
+         base::BindOnce(&TabClosedDetector::NotifyTabClosed,
+                        base::Unretained(&second_tab)));
 
   animator_.RemoveTab(1);
 
@@ -213,53 +202,61 @@
   EXPECT_FALSE(second_tab.was_closed_);
 }
 
-TEST_F(TabStripAnimatorTest, MoveTabRight) {
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(1, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kInactive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  EXPECT_EQ(1.0f, ActivenessOf(animator_.GetCurrentTabStates()[0]));
-  EXPECT_EQ(0.0f, ActivenessOf(animator_.GetCurrentTabStates()[1]));
+TEST_F(TabStripAnimatorTest, MoveTabsRight) {
+  std::vector<int> tabs_closed;
+  for (int i = 0; i < 5; i++) {
+    AddTab(i, TabAnimationState::TabActiveness::kInactive,
+           TabAnimationState::TabPinnedness::kPinned,
+           base::BindLambdaForTesting(
+               [&tabs_closed, i]() { tabs_closed.push_back(i); }));
+  }
 
-  animator_.MoveTabNoAnimation(0, 1);
+  animator_.MoveTabsNoAnimation({0, 1}, 2);
 
-  EXPECT_FALSE(animator_.IsAnimating());
-  EXPECT_EQ(2u, animator_.GetCurrentTabStates().size());
-  EXPECT_EQ(0.0f, ActivenessOf(animator_.GetCurrentTabStates()[0]));
-  EXPECT_EQ(1.0f, ActivenessOf(animator_.GetCurrentTabStates()[1]));
+  // Close tabs to run their callbacks.
+  while (animator_.animation_count() > 0) {
+    animator_.RemoveTab(0);
+    animator_.CompleteAnimations();
+  }
+
+  std::vector<int> expected_tabs_closed = {2, 3, 0, 1, 4};
+  EXPECT_EQ(expected_tabs_closed.size(), tabs_closed.size());
+  for (size_t i = 0; i < tabs_closed.size(); ++i) {
+    SCOPED_TRACE(i);
+    EXPECT_EQ(expected_tabs_closed[i], tabs_closed[i]);
+  }
 }
 
-TEST_F(TabStripAnimatorTest, MoveTabLeft) {
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(1, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kInactive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  EXPECT_EQ(1.0f, ActivenessOf(animator_.GetCurrentTabStates()[0]));
-  EXPECT_EQ(0.0f, ActivenessOf(animator_.GetCurrentTabStates()[1]));
+TEST_F(TabStripAnimatorTest, MoveTabsLeft) {
+  std::vector<int> tabs_closed;
+  for (int i = 0; i < 5; i++) {
+    AddTab(i, TabAnimationState::TabActiveness::kInactive,
+           TabAnimationState::TabPinnedness::kPinned,
+           base::BindLambdaForTesting(
+               [&tabs_closed, i]() { tabs_closed.push_back(i); }));
+  }
 
-  animator_.MoveTabNoAnimation(1, 0);
+  animator_.MoveTabsNoAnimation({3, 4}, 1);
 
-  EXPECT_FALSE(animator_.IsAnimating());
-  EXPECT_EQ(2u, animator_.GetCurrentTabStates().size());
-  EXPECT_EQ(0.0f, ActivenessOf(animator_.GetCurrentTabStates()[0]));
-  EXPECT_EQ(1.0f, ActivenessOf(animator_.GetCurrentTabStates()[1]));
+  // Close tabs to run their callbacks.
+  while (animator_.animation_count() > 0) {
+    animator_.RemoveTab(0);
+    animator_.CompleteAnimations();
+  }
+
+  std::vector<int> expected_tabs_closed = {0, 3, 4, 1, 2};
+  EXPECT_EQ(expected_tabs_closed.size(), tabs_closed.size());
+  for (size_t i = 0; i < tabs_closed.size(); ++i) {
+    SCOPED_TRACE(i);
+    EXPECT_EQ(expected_tabs_closed[i], tabs_closed[i]);
+  }
 }
 
-TEST_F(TabStripAnimatorTest, MoveTabSamePosition) {
-  animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kActive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  animator_.InsertTabAtNoAnimation(1, base::BindOnce([]() {}),
-                                   TabAnimationState::TabActiveness::kInactive,
-                                   TabAnimationState::TabPinnedness::kUnpinned);
-  EXPECT_EQ(1.0f, ActivenessOf(animator_.GetCurrentTabStates()[0]));
-  EXPECT_EQ(0.0f, ActivenessOf(animator_.GetCurrentTabStates()[1]));
+TEST_F(TabStripAnimatorTest, MoveTabsSamePosition) {
+  AddTab(0, TabAnimationState::TabActiveness::kActive);
+  AddTab(1, TabAnimationState::TabActiveness::kInactive);
 
-  animator_.MoveTabNoAnimation(0, 0);
+  animator_.MoveTabsNoAnimation({0}, 0);
 
   EXPECT_FALSE(animator_.IsAnimating());
   EXPECT_EQ(2u, animator_.GetCurrentTabStates().size());
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
index a81a2993..290f62e 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
@@ -4,12 +4,15 @@
 
 #include "chrome/browser/ui/views/tabs/tab_strip_layout_helper.h"
 
+#include <algorithm>
 #include <set>
+#include <utility>
 
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_style.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/browser/ui/views/tabs/tab_animation.h"
 #include "chrome/browser/ui/views/tabs/tab_animation_state.h"
 #include "chrome/browser/ui/views/tabs/tab_group_header.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
@@ -38,16 +41,66 @@
 
 }  // namespace
 
-TabStripLayoutHelper::TabStripLayoutHelper()
-    : active_tab_width_(TabStyle::GetStandardWidth()),
+struct TabStripLayoutHelper::TabSlot {
+  static TabStripLayoutHelper::TabSlot CreateForTab(int tab_model_index,
+                                                    bool pinned) {
+    TabStripLayoutHelper::TabSlot slot;
+    slot.type = TabAnimation::ViewType::kTab;
+    slot.tab_model_index = tab_model_index;
+    slot.pinned = pinned;
+    slot.active = false;
+    return slot;
+  }
+
+  static TabStripLayoutHelper::TabSlot CreateForGroupHeader(TabGroupId group,
+                                                            bool pinned) {
+    TabStripLayoutHelper::TabSlot slot;
+    slot.type = TabAnimation::ViewType::kGroupHeader;
+    slot.group = group;
+    slot.pinned = pinned;
+    slot.active = false;
+    return slot;
+  }
+
+  int GetTabModelIndex() const {
+    DCHECK(tab_model_index.has_value());
+    return tab_model_index.value();
+  }
+
+  TabGroupId GetGroup() const {
+    DCHECK(group.has_value());
+    return group.value();
+  }
+
+  TabAnimation::ViewType type;
+  base::Optional<int> tab_model_index;
+  base::Optional<TabGroupId> group;
+  bool pinned;
+  bool active;
+};
+
+TabStripLayoutHelper::TabStripLayoutHelper(
+    const TabStripController* controller,
+    GetTabsCallback get_tabs_callback,
+    GetGroupHeadersCallback get_group_headers_callback,
+    base::RepeatingClosure on_animation_progressed)
+    : controller_(controller),
+      get_tabs_callback_(get_tabs_callback),
+      get_group_headers_callback_(get_group_headers_callback),
+      animator_(on_animation_progressed),
+      active_tab_width_(TabStyle::GetStandardWidth()),
       inactive_tab_width_(TabStyle::GetStandardWidth()),
       first_non_pinned_tab_index_(0),
       first_non_pinned_tab_x_(0) {}
 
 TabStripLayoutHelper::~TabStripLayoutHelper() = default;
 
-int TabStripLayoutHelper::GetPinnedTabCount(
-    const views::ViewModelT<Tab>* tabs) const {
+bool TabStripLayoutHelper::IsAnimating() const {
+  return animator_.IsAnimating();
+}
+
+int TabStripLayoutHelper::GetPinnedTabCount() const {
+  views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
   int pinned_count = 0;
   while (pinned_count < tabs->view_size() &&
          tabs->view_at(pinned_count)->data().pinned) {
@@ -56,117 +109,154 @@
   return pinned_count;
 }
 
-namespace {
+void TabStripLayoutHelper::InsertTabAtNoAnimation(
+    int index,
+    base::OnceClosure tab_removed_callback,
+    TabAnimationState::TabActiveness active,
+    TabAnimationState::TabPinnedness pinned) {
+  UpdateCachedTabSlots();
+  animator_.InsertTabAtNoAnimation(
+      TabAnimation::ViewType::kTab, AnimatorIndexForTab(index),
+      std::move(tab_removed_callback), active, pinned);
+  VerifyAnimationsMatchTabSlots();
+}
 
-// Helper types for UpdateIdealBounds.
+void TabStripLayoutHelper::InsertTabAt(
+    int index,
+    base::OnceClosure tab_removed_callback,
+    TabAnimationState::TabActiveness active,
+    TabAnimationState::TabPinnedness pinned) {
+  UpdateCachedTabSlots();
+  animator_.InsertTabAt(TabAnimation::ViewType::kTab,
+                        AnimatorIndexForTab(index),
+                        std::move(tab_removed_callback), active, pinned);
+  VerifyAnimationsMatchTabSlots();
+}
 
-enum class TabSlotType {
-  kTab,
-  kGroupHeader,
-};
+void TabStripLayoutHelper::RemoveTabAt(int index) {
+  // Remove animation before calling UpdateCachedTabSlots so that we can get the
+  // correct animator index.
+  // TODO(958173): Animate closed.
+  animator_.RemoveTabNoAnimation(AnimatorIndexForTab(index));
+  UpdateCachedTabSlots();
+  VerifyAnimationsMatchTabSlots();
+}
 
-struct TabSlot {
-  static TabSlot CreateForTab(int tab, bool pinned) {
-    TabSlot slot;
-    slot.type = TabSlotType::kTab;
-    slot.tab = tab;
-    slot.pinned = pinned;
-    return slot;
+void TabStripLayoutHelper::MoveTab(
+    base::Optional<TabGroupId> group_at_prev_index,
+    int prev_index,
+    int new_index) {
+  int prev_header_animator_index;
+  if (group_at_prev_index.has_value()) {
+    prev_header_animator_index =
+        AnimatorIndexForGroupHeader(group_at_prev_index.value());
   }
+  const int prev_tab_animator_index = AnimatorIndexForTab(prev_index);
 
-  static TabSlot CreateForGroupHeader(TabGroupId group, bool pinned) {
-    TabSlot slot;
-    slot.type = TabSlotType::kGroupHeader;
-    slot.group = group;
-    slot.pinned = pinned;
-    return slot;
-  }
+  UpdateCachedTabSlots();
 
-  int GetTab() const {
-    DCHECK(tab.has_value());
-    return tab.value();
-  }
+  int target_animator_index = AnimatorIndexForTab(new_index);
 
-  TabGroupId GetGroup() const {
-    DCHECK(group.has_value());
-    return group.value();
-  }
-
-  TabSlotType type;
-  base::Optional<int> tab;
-  base::Optional<TabGroupId> group;
-  bool pinned;
-};
-
-}  // namespace
-
-void TabStripLayoutHelper::UpdateIdealBounds(
-    TabStripController* controller,
-    views::ViewModelT<Tab>* tabs,
-    std::map<TabGroupId, TabGroupHeader*> group_headers,
-    int available_width) {
-  std::vector<base::Optional<TabGroupId>> tab_to_group_mapping(
-      tabs->view_size());
-  for (const auto& header_pair : group_headers) {
-    const TabGroupId group = header_pair.first;
-    std::vector<int> tabs = controller->ListTabsInGroup(group);
-    for (int tab : tabs)
-      tab_to_group_mapping[tab] = group;
-  }
-
-  const int num_pinned_tabs = GetPinnedTabCount(tabs);
-  const int active_tab_index = controller->GetActiveIndex();
-
-  std::vector<TabSlot> slots;
-  std::set<TabGroupId> headers_already_added;
-  int active_index_in_slots = TabStripModel::kNoTab;
-  for (int i = 0; i < tabs->view_size(); ++i) {
-    const bool pinned = i < num_pinned_tabs;
-    base::Optional<TabGroupId> group = tab_to_group_mapping[i];
-    if (group.has_value() &&
-        !base::Contains(headers_already_added, group.value())) {
-      // Start of a group.
-      slots.push_back(TabSlot::CreateForGroupHeader(group.value(), pinned));
-      headers_already_added.insert(group.value());
+  std::vector<int> moving_tabs;
+  if (group_at_prev_index.has_value()) {
+    const int new_header_animator_index =
+        AnimatorIndexForGroupHeader(group_at_prev_index.value());
+    if (prev_header_animator_index != new_header_animator_index) {
+      moving_tabs.push_back(prev_header_animator_index);
+      target_animator_index = new_header_animator_index;
     }
-    slots.push_back(TabSlot::CreateForTab(i, pinned));
-    if (i == active_tab_index)
-      active_index_in_slots = slots.size() - 1;
   }
+  moving_tabs.push_back(prev_tab_animator_index);
 
+  // TODO(958173): Animate move.
+  animator_.MoveTabsNoAnimation(std::move(moving_tabs), target_animator_index);
+
+  VerifyAnimationsMatchTabSlots();
+}
+
+void TabStripLayoutHelper::SetTabPinnedness(
+    int index,
+    TabAnimationState::TabPinnedness pinnedness) {
+  UpdateCachedTabSlots();
+  // TODO(958173): Animate state change.
+  animator_.SetPinnednessNoAnimation(AnimatorIndexForTab(index), pinnedness);
+  VerifyAnimationsMatchTabSlots();
+}
+
+void TabStripLayoutHelper::InsertGroupHeader(
+    TabGroupId group,
+    base::OnceClosure header_removed_callback) {
+  UpdateCachedTabSlots();
+  // TODO(958173): Animate open.
+  animator_.InsertTabAtNoAnimation(TabAnimation::ViewType::kGroupHeader,
+                                   AnimatorIndexForGroupHeader(group),
+                                   std::move(header_removed_callback),
+                                   TabAnimationState::TabActiveness::kInactive,
+                                   TabAnimationState::TabPinnedness::kUnpinned);
+  VerifyAnimationsMatchTabSlots();
+}
+
+void TabStripLayoutHelper::RemoveGroupHeader(TabGroupId group) {
+  // Remove animation before calling UpdateCachedTabSlots so that we can get the
+  // correct animator index.
+  // TODO(958173): Animate closed.
+  animator_.RemoveTabNoAnimation(AnimatorIndexForGroupHeader(group));
+  UpdateCachedTabSlots();
+  VerifyAnimationsMatchTabSlots();
+}
+
+void TabStripLayoutHelper::SetActiveTab(int prev_active_index,
+                                        int new_active_index) {
+  UpdateCachedTabSlots();
+  animator_.SetActiveTab(AnimatorIndexForTab(prev_active_index),
+                         AnimatorIndexForTab(new_active_index));
+  VerifyAnimationsMatchTabSlots();
+}
+
+void TabStripLayoutHelper::CompleteAnimations() {
+  animator_.CompleteAnimations();
+}
+
+void TabStripLayoutHelper::CompleteAnimationsWithoutDestroyingTabs() {
+  animator_.CompleteAnimationsWithoutDestroyingTabs();
+}
+
+void TabStripLayoutHelper::UpdateIdealBounds(int available_width) {
   std::vector<TabAnimationState> ideal_animation_states;
-  for (int i = 0; i < int{slots.size()}; ++i) {
+  for (const auto& slot : cached_slots_) {
+    auto pinned = slot.pinned ? TabAnimationState::TabPinnedness::kPinned
+                              : TabAnimationState::TabPinnedness::kUnpinned;
+    auto active = slot.active ? TabAnimationState::TabActiveness::kActive
+                              : TabAnimationState::TabActiveness::kInactive;
     ideal_animation_states.push_back(TabAnimationState::ForIdealTabState(
-        TabAnimationState::TabOpenness::kOpen,
-        slots[i].pinned ? TabAnimationState::TabPinnedness::kPinned
-                        : TabAnimationState::TabPinnedness::kUnpinned,
-        i == active_index_in_slots
-            ? TabAnimationState::TabActiveness::kActive
-            : TabAnimationState::TabActiveness::kInactive,
-        0));
+        TabAnimationState::TabOpenness::kOpen, pinned, active, 0));
   }
 
   const std::vector<gfx::Rect> bounds = CalculateTabBounds(
       GetTabSizeInfo(), ideal_animation_states, available_width);
-  DCHECK_EQ(slots.size(), bounds.size());
+  DCHECK_EQ(cached_slots_.size(), bounds.size());
+
+  views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
+  std::map<TabGroupId, TabGroupHeader*> group_headers =
+      get_group_headers_callback_.Run();
 
   for (size_t i = 0; i < bounds.size(); ++i) {
-    const TabSlot& slot = slots[i];
+    const TabSlot& slot = cached_slots_[i];
     switch (slot.type) {
-      case TabSlotType::kTab:
-        tabs->set_ideal_bounds(slot.GetTab(), bounds[i]);
-        UpdateCachedTabWidth(i, bounds[i].width(), active_index_in_slots);
+      case TabAnimation::ViewType::kTab:
+        tabs->set_ideal_bounds(slot.GetTabModelIndex(), bounds[i]);
+        UpdateCachedTabWidth(i, bounds[i].width(), slot.active);
         break;
-      case TabSlotType::kGroupHeader:
+      case TabAnimation::ViewType::kGroupHeader:
         group_headers[slot.GetGroup()]->SetBoundsRect(bounds[i]);
         break;
     }
   }
 }
 
-void TabStripLayoutHelper::UpdateIdealBoundsForPinnedTabs(
-    views::ViewModelT<Tab>* tabs) {
-  const int pinned_tab_count = GetPinnedTabCount(tabs);
+void TabStripLayoutHelper::UpdateIdealBoundsForPinnedTabs() {
+  views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
+  const int pinned_tab_count = GetPinnedTabCount();
 
   first_non_pinned_tab_index_ = pinned_tab_count;
   first_non_pinned_tab_x_ = 0;
@@ -188,35 +278,113 @@
   }
 }
 
-int TabStripLayoutHelper::LayoutTabs(views::ViewModelT<Tab>* tabs,
-                                     std::vector<TabAnimationState> tab_states,
-                                     int available_width,
-                                     int active_tab_model_index) {
-  std::vector<gfx::Rect> bounds =
-      CalculateTabBounds(GetTabSizeInfo(), tab_states, available_width);
+int TabStripLayoutHelper::LayoutTabs(int available_width) {
+  views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
+  std::map<TabGroupId, TabGroupHeader*> group_headers =
+      get_group_headers_callback_.Run();
 
-  // TODO(958173): Assume for now that there are no closing tabs.
-  // TODO(958173): Assume for now that there are no group headers.
-  DCHECK_EQ(bounds.size(), static_cast<size_t>(tabs->view_size()));
+  std::vector<gfx::Rect> bounds = CalculateTabBounds(
+      GetTabSizeInfo(), animator_.GetCurrentTabStates(), available_width);
+
+  // TODO(958173): Assume for now that there are no closing tabs or headers.
+  DCHECK_EQ(bounds.size(), tabs->view_size() + group_headers.size());
+
   int trailing_x = 0;
-  for (size_t i = 0; i < bounds.size(); i++) {
+
+  const int active_tab_model_index = controller_->GetActiveIndex();
+  for (int i = 0; i < tabs->view_size(); i++) {
     if (tabs->view_at(i)->dragging())
       continue;
-    tabs->view_at(i)->SetBoundsRect(bounds[i]);
-    trailing_x = std::max(trailing_x, bounds[i].right());
+    const int animator_index = AnimatorIndexForTab(i);
+    tabs->view_at(i)->SetBoundsRect(bounds[animator_index]);
+    trailing_x = std::max(trailing_x, bounds[animator_index].right());
     // TODO(958173): We shouldn't need to update the cached widths here, since
     // they're also updated in UpdateIdealBounds. However, tests will fail
     // without this line; we should investigate why.
-    UpdateCachedTabWidth(i, bounds[i].width(), active_tab_model_index);
+    UpdateCachedTabWidth(i, bounds[animator_index].width(),
+                         i == active_tab_model_index);
+  }
+
+  for (const auto& header_pair : group_headers) {
+    const int animator_index = AnimatorIndexForGroupHeader(header_pair.first);
+    header_pair.second->SetBoundsRect(bounds[animator_index]);
+    trailing_x = std::max(trailing_x, bounds[animator_index].right());
   }
 
   return trailing_x;
 }
 
+void TabStripLayoutHelper::UpdateCachedTabSlots() {
+  views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
+  std::map<TabGroupId, TabGroupHeader*> group_headers =
+      get_group_headers_callback_.Run();
+
+  std::vector<base::Optional<TabGroupId>> tab_to_group_mapping(
+      tabs->view_size());
+  for (const auto& header_pair : group_headers) {
+    const TabGroupId group = header_pair.first;
+    std::vector<int> tabs = controller_->ListTabsInGroup(group);
+    for (int tab : tabs)
+      tab_to_group_mapping[tab] = group;
+  }
+
+  const int num_pinned_tabs = GetPinnedTabCount();
+  const int active_tab_index = controller_->GetActiveIndex();
+
+  std::vector<TabSlot> new_slots;
+  std::set<TabGroupId> headers_already_added;
+  for (int i = 0; i < tabs->view_size(); ++i) {
+    const bool pinned = i < num_pinned_tabs;
+    base::Optional<TabGroupId> group = tab_to_group_mapping[i];
+    if (group.has_value() &&
+        !base::Contains(headers_already_added, group.value())) {
+      // Start of a group.
+      new_slots.push_back(TabSlot::CreateForGroupHeader(group.value(), pinned));
+      headers_already_added.insert(group.value());
+    }
+    TabSlot slot = TabSlot::CreateForTab(i, pinned);
+    if (i == active_tab_index)
+      slot.active = true;
+    new_slots.push_back(slot);
+  }
+
+  cached_slots_ = std::move(new_slots);
+}
+
+int TabStripLayoutHelper::AnimatorIndexForTab(int tab_model_index) const {
+  if (tab_model_index == TabStripModel::kNoTab)
+    return TabStripModel::kNoTab;
+
+  auto it = std::find_if(cached_slots_.begin(), cached_slots_.end(),
+                         [&tab_model_index](const TabSlot& tab_slot) {
+                           return tab_slot.tab_model_index == tab_model_index;
+                         });
+  DCHECK(it != cached_slots_.end());
+  return it - cached_slots_.begin();
+}
+
+int TabStripLayoutHelper::AnimatorIndexForGroupHeader(TabGroupId group) const {
+  auto it = std::find_if(
+      cached_slots_.begin(), cached_slots_.end(),
+      [&group](const TabSlot& tab_slot) { return tab_slot.group == group; });
+  DCHECK(it != cached_slots_.end());
+  return it - cached_slots_.begin();
+}
+
+void TabStripLayoutHelper::VerifyAnimationsMatchTabSlots() const {
+  if (!DCHECK_IS_ON())
+    return;
+  DCHECK_EQ(cached_slots_.size(), animator_.animation_count());
+  for (size_t i = 0; i < cached_slots_.size(); i++) {
+    DCHECK_EQ(cached_slots_[i].type, animator_.GetAnimationViewTypeAt(i))
+        << "At index " << i;
+  }
+}
+
 void TabStripLayoutHelper::UpdateCachedTabWidth(int tab_index,
                                                 int tab_width,
-                                                int active_tab_index) {
-  if (tab_index == active_tab_index)
+                                                bool active) {
+  if (active)
     active_tab_width_ = tab_width;
   else
     inactive_tab_width_ = tab_width;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h
index f8aa9fa2..1da8327 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h
@@ -10,6 +10,7 @@
 
 #include "base/optional.h"
 #include "chrome/browser/ui/views/tabs/tab_animation_state.h"
+#include "chrome/browser/ui/views/tabs/tab_strip_animator.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/view_model.h"
 
@@ -18,48 +19,132 @@
 class TabGroupId;
 class TabStripController;
 
-// Helper class for TabStrip, that is responsible for calculating the tabs'
-// ideal layout and assigning those bounds, as well as caching the derived
-// values resulting from that calculation.
+// Helper class for TabStrip, that is responsible for calculating and assigning
+// layouts for tabs and group headers. It tracks animations and changes to the
+// model so that it has all necessary information for layout purposes.
 class TabStripLayoutHelper {
  public:
-  TabStripLayoutHelper();
+  using GetTabsCallback = base::RepeatingCallback<views::ViewModelT<Tab>*()>;
+  using GetGroupHeadersCallback =
+      base::RepeatingCallback<std::map<TabGroupId, TabGroupHeader*>()>;
+
+  TabStripLayoutHelper(const TabStripController* controller,
+                       GetTabsCallback get_tabs_callback,
+                       GetGroupHeadersCallback get_group_headers_callback,
+                       base::RepeatingClosure on_animation_progressed);
   ~TabStripLayoutHelper();
 
+  // Returns whether any animations for tabs or group headers are in progress.
+  bool IsAnimating() const;
+
   int active_tab_width() { return active_tab_width_; }
   int inactive_tab_width() { return inactive_tab_width_; }
   int first_non_pinned_tab_index() { return first_non_pinned_tab_index_; }
   int first_non_pinned_tab_x() { return first_non_pinned_tab_x_; }
 
-  // Returns the number of pinned tabs in |tabs|.
-  int GetPinnedTabCount(const views::ViewModelT<Tab>* tabs) const;
+  // Returns the number of pinned tabs in the tabstrip.
+  int GetPinnedTabCount() const;
+
+  // Inserts a new tab at |index|, without animation. |tab_removed_callback|
+  // will be invoked if the tab is removed at the end of a remove animation.
+  void InsertTabAtNoAnimation(int index,
+                              base::OnceClosure tab_removed_callback,
+                              TabAnimationState::TabActiveness active,
+                              TabAnimationState::TabPinnedness pinned);
+
+  // Inserts a new tab at |index|, with animation. |tab_removed_callback| will
+  // be invoked if the tab is removed at the end of a remove animation.
+  void InsertTabAt(int index,
+                   base::OnceClosure tab_removed_callback,
+                   TabAnimationState::TabActiveness active,
+                   TabAnimationState::TabPinnedness pinned);
+
+  // Removes the tab at |index|. TODO(958173): This should invoke the associated
+  // |tab_removed_callback| but currently does not, as it would duplicate
+  // TabStrip::RemoveTabDelegate::AnimationEnded.
+  void RemoveTabAt(int index);
+
+  // Moves the tab at |prev_index| with group |group_at_prev_index| to
+  // |new_index|. Also updates the group header's location if necessary.
+  void MoveTab(base::Optional<TabGroupId> group_at_prev_index,
+               int prev_index,
+               int new_index);
+
+  // Sets the tab at |index|'s pinnedness to |pinnedness|.
+  void SetTabPinnedness(int index, TabAnimationState::TabPinnedness pinnedness);
+
+  // Inserts a new group header for |group|. |header_removed_callback| will be
+  // invoked if the group is removed at the end of a remove animation.
+  void InsertGroupHeader(TabGroupId group,
+                         base::OnceClosure header_removed_callback);
+
+  // Removes the group header for |group|. TODO(958173): This should invoke the
+  // associated |header_removed_callback| but currently does not because
+  // RemoveTabAt also does not, and they share codepaths.
+  void RemoveGroupHeader(TabGroupId group);
+
+  // Changes the active tab from |prev_active_index| to |new_active_index|.
+  void SetActiveTab(int prev_active_index, int new_active_index);
+
+  // Finishes all in-progress animations.
+  void CompleteAnimations();
+
+  // TODO(958173): Temporary method. Like CompleteAnimations, but does not call
+  // any associated |tab_removed_callback| or |header_removed_callback|. See
+  // comment on TabStripAnimator::CompleteAnimationsWithoutDestroyingTabs.
+  void CompleteAnimationsWithoutDestroyingTabs();
 
   // Generates and sets the ideal bounds for the views in |tabs| and
   // |group_headers|. Updates the cached widths in |active_tab_width_| and
   // |inactive_tab_width_|.
   // TODO(958173): The notion of ideal bounds is going away. Delete this.
-  void UpdateIdealBounds(TabStripController* controller,
-                         views::ViewModelT<Tab>* tabs,
-                         std::map<TabGroupId, TabGroupHeader*> group_headers,
-                         int available_width);
+  void UpdateIdealBounds(int available_width);
 
   // Generates and sets the ideal bounds for |tabs|. Updates
   // the cached values in |first_non_pinned_tab_index_| and
   // |first_non_pinned_tab_x_|.
   // TODO(958173): The notion of ideal bounds is going away. Delete this.
-  void UpdateIdealBoundsForPinnedTabs(views::ViewModelT<Tab>* tabs);
+  void UpdateIdealBoundsForPinnedTabs();
 
-  // Sets the current bounds of the tabs in |tabs|. Returns the x coordinate
-  // of the trailing edge of the trailing-most tab.
-  int LayoutTabs(views::ViewModelT<Tab>* tabs,
-                 std::vector<TabAnimationState> tab_states,
-                 int available_width,
-                 int active_tab_model_index);
+  // Lays out tabs and group headers to their current bounds. Returns the
+  // x-coordinate of the trailing edge of the trailing-most tab.
+  int LayoutTabs(int available_width);
 
  private:
+  struct TabSlot;
+
+  // Recalculate |cached_slots_|, called whenever state changes.
+  void UpdateCachedTabSlots();
+
+  // Finds the index of the TabAnimation in |animator_| for the tab at
+  // |tab_model_index|.
+  int AnimatorIndexForTab(int tab_model_index) const;
+
+  // Finds the index of the TabAnimation in |animator_| for |group|.
+  int AnimatorIndexForGroupHeader(TabGroupId group) const;
+
+  // Compares |cached_slots_| to the TabAnimations in |animator_| and DCHECKs if
+  // the TabAnimation::ViewType do not match. Prevents bugs that could cause the
+  // wrong callback being run when a tab or group is deleted.
+  void VerifyAnimationsMatchTabSlots() const;
+
   // Updates the value of either |active_tab_width_| or |inactive_tab_width_|,
   // as appropriate.
-  void UpdateCachedTabWidth(int tab_index, int tab_width, int active_tab_index);
+  void UpdateCachedTabWidth(int tab_index, int tab_width, bool active);
+
+  // The owning tabstrip's controller.
+  const TabStripController* const controller_;
+
+  // Callbacks to get the necessary View objects from the owning tabstrip.
+  GetTabsCallback get_tabs_callback_;
+  GetGroupHeadersCallback get_group_headers_callback_;
+
+  // Responsible for tracking the animations of tabs and group headers.
+  TabStripAnimator animator_;
+
+  // Tracks changes to the tab strip in order to properly collate group headers
+  // with tabs.
+  std::vector<TabSlot> cached_slots_;
 
   // The current widths of tabs. If the space for tabs is not evenly divisible
   // into these widths, the initial tabs in the strip will be 1 px larger.
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index 65827b3..dc7ecc1 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -1013,6 +1013,7 @@
 
   base::Optional<TabGroupId> group = TabGroupId::GenerateNew();
   controller_->MoveTabIntoGroup(0, group);
+  CompleteAnimationAndLayout();
 
   std::vector<TabGroupHeader*> headers = ListGroupHeaders();
   EXPECT_EQ(1u, headers.size());
@@ -1038,6 +1039,69 @@
   EXPECT_EQ(header->x(), second_slot_x);
 }
 
+TEST_P(TabStripTest, GroupHeaderMovesRightWithTab) {
+  tab_strip_->SetBounds(0, 0, 2000, 100);
+  for (int i = 0; i < 4; i++)
+    tab_strip_->AddTabAt(i, TabRendererData(), false);
+  base::Optional<TabGroupId> group = TabGroupId::GenerateNew();
+  controller_->MoveTabIntoGroup(1, group);
+  CompleteAnimationAndLayout();
+
+  TabGroupHeader* header = ListGroupHeaders()[0];
+  const int initial_header_x = header->x();
+  const int initial_tab_1_x = tab_strip_->tab_at(1)->x();
+
+  controller_->MoveTab(1, 2);
+  CompleteAnimationAndLayout();
+
+  EXPECT_EQ(initial_header_x, tab_strip_->tab_at(1)->x());
+  EXPECT_EQ(initial_tab_1_x, header->x());
+}
+
+TEST_P(TabStripTest, GroupHeaderMovesLeftWithTab) {
+  tab_strip_->SetBounds(0, 0, 2000, 100);
+  for (int i = 0; i < 4; i++)
+    tab_strip_->AddTabAt(i, TabRendererData(), false);
+  base::Optional<TabGroupId> group = TabGroupId::GenerateNew();
+  controller_->MoveTabIntoGroup(2, group);
+  CompleteAnimationAndLayout();
+
+  TabGroupHeader* header = ListGroupHeaders()[0];
+  const int initial_header_x = header->x();
+  const int initial_tab_1_x = tab_strip_->tab_at(1)->x();
+
+  controller_->MoveTab(2, 1);
+  CompleteAnimationAndLayout();
+
+  EXPECT_EQ(initial_header_x, tab_strip_->tab_at(1)->x());
+  EXPECT_EQ(initial_tab_1_x, header->x());
+}
+
+TEST_P(TabStripTest, GroupHeaderDoesntMoveReorderingTabsInGroup) {
+  tab_strip_->SetBounds(0, 0, 2000, 100);
+  for (int i = 0; i < 4; i++)
+    tab_strip_->AddTabAt(i, TabRendererData(), false);
+  base::Optional<TabGroupId> group = TabGroupId::GenerateNew();
+  controller_->MoveTabIntoGroup(1, group);
+  controller_->MoveTabIntoGroup(2, group);
+  CompleteAnimationAndLayout();
+
+  TabGroupHeader* header = ListGroupHeaders()[0];
+  const int initial_header_x = header->x();
+  Tab* tab1 = tab_strip_->tab_at(1);
+  const int initial_tab_1_x = tab1->x();
+  Tab* tab2 = tab_strip_->tab_at(2);
+  const int initial_tab_2_x = tab2->x();
+
+  controller_->MoveTab(1, 2);
+  CompleteAnimationAndLayout();
+
+  // Header has not moved.
+  EXPECT_EQ(initial_header_x, header->x());
+  EXPECT_EQ(initial_tab_1_x, tab2->x());
+  EXPECT_EQ(initial_tab_2_x, tab1->x());
+}
+
 // This can happen when a tab in the middle of a group starts to close.
 TEST_P(TabStripTest, DiscontinuousGroup) {
   tab_strip_->SetBounds(0, 0, 1000, 100);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
index d477e18..48a08a9 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
@@ -17,7 +17,8 @@
 ToolbarIconContainerView::ToolbarIconContainerView(bool uses_highlight)
     : uses_highlight_(uses_highlight) {
   auto layout_manager = std::make_unique<views::FlexLayout>();
-  layout_manager->SetCollapseMargins(true).SetDefaultChildMargins(
+  layout_manager->SetCollapseMargins(true).SetDefault(
+      views::kMarginsKey,
       gfx::Insets(0, 0, 0, GetLayoutConstant(TOOLBAR_ELEMENT_PADDING)));
   SetLayoutManager(std::move(layout_manager));
 }
@@ -26,20 +27,28 @@
 
 void ToolbarIconContainerView::UpdateAllIcons() {}
 
-void ToolbarIconContainerView::AddMainView(views::View* main_view) {
-  DCHECK(!main_view_);
-  // Set empty margins from this view to negate the default ones set in the
+void ToolbarIconContainerView::AddMainButton(views::Button* main_button) {
+  DCHECK(!main_button_);
+  // Set empty margins from this view to remove the default ones set in the
   // constructor.
-  main_view->SetProperty(views::kMarginsKey, gfx::Insets());
-  main_view_ = main_view;
-  AddChildView(main_view_);
+  main_button->SetProperty(views::kMarginsKey, gfx::Insets());
+  main_button->AddObserver(this);
+  main_button->AddButtonObserver(this);
+  main_button_ = main_button;
+  AddChildView(main_button_);
 }
 
 void ToolbarIconContainerView::OnHighlightChanged(
     views::Button* observed_button,
     bool highlighted) {
+  if (highlighted) {
+    DCHECK(observed_button);
+    DCHECK(observed_button->GetVisible());
+  }
+
   // TODO(crbug.com/932818): Pass observed button type to container.
-  highlighted_view_ = highlighted ? observed_button : nullptr;
+  highlighted_button_ = highlighted ? observed_button : nullptr;
+
   UpdateHighlight();
 }
 
@@ -65,12 +74,22 @@
   UpdateHighlight();
 }
 
+void ToolbarIconContainerView::ViewHierarchyChanged(
+    const views::ViewHierarchyChangedDetails& details) {
+  // Update the highlight as this might have changed the number of visible
+  // children.
+  UpdateHighlight();
+}
+
 void ToolbarIconContainerView::ChildPreferredSizeChanged(views::View* child) {
   PreferredSizeChanged();
 }
 
 void ToolbarIconContainerView::ChildVisibilityChanged(views::View* child) {
   PreferredSizeChanged();
+  // Update the highlight as this might have changed the number of visible
+  // children.
+  UpdateHighlight();
 }
 
 gfx::Insets ToolbarIconContainerView::GetInsets() const {
@@ -80,39 +99,43 @@
   return gfx::Insets();
 }
 
-void ToolbarIconContainerView::UpdateHighlight() {
+bool ToolbarIconContainerView::ShouldDisplayHighlight() {
   if (!uses_highlight_)
-    return;
+    return false;
 
-  bool highlighted = false;
+  const int num_visible_children =
+      std::count_if(children().begin(), children().end(),
+                    [](views::View* child) { return child->GetVisible(); });
 
-  // If this container is mouse hovered and the main view is not mouse hovered
-  // the container should be highlighted.
-  if (!main_view_ || !main_view_->IsMouseHovered())
-    highlighted = IsMouseHovered();
+  // If there's only one visible child we never need to draw a border stroke to
+  // connect them.
+  if (num_visible_children <= 1)
+    return false;
 
   // The container should also be highlighted if a dialog is anchored to.
-  if (highlighted_view_)
-    highlighted = true;
+  if (highlighted_button_)
+    return true;
 
-  // The container should also highlight if any of the child buttons are
-  // either pressed or hovered.
+  if (IsMouseHovered())
+    return true;
+
+  // Focused, pressed or hovered children should trigger the highlight.
   for (views::View* child : children()) {
-    // The main view should not be considered for focus / button state.
-    if (child == main_view_)
-      continue;
     if (child->HasFocus())
-      highlighted = true;
+      return true;
     views::Button* button = views::Button::AsButton(child);
     if (!button)
       continue;
     if (button->state() == views::Button::ButtonState::STATE_PRESSED ||
         button->state() == views::Button::ButtonState::STATE_HOVERED) {
-      highlighted = true;
+      return true;
     }
   }
+  return false;
+}
 
-  SetBorder(highlighted
+void ToolbarIconContainerView::UpdateHighlight() {
+  SetBorder(ShouldDisplayHighlight()
                 ? views::CreateRoundedRectBorder(
                       1,
                       ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
index 8a09423..2fe0dd3 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
@@ -22,7 +22,7 @@
   virtual void UpdateAllIcons();
 
   // Adds the RHS child as well as setting its margins.
-  void AddMainView(views::View* main_view);
+  void AddMainButton(views::Button* main_button);
 
   // views::ButtonObserver:
   void OnHighlightChanged(views::Button* observed_button,
@@ -44,16 +44,19 @@
   void OnMouseExited(const ui::MouseEvent& event) override;
   void ChildPreferredSizeChanged(views::View* child) override;
   void ChildVisibilityChanged(views::View* child) override;
+  void ViewHierarchyChanged(
+      const views::ViewHierarchyChangedDetails& details) override;
   gfx::Insets GetInsets() const override;
 
+  bool ShouldDisplayHighlight();
   void UpdateHighlight();
 
   // The main view is nominally always present and is last child in the view
   // hierarchy.
-  views::View* main_view_ = nullptr;
+  views::Button* main_button_ = nullptr;
 
   // Points to the child view that is currently highlighted.
-  views::View* highlighted_view_ = nullptr;
+  views::Button* highlighted_button_ = nullptr;
 
   // Determine whether the container shows its highlight background.
   const bool uses_highlight_;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.cc
index 0535770..87a086a 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.cc
@@ -51,7 +51,7 @@
   }
 
   avatar_ = new AvatarToolbarButton(browser);
-  AddMainView(avatar_);
+  AddMainButton(avatar_);
 }
 
 ToolbarPageActionIconContainerView::~ToolbarPageActionIconContainerView() {
diff --git a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view_browsertest.cc b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view_browsertest.cc
index 6cf75d4..7309c64 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view_browsertest.cc
@@ -54,6 +54,11 @@
   void TestUsesHighlight(ToolbarPageActionIconContainerView* container,
                          bool expect_highlight) {
     DCHECK(container);
+
+    // Make sure the save-card icon is visible so that at least two children are
+    // visible. Otherwise the border highlight would never be drawn.
+    container->save_card_icon_view()->SetVisible(true);
+
     EXPECT_EQ(container->uses_highlight(), expect_highlight);
     EXPECT_EQ(container->border(), nullptr);
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 807b651..f4d0f272 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -687,7 +687,8 @@
     // code cleaner.
     const int default_margin = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
     const int location_bar_margin = GetLayoutConstant(TOOLBAR_STANDARD_SPACING);
-    layout_manager_->SetDefaultChildMargins(gfx::Insets(0, default_margin));
+    layout_manager_->SetDefault(views::kMarginsKey,
+                                gfx::Insets(0, default_margin));
     location_bar_->SetProperty(views::kMarginsKey,
                                gfx::Insets(0, location_bar_margin));
     if (browser_actions_) {
@@ -722,15 +723,15 @@
   layout_manager_->SetOrientation(views::LayoutOrientation::kHorizontal)
       .SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
       .SetCollapseMargins(true)
-      .SetDefaultChildMargins(gfx::Insets(0, default_margin));
+      .SetDefault(views::kMarginsKey, gfx::Insets(0, default_margin));
 
-  layout_manager_->SetFlexForView(location_bar_, location_bar_flex_rule);
+  location_bar_->SetProperty(views::kFlexBehaviorKey, location_bar_flex_rule);
   location_bar_->SetProperty(views::kMarginsKey,
                              gfx::Insets(0, location_bar_margin));
 
   if (browser_actions_) {
-    layout_manager_->SetFlexForView(browser_actions_,
-                                    browser_actions_flex_rule);
+    browser_actions_->SetProperty(views::kFlexBehaviorKey,
+                                  browser_actions_flex_rule);
     browser_actions_->SetProperty(views::kMarginsKey, gfx::Insets());
     browser_actions_->SetProperty(views::kInternalPaddingKey,
                                   gfx::Insets(0, location_bar_margin));
diff --git a/chrome/browser/ui/web_applications/web_app_ui_service.cc b/chrome/browser/ui/web_applications/web_app_ui_service.cc
index eb57e403..f4255194 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_service.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_service.cc
@@ -27,6 +27,8 @@
 }
 
 WebAppUiService::WebAppUiService(Profile* profile) : profile_(profile) {
+  provider_ = WebAppProvider::Get(profile_);
+
   for (Browser* browser : *BrowserList::GetInstance()) {
     base::Optional<AppId> app_id = GetAppIdForBrowser(browser);
     if (!app_id.has_value())
@@ -36,14 +38,14 @@
   }
 
   BrowserList::AddObserver(this);
-  auto* provider = WebAppProvider::Get(profile_);
-  provider->set_ui_delegate(this);
+  provider_->set_ui_delegate(this);
 }
 
-WebAppUiService::~WebAppUiService() = default;
+WebAppUiService::~WebAppUiService() {
+  provider_->set_ui_delegate(nullptr);
+}
 
 void WebAppUiService::Shutdown() {
-  WebAppProvider::Get(profile_)->set_ui_delegate(nullptr);
   BrowserList::RemoveObserver(this);
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_ui_service.h b/chrome/browser/ui/web_applications/web_app_ui_service.h
index 7dc64da..810dd4ce 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_service.h
+++ b/chrome/browser/ui/web_applications/web_app_ui_service.h
@@ -21,6 +21,8 @@
 
 namespace web_app {
 
+class WebAppProvider;
+
 // This KeyedService is a UI counterpart for WebAppProvider.
 class WebAppUiService : public KeyedService,
                         public BrowserListObserver,
@@ -47,6 +49,7 @@
  private:
   base::Optional<AppId> GetAppIdForBrowser(Browser* browser);
 
+  WebAppProvider* provider_;
   Profile* profile_;
 
   std::map<AppId, std::vector<base::OnceClosure>> windows_closed_requests_map_;
diff --git a/chrome/browser/ui/webui/app_management/app_management.mojom b/chrome/browser/ui/webui/app_management/app_management.mojom
index a1c9e39b..c2e7c54 100644
--- a/chrome/browser/ui/webui/app_management/app_management.mojom
+++ b/chrome/browser/ui/webui/app_management/app_management.mojom
@@ -22,6 +22,7 @@
   string? size;
   map<uint32, apps.mojom.Permission> permissions;
   apps.mojom.InstallSource install_source;
+  bool hide_more_settings;
 };
 
 // Extension-based apps primarily use install-time permissions that cannot be
@@ -67,6 +68,8 @@
   LOCATION        = 1,
   MICROPHONE      = 2,
   NOTIFICATIONS   = 3,
+  CONTACTS        = 4,
+  STORAGE         = 5,
 };
 
 // This enum takes the important permission values from the
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index cb4663c..f9c7788 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/containers/flat_map.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -16,6 +17,7 @@
 #include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/permissions/permission_message.h"
 #include "extensions/common/permissions/permissions_data.h"
@@ -24,6 +26,11 @@
 
 namespace {
 
+constexpr char const* kAppIdsWithHiddenMoreSettings[] = {
+    extension_misc::kFilesManagerAppId,
+    extensions::kWebStoreAppId,
+};
+
 app_management::mojom::ExtensionAppPermissionMessagePtr
 CreateExtensionAppPermissionMessage(
     const extensions::PermissionMessage& message) {
@@ -35,6 +42,10 @@
       base::UTF16ToUTF8(message.message()), std::move(submessages));
 }
 
+bool ShouldHideMoreSettings(const std::string app_id) {
+  return base::Contains(kAppIdsWithHiddenMoreSettings, app_id);
+}
+
 }  // namespace
 
 AppManagementPageHandler::AppManagementPageHandler(
@@ -202,6 +213,8 @@
                               : OptionalBool::kFalse;
 #endif
 
+  app->hide_more_settings = ShouldHideMoreSettings(app->id);
+
   return app;
 }
 
diff --git a/chrome/browser/ui/webui/app_management/app_management_ui.cc b/chrome/browser/ui/webui/app_management/app_management_ui.cc
index 12afa0f7..b1872df7 100644
--- a/chrome/browser/ui/webui/app_management/app_management_ui.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_ui.cc
@@ -34,6 +34,7 @@
       {"appNoPermission", IDS_APPLICATION_INFO_APP_NO_PERMISSIONS_TEXT},
       {"back", IDS_APP_MANAGEMENT_BACK},
       {"camera", IDS_APP_MANAGEMENT_CAMERA},
+      {"contacts", IDS_APP_MANAGEMENT_CONTACTS},
       {"controlledByPolicy", IDS_CONTROLLED_SETTING_POLICY},
       {"lessApps", IDS_APP_MANAGEMENT_LESS_APPS},
       {"location", IDS_APP_MANAGEMENT_LOCATION},
@@ -51,6 +52,7 @@
       {"pinToShelf", IDS_APP_MANAGEMENT_PIN_TO_SHELF},
       {"searchPrompt", IDS_APP_MANAGEMENT_SEARCH_PROMPT},
       {"size", IDS_APP_MANAGEMENT_SIZE},
+      {"storage", IDS_APP_MANAGEMENT_STORAGE},
       {"thisAppCan", IDS_APP_MANAGEMENT_THIS_APP_CAN},
       {"title", IDS_APP_MANAGEMENT_TITLE},
       {"uninstall", IDS_APP_MANAGEMENT_UNINSTALL},
@@ -58,31 +60,24 @@
   };
   AddLocalizedStringsBulk(source, kStrings, base::size(kStrings));
 
-  source->AddResourcePath("app_management.mojom-lite.js",
-                          IDR_APP_MANAGEMENT_MOJO_LITE_JS);
-  source->AddResourcePath("types.mojom-lite.js",
-                          IDR_APP_MANAGEMENT_TYPES_MOJO_LITE_JS);
-  source->AddResourcePath("bitmap.mojom-lite.js",
-                          IDR_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS);
-  source->AddResourcePath("image.mojom-lite.js",
-                          IDR_APP_MANAGEMENT_IMAGE_MOJO_LITE_JS);
-  source->AddResourcePath("image_info.mojom-lite.js",
-                          IDR_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS);
-
   source->AddResourcePath("actions.html", IDR_APP_MANAGEMENT_ACTIONS_HTML);
   source->AddResourcePath("actions.js", IDR_APP_MANAGEMENT_ACTIONS_JS);
   source->AddResourcePath("api_listener.html",
                           IDR_APP_MANAGEMENT_API_LISTENER_HTML);
   source->AddResourcePath("api_listener.js",
                           IDR_APP_MANAGEMENT_API_LISTENER_JS);
-  source->AddResourcePath("app.html", IDR_APP_MANAGEMENT_APP_HTML);
-  source->AddResourcePath("app.js", IDR_APP_MANAGEMENT_APP_JS);
   source->AddResourcePath("app_item.html", IDR_APP_MANAGEMENT_APP_ITEM_HTML);
   source->AddResourcePath("app_item.js", IDR_APP_MANAGEMENT_APP_ITEM_JS);
+  source->AddResourcePath("app_management.mojom-lite.js",
+                          IDR_APP_MANAGEMENT_MOJO_LITE_JS);
+  source->AddResourcePath("app.html", IDR_APP_MANAGEMENT_APP_HTML);
+  source->AddResourcePath("app.js", IDR_APP_MANAGEMENT_APP_JS);
   source->AddResourcePath("arc_permission_view.html",
                           IDR_APP_MANAGEMENT_ARC_PERMISSION_VIEW_HTML);
   source->AddResourcePath("arc_permission_view.js",
                           IDR_APP_MANAGEMENT_ARC_PERMISSION_VIEW_JS);
+  source->AddResourcePath("bitmap.mojom-lite.js",
+                          IDR_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS);
   source->AddResourcePath("browser_proxy.html",
                           IDR_APP_MANAGEMENT_BROWSER_PROXY_HTML);
   source->AddResourcePath("browser_proxy.js",
@@ -93,29 +88,30 @@
                           IDR_APP_MANAGEMENT_CHROME_APP_PERMISSION_VIEW_JS);
   source->AddResourcePath("constants.html", IDR_APP_MANAGEMENT_CONSTANTS_HTML);
   source->AddResourcePath("constants.js", IDR_APP_MANAGEMENT_CONSTANTS_JS);
+  source->AddResourcePath("dom_switch.html",
+                          IDR_APP_MANAGEMENT_DOM_SWITCH_HTML);
+  source->AddResourcePath("dom_switch.js", IDR_APP_MANAGEMENT_DOM_SWITCH_JS);
   source->AddResourcePath("expandable_app_list.html",
                           IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_HTML);
   source->AddResourcePath("expandable_app_list.js",
                           IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_JS);
-  source->AddResourcePath("dom_switch.html",
-                          IDR_APP_MANAGEMENT_DOM_SWITCH_HTML);
-  source->AddResourcePath("dom_switch.js", IDR_APP_MANAGEMENT_DOM_SWITCH_JS);
   source->AddResourcePath("fake_page_handler.js",
                           IDR_APP_MANAGEMENT_FAKE_PAGE_HANDLER_JS);
+  source->AddResourcePath("icons.html", IDR_APP_MANAGEMENT_ICONS_HTML);
+  source->AddResourcePath("image_info.mojom-lite.js",
+                          IDR_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS);
+  source->AddResourcePath("image.mojom-lite.js",
+                          IDR_APP_MANAGEMENT_IMAGE_MOJO_LITE_JS);
   source->AddResourcePath("main_view.html", IDR_APP_MANAGEMENT_MAIN_VIEW_HTML);
   source->AddResourcePath("main_view.js", IDR_APP_MANAGEMENT_MAIN_VIEW_JS);
-  source->AddResourcePath("notifications_view.html",
-                          IDR_APP_MANAGEMENT_NOTIFICATIONS_VIEW_HTML);
-  source->AddResourcePath("notifications_view.js",
-                          IDR_APP_MANAGEMENT_NOTIFICATIONS_VIEW_JS);
-  source->AddResourcePath("permission_view_header.html",
-                          IDR_APP_MANAGEMENT_PERMISSION_VIEW_HEADER_HTML);
-  source->AddResourcePath("permission_view_header.js",
-                          IDR_APP_MANAGEMENT_PERMISSION_VIEW_HEADER_JS);
   source->AddResourcePath("metadata_view.html",
                           IDR_APP_MANAGEMENT_METADATA_VIEW_HTML);
   source->AddResourcePath("metadata_view.js",
                           IDR_APP_MANAGEMENT_METADATA_VIEW_JS);
+  source->AddResourcePath("notifications_view.html",
+                          IDR_APP_MANAGEMENT_NOTIFICATIONS_VIEW_HTML);
+  source->AddResourcePath("notifications_view.js",
+                          IDR_APP_MANAGEMENT_NOTIFICATIONS_VIEW_JS);
   source->AddResourcePath("permission_item.html",
                           IDR_APP_MANAGEMENT_PERMISSION_ITEM_HTML);
   source->AddResourcePath("permission_item.js",
@@ -124,6 +120,10 @@
                           IDR_APP_MANAGEMENT_PERMISSION_TOGGLE_HTML);
   source->AddResourcePath("permission_toggle.js",
                           IDR_APP_MANAGEMENT_PERMISSION_TOGGLE_JS);
+  source->AddResourcePath("permission_view_header.html",
+                          IDR_APP_MANAGEMENT_PERMISSION_VIEW_HEADER_HTML);
+  source->AddResourcePath("permission_view_header.js",
+                          IDR_APP_MANAGEMENT_PERMISSION_VIEW_HEADER_JS);
   source->AddResourcePath("pwa_permission_view.html",
                           IDR_APP_MANAGEMENT_PWA_PERMISSION_VIEW_HTML);
   source->AddResourcePath("pwa_permission_view.js",
@@ -139,13 +139,15 @@
                           IDR_APP_MANAGEMENT_SHARED_STYLE_HTML);
   source->AddResourcePath("shared_vars.html",
                           IDR_APP_MANAGEMENT_SHARED_VARS_HTML);
-  source->AddResourcePath("store.html", IDR_APP_MANAGEMENT_STORE_HTML);
-  source->AddResourcePath("store.js", IDR_APP_MANAGEMENT_STORE_JS);
   source->AddResourcePath("store_client.html",
                           IDR_APP_MANAGEMENT_STORE_CLIENT_HTML);
   source->AddResourcePath("store_client.js",
                           IDR_APP_MANAGEMENT_STORE_CLIENT_JS);
+  source->AddResourcePath("store.html", IDR_APP_MANAGEMENT_STORE_HTML);
+  source->AddResourcePath("store.js", IDR_APP_MANAGEMENT_STORE_JS);
   source->AddResourcePath("types.js", IDR_APP_MANAGEMENT_TYPES_JS);
+  source->AddResourcePath("types.mojom-lite.js",
+                          IDR_APP_MANAGEMENT_TYPES_MOJO_LITE_JS);
   source->AddResourcePath("util.html", IDR_APP_MANAGEMENT_UTIL_HTML);
   source->AddResourcePath("util.js", IDR_APP_MANAGEMENT_UTIL_JS);
 
diff --git a/chrome/browser/ui/webui/browsing_history_handler.cc b/chrome/browser/ui/webui/browsing_history_handler.cc
index 29eda8f..4d88b65b 100644
--- a/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -35,6 +35,7 @@
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/favicon/core/fallback_url_util.h"
 #include "components/favicon/core/large_icon_service.h"
+#include "components/favicon_base/favicon_url_parser.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/prefs/pref_service.h"
 #include "components/query_parser/snippet.h"
@@ -244,8 +245,9 @@
       this, local_history, sync_service);
 
   // Create our favicon data source.
-  content::URLDataSource::Add(profile,
-                              std::make_unique<FaviconSource>(profile));
+  content::URLDataSource::Add(
+      profile, std::make_unique<FaviconSource>(
+                   profile, chrome::FaviconUrlFormat::kFavicon2));
 
   web_ui()->RegisterMessageCallback(
       "queryHistory",
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 b940a2e..51ac7aa 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -493,6 +493,13 @@
     }
     return &NewWebUI<chromeos::InSessionPasswordChangeUI>;
   }
+  if (url.host_piece() == chrome::kChromeUIConfirmPasswordChangeHost) {
+    if (!profile->GetPrefs()->GetBoolean(
+            prefs::kSamlInSessionPasswordChangeEnabled)) {
+      return nullptr;
+    }
+    return &NewWebUI<chromeos::InSessionConfirmPasswordChangeUI>;
+  }
   if (url.host_piece() == chrome::kChromeUIAccountManagerWelcomeHost)
     return &NewWebUI<chromeos::AccountManagerWelcomeUI>;
   if (url.host_piece() == chrome::kChromeUIAccountMigrationWelcomeHost)
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom
index 7d50324..c46353f 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom
@@ -12,12 +12,20 @@
 // Interface that supports integration between the AddSupervision WebUI and
 // ChromeOS.
 interface AddSupervisionHandler {
-  // Triggers a logout from the current login session.
-  LogOut() => ();
+  // Attempts to close the AddSupervision WebUI. Returns a bool indicating
+  // whether the WebUI was closed.
+  RequestClose() => (bool closed);
 
   // Returns an array of package names of installed ARC apps.
   GetInstalledArcApps() => (array<string> package_names);
 
   // Returns the oauth token to be passed to the server.
   GetOAuthToken() => (OAuthTokenFetchStatus status, string oauth_token);
+
+  // Triggers a logout from the current login session.
+  LogOut();
+
+  // Indicates to the system that Supervision has been enabled for the primary
+  // user.
+  NotifySupervisionEnabled();
 };
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
index 2bf9775a..e0d749d 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
@@ -13,6 +13,8 @@
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler_utils.h"
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
@@ -22,18 +24,23 @@
 #include "services/identity/public/cpp/access_token_fetcher.h"
 #include "services/identity/public/cpp/identity_manager.h"
 
+namespace chromeos {
+
 AddSupervisionHandler::AddSupervisionHandler(
     add_supervision::mojom::AddSupervisionHandlerRequest request,
-    content::WebUI* web_ui)
+    content::WebUI* web_ui,
+    Delegate* delegate)
     : binding_(this, std::move(request)),
       web_ui_(web_ui),
       identity_manager_(
-          IdentityManagerFactory::GetForProfile(Profile::FromWebUI(web_ui))) {}
+          IdentityManagerFactory::GetForProfile(Profile::FromWebUI(web_ui))),
+      delegate_(delegate) {}
 
 AddSupervisionHandler::~AddSupervisionHandler() = default;
 
-void AddSupervisionHandler::LogOut(LogOutCallback callback) {
-  chrome::AttemptUserExit();
+void AddSupervisionHandler::RequestClose(RequestCloseCallback callback) {
+  bool dialog_closed = delegate_->CloseDialog();
+  std::move(callback).Run(dialog_closed);
 }
 
 void AddSupervisionHandler::GetInstalledArcApps(
@@ -74,6 +81,17 @@
           identity::AccessTokenFetcher::Mode::kImmediate);
 }
 
+void AddSupervisionHandler::LogOut() {
+  chrome::AttemptUserExit();
+}
+
+void AddSupervisionHandler::NotifySupervisionEnabled() {
+  SupervisedUserService* service =
+      SupervisedUserServiceFactory::GetForProfile(Profile::FromWebUI(web_ui_));
+
+  service->set_signout_required_after_supervision_enabled();
+}
+
 void AddSupervisionHandler::OnAccessTokenFetchComplete(
     GetOAuthTokenCallback callback,
     GoogleServiceAuthError error,
@@ -91,3 +109,5 @@
                             access_token_info.token);
   }
 }
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
index d07c2886..cd7c3dec 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
@@ -23,18 +23,33 @@
 
 class GoogleServiceAuthError;
 
+namespace chromeos {
+
 class AddSupervisionHandler
     : public add_supervision::mojom::AddSupervisionHandler {
  public:
+  // Interface for Delegates for specific behavior of AddSupervisionHandler.
+  class Delegate {
+   public:
+    // Implementing methods should override this to implement
+    // the request to close the Add Supervision dialog and return
+    // a boolean to indicate whether the dialog is closing.
+    virtual bool CloseDialog() = 0;
+  };
+
+  // |delegate| is owned by the caller and its lifetime must outlive |this|.
   AddSupervisionHandler(
       add_supervision::mojom::AddSupervisionHandlerRequest request,
-      content::WebUI* web_ui);
+      content::WebUI* web_ui,
+      Delegate* delegate);
   ~AddSupervisionHandler() override;
 
   // add_supervision::mojom::AddSupervisionHandler overrides:
-  void LogOut(LogOutCallback callback) override;
+  void RequestClose(RequestCloseCallback callback) override;
   void GetInstalledArcApps(GetInstalledArcAppsCallback callback) override;
   void GetOAuthToken(GetOAuthTokenCallback callback) override;
+  void LogOut() override;
+  void NotifySupervisionEnabled() override;
 
  private:
   void OnAccessTokenFetchComplete(GetOAuthTokenCallback callback,
@@ -50,9 +65,13 @@
   identity::IdentityManager* identity_manager_;
   std::unique_ptr<identity::AccessTokenFetcher> oauth2_access_token_fetcher_;
 
+  Delegate* delegate_;
+
   base::WeakPtrFactory<AddSupervisionHandler> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(AddSupervisionHandler);
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_ADD_SUPERVISION_ADD_SUPERVISION_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
index 5d25b7f2..7f40db1 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
@@ -12,8 +12,10 @@
 #include "base/system/sys_info.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/ui/views/chrome_web_dialog_view.h"
+#include "chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
@@ -22,10 +24,13 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/resources/grit/ui_resources.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+
+namespace chromeos {
 
 namespace {
 
-constexpr int kDialogHeightPx = 640;
+constexpr int kDialogHeightPx = 608;
 constexpr int kDialogWidthPx = 768;
 // Id of System Dialog used to show the Add Supervision flow.
 std::string& GetDialogId() {
@@ -33,19 +38,31 @@
   return *dialog_id;
 }
 
-}  // namespace
-
-namespace chromeos {
+// Shows the dialog indicating that user has to sign out if supervision has been
+// enabled for their account.  Returns a boolean indicating whether the
+// ConfirmSignoutDialog is being shown.
+bool MaybeShowConfirmSignoutDialog() {
+  SupervisedUserService* service = SupervisedUserServiceFactory::GetForProfile(
+      ProfileManager::GetPrimaryUserProfile());
+  if (service->signout_required_after_supervision_enabled()) {
+    ConfirmSignoutDialog::Show();
+    return true;
+  }
+  return false;
+}
 
 const char kAddSupervisionURL[] =
     "https://families.google.com/supervision/setup";
 const char kAddSupervisionEventOriginFilter[] = "https://families.google.com";
 const char kAddSupervisionFlowType[] = "1";
 
+}  // namespace
+
 // AddSupervisionDialog implementations.
 
 // static
-void AddSupervisionDialog::Show() {
+void AddSupervisionDialog::Show(gfx::NativeView parent) {
+  // Get the system singleton instance of the AddSupervisionDialog.
   SystemWebDialogDelegate* current_instance = GetInstance();
   if (current_instance) {
     // Focus the dialog if it is already there.  Currently, this is
@@ -61,43 +78,48 @@
 
   GetDialogId() = current_instance->Id();
 
-  // TODO(danan): This should pass ash_util::GetFramelessInitParams() for
-  // extra_params. Currently however, that prevents the dialog from presenting
-  // in full screen if tablet mode is enabled. See https://crbug.com/888629.
-  chrome::ShowWebDialog(nullptr /* no parent */,
-                        ProfileManager::GetPrimaryUserProfile(),
-                        current_instance);
+  current_instance->ShowSystemDialogForBrowserContext(
+      ProfileManager::GetPrimaryUserProfile(), parent);
 }
 
-void AddSupervisionDialog::AddOnCloseCallback(base::OnceClosure callback) {
-  on_close_callbacks_.push_back(std::move(callback));
+// static
+void AddSupervisionDialog::Close() {
+  SystemWebDialogDelegate* current_instance = GetInstance();
+  if (current_instance) {
+    current_instance->Close();
+    GetDialogId() = std::string();
+  }
 }
 
 ui::ModalType AddSupervisionDialog::GetDialogModalType() const {
-  return ui::ModalType::MODAL_TYPE_SYSTEM;
+  return ui::ModalType::MODAL_TYPE_WINDOW;
 }
 
 void AddSupervisionDialog::GetDialogSize(gfx::Size* size) const {
   size->SetSize(kDialogWidthPx, kDialogHeightPx);
 }
 
-void AddSupervisionDialog::OnDialogClosed(const std::string& json_retval) {
-  DCHECK(this == GetInstance());
-  GetDialogId() = "";
+bool AddSupervisionDialog::OnDialogCloseRequested() {
+  bool showing_confirm_dialog = MaybeShowConfirmSignoutDialog();
+  return !showing_confirm_dialog;
+}
 
-  // Note: The call below deletes |this|, so there is no further need to keep
-  // track of the pointer.
-  SystemWebDialogDelegate::OnDialogClosed(json_retval);
+void AddSupervisionDialog::OnCloseContents(content::WebContents* source,
+                                           bool* out_close_dialog) {
+  // This code gets called by a different path that OnDialogCloseRequested(),
+  // and actually masks the call to OnDialogCloseRequested() the first time the
+  // user clicks on the [x].  Because the first [x] click comes here, we need to
+  // show the confirmation dialog here and signal the caller to possibly close
+  // the dialog.  Subsequent clicks on [x] during the lifetime of the dialog
+  // will result in calls to OnDialogCloseRequested().
+  *out_close_dialog = OnDialogCloseRequested();
 }
 
 AddSupervisionDialog::AddSupervisionDialog()
     : SystemWebDialogDelegate(GURL(chrome::kChromeUIAddSupervisionURL),
                               base::string16()) {}
 
-AddSupervisionDialog::~AddSupervisionDialog() {
-  for (auto& callback : on_close_callbacks_)
-    std::move(callback).Run();
-}
+AddSupervisionDialog::~AddSupervisionDialog() = default;
 
 // static
 SystemWebDialogDelegate* AddSupervisionDialog::GetInstance() {
@@ -107,7 +129,7 @@
 // AddSupervisionUI implementations.
 
 AddSupervisionUI::AddSupervisionUI(content::WebUI* web_ui)
-    : ui::MojoWebDialogUI(web_ui) {
+    : ui::MojoWebUIController(web_ui) {
   // Register the Mojo API handler.
   AddHandlerToRegistry(base::BindRepeating(
       &AddSupervisionUI::BindAddSupervisionHandler, base::Unretained(this)));
@@ -126,7 +148,16 @@
   source->AddResourcePath("add_supervision_api_server.js",
                           IDR_ADD_SUPERVISION_API_SERVER_JS);
   source->AddResourcePath("add_supervision.js", IDR_ADD_SUPERVISION_JS);
+  source->AddResourcePath("images/network_unavailable.svg",
+                          IDR_ADD_SUPERVISION_NETWORK_UNAVAILABLE_SVG);
+
   source->AddLocalizedString("pageTitle", IDS_ADD_SUPERVISION_PAGE_TITLE);
+  source->AddLocalizedString("networkDownHeading",
+                             IDS_ADD_SUPERVISION_NETWORK_DOWN_HEADING);
+  source->AddLocalizedString("networkDownDescription",
+                             IDS_ADD_SUPERVISION_NETWORK_DOWN_DESCRIPTION);
+  source->AddLocalizedString("networkDownButtonLabel",
+                             IDS_ADD_SUPERVISION_NETWORK_DOWN_BUTTON_LABEL);
 
   // Full paths (relative to src) are important for Mojom generated files.
   source->AddResourcePath(
@@ -146,10 +177,19 @@
 
 AddSupervisionUI::~AddSupervisionUI() = default;
 
+bool AddSupervisionUI::CloseDialog() {
+  bool showing_confirm_dialog = MaybeShowConfirmSignoutDialog();
+  if (!showing_confirm_dialog) {
+    // We aren't showing the confirm dialog, so close the AddSupervisionDialog.
+    AddSupervisionDialog::Close();
+  }
+  return !showing_confirm_dialog;
+}
+
 void AddSupervisionUI::BindAddSupervisionHandler(
     add_supervision::mojom::AddSupervisionHandlerRequest request) {
-  mojo_api_handler_.reset(
-      new AddSupervisionHandler(std::move(request), web_ui()));
+  mojo_api_handler_ = std::make_unique<AddSupervisionHandler>(
+      std::move(request), web_ui(), this);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h
index f6cb49f1..dfb7aab2 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h
@@ -7,30 +7,35 @@
 
 #include <memory>
 
-#include "base/callback.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h"
+#include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h"
 #include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
 #include "ui/base/ui_base_types.h"
-#include "ui/web_dialogs/web_dialog_ui.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/views/controls/label.h"
+#include "ui/webui/mojo_web_ui_controller.h"
 
 namespace chromeos {
 
-// Dialog which displays the add-supeivision flow which allows users to
+// Dialog which displays the add-supervision flow which allows users to
 // convert a regular Google account into a Family-Link managed account.
 class AddSupervisionDialog : public SystemWebDialogDelegate {
  public:
   // Shows the dialog; if the dialog is already displayed, this function is a
   // no-op.
-  static void Show();
+  static void Show(gfx::NativeView parent);
 
-  // Registers a callback which will be called when the dialog is closed.
-  void AddOnCloseCallback(base::OnceClosure callback);
+  // Closes the dialog, if the dialog doesn't exist, this function is a
+  // no-op.
+  static void Close();
 
   // ui::WebDialogDelegate:
   ui::ModalType GetDialogModalType() const override;
   void GetDialogSize(gfx::Size* size) const override;
-  void OnDialogClosed(const std::string& json_retval) override;
+  bool OnDialogCloseRequested() override;
+  void OnCloseContents(content::WebContents* source,
+                       bool* out_close_dialog) override;
 
  protected:
   AddSupervisionDialog();
@@ -39,19 +44,19 @@
  private:
   static SystemWebDialogDelegate* GetInstance();
 
-  // List of callbacks that have registered themselves to be invoked once this
-  // dialog is closed.
-  std::vector<base::OnceClosure> on_close_callbacks_;
-
   DISALLOW_COPY_AND_ASSIGN(AddSupervisionDialog);
 };
 
 // Controller for chrome://add-supervision
-class AddSupervisionUI : public ui::MojoWebDialogUI {
+class AddSupervisionUI : public ui::MojoWebUIController,
+                         public AddSupervisionHandler::Delegate {
  public:
   explicit AddSupervisionUI(content::WebUI* web_ui);
   ~AddSupervisionUI() override;
 
+  // AddSupervisionHandler::Delegate:
+  bool CloseDialog() override;
+
  private:
   void BindAddSupervisionHandler(
       add_supervision::mojom::AddSupervisionHandlerRequest request);
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.cc b/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.cc
new file mode 100644
index 0000000..9ee08568
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.cc
@@ -0,0 +1,90 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h"
+
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/user_manager/user_manager.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/resources/grit/ui_resources.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace chromeos {
+
+namespace {
+// The width of the text body in the dialog.
+const int kDialogBodyTextWidth = 250;
+}  // namespace
+
+ConfirmSignoutDialog::ConfirmSignoutDialog() {
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+  SetBorder(views::CreateEmptyBorder(
+      views::LayoutProvider::Get()->GetDialogInsetsForContentType(
+          views::DialogContentType::TEXT, views::DialogContentType::TEXT)));
+
+  base::string16 given_name =
+      user_manager::UserManager::Get()->GetPrimaryUser()->GetGivenName();
+
+  // |body_| will be owned by the views system.
+  views::Label* body = new views::Label;
+  body->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  body->SetMultiLine(true);
+  body->SetText(l10n_util::GetStringFUTF16(IDS_ADD_SUPERVISION_EXIT_DIALOG_BODY,
+                                           given_name));
+  body->SizeToFit(kDialogBodyTextWidth);
+  AddChildView(body);
+}
+
+ConfirmSignoutDialog::~ConfirmSignoutDialog() = default;
+
+ui::ModalType ConfirmSignoutDialog::GetModalType() const {
+  return ui::ModalType::MODAL_TYPE_SYSTEM;
+}
+
+base::string16 ConfirmSignoutDialog::GetWindowTitle() const {
+  return l10n_util::GetStringUTF16(IDS_ADD_SUPERVISION_EXIT_DIALOG_TITLE);
+}
+
+bool ConfirmSignoutDialog::Accept() {
+  chrome::AttemptUserExit();
+  return true;
+}
+
+int ConfirmSignoutDialog::GetDialogButtons() const {
+  return ui::DialogButton::DIALOG_BUTTON_OK |
+         ui::DialogButton::DIALOG_BUTTON_CANCEL;
+}
+
+base::string16 ConfirmSignoutDialog::GetDialogButtonLabel(
+    ui::DialogButton button) const {
+  if (button == ui::DialogButton::DIALOG_BUTTON_OK) {
+    return l10n_util::GetStringUTF16(
+        IDS_ADD_SUPERVISION_EXIT_DIALOG_SIGNOUT_BUTTON_LABEL);
+  }
+  return l10n_util::GetStringUTF16(
+      IDS_ADD_SUPERVISION_EXIT_DIALOG_CANCEL_BUTTON_LABEL);
+}
+
+// static
+void ConfirmSignoutDialog::Show() {
+  // Ownership of the ConfirmSignoutDialog is passed to the views system.
+  // Dialog is system-modal, so no parent window is needed.
+  constrained_window::CreateBrowserModalDialogViews(new ConfirmSignoutDialog(),
+                                                    nullptr /* parent window */)
+      ->Show();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h b/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h
new file mode 100644
index 0000000..6e7bff6
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_ADD_SUPERVISION_CONFIRM_SIGNOUT_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_ADD_SUPERVISION_CONFIRM_SIGNOUT_DIALOG_H_
+
+#include "base/macros.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace chromeos {
+
+// Dialog shown when the user tries to close the flow when account has already
+// become supervised, and the user only has the choice of finishing the flow, or
+// signing out.
+class ConfirmSignoutDialog : public views::DialogDelegateView {
+ public:
+  ~ConfirmSignoutDialog() override;
+
+  // views::WidgetDelegate:
+  ui::ModalType GetModalType() const override;
+  base::string16 GetWindowTitle() const override;
+
+  // views::DialogDelegate:
+  bool Accept() override;
+  int GetDialogButtons() const override;
+  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
+
+  static void Show();
+
+ private:
+  ConfirmSignoutDialog();
+
+  DISALLOW_COPY_AND_ASSIGN(ConfirmSignoutDialog);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_ADD_SUPERVISION_CONFIRM_SIGNOUT_DIALOG_H_
diff --git a/chrome/browser/ui/webui/chromeos/insession_password_change_ui.cc b/chrome/browser/ui/webui/chromeos/insession_password_change_ui.cc
index 76cc7570..dc8188d 100644
--- a/chrome/browser/ui/webui/chromeos/insession_password_change_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/insession_password_change_ui.cc
@@ -12,12 +12,14 @@
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chromeos/insession_password_change_handler_chromeos.h"
+#include "chrome/browser/ui/webui/localized_string.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/login/auth/saml_password_attributes.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -132,4 +134,43 @@
 
 InSessionPasswordChangeUI::~InSessionPasswordChangeUI() = default;
 
+InSessionConfirmPasswordChangeUI::InSessionConfirmPasswordChangeUI(
+    content::WebUI* web_ui)
+    : ui::WebDialogUI(web_ui) {
+  Profile* profile = Profile::FromWebUI(web_ui);
+  CHECK(profile->GetPrefs()->GetBoolean(
+      prefs::kSamlInSessionPasswordChangeEnabled));
+  content::WebUIDataSource* source = content::WebUIDataSource::Create(
+      chrome::kChromeUIConfirmPasswordChangeHost);
+
+  static constexpr LocalizedString kLocalizedStrings[] = {
+      {"title", IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_TITLE},
+      {"bothPasswordsPrompt",
+       IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_BOTH_PASSWORDS_PROMPT},
+      {"oldPasswordPrompt",
+       IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_OLD_PASSWORD_PROMPT},
+      {"newPasswordPrompt",
+       IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_NEW_PASSWORD_PROMPT},
+      {"oldPassword", IDS_PASSWORD_CHANGE_OLD_PASSWORD_LABEL},
+      {"newPassword", IDS_PASSWORD_CHANGE_NEW_PASSWORD_LABEL},
+      {"confirmNewPassword", IDS_PASSWORD_CHANGE_CONFIRM_NEW_PASSWORD_LABEL},
+      {"matchError", IDS_PASSWORD_CHANGE_PASSWORDS_DONT_MATCH},
+      {"save", IDS_PASSWORD_CHANGE_CONFIRM_SAVE_BUTTON}};
+
+  AddLocalizedStringsBulk(source, kLocalizedStrings,
+                          base::size(kLocalizedStrings));
+
+  source->SetJsonPath("strings.js");
+  source->SetDefaultResource(IDR_CONFIRM_PASSWORD_CHANGE_HTML);
+  source->AddResourcePath("confirm_password_change.js",
+                          IDR_CONFIRM_PASSWORD_CHANGE_JS);
+
+  // TODO(https://crbug.com/930109): Add JS and message handler to handle tap
+  // on the save button, errors, etc.
+
+  content::WebUIDataSource::Add(profile, source);
+}
+
+InSessionConfirmPasswordChangeUI::~InSessionConfirmPasswordChangeUI() = default;
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/insession_password_change_ui.h b/chrome/browser/ui/webui/chromeos/insession_password_change_ui.h
index 32007ce..1802d3ec 100644
--- a/chrome/browser/ui/webui/chromeos/insession_password_change_ui.h
+++ b/chrome/browser/ui/webui/chromeos/insession_password_change_ui.h
@@ -39,6 +39,16 @@
   DISALLOW_COPY_AND_ASSIGN(InSessionPasswordChangeUI);
 };
 
+// For chrome:://confirm-password-change
+class InSessionConfirmPasswordChangeUI : public ui::WebDialogUI {
+ public:
+  explicit InSessionConfirmPasswordChangeUI(content::WebUI* web_ui);
+  ~InSessionConfirmPasswordChangeUI() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InSessionConfirmPasswordChangeUI);
+};
+
 }  // namespace chromeos
 
 #endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_INSESSION_PASSWORD_CHANGE_UI_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index cade0803..67ce027 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -16,6 +16,7 @@
 #include "base/guid.h"
 #include "base/json/json_reader.h"
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
@@ -57,6 +58,7 @@
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/constants/devicetype.h"
 #include "chromeos/dbus/util/version_loader.h"
+#include "chromeos/login/auth/challenge_response/cert_utils.h"
 #include "chromeos/login/auth/saml_password_attributes.h"
 #include "chromeos/login/auth/user_context.h"
 #include "chromeos/settings/cros_settings_names.h"
@@ -73,6 +75,7 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "net/cert/x509_certificate.h"
 #include "services/network/nss_temp_certs_cache_chromeos.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
@@ -638,6 +641,10 @@
 void GaiaScreenHandler::HandleAuthExtensionLoaded() {
   VLOG(1) << "Auth extension finished loading";
   auth_extension_being_loaded_ = false;
+  // Recreate the client cert usage observer, in order to track only the certs
+  // used during the current sign-in attempt.
+  extension_provided_client_cert_usage_observer_ =
+      std::make_unique<LoginClientCertUsageObserver>();
 }
 
 void GaiaScreenHandler::HandleWebviewLoadAborted(
@@ -817,23 +824,18 @@
   const std::string sanitized_email = gaia::SanitizeEmail(email);
   LoginDisplayHost::default_host()->SetDisplayEmail(sanitized_email);
 
-  const user_manager::UserType user_type =
-      GetUsertypeFromServicesString(services);
-
-  UserContext user_context(user_type,
-                           GetAccountId(email, gaia_id, AccountType::GOOGLE));
-  user_context.SetKey(Key(password));
-  user_context.SetPasswordKey(Key(password));
-  user_context.SetAuthCode(auth_code);
-  user_context.SetAuthFlow(using_saml
-                               ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
-                               : UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
-  if (using_saml)
-    user_context.SetIsUsingSamlPrincipalsApi(using_saml_api_);
-  user_context.SetGAPSCookie(gaps_cookie);
-  if (using_saml && ExtractSamlPasswordAttributesEnabled()) {
-    user_context.SetSamlPasswordAttributes(password_attributes);
+  UserContext user_context;
+  std::string error_message;
+  if (!BuildUserContextForGaiaSignIn(
+          GetUsertypeFromServicesString(services),
+          GetAccountId(email, gaia_id, AccountType::GOOGLE), using_saml,
+          password, auth_code, gaps_cookie, password_attributes, &user_context,
+          &error_message)) {
+    core_oobe_view_->ShowSignInError(0, error_message, std::string(),
+                                     HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
+    return;
   }
+
   LoginDisplayHost::default_host()->CompleteLogin(user_context);
 }
 
@@ -920,11 +922,11 @@
     const std::string& typed_email,
     const std::string& gaia_id) {
   AllowJavascript();
-  // TODO(emaxx,https://crbug.com/826417): Determine the result value based on
-  // known_user properties if the user already existed, or the
-  // DeviceSamlLoginAuthenticationType policy if that's a new user.
+  const bool is_saml_user_passwordless =
+      extension_provided_client_cert_usage_observer_ &&
+      extension_provided_client_cert_usage_observer_->ClientCertsWereUsed();
   ResolveJavascriptCallback(base::Value(callback_id),
-                            base::Value(false) /* isSamlUserPasswordless */);
+                            base::Value(is_saml_user_passwordless));
 }
 
 void GaiaScreenHandler::HandleUpdateSigninUIState(int state) {
@@ -962,19 +964,17 @@
   const user_manager::User* const user =
       user_manager::UserManager::Get()->FindUser(account_id);
 
-  UserContext user_context =
-      user ? UserContext(*user)
-           : UserContext(CalculateUserType(account_id), account_id);
-  user_context.SetKey(Key(password));
-  user_context.SetPasswordKey(Key(password));
-  user_context.SetAuthFlow(using_saml
-                               ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
-                               : UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
-  if (using_saml)
-    user_context.SetIsUsingSamlPrincipalsApi(using_saml_api_);
-
-  if (using_saml && ExtractSamlPasswordAttributesEnabled()) {
-    user_context.SetSamlPasswordAttributes(password_attributes);
+  UserContext user_context;
+  std::string error_message;
+  if (!BuildUserContextForGaiaSignIn(
+          user ? user->GetType() : CalculateUserType(account_id),
+          GetAccountId(typed_email, gaia_id, AccountType::GOOGLE), using_saml,
+          password, std::string() /* auth_code */,
+          std::string() /* gaps_cookie */, password_attributes, &user_context,
+          &error_message)) {
+    core_oobe_view_->ShowSignInError(0, error_message, std::string(),
+                                     HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
+    return;
   }
 
   LoginDisplayHost::default_host()->CompleteLogin(user_context);
@@ -1276,4 +1276,52 @@
          !IsOnline(captive_portal_status_);
 }
 
+bool GaiaScreenHandler::BuildUserContextForGaiaSignIn(
+    user_manager::UserType user_type,
+    const AccountId& account_id,
+    bool using_saml,
+    const std::string& password,
+    const std::string& auth_code,
+    const std::string& gaps_cookie,
+    const SamlPasswordAttributes& password_attributes,
+    UserContext* user_context,
+    std::string* error_message) {
+  // TODO(emaxx,https://crbug.com/826417): Localize the error messages.
+  *user_context = UserContext(user_type, account_id);
+  if (using_saml && extension_provided_client_cert_usage_observer_ &&
+      extension_provided_client_cert_usage_observer_->ClientCertsWereUsed()) {
+    scoped_refptr<net::X509Certificate> saml_client_cert;
+    std::vector<ChallengeResponseKey::SignatureAlgorithm> signature_algorithms;
+    if (!extension_provided_client_cert_usage_observer_->GetOnlyUsedClientCert(
+            &saml_client_cert, &signature_algorithms)) {
+      *error_message = "Multiple client certificates are not supported";
+      return false;
+    }
+    ChallengeResponseKey challenge_response_key;
+    if (!ExtractChallengeResponseKeyFromCert(
+            *saml_client_cert, signature_algorithms, &challenge_response_key)) {
+      *error_message = "Internal error";
+      return false;
+    }
+    user_context->GetMutableChallengeResponseKeys()->push_back(
+        challenge_response_key);
+  } else {
+    user_context->SetKey(Key(password));
+    user_context->SetPasswordKey(Key(password));
+  }
+  if (!auth_code.empty())
+    user_context->SetAuthCode(auth_code);
+  user_context->SetAuthFlow(using_saml
+                                ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
+                                : UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
+  if (using_saml)
+    user_context->SetIsUsingSamlPrincipalsApi(using_saml_api_);
+  if (!gaps_cookie.empty())
+    user_context->SetGAPSCookie(gaps_cookie);
+  if (using_saml && ExtractSamlPasswordAttributesEnabled()) {
+    user_context->SetSamlPasswordAttributes(password_attributes);
+  }
+  return true;
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index 27a2759..1b1dd70 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -12,10 +12,12 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/chromeos/authpolicy/authpolicy_helper.h"
+#include "chrome/browser/chromeos/login/login_client_cert_usage_observer.h"
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
 #include "chromeos/network/portal_detector/network_portal_detector.h"
+#include "components/user_manager/user_type.h"
 #include "net/base/net_errors.h"
 
 class AccountId;
@@ -38,6 +40,7 @@
 class Key;
 class SamlPasswordAttributes;
 class SigninScreenHandler;
+class UserContext;
 
 class GaiaView {
  public:
@@ -293,6 +296,19 @@
   // Records whether WebUI is currently in offline mode.
   void SetOfflineLoginIsActive(bool is_active);
 
+  // Builds the UserContext with the information from the given Gaia user
+  // sign-in. On failure, returns false and sets |error_message|.
+  bool BuildUserContextForGaiaSignIn(
+      user_manager::UserType user_type,
+      const AccountId& account_id,
+      bool using_saml,
+      const std::string& password,
+      const std::string& auth_code,
+      const std::string& gaps_cookie,
+      const SamlPasswordAttributes& password_attributes,
+      UserContext* user_context,
+      std::string* error_message);
+
   // Current state of Gaia frame.
   FrameState frame_state_ = FRAME_STATE_UNKNOWN;
 
@@ -378,6 +394,9 @@
   // The type of Gaia page to show.
   GaiaScreenMode screen_mode_ = GAIA_SCREEN_MODE_DEFAULT;
 
+  std::unique_ptr<LoginClientCertUsageObserver>
+      extension_provided_client_cert_usage_observer_;
+
   base::WeakPtrFactory<GaiaScreenHandler> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GaiaScreenHandler);
diff --git a/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc b/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
index f0dcc383..34e2447 100644
--- a/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
@@ -4,17 +4,21 @@
 
 #include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h"
 
+#include "base/feature_list.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/chromeos/net/shill_error.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/localized_string.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/network_connection_handler.h"
 #include "components/login/localized_values_builder.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace chromeos {
 namespace network_element {
@@ -255,6 +259,7 @@
        IDS_SETTINGS_INTERNET_NETWORK_NAMESERVERS_GOOGLE},
       {"networkProxyWpad", IDS_SETTINGS_INTERNET_NETWORK_PROXY_WPAD},
       {"networkProxyWpadNone", IDS_SETTINGS_INTERNET_NETWORK_PROXY_WPAD_NONE},
+      {"remove", IDS_REMOVE},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings,
                           base::size(kLocalizedStrings));
@@ -274,12 +279,18 @@
       {"networkConfigSaveCredentials",
        IDS_SETTINGS_INTERNET_CONFIG_SAVE_CREDENTIALS},
       {"networkConfigShare", IDS_SETTINGS_INTERNET_CONFIG_SHARE},
+      {"networkAutoConnect", IDS_SETTINGS_INTERNET_NETWORK_AUTO_CONNECT},
+      {"hiddenNetworkWarning", IDS_SETTINGS_HIDDEN_NETWORK_WARNING},
       {"hidePassword", IDS_SETTINGS_PASSWORD_HIDE},
       {"showPassword", IDS_SETTINGS_PASSWORD_SHOW},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings,
                           base::size(kLocalizedStrings));
 
+  html_source->AddBoolean(
+      "showHiddenNetworkWarning",
+      base::FeatureList::IsEnabled(features::kHiddenNetworkWarning));
+
   // Login screen and public account users can only create shared network
   // configurations. Other users default to unshared network configurations.
   // NOTE: Guest and kiosk users can only create unshared network configs.
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
index 8e4841b0..0d2c11b 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
@@ -91,6 +91,11 @@
     dialog_window()->Focus();
 }
 
+void SystemWebDialogDelegate::Close() {
+  DCHECK(dialog_window());
+  views::Widget::GetWidgetForNativeWindow(dialog_window())->Close();
+}
+
 ui::ModalType SystemWebDialogDelegate::GetDialogModalType() const {
   return modal_type_;
 }
@@ -149,9 +154,9 @@
   return !title_.empty();
 }
 
-void SystemWebDialogDelegate::ShowSystemDialog(gfx::NativeWindow parent) {
-  content::BrowserContext* browser_context =
-      ProfileManager::GetActiveUserProfile();
+void SystemWebDialogDelegate::ShowSystemDialogForBrowserContext(
+    content::BrowserContext* browser_context,
+    gfx::NativeWindow parent) {
   views::Widget::InitParams extra_params;
   // If unparented and not modal, keep it on top (see header comment).
   if (!parent && GetDialogModalType() == ui::MODAL_TYPE_NONE)
@@ -161,4 +166,8 @@
                                                    this, &extra_params);
 }
 
+void SystemWebDialogDelegate::ShowSystemDialog(gfx::NativeWindow parent) {
+  ShowSystemDialogForBrowserContext(ProfileManager::GetActiveUserProfile(),
+                                    parent);
+}
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h
index f18c91c..e9b0e2f 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h
@@ -49,6 +49,9 @@
   // implementation for details.
   void Focus();
 
+  // Closes the dialog window.
+  void Close();
+
   // ui::WebDialogDelegate
   ui::ModalType GetDialogModalType() const override;
   base::string16 GetDialogTitle() const override;
@@ -66,10 +69,14 @@
                        bool* out_close_dialog) override;
   bool ShouldShowDialogTitle() const override;
 
-  // Shows a system dialog using the current ative profile.
+  // Shows a system dialog using the specified BrowserContext (or Profile).
   // If |parent| is not null, the dialog will be parented to |parent|.
   // Otherwise it will be attached to either the AlwaysOnTop container or the
   // LockSystemModal container, depending on the session state at creation.
+  void ShowSystemDialogForBrowserContext(content::BrowserContext* context,
+                                         gfx::NativeWindow parent = nullptr);
+  // Same as previous but shows a system dialog using the current active
+  // profile.
   void ShowSystemDialog(gfx::NativeWindow parent = nullptr);
 
   content::WebUI* GetWebUIForTest() { return webui_; }
diff --git a/chrome/browser/ui/webui/device_log_ui.cc b/chrome/browser/ui/webui/device_log_ui.cc
index b87b5d35..1eb8545 100644
--- a/chrome/browser/ui/webui/device_log_ui.cc
+++ b/chrome/browser/ui/webui/device_log_ui.cc
@@ -36,6 +36,10 @@
         "DeviceLog.getLog",
         base::BindRepeating(&DeviceLogMessageHandler::GetLog,
                             base::Unretained(this)));
+    web_ui()->RegisterMessageCallback(
+        "DeviceLog.clearLog",
+        base::BindRepeating(&DeviceLogMessageHandler::ClearLog,
+                            base::Unretained(this)));
   }
 
  private:
@@ -46,6 +50,10 @@
     web_ui()->CallJavascriptFunctionUnsafe("DeviceLogUI.getLogCallback", data);
   }
 
+  void ClearLog(const base::ListValue* value) const {
+    device_event_log::ClearAll();
+  }
+
   DISALLOW_COPY_AND_ASSIGN(DeviceLogMessageHandler);
 };
 
@@ -62,6 +70,8 @@
       {"titleText", IDS_DEVICE_LOG_TITLE},
       {"autoRefreshText", IDS_DEVICE_AUTO_REFRESH},
       {"logRefreshText", IDS_DEVICE_LOG_REFRESH},
+      {"logClearText", IDS_DEVICE_LOG_CLEAR},
+      {"logNoEntriesText", IDS_DEVICE_LOG_NO_ENTRIES},
       {"logLevelShowText", IDS_DEVICE_LOG_LEVEL_SHOW},
       {"logLevelErrorText", IDS_DEVICE_LOG_LEVEL_ERROR},
       {"logLevelUserText", IDS_DEVICE_LOG_LEVEL_USER},
diff --git a/chrome/browser/ui/webui/favicon_source.cc b/chrome/browser/ui/webui/favicon_source.cc
index 46aa1a7..0153c7e 100644
--- a/chrome/browser/ui/webui/favicon_source.cc
+++ b/chrome/browser/ui/webui/favicon_source.cc
@@ -62,14 +62,22 @@
 FaviconSource::IconRequest::~IconRequest() {
 }
 
-FaviconSource::FaviconSource(Profile* profile)
-    : profile_(profile->GetOriginalProfile()) {}
+FaviconSource::FaviconSource(Profile* profile,
+                             chrome::FaviconUrlFormat url_format)
+    : profile_(profile->GetOriginalProfile()), url_format_(url_format) {}
 
 FaviconSource::~FaviconSource() {
 }
 
 std::string FaviconSource::GetSource() {
-  return chrome::kChromeUIFaviconHost;
+  switch (url_format_) {
+    case chrome::FaviconUrlFormat::kFaviconLegacy:
+      return chrome::kChromeUIFaviconHost;
+    case chrome::FaviconUrlFormat::kFavicon2:
+      return chrome::kChromeUIFavicon2Host;
+  }
+  NOTREACHED();
+  return "";
 }
 
 void FaviconSource::StartDataRequest(
@@ -85,7 +93,7 @@
   }
 
   chrome::ParsedFaviconPath parsed;
-  bool success = chrome::ParseFaviconPath(path, &parsed);
+  bool success = chrome::ParseFaviconPath(path, url_format_, &parsed);
   if (!success) {
     SendDefaultResponse(callback);
     return;
diff --git a/chrome/browser/ui/webui/favicon_source.h b/chrome/browser/ui/webui/favicon_source.h
index af25ade3..262c780 100644
--- a/chrome/browser/ui/webui/favicon_source.h
+++ b/chrome/browser/ui/webui/favicon_source.h
@@ -21,41 +21,23 @@
 class RefCountedMemory;
 }
 
+namespace chrome {
+enum class FaviconUrlFormat;
+}
+
 namespace ui {
 class NativeTheme;
 }
 
 // FaviconSource is the gateway between network-level chrome:
 // requests for favicons and the history backend that serves these.
-//
-// Format:
-//   chrome://favicon/size&scalefactor/iconurl/url
-// Some parameters are optional as described below. However, the order of the
-// parameters is not interchangeable.
-//
-// Parameter:
-//  'url'               Required
-//    Specifies the page URL of the requested favicon. If the 'iconurl'
-//    parameter is specified, the URL refers to the URL of the favicon image
-//    instead.
-//  'size&scalefactor'  Optional
-//    Values: ['size/aa@bx/']
-//      Specifies the requested favicon's size in DIP (aa) and the requested
-//      favicon's scale factor. (b).
-//      The supported requested DIP sizes are: 16x16, 32x32 and 64x64.
-//      If the parameter is unspecified, the requested favicon's size defaults
-//      to 16 and the requested scale factor defaults to 1x.
-//      Example: chrome://favicon/size/16@2x/https://www.google.com/
-//  'iconurl'           Optional
-//    Values: ['iconurl']
-//    'iconurl': Specifies that the url parameter refers to the URL of
-//    the favicon image as opposed to the URL of the page that the favicon is
-//    on.
-//    Example: chrome://favicon/iconurl/https://www.google.com/favicon.ico
+// Two possible formats are allowed: chrome://favicon, kept only for backwards
+// compatibility for extensions, and chrome://favicon2. Formats are described in
+// favicon_url_parser.h.
 class FaviconSource : public content::URLDataSource {
  public:
   // |type| is the type of icon this FaviconSource will provide.
-  explicit FaviconSource(Profile* profile);
+  explicit FaviconSource(Profile* profile, chrome::FaviconUrlFormat format);
 
   ~FaviconSource() override;
 
@@ -116,6 +98,8 @@
   // Sends the default favicon.
   void SendDefaultResponse(const IconRequest& request);
 
+  chrome::FaviconUrlFormat url_format_;
+
   base::CancelableTaskTracker cancelable_task_tracker_;
 
   DISALLOW_COPY_AND_ASSIGN(FaviconSource);
diff --git a/chrome/browser/ui/webui/favicon_source_unittest.cc b/chrome/browser/ui/webui/favicon_source_unittest.cc
index d0d1b14..95b451a 100644
--- a/chrome/browser/ui/webui/favicon_source_unittest.cc
+++ b/chrome/browser/ui/webui/favicon_source_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/favicon_base/favicon_url_parser.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -23,8 +24,10 @@
 
 class TestFaviconSource : public FaviconSource {
  public:
+  // chrome::FaviconUrlFormat::kFavicon2 is arbitrary below.
   TestFaviconSource(Profile* profile, ui::NativeTheme* theme)
-      : FaviconSource(profile), theme_(theme) {}
+      : FaviconSource(profile, chrome::FaviconUrlFormat::kFavicon2),
+        theme_(theme) {}
 
   ~TestFaviconSource() override {}
 
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
index fb31dea..fd278d7 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos.cc
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -80,21 +80,19 @@
 }
 
 base::string16 GetConnectionTypeAsUTF16(const chromeos::NetworkState* network) {
-  const std::string type =
-      network->IsUsingMobileData() ? shill::kTypeCellular : network->type();
+  const std::string type = network->type();
+  if (chromeos::NetworkTypePattern::WiFi().MatchesType(type)) {
+    if (network->IsUsingMobileData())
+      return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_METERED_WIFI);
+    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIFI);
+  }
   if (chromeos::NetworkTypePattern::Ethernet().MatchesType(type))
     return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_ETHERNET);
-  if (type == shill::kTypeWifi)
-    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIFI);
-  if (type == shill::kTypeWimax)
+  if (chromeos::NetworkTypePattern::Wimax().MatchesType(type))
     return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIMAX);
-  if (type == shill::kTypeBluetooth)
-    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_BLUETOOTH);
-  if (type == shill::kTypeCellular ||
-      chromeos::NetworkTypePattern::Tether().MatchesType(type)) {
+  if (chromeos::NetworkTypePattern::Mobile().MatchesType(type))
     return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_MOBILE_DATA);
-  }
-  if (type == shill::kTypeVPN)
+  if (chromeos::NetworkTypePattern::VPN().MatchesType(type))
     return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_VPN);
   NOTREACHED();
   return base::string16();
diff --git a/chrome/browser/ui/webui/management_ui.cc b/chrome/browser/ui/webui/management_ui.cc
index 32098159..d4151d1 100644
--- a/chrome/browser/ui/webui/management_ui.cc
+++ b/chrome/browser/ui/webui/management_ui.cc
@@ -71,7 +71,6 @@
     {"searchPrompt", IDS_SETTINGS_SEARCH_PROMPT},
     {"clearSearch", IDS_DOWNLOAD_CLEAR_SEARCH},
     {"backButton", IDS_ACCNAME_BACK},
-#if BUILDFLAG(ENABLE_EXTENSIONS)
     {kManagementExtensionReportMachineName,
      IDS_MANAGEMENT_EXTENSION_REPORT_MACHINE_NAME},
     {kManagementExtensionReportMachineNameAddress,
@@ -86,7 +85,6 @@
      IDS_MANAGEMENT_EXTENSION_REPORT_PERF_CRASH},
     {kManagementExtensionReportUserBrowsingData,
      IDS_MANAGEMENT_EXTENSION_REPORT_USER_BROWSING_DATA},
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   };
 
   AddLocalizedStringsBulk(source, kLocalizedStrings,
diff --git a/chrome/browser/ui/webui/management_ui_handler.cc b/chrome/browser/ui/webui/management_ui_handler.cc
index e6cdb78a..7229fe4 100644
--- a/chrome/browser/ui/webui/management_ui_handler.cc
+++ b/chrome/browser/ui/webui/management_ui_handler.cc
@@ -50,7 +50,6 @@
 #include "ui/chromeos/devicetype_utils.h"
 #endif  // defined(OS_CHROMEOS)
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
 #include "components/policy/core/common/policy_map.h"
@@ -109,8 +108,6 @@
   kUserActivity
 };
 
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
-
 #if defined(OS_CHROMEOS)
 const char kManagementLogUploadEnabled[] = "managementLogUploadEnabled";
 const char kManagementReportActivityTimes[] = "managementReportActivityTimes";
@@ -237,7 +234,6 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 std::vector<base::Value> GetPermissionsForExtension(
     scoped_refptr<const extensions::Extension> extension) {
   std::vector<base::Value> permission_messages;
@@ -298,8 +294,6 @@
   }
 }
 
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
-
 }  // namespace
 
 // TODO(raleksandov) Move to util class or smth similar.
@@ -323,11 +317,9 @@
 }
 
 ManagementUIHandler::ManagementUIHandler() {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   reporting_extension_ids_ = {kOnPremReportingExtensionStableId,
                               kOnPremReportingExtensionBetaId,
                               kCloudReportingExtensionId};
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
 ManagementUIHandler::~ManagementUIHandler() {
@@ -379,7 +371,6 @@
                           base::Unretained(this)));
 }
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 void ManagementUIHandler::OnJavascriptAllowed() {
   AddObservers();
 }
@@ -570,7 +561,6 @@
       ->GetExtensionById(kCloudReportingExtensionId,
                          extensions::ExtensionRegistry::ENABLED);
 }
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 void ManagementUIHandler::AsyncUpdateLogo() {
 #if defined(OS_CHROMEOS)
@@ -701,7 +691,6 @@
 
 void ManagementUIHandler::HandleGetExtensions(const base::ListValue* args) {
   AllowJavascript();
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   // List of all enabled extensions
   const extensions::ExtensionSet& extensions =
       extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
@@ -711,10 +700,6 @@
 
   ResolveJavascriptCallback(args->GetList()[0] /* callback_id */,
                             powerful_extensions);
-#else
-  ResolveJavascriptCallback(args->GetList()[0] /* callback_id */,
-                            base::Value(base::Value::Type::LIST));
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
 #if defined(OS_CHROMEOS)
@@ -758,14 +743,11 @@
     const base::ListValue* args) {
   base::Value report_sources(base::Value::Type::LIST);
   AllowJavascript();
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   AddExtensionReportingInfo(&report_sources);
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   ResolveJavascriptCallback(args->GetList()[0] /* callback_id */,
                             report_sources);
 }
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 void ManagementUIHandler::NotifyBrowserReportingInfoUpdated() {
   base::Value report_sources(base::Value::Type::LIST);
   AddExtensionReportingInfo(&report_sources);
@@ -854,4 +836,3 @@
 
   pref_registrar_.RemoveAll();
 }
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/ui/webui/management_ui_handler.h b/chrome/browser/ui/webui/management_ui_handler.h
index 2247b4b..dc1f39f 100644
--- a/chrome/browser/ui/webui/management_ui_handler.h
+++ b/chrome/browser/ui/webui/management_ui_handler.h
@@ -14,18 +14,15 @@
 #include "base/strings/string16.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
 #include "chrome/common/url_constants.h"
+#include "components/policy/core/common/policy_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/browser/extension_registry_observer.h"
 #include "extensions/buildflags/buildflags.h"
 #include "url/gurl.h"
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "components/policy/core/common/policy_service.h"
-#include "extensions/browser/extension_registry_observer.h"
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
-
 #if defined(OS_CHROMEOS)
 // Constants defining the IDs for the localized strings sent to the page as
 // load time data.
@@ -37,7 +34,6 @@
 extern const char kManagementPrinting[];
 #endif  // defined(OS_CHROMEOS)
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 extern const char kCloudReportingExtensionId[];
 extern const char kOnPremReportingExtensionStableId[];
 extern const char kOnPremReportingExtensionBetaId[];
@@ -66,8 +62,6 @@
 extern const char kReportingTypeUser[];
 extern const char kReportingTypeUserActivity[];
 
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
-
 namespace base {
 class ListValue;
 }  // namespace base
@@ -83,15 +77,10 @@
 class Profile;
 
 // The JavaScript message handler for the chrome://management page.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 class ManagementUIHandler : public content::WebUIMessageHandler,
                             public extensions::ExtensionRegistryObserver,
                             public policy::PolicyService::Observer,
                             public BitmapFetcherDelegate {
-#else
-class ManagementUIHandler : public content::WebUIMessageHandler,
-                            public BitmapFetcherDelegate {
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
  public:
   ManagementUIHandler();
   ~ManagementUIHandler() override;
@@ -107,13 +96,10 @@
 
   static std::string GetAccountDomain(Profile* profile);
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   void OnJavascriptAllowed() override;
   void OnJavascriptDisallowed() override;
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
  protected:
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Protected for testing.
   static void InitializeInternal(content::WebUI* web_ui,
                                  content::WebUIDataSource* source,
@@ -124,7 +110,6 @@
   virtual policy::PolicyService* GetPolicyService() const;
   virtual const extensions::Extension* GetEnabledExtension(
       const std::string& extensionId) const;
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if defined(OS_CHROMEOS)
   // Protected for testing.
@@ -151,7 +136,6 @@
   // BitmapFetcherDelegate
   void OnFetchComplete(const GURL& url, const SkBitmap* bitmap) override;
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   void NotifyBrowserReportingInfoUpdated();
 
   // extensions::ExtensionRegistryObserver implementation.
@@ -182,7 +166,6 @@
   PrefChangeRegistrar pref_registrar_;
 
   std::set<extensions::ExtensionId> reporting_extension_ids_;
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   GURL logo_url_;
   std::string fetched_image_;
   std::unique_ptr<BitmapFetcher> icon_fetcher_;
diff --git a/chrome/browser/ui/webui/notifications_internals/notifications_internals_ui_message_handler.cc b/chrome/browser/ui/webui/notifications_internals/notifications_internals_ui_message_handler.cc
index a74af87..adcd249e 100644
--- a/chrome/browser/ui/webui/notifications_internals/notifications_internals_ui_message_handler.cc
+++ b/chrome/browser/ui/webui/notifications_internals/notifications_internals_ui_message_handler.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/notifications/scheduler/notification_schedule_service_factory.h"
 #include "chrome/browser/notifications/scheduler/public/notification_params.h"
@@ -38,8 +39,8 @@
   notifications::ScheduleParams schedule_params;
   notifications::NotificationData data;
   data.url = args->GetList()[1].GetString();
-  data.title = args->GetList()[2].GetString();
-  data.message = args->GetList()[3].GetString();
+  data.title = base::UTF8ToUTF16(args->GetList()[2].GetString());
+  data.message = base::UTF8ToUTF16(args->GetList()[3].GetString());
   auto params = std::make_unique<notifications::NotificationParams>(
       notifications::SchedulerClientType::kWebUI, std::move(data),
       std::move(schedule_params));
diff --git a/chrome/browser/ui/webui/policy_ui_browsertest.cc b/chrome/browser/ui/webui/policy_ui_browsertest.cc
index 6286246..e4ac4cf3 100644
--- a/chrome/browser/ui/webui/policy_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy_ui_browsertest.cc
@@ -394,14 +394,14 @@
              policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_PLATFORM,
              popups_blocked_for_urls.CreateDeepCopy(), nullptr);
   SetExpectedPolicy(&expected_values, policy::key::kPopupsBlockedForUrls,
-                    "mandatory", "machine", "sourcePlatform", std::string(),
+                    "mandatory", "machine", "platform", std::string(),
                     std::string(), false, popups_blocked_for_urls);
 
   values.Set(policy::key::kDefaultImagesSetting, policy::POLICY_LEVEL_MANDATORY,
              policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
              std::make_unique<base::Value>(2), nullptr);
   SetExpectedPolicy(&expected_values, policy::key::kDefaultImagesSetting,
-                    "mandatory", "machine", "sourceCloud", std::string(),
+                    "mandatory", "machine", "cloud", std::string(),
                     std::string(), false, base::Value(2));
 
   // This also checks that we save complex policies correctly.
@@ -416,7 +416,7 @@
              policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
              unknown_policy.CreateDeepCopy(), nullptr);
   SetExpectedPolicy(&expected_values, kUnknownPolicy, "recommended", "user",
-                    "sourceCloud", l10n_util::GetStringUTF8(IDS_POLICY_UNKNOWN),
+                    "cloud", l10n_util::GetStringUTF8(IDS_POLICY_UNKNOWN),
                     std::string(), false, unknown_policy);
 
   // Set the extension policies to an empty dictionary as we haven't added any
@@ -448,7 +448,7 @@
              policy::POLICY_SOURCE_PLATFORM,
              std::make_unique<base::Value>(false), nullptr);
   SetExpectedPolicy(&expected_values, policy::key::kAllowFileSelectionDialogs,
-                    "mandatory", "machine", "sourcePlatform", std::string(),
+                    "mandatory", "machine", "platform", std::string(),
                     std::string(), false, base::Value(false));
 #endif
 
@@ -457,7 +457,7 @@
              policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_PLATFORM,
              popups_blocked_for_urls.CreateDeepCopy(), nullptr);
   SetExpectedPolicy(&expected_values, policy::key::kPopupsBlockedForUrls,
-                    "mandatory", "machine", "sourcePlatform", std::string(),
+                    "mandatory", "machine", "platform", std::string(),
                     std::string(), false, popups_blocked_for_urls);
 
   provider_.UpdateChromePolicy(values);
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 a4f29db..46708272 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -257,6 +257,8 @@
 // Dictionary field to indicate whether Chrome is running in forced app (app
 // kiosk) mode. It's not the same as desktop Chrome kiosk (the one above).
 const char kIsInAppKioskMode[] = "isInAppKioskMode";
+// Name of a dictionary field holding the UI locale.
+const char kUiLocale[] = "uiLocale";
 // Name of a dictionary field holding the thousands delimeter according to the
 // locale.
 const char kThousandsDelimeter[] = "thousandsDelimeter";
@@ -940,11 +942,11 @@
 }
 #endif
 
-void PrintPreviewHandler::GetNumberFormatAndMeasurementSystem(
-    base::Value* settings) {
+void PrintPreviewHandler::GetLocaleInformation(base::Value* settings) {
   // Getting the measurement system based on the locale.
   UErrorCode errorCode = U_ZERO_ERROR;
   const char* locale = g_browser_process->GetApplicationLocale().c_str();
+  settings->SetStringKey(kUiLocale, std::string(locale));
   UMeasurementSystem system = ulocdata_getMeasurementSystem(locale, &errorCode);
   // On error, assume the units are SI.
   // Since the only measurement units print preview's WebUI cares about are
@@ -1040,7 +1042,7 @@
     initial_settings.SetStringKey(kDefaultDestinationSelectionRules, rules_str);
   }
 
-  GetNumberFormatAndMeasurementSystem(&initial_settings);
+  GetLocaleInformation(&initial_settings);
   if (IsCloudPrintEnabled()) {
     GetUserAccountList(&initial_settings);
   }
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 a3d35fd..ff2893e 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -279,7 +279,7 @@
   void ClearInitiatorDetails();
 
   // Populates |settings| according to the current locale.
-  void GetNumberFormatAndMeasurementSystem(base::Value* settings);
+  void GetLocaleInformation(base::Value* settings);
 
   // Populates |settings| with the list of logged in accounts.
   void GetUserAccountList(base::Value* settings);
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 446db2e1..004dec2 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
@@ -340,6 +340,10 @@
     ASSERT_TRUE(settings->FindKeyOfType("isInAppKioskMode",
                                         base::Value::Type::BOOLEAN));
 
+    const base::Value* locale =
+        settings->FindKeyOfType("uiLocale", base::Value::Type::STRING);
+    ASSERT_TRUE(locale);
+    EXPECT_EQ("en", locale->GetString());
     const base::Value* thousands_delimeter = settings->FindKeyOfType(
         "thousandsDelimeter", base::Value::Type::STRING);
     ASSERT_TRUE(thousands_delimeter);
diff --git a/chrome/browser/ui/webui/settings/captions_handler.cc b/chrome/browser/ui/webui/settings/captions_handler.cc
new file mode 100644
index 0000000..f91f2fdd
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/captions_handler.cc
@@ -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.
+
+#include "chrome/browser/ui/webui/settings/captions_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "chrome/browser/accessibility/caption_settings_dialog.h"
+#include "content/public/browser/web_ui.h"
+
+namespace settings {
+
+CaptionsHandler::CaptionsHandler() {}
+
+CaptionsHandler::~CaptionsHandler() {}
+
+void CaptionsHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "openSystemCaptionsDialog",
+      base::BindRepeating(&CaptionsHandler::HandleOpenSystemCaptionsDialog,
+                          base::Unretained(this)));
+}
+
+void CaptionsHandler::OnJavascriptAllowed() {}
+
+void CaptionsHandler::OnJavascriptDisallowed() {}
+
+void CaptionsHandler::HandleOpenSystemCaptionsDialog(
+    const base::ListValue* args) {
+  captions::CaptionSettingsDialog::ShowCaptionSettingsDialog();
+}
+
+}  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/captions_handler.h b/chrome/browser/ui/webui/settings/captions_handler.h
new file mode 100644
index 0000000..28ea30d
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/captions_handler.h
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CAPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CAPTIONS_HANDLER_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace settings {
+
+// UI handler for Chrome caption settings subpage on operating systems other
+// than Chrome OS and Linux.
+class CaptionsHandler : public SettingsPageUIHandler {
+ public:
+  CaptionsHandler();
+  ~CaptionsHandler() override;
+
+  // SettingsPageUIHandler overrides:
+  void RegisterMessages() override;
+  void OnJavascriptAllowed() override;
+  void OnJavascriptDisallowed() override;
+
+ private:
+  void HandleOpenSystemCaptionsDialog(const base::ListValue* args);
+
+  DISALLOW_COPY_AND_ASSIGN(CaptionsHandler);
+};
+
+}  // namespace settings
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CAPTIONS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
index 7559e03..28e18b4 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
@@ -12,9 +12,9 @@
 #include "base/bind_helpers.h"
 #include "chrome/browser/chromeos/crostini/crostini_export_import.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -126,7 +126,7 @@
   std::string path;
   CHECK(args->GetString(1, &path));
 
-  crostini::CrostiniSharePath::GetForProfile(profile_)->UnsharePath(
+  guest_os::GuestOsSharePath::GetForProfile(profile_)->UnsharePath(
       vm_name, base::FilePath(path),
       /*unpersist=*/true,
       base::BindOnce(
diff --git a/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc b/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
index a5db2fe..812cffc 100644
--- a/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
@@ -165,7 +165,7 @@
       user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(),
       base::BindRepeating(&DateTimeHandler::OnParentAccessValidation,
                           weak_ptr_factory_.GetWeakPtr()),
-      ash::ParentAccessRequestReason::kChangeTime);
+      ash::ParentAccessRequestReason::kChangeTimezone);
 }
 
 void DateTimeHandler::OnParentAccessValidation(bool success) {
diff --git a/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc b/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc
index 56b74e88..851bdac 100644
--- a/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc
@@ -62,7 +62,8 @@
 void ParentalControlsHandler::HandleShowAddSupervisionDialog(
     const base::ListValue* args) {
   DCHECK(args->empty());
-  AddSupervisionDialog::Show();
+  AddSupervisionDialog::Show(
+      web_ui()->GetWebContents()->GetTopLevelNativeWindow());
 }
 
 void ParentalControlsHandler::HandleLaunchFamilyLinkSettings(
diff --git a/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc b/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc
index c45f879..09518e99 100644
--- a/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc
@@ -10,8 +10,8 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -55,7 +55,7 @@
   std::string vm_name = args->GetList()[0].GetString();
   std::string path = args->GetList()[1].GetString();
 
-  crostini::CrostiniSharePath::GetForProfile(profile_)->UnsharePath(
+  guest_os::GuestOsSharePath::GetForProfile(profile_)->UnsharePath(
       vm_name, base::FilePath(path),
       /*unpersist=*/true,
       base::BindOnce(
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index cc86b18..10ac391 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/win/windows_version.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
@@ -151,7 +152,6 @@
     {"menuButtonLabel", IDS_SETTINGS_MENU_BUTTON_LABEL},
     {"moreActions", IDS_SETTINGS_MORE_ACTIONS},
     {"ok", IDS_OK},
-    {"remove", IDS_REMOVE},
     {"restart", IDS_SETTINGS_RESTART},
 #if !defined(OS_CHROMEOS)
     {"restartToApplyChanges", IDS_SETTINGS_RESTART_TO_APPLY_CHANGES},
@@ -192,6 +192,32 @@
     {"moreFeaturesLink", IDS_SETTINGS_MORE_FEATURES_LINK},
     {"moreFeaturesLinkDescription",
      IDS_SETTINGS_MORE_FEATURES_LINK_DESCRIPTION},
+    {"captionsTitle", IDS_SETTINGS_CAPTIONS},
+    {"captionsTextSize", IDS_SETTINGS_CAPTIONS_TEXT_SIZE},
+    {"captionsTextFont", IDS_SETTINGS_CAPTIONS_TEXT_FONT},
+    {"captionsTextColor", IDS_SETTINGS_CAPTIONS_TEXT_COLOR},
+    {"captionsTextOpacity", IDS_SETTINGS_CAPTIONS_TEXT_OPACITY},
+    {"captionsBackgroundOpacity", IDS_SETTINGS_CAPTIONS_BACKGROUND_OPACITY},
+    {"captionsOpacityMin", IDS_SETTINGS_CAPTIONS_OPACITY_MIN},
+    {"captionsOpacityMax", IDS_SETTINGS_CAPTIONS_OPACITY_MAX},
+    {"captionsTextShadow", IDS_SETTINGS_CAPTIONS_TEXT_SHADOW},
+    {"captionsTextShadowNone", IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_NONE},
+    {"captionsTextShadowRaised", IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_RAISED},
+    {"captionsTextShadowDepressed",
+     IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_DEPRESSED},
+    {"captionsTextShadowUniform", IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_UNIFORM},
+    {"captionsTextShadowDropShadow",
+     IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_DROP_SHADOW},
+    {"captionsBackgroundColor", IDS_SETTINGS_CAPTIONS_BACKGROUND_COLOR},
+    {"captionsColorBlack", IDS_SETTINGS_CAPTIONS_COLOR_BLACK},
+    {"captionsColorWhite", IDS_SETTINGS_CAPTIONS_COLOR_WHITE},
+    {"captionsColorRed", IDS_SETTINGS_CAPTIONS_COLOR_RED},
+    {"captionsColorGreen", IDS_SETTINGS_CAPTIONS_COLOR_GREEN},
+    {"captionsColorBlue", IDS_SETTINGS_CAPTIONS_COLOR_BLUE},
+    {"captionsColorYellow", IDS_SETTINGS_CAPTIONS_COLOR_YELLOW},
+    {"captionsColorCyan", IDS_SETTINGS_CAPTIONS_COLOR_CYAN},
+    {"captionsColorMagenta", IDS_SETTINGS_CAPTIONS_COLOR_MAGENTA},
+    {"captionsDefaultSetting", IDS_SETTINGS_CAPTIONS_DEFAULT_SETTING},
 #if defined(OS_CHROMEOS)
     {"optionsInMenuLabel", IDS_SETTINGS_OPTIONS_IN_MENU_LABEL},
     {"largeMouseCursorLabel", IDS_SETTINGS_LARGE_MOUSE_CURSOR_LABEL},
@@ -345,6 +371,15 @@
       "showExperimentalA11yLabels",
       base::FeatureList::IsEnabled(features::kExperimentalAccessibilityLabels));
 
+  html_source->AddBoolean(
+      "enableCaptionSettings",
+      base::FeatureList::IsEnabled(features::kCaptionSettings));
+
+#if defined(OS_WIN)
+  html_source->AddBoolean("isWindows10OrNewer",
+                          base::win::GetVersion() >= base::win::Version::WIN10);
+#endif
+
 #if defined(OS_CHROMEOS)
   html_source->AddString("accountManagerLearnMoreUrl",
                          chrome::kAccountManagerLearnMoreURL);
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index fe76537f..09792bf 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/ui/webui/settings/accessibility_main_handler.h"
 #include "chrome/browser/ui/webui/settings/appearance_handler.h"
 #include "chrome/browser/ui/webui/settings/browser_lifetime_handler.h"
+#include "chrome/browser/ui/webui/settings/captions_handler.h"
 #include "chrome/browser/ui/webui/settings/downloads_handler.h"
 #include "chrome/browser/ui/webui/settings/extension_control_handler.h"
 #include "chrome/browser/ui/webui/settings/font_handler.h"
@@ -86,6 +87,7 @@
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h"
@@ -116,10 +118,12 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_pref_names.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "chromeos/login/auth/password_visibility_utils.h"
 #include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
 #include "chromeos/services/network_config/public/mojom/constants.mojom.h"  // nogncheck
 #include "components/arc/arc_util.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/chromeos/resources/grit/ui_chromeos_resources.h"
@@ -226,6 +230,10 @@
   AddSettingsPageUIHandler(std::make_unique<StartupPagesHandler>(web_ui));
   AddSettingsPageUIHandler(std::make_unique<SecurityKeysHandler>());
 
+#if defined(OS_WIN) || defined(OS_MACOSX)
+  AddSettingsPageUIHandler(std::make_unique<CaptionsHandler>());
+#endif
+
 #if defined(OS_CHROMEOS)
   // TODO(950007): Remove this when SplitSettings is the default and there are
   // no Chrome OS settings in the browser settings page.
@@ -451,6 +459,12 @@
   html_source->AddBoolean(
       "quickUnlockDisabledByPolicy",
       chromeos::quick_unlock::IsPinDisabledByPolicy(profile->GetPrefs()));
+  html_source->AddBoolean(
+      "userCannotManuallyEnterPassword",
+      !chromeos::password_visibility::AccountHasUserFacingPassword(
+          chromeos::ProfileHelper::Get()
+              ->GetUserByProfile(profile)
+              ->GetAccountId()));
   const bool fingerprint_unlock_enabled =
       chromeos::quick_unlock::IsFingerprintEnabled(profile);
   html_source->AddBoolean("fingerprintUnlockEnabled",
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc b/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
index 5fe27fbb..aff5ff8 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
@@ -64,6 +64,9 @@
       "createProfileTitle",
       l10n_util::GetStringUTF16(IDS_PROFILES_CREATE_TITLE));
   localized_strings->SetString(
+      "createProfileNamePlaceholder",
+      l10n_util::GetStringUTF16(IDS_PROFILES_CREATE_NAME_PLACEHOLDER));
+  localized_strings->SetString(
       "exitAndChildlockLabel",
       l10n_util::GetStringUTF16(
           IDS_PROFILES_PROFILE_SIGNOUT_BUTTON));
@@ -87,19 +90,6 @@
   web_ui()->CallJavascriptFunctionUnsafe(
       "cr.webUIListenerCallback", base::Value("profile-icons-received"),
       *profiles::GetDefaultProfileAvatarIconsAndLabels());
-
-  SendNewProfileDefaults();
-}
-
-void SigninCreateProfileHandler::SendNewProfileDefaults() {
-  ProfileAttributesStorage& storage =
-      g_browser_process->profile_manager()->GetProfileAttributesStorage();
-  base::DictionaryValue profile_info;
-  profile_info.SetString("name", storage.ChooseNameForNewProfile(0));
-
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "cr.webUIListenerCallback", base::Value("profile-defaults-received"),
-      profile_info);
 }
 
 void SigninCreateProfileHandler::CreateProfile(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler.h b/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
index 12e3a060..8462672 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
@@ -34,7 +34,7 @@
 
  protected:
   FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
-                           ReturnDefaultProfileNameAndIcons);
+                           ReturnDefaultProfileIcons);
   FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
                            ReturnSignedInProfiles);
   FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
@@ -69,10 +69,6 @@
   // Sends the array of default profile icon URLs to WebUI.
   void RequestDefaultProfileIcons(const base::ListValue* args);
 
-  // Sends an object to WebUI of the form: { "name": profileName } after
-  // "requestDefaultProfileIcons" is fulfilled.
-  void SendNewProfileDefaults();
-
   // Asynchronously creates and initializes a new profile.
   // The arguments are as follows:
   //   0: name (string)
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc b/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
index c25759cb4..8e714ee6 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
@@ -123,14 +123,13 @@
   std::unique_ptr<TestSigninCreateProfileHandler> handler_;
 };
 
-TEST_F(SigninCreateProfileHandlerTest, ReturnDefaultProfileNameAndIcons) {
+TEST_F(SigninCreateProfileHandlerTest, ReturnDefaultProfileIcons) {
   // Request default profile information.
   base::ListValue list_args;
   handler()->RequestDefaultProfileIcons(&list_args);
 
-  // Expect two JS callbacks. One with profile avatar icons and the other with
-  // the default profile name.
-  EXPECT_EQ(2U, web_ui()->call_data().size());
+  // Expect one JS callbacks for the profile avatar icons.
+  EXPECT_EQ(1U, web_ui()->call_data().size());
 
   EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
 
@@ -141,17 +140,6 @@
   const base::ListValue* profile_icons;
   ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsList(&profile_icons));
   EXPECT_NE(0U, profile_icons->GetSize());
-
-  EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[1]->function_name());
-
-  ASSERT_TRUE(web_ui()->call_data()[1]->arg1()->GetAsString(&callback_name));
-  EXPECT_EQ("profile-defaults-received", callback_name);
-
-  const base::DictionaryValue* profile_info;
-  ASSERT_TRUE(web_ui()->call_data()[1]->arg2()->GetAsDictionary(&profile_info));
-  std::string profile_name;
-  ASSERT_TRUE(profile_info->GetString("name", &profile_name));
-  EXPECT_NE("", profile_name);
 }
 
 TEST_F(SigninCreateProfileHandlerTest, CreateProfile) {
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
index 2c2978c..540133c 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
@@ -33,13 +33,11 @@
 
 SyncConfirmationHandler::SyncConfirmationHandler(
     Browser* browser,
-    const std::unordered_map<std::string, int>& string_to_grd_id_map,
-    consent_auditor::Feature consent_feature)
+    const std::unordered_map<std::string, int>& string_to_grd_id_map)
     : profile_(browser->profile()),
       browser_(browser),
       did_user_explicitly_interact(false),
       string_to_grd_id_map_(string_to_grd_id_map),
-      consent_feature_(consent_feature),
       identity_manager_(IdentityManagerFactory::GetForProfile(profile_)) {
   DCHECK(profile_);
   DCHECK(browser_);
@@ -123,12 +121,11 @@
       args->GetList()[0].GetList();
   const std::string& consent_confirmation = args->GetList()[1].GetString();
 
-  std::vector<int> consent_text_ids;
-
   // The strings returned by the WebUI are not free-form, they must belong into
   // a pre-determined set of strings (stored in |string_to_grd_id_map_|). As
   // this has privacy and legal implications, CHECK the integrity of the strings
   // received from the renderer process before recording the consent.
+  std::vector<int> consent_text_ids;
   for (const base::Value& text : consent_description) {
     auto iter = string_to_grd_id_map_.find(text.GetString());
     CHECK(iter != string_to_grd_id_map_.end()) << "Unexpected string:\n"
@@ -141,30 +138,18 @@
                                              << consent_confirmation;
   int consent_confirmation_id = iter->second;
 
+  sync_pb::UserConsentTypes::SyncConsent sync_consent;
+  sync_consent.set_confirmation_grd_id(consent_confirmation_id);
+  for (int id : consent_text_ids) {
+    sync_consent.add_description_grd_ids(id);
+  }
+  sync_consent.set_status(sync_pb::UserConsentTypes::ConsentStatus::
+                              UserConsentTypes_ConsentStatus_GIVEN);
+
   consent_auditor::ConsentAuditor* consent_auditor =
       ConsentAuditorFactory::GetForProfile(profile_);
-  const std::string& account_id = identity_manager_->GetPrimaryAccountId();
-  // TODO(markusheintz): Use a bool unified_consent_enabled instead of a
-  // consent_auditor::Feature type variable.
-  if (consent_feature_ == consent_auditor::Feature::CHROME_UNIFIED_CONSENT) {
-    sync_pb::UserConsentTypes::UnifiedConsent unified_consent;
-    unified_consent.set_confirmation_grd_id(consent_confirmation_id);
-    for (int id : consent_text_ids) {
-      unified_consent.add_description_grd_ids(id);
-    }
-    unified_consent.set_status(sync_pb::UserConsentTypes::ConsentStatus::
-                                   UserConsentTypes_ConsentStatus_GIVEN);
-    consent_auditor->RecordUnifiedConsent(account_id, unified_consent);
-  } else {
-    sync_pb::UserConsentTypes::SyncConsent sync_consent;
-    sync_consent.set_confirmation_grd_id(consent_confirmation_id);
-    for (int id : consent_text_ids) {
-      sync_consent.add_description_grd_ids(id);
-    }
-    sync_consent.set_status(sync_pb::UserConsentTypes::ConsentStatus::
-                                UserConsentTypes_ConsentStatus_GIVEN);
-    consent_auditor->RecordSyncConsent(account_id, sync_consent);
-  }
+  consent_auditor->RecordSyncConsent(identity_manager_->GetPrimaryAccountId(),
+                                     sync_consent);
 }
 
 void SyncConfirmationHandler::SetUserImageURL(const std::string& picture_url) {
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler.h b/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
index 5407c208..b3a8eba 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
@@ -32,8 +32,7 @@
   // mapped to their GRD IDs.
   explicit SyncConfirmationHandler(
       Browser* browser,
-      const std::unordered_map<std::string, int>& string_to_grd_id_map,
-      consent_auditor::Feature consent_feature);
+      const std::unordered_map<std::string, int>& string_to_grd_id_map);
   ~SyncConfirmationHandler() override;
 
   // content::WebUIMessageHandler:
@@ -104,9 +103,6 @@
   // and their respective GRD IDs.
   std::unordered_map<std::string, int> string_to_grd_id_map_;
 
-  // Contains the features to use when the user consent decision is recorded.
-  consent_auditor::Feature consent_feature_;
-
   identity::IdentityManager* identity_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(SyncConfirmationHandler);
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
index e3d4c495..de704d7 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
@@ -46,9 +46,7 @@
       Browser* browser,
       content::WebUI* web_ui,
       std::unordered_map<std::string, int> string_to_grd_id_map)
-      : SyncConfirmationHandler(browser,
-                                string_to_grd_id_map,
-                                consent_auditor::Feature::CHROME_SYNC) {
+      : SyncConfirmationHandler(browser, string_to_grd_id_map) {
     set_web_ui(web_ui);
   }
 
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
index 87208b1..7d27ddd 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -26,8 +26,7 @@
 #include "ui/base/webui/web_ui_util.h"
 
 SyncConfirmationUI::SyncConfirmationUI(content::WebUI* web_ui)
-    : SigninWebDialogUI(web_ui),
-      consent_feature_(consent_auditor::Feature::CHROME_SYNC) {
+    : SigninWebDialogUI(web_ui) {
   DCHECK(unified_consent::IsUnifiedConsentFeatureEnabled());
   Profile* profile = Profile::FromWebUI(web_ui);
   bool is_sync_allowed = profile->IsSyncAllowed();
@@ -80,8 +79,6 @@
                                .spec();
     }
     source->AddString("accountPictureUrl", custom_picture_url);
-
-    consent_feature_ = consent_auditor::Feature::CHROME_UNIFIED_CONSENT;
   } else {
     source->SetDefaultResource(IDR_SYNC_DISABLED_CONFIRMATION_HTML);
     source->AddResourcePath("sync_disabled_confirmation.js",
@@ -95,8 +92,6 @@
                       IDS_SYNC_DISABLED_CONFIRMATION_CONFIRM_BUTTON_LABEL);
     AddStringResource(source, "syncDisabledConfirmationUndoLabel",
                       IDS_SYNC_DISABLED_CONFIRMATION_UNDO_BUTTON_LABEL);
-
-    consent_feature_ = consent_auditor::Feature::CHROME_SYNC;
   }
 
   base::DictionaryValue strings;
@@ -111,7 +106,7 @@
 
 void SyncConfirmationUI::InitializeMessageHandlerWithBrowser(Browser* browser) {
   web_ui()->AddMessageHandler(std::make_unique<SyncConfirmationHandler>(
-      browser, js_localized_string_to_ids_map_, consent_feature_));
+      browser, js_localized_string_to_ids_map_));
 }
 
 void SyncConfirmationUI::AddStringResource(content::WebUIDataSource* source,
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.h b/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
index a2b773e1..97a0634 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
@@ -44,8 +44,6 @@
 
   std::unordered_map<std::string, int> js_localized_string_to_ids_map_;
 
-  consent_auditor::Feature consent_feature_;
-
   DISALLOW_COPY_AND_ASSIGN(SyncConfirmationUI);
 };
 
diff --git a/chrome/browser/ui/webui/version_ui.cc b/chrome/browser/ui/webui/version_ui.cc
index deba24f..8127a54 100644
--- a/chrome/browser/ui/webui/version_ui.cc
+++ b/chrome/browser/ui/webui/version_ui.cc
@@ -12,6 +12,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/localized_string.h"
 #include "chrome/browser/ui/webui/version_handler.h"
 #include "chrome/browser/ui/webui/version_util_win.h"
 #include "chrome/common/channel_info.h"
@@ -55,58 +56,59 @@
   WebUIDataSource* html_source =
       WebUIDataSource::Create(chrome::kChromeUIVersionHost);
 
-  // Localized and data strings.
-  html_source->AddLocalizedString(version_ui::kTitle, IDS_VERSION_UI_TITLE);
-  html_source->AddLocalizedString(version_ui::kApplicationLabel,
-                                  IDS_PRODUCT_NAME);
+  // Localized strings.
+  static constexpr LocalizedString kStrings[] = {
+    {version_ui::kTitle, IDS_VERSION_UI_TITLE},
+    {version_ui::kApplicationLabel, IDS_PRODUCT_NAME},
+    {version_ui::kCompany, IDS_ABOUT_VERSION_COMPANY_NAME},
+    {version_ui::kRevision, IDS_VERSION_UI_REVISION},
+    {version_ui::kUserAgentName, IDS_VERSION_UI_USER_AGENT},
+    {version_ui::kCommandLineName, IDS_VERSION_UI_COMMAND_LINE},
+    {version_ui::kExecutablePathName, IDS_VERSION_UI_EXECUTABLE_PATH},
+    {version_ui::kProfilePathName, IDS_VERSION_UI_PROFILE_PATH},
+    {version_ui::kVariationsName, IDS_VERSION_UI_VARIATIONS},
+    {version_ui::kVariationsCmdName, IDS_VERSION_UI_VARIATIONS_CMD},
+#if defined(OS_CHROMEOS)
+    {version_ui::kARC, IDS_ARC_LABEL},
+    {version_ui::kPlatform, IDS_PLATFORM_LABEL},
+    {version_ui::kCustomizationId, IDS_VERSION_UI_CUSTOMIZATION_ID},
+    {version_ui::kFirmwareVersion, IDS_VERSION_UI_FIRMWARE_VERSION},
+#else
+    {version_ui::kOSName, IDS_VERSION_UI_OS},
+#endif  // OS_CHROMEOS
+#if defined(OS_ANDROID)
+    {version_ui::kGmsName, IDS_VERSION_UI_GMS},
+#endif  // OS_ANDROID
+  };
+  AddLocalizedStringsBulk(html_source, kStrings, base::size(kStrings));
+
+  html_source->AddLocalizedString(version_ui::kOfficial,
+                                  version_info::IsOfficialBuild()
+                                      ? IDS_VERSION_UI_OFFICIAL
+                                      : IDS_VERSION_UI_UNOFFICIAL);
+  html_source->AddLocalizedString(
+      version_ui::kVersionBitSize,
+      sizeof(void*) == 8 ? IDS_VERSION_UI_64BIT : IDS_VERSION_UI_32BIT);
+
+  // Data strings.
   html_source->AddString(version_ui::kVersion,
                          version_info::GetVersionNumber());
   html_source->AddString(version_ui::kVersionModifier,
                          chrome::GetChannelName());
   html_source->AddString(version_ui::kJSEngine, "V8");
   html_source->AddString(version_ui::kJSVersion, V8_VERSION_STRING);
-  html_source->AddLocalizedString(version_ui::kCompany,
-                                  IDS_ABOUT_VERSION_COMPANY_NAME);
   html_source->AddString(
       version_ui::kCopyright,
       base::i18n::MessageFormatter::FormatWithNumberedArgs(
           l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT),
           base::Time::Now()));
-  html_source->AddLocalizedString(version_ui::kRevision,
-                                  IDS_VERSION_UI_REVISION);
   html_source->AddString(version_ui::kCL, version_info::GetLastChange());
-  html_source->AddLocalizedString(version_ui::kOfficial,
-                                  version_info::IsOfficialBuild()
-                                      ? IDS_VERSION_UI_OFFICIAL
-                                      : IDS_VERSION_UI_UNOFFICIAL);
-  html_source->AddLocalizedString(version_ui::kUserAgentName,
-                                  IDS_VERSION_UI_USER_AGENT);
   html_source->AddString(version_ui::kUserAgent, GetUserAgent());
-  html_source->AddLocalizedString(version_ui::kCommandLineName,
-                                  IDS_VERSION_UI_COMMAND_LINE);
   // Note that the executable path and profile path are retrieved asynchronously
   // and returned in VersionHandler::OnGotFilePaths. The area is initially
   // blank.
-  html_source->AddLocalizedString(version_ui::kExecutablePathName,
-                                  IDS_VERSION_UI_EXECUTABLE_PATH);
   html_source->AddString(version_ui::kExecutablePath, std::string());
-  html_source->AddLocalizedString(version_ui::kProfilePathName,
-                                  IDS_VERSION_UI_PROFILE_PATH);
   html_source->AddString(version_ui::kProfilePath, std::string());
-  html_source->AddLocalizedString(version_ui::kVariationsName,
-                                  IDS_VERSION_UI_VARIATIONS);
-  html_source->AddLocalizedString(version_ui::kVariationsCmdName,
-                                  IDS_VERSION_UI_VARIATIONS_CMD);
-#if defined(OS_CHROMEOS)
-  html_source->AddLocalizedString(version_ui::kARC, IDS_ARC_LABEL);
-  html_source->AddLocalizedString(version_ui::kPlatform, IDS_PLATFORM_LABEL);
-  html_source->AddLocalizedString(version_ui::kCustomizationId,
-                                  IDS_VERSION_UI_CUSTOMIZATION_ID);
-  html_source->AddLocalizedString(version_ui::kFirmwareVersion,
-                                  IDS_VERSION_UI_FIRMWARE_VERSION);
-#else
-  html_source->AddLocalizedString(version_ui::kOSName, IDS_VERSION_UI_OS);
-#endif  // OS_CHROMEOS
 
 #if defined(OS_MACOSX)
   html_source->AddString(version_ui::kOSType, base::mac::GetOSDisplayName());
@@ -117,7 +119,6 @@
 #if defined(OS_ANDROID)
   html_source->AddString(version_ui::kOSVersion,
                          AndroidAboutAppInfo::GetOsInfo());
-  html_source->AddLocalizedString(version_ui::kGmsName, IDS_VERSION_UI_GMS);
   html_source->AddString(version_ui::kGmsVersion,
                          AndroidAboutAppInfo::GetGmsInfo());
 #else
@@ -127,9 +128,6 @@
   html_source->AddString(version_ui::kFlashVersion, std::string());
 #endif  // OS_ANDROID
 
-  html_source->AddLocalizedString(
-      version_ui::kVersionBitSize,
-      sizeof(void*) == 8 ? IDS_VERSION_UI_64BIT : IDS_VERSION_UI_32BIT);
 
 #if defined(OS_WIN)
   html_source->AddString(
diff --git a/chrome/browser/ui/webui/welcome/welcome_win10_ui.cc b/chrome/browser/ui/webui/welcome/welcome_win10_ui.cc
index efabd67..12c4a63 100644
--- a/chrome/browser/ui/webui/welcome/welcome_win10_ui.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_win10_ui.cc
@@ -71,10 +71,6 @@
 
 WelcomeWin10UI::WelcomeWin10UI(content::WebUI* web_ui, const GURL& url)
     : content::WebUIController(web_ui) {
-  // Remember that the Win10 promo page has been shown.
-  g_browser_process->local_state()->SetBoolean(prefs::kHasSeenWin10PromoPage,
-                                               true);
-
   // Determine which variation to show.
   bool is_first_run = !UrlContainsKeyValueInQuery(url, "text", "faster");
 
diff --git a/chrome/browser/vr/service/browser_xr_runtime.cc b/chrome/browser/vr/service/browser_xr_runtime.cc
index 5c2ce64..9558057 100644
--- a/chrome/browser/vr/service/browser_xr_runtime.cc
+++ b/chrome/browser/vr/service/browser_xr_runtime.cc
@@ -17,14 +17,7 @@
 
 namespace vr {
 
-bool IsValidStandingTransform(std::vector<float> standingTransform) {
-  gfx::Transform transform = gfx::Transform(
-      standingTransform[0], standingTransform[1], standingTransform[2],
-      standingTransform[3], standingTransform[4], standingTransform[5],
-      standingTransform[6], standingTransform[7], standingTransform[8],
-      standingTransform[9], standingTransform[10], standingTransform[11],
-      standingTransform[12], standingTransform[13], standingTransform[14],
-      standingTransform[15]);
+bool IsValidStandingTransform(const gfx::Transform& transform) {
   if (!transform.IsInvertible() || transform.HasPerspective())
     return false;
 
@@ -88,11 +81,11 @@
 
   // Offset
   float kMaxOffset = 10;
-  if (eye->offset.size() == 3 && abs(eye->offset[0]) < kMaxOffset &&
-      abs(eye->offset[1]) < kMaxOffset && abs(eye->offset[2]) < kMaxOffset) {
+  if (abs(eye->offset.x()) < kMaxOffset && abs(eye->offset.y()) < kMaxOffset &&
+      abs(eye->offset.z()) < kMaxOffset) {
     ret->offset = eye->offset;
   } else {
-    ret->offset = std::vector<float>({0, 0, 0});
+    ret->offset = gfx::Vector3dF(0, 0, 0);
   }
 
   // Renderwidth/height
diff --git a/chrome/browser/vr/win/graphics_delegate_win.cc b/chrome/browser/vr/win/graphics_delegate_win.cc
index 8d3e2eb..9f4b23b 100644
--- a/chrome/browser/vr/win/graphics_delegate_win.cc
+++ b/chrome/browser/vr/win/graphics_delegate_win.cc
@@ -221,8 +221,7 @@
   // calculate the transform from head space to eye space.  For example,
   // (0, 0, 0) in head space is (-offset.x, -offset.y, -offset.z) in eye space,
   // and (offset.x, offset.y, offset.z) in head space is (0, 0, 0) in eye space.
-  eye_from_head.Translate3d(-eye_params->offset[0], -eye_params->offset[1],
-                            -eye_params->offset[2]);
+  eye_from_head.Translate3d(-eye_params->offset);
   model.view_matrix = eye_from_head * head_from_world;
 
   float up_tan =
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
index 083b287d..a234cf5 100644
--- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
+++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
@@ -29,7 +29,15 @@
 constexpr base::TimeDelta kWebVrSpinnerTimeout =
     base::TimeDelta::FromSeconds(2);
 
+constexpr float kEpsilon = 0.1f;
+constexpr float kMaxPosition = 1000000;
+constexpr float kMinPosition = -kMaxPosition;
 bool g_frame_timeout_ui_disabled_for_testing_ = false;
+
+bool InRange(float val, float min = kMinPosition, float max = kMaxPosition) {
+  return val > min && val < max;
+}
+
 }  // namespace
 
 namespace vr {
@@ -339,46 +347,34 @@
   ret->pose = device::mojom::VRPose::New();
 
   if (data->pose) {
-    if (data->pose->orientation && data->pose->orientation->size() == 4) {
-      float length = 0;
-      for (int i = 0; i < 4; ++i) {
-        length += (*data->pose->orientation)[i] * (*data->pose->orientation)[i];
-      }
-
-      float kEpsilson = 0.1f;
-      if (abs(length - 1) < kEpsilson) {
-        ret->pose->orientation = std::vector<float>{0, 0, 0, 1};
-        for (int i = 0; i < 4; ++i) {
-          (*ret->pose->orientation)[i] = (*data->pose->orientation)[i] / length;
-        }
+    if (data->pose->orientation) {
+      if (abs(data->pose->orientation->Length() - 1) < kEpsilon) {
+        ret->pose->orientation = data->pose->orientation->Normalized();
       }
     }
 
-    if (data->pose->position && data->pose->position->size() == 3) {
+    if (data->pose->position) {
       ret->pose->position = data->pose->position;
-      // We'll never give position values outside this range.
-      float kMaxPosition = 1000000;
-      float kMinPosition = -kMaxPosition;
-      for (int i = 0; i < 3; ++i) {
-        if (!((*ret->pose->position)[i] < kMaxPosition) ||
-            !((*ret->pose->position)[i] > kMinPosition)) {
-          ret->pose->position = base::nullopt;
-          // If testing with unexpectedly high values, catch on debug builds
-          // rather than silently change data.  On release builds its better to
-          // be safe and validate.
-          DCHECK(false);
-          break;
-        }
+
+      bool any_out_of_range = !(InRange(ret->pose->position->x()) &&
+                                InRange(ret->pose->position->y()) &&
+                                InRange(ret->pose->position->z()));
+      if (any_out_of_range) {
+        ret->pose->position = base::nullopt;
+        // If testing with unexpectedly high values, catch on debug builds
+        // rather than silently change data.  On release builds its better to
+        // be safe and validate.
+        DCHECK(false);
       }
     }
   }  // if (data->pose)
 
   if (!ret->pose->orientation) {
-    ret->pose->orientation = std::vector<float>{0, 0, 0, 1};
+    ret->pose->orientation = gfx::Quaternion();
   }
 
   if (!ret->pose->position) {
-    ret->pose->position = std::vector<float>{0, 0, 0};
+    ret->pose->position = gfx::Point3F();
   }
 
   ret->frame_id = data->frame_id;
@@ -411,19 +407,15 @@
   DCHECK(data->pose);
   DCHECK(data->pose->orientation);
   DCHECK(data->pose->position);
-  const std::vector<float>& quat = *data->pose->orientation;
-  const std::vector<float>& pos = *data->pose->position;
+  const gfx::Point3F& pos = *data->pose->position;
 
   // The incoming pose represents where the headset is in "world space".  So
   // we'll need to invert to get the view transform.
-
-  // Negating the w component will invert the rotation.
-  gfx::Transform head_from_unoriented_head(
-      gfx::Quaternion(quat[0], quat[1], quat[2], -quat[3]));
+  gfx::Transform head_from_unoriented_head(data->pose->orientation->inverse());
 
   // Negating all components will invert the translation.
   gfx::Transform unoriented_head_from_world;
-  unoriented_head_from_world.Translate3d(-pos[0], -pos[1], -pos[2]);
+  unoriented_head_from_world.Translate3d(-pos.x(), -pos.y(), -pos.z());
 
   // Compose these to get the base "view" matrix (before accounting for per-eye
   // transforms).
diff --git a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
index 929d0df..dd8098b 100644
--- a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/values.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h"
@@ -473,4 +474,41 @@
     EXPECT_EQ(it.second, kTabbedUrl);
 }
 
+TEST_F(WebAppPolicyManagerTest, InstallResultHistogram) {
+  base::HistogramTester histograms;
+  policy_manager()->Start();
+  {
+    base::Value list(base::Value::Type::LIST);
+    list.GetList().push_back(GetWindowedItem());
+    profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list));
+
+    histograms.ExpectTotalCount(
+        WebAppPolicyManager::kInstallResultHistogramName, 0);
+
+    base::RunLoop().RunUntilIdle();
+
+    histograms.ExpectTotalCount(
+        WebAppPolicyManager::kInstallResultHistogramName, 1);
+    histograms.ExpectBucketCount(
+        WebAppPolicyManager::kInstallResultHistogramName,
+        InstallResultCode::kSuccess, 1);
+  }
+  {
+    base::Value list(base::Value::Type::LIST);
+    list.GetList().push_back(GetTabbedItem());
+    list.GetList().push_back(GetNoContainerItem());
+    pending_app_manager()->SetInstallResultCode(
+        InstallResultCode::kInstallManagerDestroyed);
+
+    profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list));
+
+    base::RunLoop().RunUntilIdle();
+    histograms.ExpectTotalCount(
+        WebAppPolicyManager::kInstallResultHistogramName, 3);
+    histograms.ExpectBucketCount(
+        WebAppPolicyManager::kInstallResultHistogramName,
+        InstallResultCode::kInstallManagerDestroyed, 2);
+  }
+}
+
 }  // namespace web_app
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 373a3d73..206f41b23 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
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "chrome/browser/prefs/browser_prefs.h"
@@ -48,7 +49,7 @@
   options.add_to_desktop = false;
   options.add_to_quick_launch_bar = false;
   options.bypass_service_worker_check = true;
-  options.always_update = true;
+  options.force_reinstall = true;
   return options;
 }
 
@@ -235,7 +236,7 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(1u, install_requests.size());
-  EXPECT_TRUE(install_requests[0].always_update);
+  EXPECT_TRUE(install_requests[0].force_reinstall);
   EXPECT_TRUE(IsInstalled(kAppUrl1));
 
   // Create another app. The version hasn't changed, but we should immediately
@@ -247,8 +248,8 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(3u, install_requests.size());
-  EXPECT_FALSE(install_requests[1].always_update);
-  EXPECT_FALSE(install_requests[2].always_update);
+  EXPECT_FALSE(install_requests[1].force_reinstall);
+  EXPECT_FALSE(install_requests[2].force_reinstall);
   EXPECT_TRUE(IsInstalled(kAppUrl1));
   EXPECT_TRUE(IsInstalled(kAppUrl2));
 
@@ -259,8 +260,8 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(5u, install_requests.size());
-  EXPECT_TRUE(install_requests[3].always_update);
-  EXPECT_TRUE(install_requests[4].always_update);
+  EXPECT_TRUE(install_requests[3].force_reinstall);
+  EXPECT_TRUE(install_requests[4].force_reinstall);
   EXPECT_TRUE(IsInstalled(kAppUrl1));
   EXPECT_TRUE(IsInstalled(kAppUrl2));
 
@@ -282,8 +283,8 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(7u, install_requests.size());
-  EXPECT_FALSE(install_requests[5].always_update);
-  EXPECT_FALSE(install_requests[6].always_update);
+  EXPECT_FALSE(install_requests[5].force_reinstall);
+  EXPECT_FALSE(install_requests[6].force_reinstall);
   EXPECT_TRUE(IsInstalled(kAppUrl1));
   EXPECT_TRUE(IsInstalled(kAppUrl2));
 
@@ -295,11 +296,47 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(9u, install_requests.size());
-  EXPECT_FALSE(install_requests[7].always_update);
-  EXPECT_FALSE(install_requests[8].always_update);
+  EXPECT_FALSE(install_requests[7].force_reinstall);
+  EXPECT_FALSE(install_requests[8].force_reinstall);
   EXPECT_FALSE(IsInstalled(kAppUrl1));
   EXPECT_TRUE(IsInstalled(kAppUrl2));
   EXPECT_TRUE(IsInstalled(kAppUrl3));
 }
 
+TEST_F(SystemWebAppManagerTest, InstallResultHistogram) {
+  base::HistogramTester histograms;
+  {
+    base::flat_map<SystemAppType, SystemAppInfo> system_apps;
+    system_apps[SystemAppType::SETTINGS] = {kAppUrl1};
+    system_web_app_manager()->SetSystemApps(system_apps);
+
+    histograms.ExpectTotalCount(
+        SystemWebAppManager::kInstallResultHistogramName, 0);
+    system_web_app_manager()->Start(ui_delegate());
+    base::RunLoop().RunUntilIdle();
+
+    histograms.ExpectTotalCount(
+        SystemWebAppManager::kInstallResultHistogramName, 1);
+    histograms.ExpectBucketCount(
+        SystemWebAppManager::kInstallResultHistogramName,
+        InstallResultCode::kSuccess, 1);
+  }
+  {
+    base::flat_map<SystemAppType, SystemAppInfo> system_apps;
+    system_apps[SystemAppType::SETTINGS] = {kAppUrl1};
+    system_apps[SystemAppType::DISCOVER] = {kAppUrl2};
+    system_web_app_manager()->SetSystemApps(system_apps);
+    pending_app_manager()->SetInstallResultCode(
+        InstallResultCode::kInstallManagerDestroyed);
+
+    system_web_app_manager()->Start(ui_delegate());
+    base::RunLoop().RunUntilIdle();
+    histograms.ExpectTotalCount(
+        SystemWebAppManager::kInstallResultHistogramName, 3);
+    histograms.ExpectBucketCount(
+        SystemWebAppManager::kInstallResultHistogramName,
+        InstallResultCode::kInstallManagerDestroyed, 2);
+  }
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/install_options.cc b/chrome/browser/web_applications/components/install_options.cc
index 58ee0aa..9bb823c 100644
--- a/chrome/browser/web_applications/components/install_options.cc
+++ b/chrome/browser/web_applications/components/install_options.cc
@@ -31,15 +31,15 @@
   return std::tie(url, launch_container, install_source,
                   add_to_applications_menu, add_to_desktop,
                   add_to_quick_launch_bar, override_previous_user_uninstall,
-                  bypass_service_worker_check, require_manifest, always_update,
-                  wait_for_windows_closed, install_placeholder,
+                  bypass_service_worker_check, require_manifest,
+                  force_reinstall, wait_for_windows_closed, install_placeholder,
                   reinstall_placeholder) ==
          std::tie(other.url, other.launch_container, other.install_source,
                   other.add_to_applications_menu, other.add_to_desktop,
                   other.add_to_quick_launch_bar,
                   other.override_previous_user_uninstall,
                   other.bypass_service_worker_check, other.require_manifest,
-                  other.always_update, other.wait_for_windows_closed,
+                  other.force_reinstall, other.wait_for_windows_closed,
                   other.install_placeholder, other.reinstall_placeholder);
 }
 
@@ -59,7 +59,7 @@
              << "\n bypass_service_worker_check: "
              << install_options.bypass_service_worker_check
              << "\n require_manifest: " << install_options.require_manifest
-             << "\n always_update: " << install_options.always_update
+             << "\n force_reinstall: " << install_options.force_reinstall
              << "\n wait_for_windows_closed: "
              << install_options.wait_for_windows_closed
              << "\n install_placeholder: "
diff --git a/chrome/browser/web_applications/components/install_options.h b/chrome/browser/web_applications/components/install_options.h
index 366608e..9983f25 100644
--- a/chrome/browser/web_applications/components/install_options.h
+++ b/chrome/browser/web_applications/components/install_options.h
@@ -63,7 +63,7 @@
   bool require_manifest = false;
 
   // Whether the app should be reinstalled even if it is already installed.
-  bool always_update = false;
+  bool force_reinstall = false;
 
   // Whether we should wait for all app windows being closed before reinstalling
   // the placeholder.
diff --git a/chrome/browser/web_applications/components/pending_app_manager.cc b/chrome/browser/web_applications/components/pending_app_manager.cc
index 8b07ba0c6..4688730 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.cc
+++ b/chrome/browser/web_applications/components/pending_app_manager.cc
@@ -66,7 +66,8 @@
   if (urls_to_remove.empty() && desired_apps_install_options.empty()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(callback), SynchronizeResult::kSuccess));
+        base::BindOnce(std::move(callback), std::map<GURL, InstallResultCode>(),
+                       std::map<GURL, bool>()));
     return;
   }
 
@@ -89,29 +90,49 @@
 void PendingAppManager::InstallForSynchronizeCallback(InstallSource source,
                                                       const GURL& app_url,
                                                       InstallResultCode code) {
-  OnAppSynchronized(source, code == InstallResultCode::kSuccess ||
-                                code == InstallResultCode::kAlreadyInstalled);
+  switch (code) {
+    case InstallResultCode::kSuccess:
+    case InstallResultCode::kAlreadyInstalled:
+      break;
+    default:
+      LOG(ERROR) << app_url << " from install source "
+                 << static_cast<int>(source)
+                 << " failed to install with reason " << static_cast<int>(code);
+      break;
+  }
+
+  auto source_and_request = synchronize_requests_.find(source);
+  DCHECK(source_and_request != synchronize_requests_.end());
+  SynchronizeRequest& request = source_and_request->second;
+  request.install_results[app_url] = code;
+
+  OnAppSynchronized(source, app_url);
 }
 
 void PendingAppManager::UninstallForSynchronizeCallback(InstallSource source,
                                                         const GURL& app_url,
                                                         bool succeeded) {
-  OnAppSynchronized(source, succeeded);
+  auto source_and_request = synchronize_requests_.find(source);
+  DCHECK(source_and_request != synchronize_requests_.end());
+  SynchronizeRequest& request = source_and_request->second;
+  request.uninstall_results[app_url] = succeeded;
+
+  OnAppSynchronized(source, app_url);
 }
 
 void PendingAppManager::OnAppSynchronized(InstallSource source,
-                                          bool succeeded) {
-  auto it = synchronize_requests_.find(source);
-  DCHECK(it != synchronize_requests_.end());
+                                          const GURL& app_url) {
+  auto source_and_request = synchronize_requests_.find(source);
+  DCHECK(source_and_request != synchronize_requests_.end());
 
-  SynchronizeRequest& request = it->second;
-  if (!succeeded)
-    request.result = SynchronizeResult::kFailed;
-
+  SynchronizeRequest& request = source_and_request->second;
   DCHECK_GT(request.remaining_requests, 0);
+
   if (--request.remaining_requests == 0) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(request.callback), request.result));
+        FROM_HERE, base::BindOnce(std::move(request.callback),
+                                  std::move(request.install_results),
+                                  std::move(request.uninstall_results)));
     synchronize_requests_.erase(source);
   }
 }
diff --git a/chrome/browser/web_applications/components/pending_app_manager.h b/chrome/browser/web_applications/components/pending_app_manager.h
index 87834d8..6e533e30 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.h
+++ b/chrome/browser/web_applications/components/pending_app_manager.h
@@ -32,11 +32,6 @@
 // should wait for the update request to finish before uninstalling the app.
 class PendingAppManager {
  public:
-  enum class SynchronizeResult {
-    kSuccess = 0,
-    kFailed = 1,
-  };
-
   using OnceInstallCallback =
       base::OnceCallback<void(const GURL& app_url, InstallResultCode code)>;
   using RepeatingInstallCallback =
@@ -45,7 +40,8 @@
   using UninstallCallback =
       base::RepeatingCallback<void(const GURL& app_url, bool succeeded)>;
   using SynchronizeCallback =
-      base::OnceCallback<void(SynchronizeResult result)>;
+      base::OnceCallback<void(std::map<GURL, InstallResultCode> install_results,
+                              std::map<GURL, bool> uninstall_results)>;
 
   explicit PendingAppManager(AppRegistrar* registrar);
   virtual ~PendingAppManager();
@@ -109,7 +105,8 @@
 
     SynchronizeCallback callback;
     int remaining_requests;
-    SynchronizeResult result = SynchronizeResult::kSuccess;
+    std::map<GURL, InstallResultCode> install_results;
+    std::map<GURL, bool> uninstall_results;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(SynchronizeRequest);
@@ -121,7 +118,7 @@
   void UninstallForSynchronizeCallback(InstallSource source,
                                        const GURL& app_url,
                                        bool succeeded);
-  void OnAppSynchronized(InstallSource source, bool succeeded);
+  void OnAppSynchronized(InstallSource source, const GURL& app_url);
 
   AppRegistrar* registrar_;
 
diff --git a/chrome/browser/web_applications/components/pending_app_manager_unittest.cc b/chrome/browser/web_applications/components/pending_app_manager_unittest.cc
index 9456752c..b093001 100644
--- a/chrome/browser/web_applications/components/pending_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/components/pending_app_manager_unittest.cc
@@ -37,8 +37,8 @@
     pending_app_manager_.SynchronizeInstalledApps(
         std::move(install_options_list), InstallSource::kInternal,
         base::BindLambdaForTesting(
-            [&run_loop](PendingAppManager::SynchronizeResult result) {
-              ASSERT_EQ(PendingAppManager::SynchronizeResult::kSuccess, result);
+            [&run_loop, urls](std::map<GURL, InstallResultCode> install_results,
+                              std::map<GURL, bool> uninstall_results) {
               run_loop.Quit();
             }));
     // Wait for SynchronizeInstalledApps to finish.
@@ -107,10 +107,8 @@
     pending_app_manager->SynchronizeInstalledApps(
         std::move(install_options_list), InstallSource::kInternal,
         base::BindLambdaForTesting(
-            [&](PendingAppManager::SynchronizeResult result) {
-              ASSERT_EQ(PendingAppManager::SynchronizeResult::kSuccess, result);
-              run_loop.Quit();
-            }));
+            [&](std::map<GURL, InstallResultCode> install_results,
+                std::map<GURL, bool> uninstall_results) { run_loop.Quit(); }));
     run_loop.Run();
   }
 
diff --git a/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
index be28327..7ec3421 100644
--- a/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/web_applications/components/install_options.h"
 #include "chrome/browser/web_applications/components/policy/web_app_policy_constants.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/browser/web_applications/components/web_app_install_utils.h"
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
@@ -68,6 +69,8 @@
 
 }  // namespace
 
+const char WebAppPolicyManager::kInstallResultHistogramName[];
+
 WebAppPolicyManager::WebAppPolicyManager(Profile* profile,
                                          PendingAppManager* pending_app_manager)
     : profile_(profile),
@@ -163,10 +166,14 @@
 }
 
 void WebAppPolicyManager::OnAppsSynchronized(
-    PendingAppManager::SynchronizeResult result) {
+    std::map<GURL, InstallResultCode> install_results,
+    std::map<GURL, bool> uninstall_results) {
   is_refreshing_ = false;
   if (needs_refresh_)
     RefreshPolicyInstalledApps();
+
+  RecordExternalAppInstallResultCode(kInstallResultHistogramName,
+                                     install_results);
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/policy/web_app_policy_manager.h b/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
index 1f67c42..92ef8818 100644
--- a/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
+++ b/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
@@ -27,6 +27,9 @@
 // apps to install, uninstall, and update, via a PendingAppManager.
 class WebAppPolicyManager {
  public:
+  static constexpr char kInstallResultHistogramName[] =
+      "Webapp.InstallResult.Policy";
+
   // Constructs a WebAppPolicyManager instance that uses
   // |pending_app_manager| to manage apps. |pending_app_manager| should outlive
   // this class.
@@ -43,7 +46,8 @@
   void InitChangeRegistrarAndRefreshPolicyInstalledApps();
 
   void RefreshPolicyInstalledApps();
-  void OnAppsSynchronized(PendingAppManager::SynchronizeResult result);
+  void OnAppsSynchronized(std::map<GURL, InstallResultCode> install_results,
+                          std::map<GURL, bool> uninstall_results);
 
   Profile* profile_;
   PrefService* pref_service_;
diff --git a/chrome/browser/web_applications/components/web_app_install_utils.cc b/chrome/browser/web_applications/components/web_app_install_utils.cc
index 1aacdde..0be6d29 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils.cc
+++ b/chrome/browser/web_applications/components/web_app_install_utils.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
 #include "base/time/time.h"
 #include "chrome/browser/banners/app_banner_manager.h"
@@ -14,6 +15,7 @@
 #include "chrome/browser/installable/installable_data.h"
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/web_applications/components/install_options.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_icon_generator.h"
 #include "chrome/common/web_application_info.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
@@ -227,4 +229,11 @@
   return metrics_install_source;
 }
 
+void RecordExternalAppInstallResultCode(
+    const char* histogram_name,
+    std::map<GURL, InstallResultCode> install_results) {
+  for (const auto& url_and_result : install_results)
+    base::UmaHistogramEnumeration(histogram_name, url_and_result.second);
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_install_utils.h b/chrome/browser/web_applications/components/web_app_install_utils.h
index 32cfd77..5830b59 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils.h
+++ b/chrome/browser/web_applications/components/web_app_install_utils.h
@@ -26,6 +26,8 @@
 
 namespace web_app {
 
+enum class InstallResultCode;
+
 struct BitmapAndSource;
 struct InstallOptions;
 
@@ -97,6 +99,10 @@
 WebappInstallSource ConvertOptionsToMetricsInstallSource(
     const InstallOptions& options);
 
+void RecordExternalAppInstallResultCode(
+    const char* histogram_name,
+    std::map<GURL, InstallResultCode> install_results);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_INSTALL_UTILS_H_
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
index 1801d5b9..5036816 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
@@ -133,7 +133,7 @@
     const web_app::InstallOptions& install_options =
         front->task->install_options();
 
-    if (install_options.always_update) {
+    if (install_options.force_reinstall) {
       StartInstallationTask(std::move(front));
       return;
     }
@@ -229,11 +229,12 @@
 
 void PendingBookmarkAppManager::OnInstalled(
     BookmarkAppInstallationTask::Result result) {
-  CurrentInstallationFinished(result.app_id);
+  CurrentInstallationFinished(result.app_id, result.code);
 }
 
 void PendingBookmarkAppManager::CurrentInstallationFinished(
-    const base::Optional<web_app::AppId>& app_id) {
+    const base::Optional<web_app::AppId>& app_id,
+    web_app::InstallResultCode code) {
   // Post a task to avoid InstallableManager crashing and do so before
   // running the callback in case the callback tries to install another
   // app.
@@ -244,14 +245,10 @@
       base::BindOnce(&PendingBookmarkAppManager::MaybeStartNextInstallation,
                      weak_ptr_factory_.GetWeakPtr()));
 
-  auto install_result_code =
-      app_id ? web_app::InstallResultCode::kSuccess
-             : web_app::InstallResultCode::kFailedUnknownReason;
-
   std::unique_ptr<TaskAndCallback> task_and_callback;
   task_and_callback.swap(current_task_and_callback_);
   std::move(task_and_callback->callback)
-      .Run(task_and_callback->task->install_options().url, install_result_code);
+      .Run(task_and_callback->task->install_options().url, code);
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
index e62ee81..675600c7 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
@@ -89,7 +89,8 @@
 
   void OnInstalled(BookmarkAppInstallationTask::Result result);
 
-  void CurrentInstallationFinished(const base::Optional<std::string>& app_id);
+  void CurrentInstallationFinished(const base::Optional<std::string>& app_id,
+                                   web_app::InstallResultCode code);
 
   Profile* profile_;
   web_app::InstallFinalizer* install_finalizer_;
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_browsertest.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_browsertest.cc
index c6bbfe0..8e8f9f9c 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_browsertest.cc
@@ -122,14 +122,14 @@
   EXPECT_FALSE(app);
 }
 
-IN_PROC_BROWSER_TEST_F(PendingBookmarkAppManagerBrowserTest, AlwaysUpdate) {
+IN_PROC_BROWSER_TEST_F(PendingBookmarkAppManagerBrowserTest, ForceReinstall) {
   ASSERT_TRUE(embedded_test_server()->Start());
   {
     GURL url(embedded_test_server()->GetURL(
         "/banners/"
         "manifest_test_page.html?manifest=manifest_short_name_only.json"));
     web_app::InstallOptions install_options = CreateInstallOptions(url);
-    install_options.always_update = true;
+    install_options.force_reinstall = true;
     InstallApp(std::move(install_options));
 
     const extensions::Extension* app =
@@ -141,7 +141,7 @@
     GURL url(
         embedded_test_server()->GetURL("/banners/manifest_test_page.html"));
     web_app::InstallOptions install_options = CreateInstallOptions(url);
-    install_options.always_update = true;
+    install_options.force_reinstall = true;
     InstallApp(std::move(install_options));
 
     const extensions::Extension* app =
@@ -186,7 +186,7 @@
   install_options.require_manifest = true;
   InstallApp(std::move(install_options));
 
-  EXPECT_EQ(web_app::InstallResultCode::kFailedUnknownReason,
+  EXPECT_EQ(web_app::InstallResultCode::kNotValidManifestForWebApp,
             result_code_.value());
   base::Optional<web_app::AppId> id =
       web_app::ExternallyInstalledWebAppPrefs(browser()->profile()->GetPrefs())
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
index 6dc8f18..ba3c7e0 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
@@ -651,11 +651,11 @@
   url_loader()->SetNextLoadUrlResult(
       kFooWebAppUrl, web_app::WebAppUrlLoader::Result::kUrlLoaded);
 
-  auto get_always_update_info = []() {
+  auto get_force_reinstall_info = []() {
     web_app::InstallOptions options(kFooWebAppUrl,
                                     web_app::LaunchContainer::kWindow,
                                     web_app::InstallSource::kExternalPolicy);
-    options.always_update = true;
+    options.force_reinstall = true;
     return options;
   };
 
@@ -663,13 +663,13 @@
     base::Optional<GURL> url;
     base::Optional<web_app::InstallResultCode> code;
     std::tie(url, code) =
-        InstallAndWait(pending_app_manager.get(), get_always_update_info());
+        InstallAndWait(pending_app_manager.get(), get_force_reinstall_info());
 
     EXPECT_EQ(web_app::InstallResultCode::kSuccess, code);
     EXPECT_EQ(kFooWebAppUrl, url);
 
     EXPECT_EQ(1u, install_run_count());
-    EXPECT_EQ(get_always_update_info(), last_install_options());
+    EXPECT_EQ(get_force_reinstall_info(), last_install_options());
   }
 
   task_factory()->SetNextInstallationTaskResult(
@@ -680,14 +680,14 @@
     base::Optional<GURL> url;
     base::Optional<web_app::InstallResultCode> code;
     std::tie(url, code) =
-        InstallAndWait(pending_app_manager.get(), get_always_update_info());
+        InstallAndWait(pending_app_manager.get(), get_force_reinstall_info());
 
     EXPECT_EQ(web_app::InstallResultCode::kSuccess, code);
     EXPECT_EQ(kFooWebAppUrl, url);
 
-    // The app should be installed again because of the |always_update| flag.
+    // The app should be installed again because of the |force_reinstall| flag.
     EXPECT_EQ(2u, install_run_count());
-    EXPECT_EQ(get_always_update_info(), last_install_options());
+    EXPECT_EQ(get_force_reinstall_info(), last_install_options());
   }
 }
 
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index 1c3d893..92d473d 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/browser/web_applications/components/web_app_install_utils.h"
 #include "chrome/browser/web_applications/components/web_app_ui_delegate.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
@@ -65,12 +66,14 @@
   install_options.add_to_desktop = false;
   install_options.add_to_quick_launch_bar = false;
   install_options.bypass_service_worker_check = true;
-  install_options.always_update = force_update;
+  install_options.force_reinstall = force_update;
   return install_options;
 }
 
 }  // namespace
 
+const char SystemWebAppManager::kInstallResultHistogramName[];
+
 SystemWebAppManager::SystemWebAppManager(Profile* profile,
                                          PendingAppManager* pending_app_manager)
     : on_apps_synchronized_(new base::OneShotEvent()),
@@ -183,12 +186,16 @@
 
 void SystemWebAppManager::OnAppsSynchronized(
     std::set<SystemAppType> already_installed,
-    PendingAppManager::SynchronizeResult result) {
+    std::map<GURL, InstallResultCode> install_results,
+    std::map<GURL, bool> uninstall_results) {
   if (IsEnabled()) {
     pref_service_->SetString(prefs::kSystemWebAppLastUpdateVersion,
                              CurrentVersion().GetString());
   }
 
+  RecordExternalAppInstallResultCode(kInstallResultHistogramName,
+                                     install_results);
+
   MigrateSystemWebApps(already_installed);
 
   // May be called more than once in tests.
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index 597c06e1..81052f30 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -58,6 +58,9 @@
     kOnVersionChange,
   };
 
+  static constexpr char kInstallResultHistogramName[] =
+      "Webapp.InstallResult.System";
+
   // Constructs a SystemWebAppManager instance that uses
   // |pending_app_manager| to manage apps. |pending_app_manager| should outlive
   // this class.
@@ -100,7 +103,8 @@
 
  private:
   void OnAppsSynchronized(std::set<SystemAppType> already_installed,
-                          PendingAppManager::SynchronizeResult result);
+                          std::map<GURL, InstallResultCode> install_results,
+                          std::map<GURL, bool> uninstall_results);
   bool NeedsUpdate() const;
 
   // TODO(calamity): Move migration into the install task once the install task
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index b30ceb1a..f5be530a 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/web_applications/components/web_app_audio_focus_id_map.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/components/web_app_install_utils.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
 #include "chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h"
 #include "chrome/browser/web_applications/extensions/bookmark_app_registrar.h"
@@ -44,6 +45,17 @@
 
 namespace web_app {
 
+namespace {
+
+void OnExternalWebAppsSynchronized(
+    std::map<GURL, InstallResultCode> install_results,
+    std::map<GURL, bool> uninstall_results) {
+  RecordExternalAppInstallResultCode("Webapp.InstallResult.Default",
+                                     install_results);
+}
+
+}  // namespace
+
 // static
 WebAppProvider* WebAppProvider::Get(Profile* profile) {
   return WebAppProviderFactory::GetForProfile(profile);
@@ -239,7 +251,7 @@
     std::vector<InstallOptions> desired_apps_install_options) {
   pending_app_manager_->SynchronizeInstalledApps(
       std::move(desired_apps_install_options), InstallSource::kExternalDefault,
-      base::DoNothing());
+      base::BindOnce(&OnExternalWebAppsSynchronized));
 }
 
 }  // namespace web_app
diff --git a/chrome/child/BUILD.gn b/chrome/child/BUILD.gn
index f00de1b..a6f9c65 100644
--- a/chrome/child/BUILD.gn
+++ b/chrome/child/BUILD.gn
@@ -14,6 +14,7 @@
 
   if (is_win) {
     sources += [
+      "delay_load_failure_hook.cc",
       "v8_crashpad_support_win.cc",
       "v8_crashpad_support_win.h",
     ]
diff --git a/chrome/child/delay_load_failure_hook.cc b/chrome/child/delay_load_failure_hook.cc
new file mode 100644
index 0000000..ef51334
--- /dev/null
+++ b/chrome/child/delay_load_failure_hook.cc
@@ -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.
+
+// windows.h needs to be included before delayimp.h.
+#include <windows.h>
+
+#include <delayimp.h>
+
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+
+namespace {
+
+// Delay load failure hook that generates a crash report. By default a failure
+// to delay load will trigger an exception handled by the delay load runtime and
+// this won't generate a crash report.
+extern "C" FARPROC WINAPI DelayLoadFailureHook(unsigned reason,
+                                               DelayLoadInfo* dll_info) {
+  char dll_name[MAX_PATH];
+  base::strlcpy(dll_name, dll_info->szDll, base::size(dll_name));
+  // It's not an error if "bthprops.cpl" fails to be loaded, there's a custom
+  // exception handler in 'device/bluetooth/bluetooth_init_win.cc" that will
+  // intercept the exception triggered by the delay load runtime. Returning 0
+  // will tell the runtime that this failure hasn't been handled and it'll cause
+  // the exception to be raised.
+  if (base::CompareCaseInsensitiveASCII(dll_name, "bthprops.cpl") == 0)
+    return 0;
+
+  base::debug::Alias(&dll_name);
+  CHECK(false);
+
+  return 0;
+}
+
+}  // namespace
+
+// Set the delay load failure hook to the function above.
+//
+// The |__pfnDliFailureHook2| failure notification hook gets called
+// automatically by the delay load runtime in case of failure, see
+// https://docs.microsoft.com/en-us/cpp/build/reference/failure-hooks?view=vs-2019
+// for more information about this.
+extern "C" const PfnDliHook __pfnDliFailureHook2 = DelayLoadFailureHook;
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index b0e032b..1e9bc3b9 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -81,10 +81,6 @@
 #include "components/nacl/common/nacl_process_type.h"
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/common/features/feature_util.h"
-#endif
-
 #if BUILDFLAG(ENABLE_PLUGINS)
 #include "content/public/common/pepper_plugin_info.h"
 #include "flapper_version.h"  // nogncheck  In SHARED_INTERMEDIATE_DIR.
@@ -633,8 +629,7 @@
   schemes->secure_schemes.push_back(chrome::kChromeNativeScheme);
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  if (extensions::feature_util::ExtensionServiceWorkersEnabled())
-    schemes->service_worker_schemes.push_back(extensions::kExtensionScheme);
+  schemes->service_worker_schemes.push_back(extensions::kExtensionScheme);
 
   // As far as Blink is concerned, they should be allowed to receive CORS
   // requests. At the Extensions layer, requests will actually be blocked unless
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index d31e143..c34ac4fa6 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -195,6 +195,11 @@
 const base::Feature kCrostiniAppUninstallGui{"CrostiniAppUninstallGui",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables infrastructure for applying Ansible playbook to default Crostini
+// container.
+const base::Feature kCrostiniAnsibleInfrastructure{
+    "CrostiniAnsibleInfrastructure", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables or disables the UI overhaul for Cups Printers in settings page.
 const base::Feature kCupsPrintersUiOverhaul{"CupsPrintersUiOverhaul",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
@@ -352,10 +357,9 @@
 
 // Enables navigation suggestions UI for lookalike URLs (e.g. internationalized
 // domain names that are visually similar to popular domains or to domains with
-// engagement score, such as googlé.com). This is prefixed with V2 because the
-// client logic handling the feature was changed.
+// engagement score, such as googlé.com).
 const base::Feature kLookalikeUrlNavigationSuggestionsUI{
-    "LookalikeUrlNavigationSuggestionsUI", base::FEATURE_DISABLED_BY_DEFAULT};
+    "LookalikeUrlNavigationSuggestionsUI", base::FEATURE_ENABLED_BY_DEFAULT};
 
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
 // A feature that controls whether Chrome warns about incompatible applications.
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 46d9417..2f6fc351 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -118,6 +118,8 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kCrostiniAppUninstallGui;
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kCrostiniAnsibleInfrastructure;
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kCupsPrintersUiOverhaul;
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kPluginVm;
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 562ecff..40a3933c 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -47,6 +47,9 @@
   error_invalid_archive
 };
 
+// Filesystem to format to.
+enum FormatFileSystemType { vfat, exfat, ntfs };
+
 // File transfer progress state.
 enum TransferState { in_progress, completed, failed };
 
@@ -1019,7 +1022,11 @@
 
   // Formats a mounted volume.
   // |volumeId| ID of the volume to be formatted.
-  static void formatVolume(DOMString volumeId);
+  // |filesystem| Filesystem type to be formatted to.
+  // |volumeLabel| Label of the drive after formatting.
+  static void formatVolume(DOMString volumeId,
+                           FormatFileSystemType filesystem,
+                           DOMString volumeLabel);
 
   // Renames a mounted volume.
   // |volumeId| ID of the volume to be renamed.
diff --git a/chrome/common/extensions/api/login.idl b/chrome/common/extensions/api/login.idl
index 28770099..8beca81 100644
--- a/chrome/common/extensions/api/login.idl
+++ b/chrome/common/extensions/api/login.idl
@@ -4,7 +4,7 @@
 
 // Use the <code>chrome.login</code> API to launch and exit user sessions.
 [platforms=("chromeos"),
- implemented_in="chrome/browser/chromeos/extensions/login/login_api.h"]
+ implemented_in="chrome/browser/chromeos/extensions/login_screen/login/login_api.h"]
 namespace login {
   callback VoidCallback = void ();
   callback BooleanCallback = void (boolean result);
diff --git a/chrome/common/extensions/api/login_screen_ui.idl b/chrome/common/extensions/api/login_screen_ui.idl
index 1e42b13d..f6c9dec 100644
--- a/chrome/common/extensions/api/login_screen_ui.idl
+++ b/chrome/common/extensions/api/login_screen_ui.idl
@@ -5,7 +5,7 @@
 // Use the <code>chrome.loginScreenUi</code> API to show and hide custom
 // login UI.
 [platforms=("chromeos"),
- implemented_in="chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.h"]
+ implemented_in="chrome/browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_api.h"]
 namespace loginScreenUi {
 
   dictionary ShowOptions {
diff --git a/chrome/common/extensions/api/windows.json b/chrome/common/extensions/api/windows.json
index 3d3df03..23c4c82 100644
--- a/chrome/common/extensions/api/windows.json
+++ b/chrome/common/extensions/api/windows.json
@@ -53,9 +53,6 @@
           "name": "fullscreen",
           "description": "Fullscreen window state."
         }, {
-          "name": "docked",
-          "description": "<i>Deprecated since Chrome M59.</i> Docked windows are no longer supported. This state will be converted to \"normal\"."
-        }, {
           "name": "locked-fullscreen",
           "nodoc": true,
           "description": "Locked fullscreen window state. This fullscreen state cannot be exited by user action and is available only to allowlisted extensions on Chrome OS."
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index 173b5041..d7a7e0f 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -212,9 +212,10 @@
     }
   }
   // If all went well, the symlink no longer exists.  Recreate it.
-  if (!base::CreateSymbolicLink(target_path, symlink_path)) {
+  base::FilePath relative_target_path = target_path.BaseName();
+  if (!base::CreateSymbolicLink(relative_target_path, symlink_path)) {
     DPLOG(ERROR) << "Unable to create symlink " << symlink_path.value()
-                 << " pointing at " << target_path.value();
+                 << " pointing at " << relative_target_path.value();
   }
   return target_path;
 }
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 998abe35..d4bbb09 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1408,17 +1408,12 @@
 const char kHasSeenWelcomePage[] = "browser.has_seen_welcome_page";
 #endif
 
-#if defined(OS_WIN)
-// Whether or not this profile has been shown the Win10 promo page.
-const char kHasSeenWin10PromoPage[] = "browser.has_seen_win10_promo_page";
-
-#if defined(GOOGLE_CHROME_BUILD)
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
 // Put the user into an onboarding group that's decided when they go through
 // the first run onboarding experience. Only users in a group will have their
 // finch group pinged to keep track of them for the experiment.
 const char kNaviOnboardGroup[] = "browser.navi_onboard_group";
-#endif  // defined(GOOGLE_CHROME_BUILD)
-#endif  // defined(OS_WIN)
+#endif  // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
 
 // *************** LOCAL STATE ***************
 // These are attached to the machine/installation
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 1f84118..01f42dc48 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -463,12 +463,9 @@
 extern const char kHasSeenWelcomePage[];
 #endif
 
-#if defined(OS_WIN)
-extern const char kHasSeenWin10PromoPage[];
-#if defined(GOOGLE_CHROME_BUILD)
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
 extern const char kNaviOnboardGroup[];
-#endif  // defined(GOOGLE_CHROME_BUILD)
-#endif  // defined(OS_WIN)
+#endif  // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
 
 // Deprecated preference for metric / crash reporting on Android. Use
 // kMetricsReportingEnabled instead.
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index dcd38c0..6f8034f4f 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -69,6 +69,8 @@
 const char kChromeUIExtensionsURL[] = "chrome://extensions/";
 const char kChromeUIFaviconHost[] = "favicon";
 const char kChromeUIFaviconURL[] = "chrome://favicon/";
+const char kChromeUIFavicon2Host[] = "favicon2";
+const char kChromeUIFavicon2URL[] = "chrome://favicon2/";
 const char kChromeUIFileiconURL[] = "chrome://fileicon/";
 const char kChromeUIFlagsHost[] = "flags";
 const char kChromeUIFlagsURL[] = "chrome://flags/";
@@ -205,6 +207,9 @@
 const char kChromeUICertificateManagerDialogURL[] =
     "chrome://certificate-manager/";
 const char kChromeUICertificateManagerHost[] = "certificate-manager";
+const char kChromeUIConfirmPasswordChangeHost[] = "confirm-password-change";
+const char kChromeUIConfirmPasswordChangeUrl[] =
+    "chrome://confirm-password-change";
 const char kChromeUICryptohomeHost[] = "cryptohome";
 const char kChromeUIDeviceEmulatorHost[] = "device-emulator";
 const char kChromeUIDiscoverURL[] = "chrome://oobe/discover";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 9aad93f0..19fe755 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -75,6 +75,8 @@
 extern const char kChromeUIExtensionsURL[];
 extern const char kChromeUIFaviconHost[];
 extern const char kChromeUIFaviconURL[];
+extern const char kChromeUIFavicon2Host[];
+extern const char kChromeUIFavicon2URL[];
 extern const char kChromeUIFileiconURL[];
 extern const char kChromeUIFlagsHost[];
 extern const char kChromeUIFlagsURL[];
@@ -203,6 +205,8 @@
 extern const char kChromeUICellularSetupUrl[];
 extern const char kChromeUICertificateManagerDialogURL[];
 extern const char kChromeUICertificateManagerHost[];
+extern const char kChromeUIConfirmPasswordChangeHost[];
+extern const char kChromeUIConfirmPasswordChangeUrl[];
 extern const char kChromeUICryptohomeHost[];
 extern const char kChromeUIDeviceEmulatorHost[];
 extern const char kChromeUIDiscoverURL[];
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.h b/chrome/credential_provider/gaiacp/associated_user_validator.h
index 73565c0f..fc8ca9b8 100644
--- a/chrome/credential_provider/gaiacp/associated_user_validator.h
+++ b/chrome/credential_provider/gaiacp/associated_user_validator.h
@@ -130,6 +130,8 @@
   // tests).
   bool IsDenyAccessUpdateBlocked() const;
 
+  bool HasInternetConnection() const;
+
  protected:
   // Returns the storage used for the instance pointer.
   static AssociatedUserValidator** GetInstanceStorage();
@@ -148,7 +150,6 @@
  private:
   bool IsTokenHandleValidForUserInternal(const base::string16& sid);
 
-  bool HasInternetConnection() const;
   void CheckTokenHandleValidity(
       const std::map<base::string16, base::string16>& handles_to_verify);
   void StartTokenValidityQuery(const base::string16& sid,
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
index a09208b9..f7c38df0 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -96,12 +96,6 @@
   return base::string16(&email_domains[0]);
 }
 
-bool EnableAdToGoogleAssociation() {
-  DWORD enable_ad_association = 0;
-  HRESULT hr = GetGlobalFlag(kRegEnableADAssociation, &enable_ad_association);
-  return SUCCEEDED(hr) && enable_ad_association;
-}
-
 // Use WinHttpUrlFetcher to communicate with the admin sdk and fetch the active
 // directory UPN from the admin configured custom attributes.
 HRESULT GetAdUpnFromCloudDirectory(const base::string16& email,
@@ -316,7 +310,7 @@
     LOGFN(INFO) << "Found existing SID created in GCPW registry entry = "
                 << sid;
     has_existing_user_sid = true;
-  } else if (EnableAdToGoogleAssociation() &&
+  } else if (CGaiaCredentialBase::IsAdToGoogleAssociationEnabled() &&
              OSUserManager::Get()->IsDeviceDomainJoined()) {
     LOGFN(INFO) << "No existing SID found in the GCPW registry.";
 
@@ -608,6 +602,12 @@
 CGaiaCredentialBase::UIProcessInfo::~UIProcessInfo() {}
 
 // static
+bool CGaiaCredentialBase::IsAdToGoogleAssociationEnabled() {
+  DWORD enable_ad_association = 0;
+  return GetGlobalFlagOrDefault(kRegEnableADAssociation, enable_ad_association);
+}
+
+// static
 HRESULT CGaiaCredentialBase::OnDllRegisterServer() {
   OSUserManager* manager = OSUserManager::Get();
 
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.h b/chrome/credential_provider/gaiacp/gaia_credential_base.h
index 051bdf5..f5c768e4 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.h
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.h
@@ -72,6 +72,9 @@
     StdParentHandles parent_handles;
   };
 
+  // Returns true if "enable_ad_association" registry key is set to 1.
+  static bool IsAdToGoogleAssociationEnabled();
+
  protected:
   CGaiaCredentialBase();
   ~CGaiaCredentialBase();
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider.cc
index ff8a855..30f0203b 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.cc
@@ -90,6 +90,9 @@
     hr = reauth->SetEmailForReauth(CComBSTR(email));
     if (FAILED(hr))
       LOGFN(ERROR) << "reauth->SetEmailForReauth hr=" << putHR(hr);
+  } else {
+    LOGFN(INFO) << "reauth for sid " << sid
+                << " doesn't contain the email association";
   }
 
   return S_OK;
@@ -382,6 +385,12 @@
 
   LOGFN(INFO) << "count=" << count;
 
+  if (!AssociatedUserValidator::Get()->HasInternetConnection()) {
+    // When there is no internet, do not associate GCPW as a reauth
+    // credential for all sids.
+    return S_OK;
+  }
+
   for (DWORD i = 0; i < count; ++i) {
     CComPtr<ICredentialProviderUser> user;
     hr = users->GetAt(i, &user);
@@ -411,10 +420,32 @@
       continue;
     }
 
-    // If the token handle is valid, no need to create a reauth credential.
-    // The user can just sign in using their password.
-    if (AssociatedUserValidator::Get()->IsTokenHandleValidForUser(sid))
+    // Get the user's gaia id from registry stored against the sid if it
+    // exists.
+    wchar_t user_id[64];
+    ULONG user_id_length = base::size(user_id);
+    hr = GetUserProperty(sid.c_str(), kUserId, user_id, &user_id_length);
+    if (FAILED(hr))
+      user_id[0] = L'\0';
+
+    bool is_token_handle_valid_for_user =
+        (AssociatedUserValidator::Get()->IsTokenHandleValidForUser(sid));
+
+    // (1) For a domain joined user, only check for token validity if the
+    // user id is not empty. If user id is empty, we should create the
+    // reauth credential by default for all AD user sids.
+    // (2) For a non-domain joined user, just check if the token handle is
+    // valid. If valid, then no need to create a re-auth credential for
+    // this sid.
+    if (CGaiaCredentialBase::IsAdToGoogleAssociationEnabled() &&
+        OSUserManager::Get()->IsUserDomainJoined(sid)) {
+      if (user_id[0] && is_token_handle_valid_for_user)
+        continue;
+    } else if (is_token_handle_valid_for_user) {
+      // If the token handle is valid, no need to create a reauth credential.
+      // The user can just sign in using their password.
       continue;
+    }
 
     GaiaCredentialComPtrStorage cred;
     HRESULT hr =
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
index fd92c70..7a13a4cc 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
@@ -497,23 +497,43 @@
 // 2. bool - is the token handle for the fake user valid (i.e. the fetch of
 // the token handle info from win_http_url_fetcher returns a valid json).
 // 3. bool - is internet available.
+// 4. bool - is active directory user.
 
 class GcpCredentialProviderWithGaiaUsersTest
     : public GcpCredentialProviderTest,
-      public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {};
+      public ::testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
+ protected:
+  void SetUp() override;
+};
+
+void GcpCredentialProviderWithGaiaUsersTest::SetUp() {
+  GcpCredentialProviderTest::SetUp();
+  ASSERT_EQ(S_OK, SetGlobalFlagForTesting(L"enable_ad_association", 0));
+}
 
 TEST_P(GcpCredentialProviderWithGaiaUsersTest, ReauthCredentialTest) {
   const bool has_token_handle = std::get<0>(GetParam());
   const bool valid_token_handle = std::get<1>(GetParam());
   const bool has_internet = std::get<2>(GetParam());
+  const bool is_ad_user = std::get<3>(GetParam());
   fake_internet_checker()->SetHasInternetConnection(
       has_internet ? FakeInternetAvailabilityChecker::kHicForceYes
                    : FakeInternetAvailabilityChecker::kHicForceNo);
 
   CComBSTR sid;
-  ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
-                      L"username", L"password", L"full name", L"comment",
-                      L"gaia-id", L"foo@gmail.com", &sid));
+  if (is_ad_user) {
+    // Add an AD user. Note that this covers the scenario where
+    // enable_ad_association is set to false.
+    ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                        L"username", L"password", L"full name", L"comment",
+                        L"gaia-id", L"foo@gmail.com", L"domain", &sid));
+
+  } else {
+    // Add a local user.
+    ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                        L"username", L"password", L"full name", L"comment",
+                        L"gaia-id", L"foo@gmail.com", &sid));
+  }
 
   if (!has_token_handle)
     ASSERT_EQ(S_OK, SetUserProperty((BSTR)sid, kUserTokenHandle, L""));
@@ -545,6 +565,120 @@
                          GcpCredentialProviderWithGaiaUsersTest,
                          ::testing::Combine(::testing::Bool(),
                                             ::testing::Bool(),
+                                            ::testing::Bool(),
+                                            ::testing::Bool()));
+
+// Check that reauth credentials only exists when either user is an AD user or
+// the token handle for the associated user is no longer valid when internet is
+// available.
+// Parameters are:
+// 1. bool - has an user_id and token handle in the registry.
+// 2. bool - is the token handle for the fake user valid (i.e. the fetch of
+// the token handle info from win_http_url_fetcher returns a valid json).
+// 3. bool - is the fake user an AD user.
+// 4. bool - is internet available.
+class GcpCredentialProviderWithADUsersTest
+    : public GcpCredentialProviderTest,
+      public ::testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
+ protected:
+  void SetUp() override;
+};
+
+void GcpCredentialProviderWithADUsersTest::SetUp() {
+  GcpCredentialProviderTest::SetUp();
+  ASSERT_EQ(S_OK, SetGlobalFlagForTesting(L"enable_ad_association", 1));
+}
+
+TEST_P(GcpCredentialProviderWithADUsersTest, ReauthCredentialTest) {
+  const bool has_user_id = std::get<0>(GetParam());
+  const bool valid_token_handle = std::get<1>(GetParam());
+  const bool is_ad_user = std::get<2>(GetParam());
+  const bool has_internet = std::get<3>(GetParam());
+
+  if (!has_user_id && !is_ad_user) {
+    // This is not a valid test scenario as the token handle wouldn't
+    // exist when user id mapping is not available in the registry.
+    return;
+  }
+
+  fake_internet_checker()->SetHasInternetConnection(
+      has_internet ? FakeInternetAvailabilityChecker::kHicForceYes
+                   : FakeInternetAvailabilityChecker::kHicForceNo);
+
+  CComBSTR local_user_sid;
+  // Always create local user to make sure that the co-existence scenarios
+  // work fine.
+  ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                      L"username-local", L"password", L"full name", L"comment",
+                      L"gaia-id", L"foolocal@gmail.com", &local_user_sid));
+
+  CComBSTR sid;
+  DWORD error;
+  base::string16 domain;
+  if (is_ad_user) {
+    // Add an AD user.
+    ASSERT_EQ(S_OK, fake_os_user_manager()->AddUser(
+                        L"username", L"password", L"full name", L"comment",
+                        true, L"domain", &sid, &error));
+  } else {
+    // Add a local user.
+    ASSERT_EQ(S_OK, fake_os_user_manager()->AddUser(L"username", L"password",
+                                                    L"full name", L"comment",
+                                                    true, &sid, &error));
+  }
+
+  if (has_user_id) {
+    base::string16 test_user_id(L"12345");
+    ASSERT_EQ(S_OK, SetUserProperty(OLE2CW(sid), kUserId, test_user_id));
+    // Set token handle to a non-empty value in registry.
+    ASSERT_EQ(S_OK, SetUserProperty((BSTR)sid, kUserTokenHandle,
+                                    L"non-empty-token-handle"));
+  }
+
+  CComPtr<ICredentialProviderCredential> cred;
+  CComPtr<ICredentialProvider> provider;
+  DWORD count = 0;
+  SetDefaultTokenHandleResponse(valid_token_handle
+                                    ? kDefaultValidTokenHandleResponse
+                                    : kDefaultInvalidTokenHandleResponse);
+  ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider));
+
+  bool should_reauth_user =
+      has_internet && ((!has_user_id && is_ad_user) || !valid_token_handle);
+
+  // Check if there is a IReauthCredential depending on the state of the token
+  // handle.
+  if (valid_token_handle) {
+    ASSERT_EQ(should_reauth_user ? 2u : 1u, count);
+  } else {
+    // When token handle is invalid. Then we expect two reauth credentials
+    // (i.e 1 for local user and 1 for AD/Local user) and one anonymous
+    // credential if should_reauth_user is true.
+    ASSERT_EQ(should_reauth_user ? 3u : 1u, count);
+  }
+
+  if (should_reauth_user) {
+    CComPtr<ICredentialProviderCredential> cred;
+    ASSERT_EQ(S_OK, provider->GetCredentialAt(1, &cred));
+    CComPtr<IReauthCredential> reauth;
+    EXPECT_EQ(S_OK, cred.QueryInterface(&reauth));
+  }
+
+  // When there are two reauth credentials, validate that the second one
+  // is also a reauth credential.
+  if (should_reauth_user && !valid_token_handle) {
+    CComPtr<ICredentialProviderCredential> cred;
+    ASSERT_EQ(S_OK, provider->GetCredentialAt(2, &cred));
+    CComPtr<IReauthCredential> reauth;
+    EXPECT_EQ(S_OK, cred.QueryInterface(&reauth));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         GcpCredentialProviderWithADUsersTest,
+                         ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool(),
+                                            ::testing::Bool(),
                                             ::testing::Bool()));
 
 // Check that the correct reauth credential type is created based on various
diff --git a/chrome/credential_provider/gaiacp/reg_utils.cc b/chrome/credential_provider/gaiacp/reg_utils.cc
index b1c8aa9..f253448 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.cc
+++ b/chrome/credential_provider/gaiacp/reg_utils.cc
@@ -184,6 +184,13 @@
   return reg_value_buffer;
 }
 
+DWORD GetGlobalFlagOrDefault(const base::string16& reg_key,
+                             const DWORD& default_value) {
+  DWORD value;
+  HRESULT hr = GetGlobalFlag(reg_key, &value);
+  return SUCCEEDED(hr) ? value : default_value;
+}
+
 HRESULT SetGlobalFlagForTesting(const base::string16& name,
                                 const base::string16& value) {
   return SetMachineRegString(kGcpRootKeyName, name, value);
diff --git a/chrome/credential_provider/gaiacp/reg_utils.h b/chrome/credential_provider/gaiacp/reg_utils.h
index 5f88439..3be9325 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.h
+++ b/chrome/credential_provider/gaiacp/reg_utils.h
@@ -51,6 +51,11 @@
 base::string16 GetGlobalFlagOrDefault(const base::string16& reg_key,
                                       const base::string16& default_value);
 
+// Gets global DWORD flag.  Returns |default_value| if no value is set or there
+// was an error fetching the flag.
+DWORD GetGlobalFlagOrDefault(const base::string16& reg_key,
+                             const DWORD& default_value);
+
 // Sets global flag. Used for testing purposes only.
 HRESULT SetGlobalFlagForTesting(const base::string16& name,
                                 const base::string16& value);
diff --git a/chrome/installer/mac/BUILD.gn b/chrome/installer/mac/BUILD.gn
index 68ff02e8..4d52bf5 100644
--- a/chrome/installer/mac/BUILD.gn
+++ b/chrome/installer/mac/BUILD.gn
@@ -11,6 +11,7 @@
 group("mac") {
   public_deps = [
     ":copies",
+    ":notarization_sentinel",
   ]
 }
 
@@ -62,6 +63,24 @@
   ]
 }
 
+action("notarization_sentinel") {
+  visibility = [ ":mac" ]
+
+  script = "//build/redirect_stdout.py"
+
+  _output = "$_packaging_dir/notarize_me.txt"
+
+  args = [
+    rebase_path(_output, root_out_dir),
+    "echo",
+    "yes",
+  ]
+
+  outputs = [
+    _output,
+  ]
+}
+
 copy("copies") {
   visibility = [ ":mac" ]
 
@@ -84,7 +103,6 @@
     "$root_out_dir/xzdec",
     "//chrome/app/helper-plugin-entitlements.plist",
     "//chrome/app/helper-renderer-entitlements.plist",
-    "app_resource_rules.plist",
     "dirdiffer.sh",
     "dirpatcher.sh",
     "dmgdiffer.sh",
diff --git a/chrome/installer/mac/app_resource_rules.plist b/chrome/installer/mac/app_resource_rules.plist
deleted file mode 100644
index 88ad5ec1..0000000
--- a/chrome/installer/mac/app_resource_rules.plist
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>rules</key>
-	<dict>
-		<key>.*</key>
-		<true/>
-		<key>^Versions/</key>
-		<dict>
-			<key>omit</key>
-			<true/>
-			<key>weight</key>
-			<real>10</real>
-		</dict>
-		<key>^Resources/.+\.lproj/</key>
-		<dict>
-			<key>optional</key>
-			<true/>
-			<key>weight</key>
-			<real>30</real>
-		</dict>
-		<key>/\.DS_Store$</key>
-		<dict>
-			<key>omit</key>
-			<true/>
-			<key>weight</key>
-			<real>50</real>
-		</dict>
-	</dict>
-	<key>rules2</key>
-	<dict>
-		<key>.*</key>
-		<true/>
-		<key>^Versions/</key>
-		<dict>
-			<key>omit</key>
-			<true/>
-			<key>weight</key>
-			<real>10</real>
-		</dict>
-		<key>^Resources/.+\.lproj/</key>
-		<dict>
-			<key>optional</key>
-			<true/>
-			<key>weight</key>
-			<real>30</real>
-		</dict>
-		<key>/\.DS_Store$</key>
-		<dict>
-			<key>omit</key>
-			<true/>
-			<key>weight</key>
-			<real>50</real>
-		</dict>
-	</dict>
-</dict>
-</plist>
diff --git a/chrome/installer/mac/signing/model.py b/chrome/installer/mac/signing/model.py
index 5a0a40a..92554732 100644
--- a/chrome/installer/mac/signing/model.py
+++ b/chrome/installer/mac/signing/model.py
@@ -19,7 +19,6 @@
                  requirements=None,
                  identifier_requirement=True,
                  sign_with_identifier=False,
-                 resource_rules=None,
                  entitlements=None,
                  verify_options=None):
         """A build product to be codesigned.
@@ -43,9 +42,6 @@
             sign_with_identifier: If True, then the identifier will be specified
                 when running the `codesign` command. If False, `codesign` will
                 infer the identifier itself.
-            resource_rules: A file in |Paths.packaging_dir| to specify for
-                `codesign --resource-rules`. macOS has deprecated resource rules
-                and this should not be used for new products.
             entitlements: File name of the entitlements file to sign the product
                 with. The file should reside in the |Paths.packaging_dir|.
             verify_options: Flags to pass to `codesign --verify`, from
@@ -59,7 +55,6 @@
         self.requirements = requirements
         self.identifier_requirement = identifier_requirement
         self.sign_with_identifier = sign_with_identifier
-        self.resource_rules = resource_rules
         self.entitlements = entitlements
         if not VerifyOptions.valid(verify_options):
             raise ValueError('Invalid VerifyOptions: {}'.format(verify_options))
diff --git a/chrome/installer/mac/signing/notarize.py b/chrome/installer/mac/signing/notarize.py
index 7310494..b91e14b 100644
--- a/chrome/installer/mac/signing/notarize.py
+++ b/chrome/installer/mac/signing/notarize.py
@@ -131,4 +131,4 @@
         path: The path to the artifact that had previously been submitted for
             notarization and is now ready for stapling.
     """
-    commands.run_command(['stapler', 'staple', '--verbose', path])
+    commands.run_command(['xcrun', 'stapler', 'staple', '--verbose', path])
diff --git a/chrome/installer/mac/signing/notarize_test.py b/chrome/installer/mac/signing/notarize_test.py
index 2cced331..fbedc34 100644
--- a/chrome/installer/mac/signing/notarize_test.py
+++ b/chrome/installer/mac/signing/notarize_test.py
@@ -180,4 +180,4 @@
     def test_staple(self, run_command):
         notarize.staple('/tmp/file.dmg')
         run_command.assert_called_once_with(
-            ['stapler', 'staple', '--verbose', '/tmp/file.dmg'])
+            ['xcrun', 'stapler', 'staple', '--verbose', '/tmp/file.dmg'])
diff --git a/chrome/installer/mac/signing/signing.py b/chrome/installer/mac/signing/signing.py
index de64fb0..b1673a02 100644
--- a/chrome/installer/mac/signing/signing.py
+++ b/chrome/installer/mac/signing/signing.py
@@ -184,11 +184,6 @@
         command.extend(['--keychain', config.keychain])
     if part.options:
         command.extend(['--options', ','.join(part.options)])
-    if part.resource_rules:
-        command.extend([
-            '--resource-rules',
-            os.path.join(paths.packaging_dir(config), part.resource_rules)
-        ])
     if part.entitlements:
         command.extend(
             ['--entitlements',
diff --git a/chrome/installer/mac/signing/signing_test.py b/chrome/installer/mac/signing/signing_test.py
index d5973eb..5163b68 100644
--- a/chrome/installer/mac/signing/signing_test.py
+++ b/chrome/installer/mac/signing/signing_test.py
@@ -182,19 +182,6 @@
             '[KEYCHAIN]', '--options', 'restrict,library', '$W/Test.app'
         ])
 
-    def test_sign_part_with_resource_rules(self, run_command):
-        part = model.CodeSignedProduct(
-            'Test.app',
-            'test.signing.app',
-            resource_rules='rules.plist',
-            identifier_requirement=False)
-        signing.sign_part(self.paths, self.config, part)
-        run_command.assert_called_once_with([
-            'codesign', '--sign', '[IDENTITY]', '--timestamp', '--keychain',
-            '[KEYCHAIN]', '--resource-rules',
-            '$I/Product Packaging/rules.plist', '$W/Test.app'
-        ])
-
     def test_sign_part_with_entitlements(self, run_command):
         part = model.CodeSignedProduct(
             'Test.app',
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index e743a93..02cbba4 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -313,11 +313,6 @@
   return true;
 }
 
-void RecordNumDeleteOldVersionsAttempsBeforeAbort(int num_attempts) {
-  UMA_HISTOGRAM_COUNTS_100(
-      "Setup.Install.NumDeleteOldVersionsAttemptsBeforeAbort", num_attempts);
-}
-
 // Repetitively attempts to delete all files that belong to old versions of
 // Chrome from |install_dir|. Waits 15 seconds before the first attempt and 5
 // minutes after each unsuccessful attempt. Returns when no files that belong to
@@ -326,7 +321,11 @@
 installer::InstallStatus RepeatDeleteOldVersions(
     const base::FilePath& install_dir,
     const installer::SetupSingleton& setup_singleton) {
-  constexpr int kMaxNumAttempts = 12;
+  // The 99th percentile of the number of attempts it takes to successfully
+  // delete old versions is 2.75. The 75th percentile is 1.77. 98% of calls to
+  // this function will successfully delete old versions.
+  // Source: 30 days of UMA data on June 25, 2019.
+  constexpr int kMaxNumAttempts = 3;
   int num_attempts = 0;
 
   while (num_attempts < kMaxNumAttempts) {
@@ -345,7 +344,6 @@
     if (setup_singleton.WaitForInterrupt(max_wait_time)) {
       VLOG(1) << "Exiting --delete-old-versions process because another "
                  "process tries to acquire the SetupSingleton.";
-      RecordNumDeleteOldVersionsAttempsBeforeAbort(num_attempts);
       return installer::SETUP_SINGLETON_RELEASED;
     }
 
@@ -360,9 +358,6 @@
     if (delete_old_versions_success) {
       VLOG(1) << "Successfully deleted all old files from "
                  "--delete-old-versions process.";
-      UMA_HISTOGRAM_COUNTS_100(
-          "Setup.Install.NumDeleteOldVersionsAttemptsBeforeSuccess",
-          num_attempts);
       return installer::DELETE_OLD_VERSIONS_SUCCESS;
     } else if (num_attempts == 1) {
       VLOG(1) << "Failed to delete all old files from --delete-old-versions "
@@ -373,7 +368,6 @@
   VLOG(1) << "Exiting --delete-old-versions process after retrying too many "
              "times to delete all old files.";
   DCHECK_EQ(num_attempts, kMaxNumAttempts);
-  RecordNumDeleteOldVersionsAttempsBeforeAbort(num_attempts);
   return installer::DELETE_OLD_VERSIONS_TOO_MANY_ATTEMPTS;
 }
 
diff --git a/chrome/installer/setup/setup_singleton.cc b/chrome/installer/setup/setup_singleton.cc
index dd69f05..7bd8e2f0 100644
--- a/chrome/installer/setup/setup_singleton.cc
+++ b/chrome/installer/setup/setup_singleton.cc
@@ -10,7 +10,6 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
@@ -19,32 +18,6 @@
 
 namespace installer {
 
-namespace {
-
-enum SetupSingletonAcquisitionResult {
-  // The setup singleton was acquired successfully.
-  SETUP_SINGLETON_ACQUISITION_SUCCESS = 0,
-  // Acquisition of the exit event mutex timed out.
-  SETUP_SINGLETON_ACQUISITION_EXIT_EVENT_MUTEX_TIMEOUT = 1,
-  // Acquisition of the setup mutex timed out.
-  SETUP_SINGLETON_ACQUISITION_SETUP_MUTEX_TIMEOUT = 2,
-  // Creation of the setup mutex failed.
-  SETUP_SINGLETON_ACQUISITION_SETUP_MUTEX_CREATION_FAILED = 3,
-  // Creation of the exit event failed.
-  SETUP_SINGLETON_ACQUISITION_EXIT_EVENT_CREATION_FAILED = 4,
-  // Creation of the exit event mutex failed.
-  SETUP_SINGLETON_ACQUISITION_EXIT_EVENT_MUTEX_CREATION_FAILED = 5,
-  SETUP_SINGLETON_ACQUISITION_RESULT_COUNT,
-};
-
-void RecordSetupSingletonAcquisitionResultHistogram(
-    SetupSingletonAcquisitionResult result) {
-  UMA_HISTOGRAM_ENUMERATION("Setup.Install.SingletonAcquisitionResult", result,
-                            SETUP_SINGLETON_ACQUISITION_RESULT_COUNT);
-}
-
-}  // namespace
-
 std::unique_ptr<SetupSingleton> SetupSingleton::Acquire(
     const base::CommandLine& command_line,
     const MasterPreferences& master_preferences,
@@ -61,8 +34,7 @@
       nullptr, FALSE,
       (L"Global\\ChromeSetupMutex_" + sync_primitive_name_suffix).c_str()));
   if (!setup_mutex.IsValid()) {
-    RecordSetupSingletonAcquisitionResultHistogram(
-        SETUP_SINGLETON_ACQUISITION_SETUP_MUTEX_CREATION_FAILED);
+    // UMA data indicates that this happens 0.03 % of the time.
     return nullptr;
   }
 
@@ -70,8 +42,7 @@
       nullptr, TRUE, FALSE,
       (L"Global\\ChromeSetupExitEvent_" + sync_primitive_name_suffix).c_str()));
   if (!exit_event.IsValid()) {
-    RecordSetupSingletonAcquisitionResultHistogram(
-        SETUP_SINGLETON_ACQUISITION_EXIT_EVENT_CREATION_FAILED);
+    // UMA data indicates that this happens < 0.01 % of the time.
     return nullptr;
   }
 
@@ -87,15 +58,13 @@
         (L"Global\\ChromeSetupExitEventMutex_" + sync_primitive_name_suffix)
             .c_str()));
     if (!exit_event_mutex.IsValid()) {
-      RecordSetupSingletonAcquisitionResultHistogram(
-          SETUP_SINGLETON_ACQUISITION_EXIT_EVENT_MUTEX_CREATION_FAILED);
+      // UMA data indicates that this happens < 0.01 % of the time.
       return nullptr;
     }
 
     ScopedHoldMutex scoped_hold_exit_event_mutex;
     if (!scoped_hold_exit_event_mutex.Acquire(exit_event_mutex.Get())) {
-      RecordSetupSingletonAcquisitionResultHistogram(
-          SETUP_SINGLETON_ACQUISITION_EXIT_EVENT_MUTEX_TIMEOUT);
+      // UMA data indicates that this happens < 0.01 % of the time.
       return nullptr;
     }
 
@@ -107,8 +76,7 @@
     // Acquire |setup_mutex_|.
     if (!setup_singleton->scoped_hold_setup_mutex_.Acquire(
             setup_singleton->setup_mutex_.Get())) {
-      RecordSetupSingletonAcquisitionResultHistogram(
-          SETUP_SINGLETON_ACQUISITION_SETUP_MUTEX_TIMEOUT);
+      // UMA data indicates that this happens 0.84 % of the time.
       return nullptr;
     }
     setup_singleton->exit_event_.Reset();
@@ -119,8 +87,7 @@
   installer_state->Initialize(command_line, master_preferences,
                               *original_state);
 
-  RecordSetupSingletonAcquisitionResultHistogram(
-      SETUP_SINGLETON_ACQUISITION_SUCCESS);
+  // UMA data indicates that this method succeeds > 99% of the time.
   return setup_singleton;
 }
 
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc
index 0a4044f..8fff85c 100644
--- a/chrome/installer/util/shell_util.cc
+++ b/chrome/installer/util/shell_util.cc
@@ -41,7 +41,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/values.h"
 #include "base/win/registry.h"
 #include "base/win/scoped_co_mem.h"
diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h
index ce1af99..8855e6f 100644
--- a/chrome/installer/util/shell_util.h
+++ b/chrome/installer/util/shell_util.h
@@ -29,7 +29,7 @@
 class RegistryEntry;
 
 namespace base {
-class CancellationFlag;
+class AtomicFlag;
 class CommandLine;
 }
 
@@ -580,7 +580,7 @@
       const base::FilePath& old_target_exe,
       const base::FilePath& new_target_exe);
 
-  typedef base::RefCountedData<base::CancellationFlag> SharedCancellationFlag;
+  typedef base::RefCountedData<base::AtomicFlag> SharedCancellationFlag;
 
   // Appends Chrome shortcuts with non-whitelisted arguments to |shortcuts| if
   // not NULL. If |do_removal|, also removes non-whitelisted arguments from
diff --git a/chrome/installer/util/shell_util_unittest.cc b/chrome/installer/util/shell_util_unittest.cc
index 386665b..2d4ed2c6a 100644
--- a/chrome/installer/util/shell_util_unittest.cc
+++ b/chrome/installer/util/shell_util_unittest.cc
@@ -20,7 +20,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/test/scoped_path_override.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/test/test_shortcut_win.h"
diff --git a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
index f016abd..175abc04 100644
--- a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
+++ b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
@@ -29,15 +29,19 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_set.h"
+#include "extensions/common/extensions_client.h"
+#include "extensions/common/manifest_handlers/background_info.h"
 #include "extensions/common/permissions/permissions_data.h"
 #include "extensions/common/switches.h"
 #include "extensions/renderer/dispatcher.h"
 #include "extensions/renderer/extension_frame_helper.h"
 #include "extensions/renderer/extensions_render_frame_observer.h"
+#include "extensions/renderer/extensions_renderer_client.h"
 #include "extensions/renderer/guest_view/extensions_guest_view_container.h"
 #include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h"
 #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h"
 #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h"
+#include "extensions/renderer/renderer_extension_registry.h"
 #include "extensions/renderer/script_context.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/web/web_document.h"
@@ -145,6 +149,33 @@
   resource_request_policy_->OnExtensionUnloaded(extension_id);
 }
 
+bool ChromeExtensionsRendererClient::ExtensionAPIEnabledForServiceWorkerScript(
+    const GURL& scope,
+    const GURL& script_url) const {
+  if (!script_url.SchemeIs(extensions::kExtensionScheme))
+    return false;
+
+  if (!extensions::ExtensionsClient::Get()
+           ->ExtensionAPIEnabledInExtensionServiceWorkers())
+    return false;
+
+  const Extension* extension =
+      extensions::RendererExtensionRegistry::Get()->GetExtensionOrAppByURL(
+          script_url);
+
+  if (!extension ||
+      !extensions::BackgroundInfo::IsServiceWorkerBased(extension))
+    return false;
+
+  if (scope != extension->url())
+    return false;
+
+  const std::string& sw_script =
+      extensions::BackgroundInfo::GetBackgroundServiceWorkerScript(extension);
+
+  return extension->GetResourceURL(sw_script) == script_url;
+}
+
 void ChromeExtensionsRendererClient::RenderThreadStarted() {
   content::RenderThread* thread = content::RenderThread::Get();
   // ChromeRenderViewTest::SetUp() creates its own ExtensionDispatcher and
diff --git a/chrome/renderer/extensions/chrome_extensions_renderer_client.h b/chrome/renderer/extensions/chrome_extensions_renderer_client.h
index 13ff1500..4b821fc 100644
--- a/chrome/renderer/extensions/chrome_extensions_renderer_client.h
+++ b/chrome/renderer/extensions/chrome_extensions_renderer_client.h
@@ -64,6 +64,10 @@
   void OnExtensionUnloaded(
       const extensions::ExtensionId& extension_id) override;
 
+  bool ExtensionAPIEnabledForServiceWorkerScript(
+      const GURL& scope,
+      const GURL& script_url) const override;
+
   // See ChromeContentRendererClient methods with the same names.
   void RenderThreadStarted();
   void RenderFrameCreated(content::RenderFrame* render_frame,
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index e1c4ad6..cedc7d9d 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -135,8 +135,10 @@
 
   // Get the starting index of the page URL.
   chrome::ParsedFaviconPath parsed;
-  if (!chrome::ParseFaviconPath(raw_path, &parsed))
+  if (!chrome::ParseFaviconPath(
+          raw_path, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed)) {
     return false;
+  }
   int path_index = parsed.path_index;
 
   std::string id_part = raw_path.substr(path_index);
diff --git a/chrome/service/cloud_print/printer_job_handler_unittest.cc b/chrome/service/cloud_print/printer_job_handler_unittest.cc
index 15160ca..1cd43bbd 100644
--- a/chrome/service/cloud_print/printer_job_handler_unittest.cc
+++ b/chrome/service/cloud_print/printer_job_handler_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind_test_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/common/cloud_print/cloud_print_constants.h"
 #include "chrome/service/cloud_print/cloud_print_service_helpers.h"
@@ -464,7 +465,7 @@
   void MakeJobFetchReturnNoJobs();
 
   base::MessageLoopForIO loop_;
-  std::unique_ptr<base::RunLoop> active_run_loop_;
+  base::OnceClosure active_run_loop_quit_closure_;
   TestURLFetcherCallback url_callback_;
   MockPrinterJobHandlerDelegate jobhandler_delegate_;
   CloudPrintTokenStore token_store_;
@@ -532,9 +533,10 @@
 
   // Everything that would be posted on the printer thread queue
   // has been posted, we can tell the main message loop to quit when idle
-  // and not worry about it idling while the print thread does work
+  // and not worry about it idling while the print thread does work.
+  DCHECK(!active_run_loop_quit_closure_.is_null());
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, active_run_loop_->QuitWhenIdleClosure());
+      FROM_HERE, std::move(active_run_loop_quit_closure_));
   return true;
 }
 
@@ -602,13 +604,17 @@
 
   job_handler_->Initialize();
 
-  active_run_loop_ = std::make_unique<base::RunLoop>();
+  base::RunLoop run_loop;
+  active_run_loop_quit_closure_ = run_loop.QuitWhenIdleClosure();
 
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, active_run_loop_->QuitWhenIdleClosure(),
-      base::TimeDelta::FromSeconds(timeout_seconds));
+  base::RunLoop::ScopedRunTimeoutForTest run_timeout(
+      base::TimeDelta::FromSeconds(timeout_seconds),
+      base::BindLambdaForTesting([&]() {
+        ADD_FAILURE();
+        run_loop.QuitWhenIdle();
+      }));
 
-  active_run_loop_->Run();
+  run_loop.Run();
 }
 
 void PrinterJobHandlerTest::SendCapsAndDefaults(
diff --git a/chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom b/chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom
index 4c5fc27..1b409d7c 100644
--- a/chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom
+++ b/chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom
@@ -40,6 +40,11 @@
   kIncompatibleDock,
   // Attached dock presents hardware failures.
   kDockError,
+  // HDMI and USB Type-C cannot be used for displays at the same time
+  // with the attached dock.
+  kDockDisplay,
+  // Attached dock will operate in USB Type-C compatible mode.
+  kDockThunderbolt,
 };
 
 // Factory interface exposed by the wilco_dtc_supportd daemon, which allows both
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8e468a5..f51a53c7 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -696,8 +696,6 @@
 
     data = [
       "data/",
-      "//chrome/browser/policy/test/asn1der.py",
-      "//chrome/browser/policy/test/policy_testserver.py",
       "//chrome/common/extensions/docs/examples/apps/calculator/",
       "//chrome/third_party/mock4js/",
       "//components/test/data/arc/",
@@ -992,6 +990,7 @@
       "../browser/previews/lazyload_browsertest.cc",
       "../browser/previews/previews_browsertest.cc",
       "../browser/previews/previews_lite_page_browsertest.cc",
+      "../browser/previews/previews_prober_browsertest.cc",
       "../browser/previews/previews_service_browser_test.cc",
       "../browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc",
       "../browser/process_singleton_browsertest.cc",
@@ -1913,8 +1912,8 @@
         "../browser/chromeos/extensions/incoming_native_messaging_apitest.cc",
         "../browser/chromeos/extensions/info_private_apitest.cc",
         "../browser/chromeos/extensions/input_method_apitest_chromeos.cc",
-        "../browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler_browsertest.cc",
-        "../browser/chromeos/extensions/login_screen_ui/login_screen_ui_apitest.cc",
+        "../browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler_browsertest.cc",
+        "../browser/chromeos/extensions/login_screen/login_screen_ui/login_screen_ui_apitest.cc",
         "../browser/chromeos/extensions/users_private/users_private_apitest.cc",
         "../browser/chromeos/extensions/wallpaper_apitest.cc",
         "../browser/chromeos/extensions/wallpaper_manager_browsertest.cc",
@@ -2197,10 +2196,7 @@
             [ "../browser/ui/ash/assistant/assistant_context_browsertest.cc" ]
       }
       if (enable_kiosk_next) {
-        sources += [
-          "../browser/chromeos/kiosk_next/kiosk_next_browser_factory_browsertest.cc",
-          "../browser/chromeos/kiosk_next_home/kiosk_next_home_browsertest.cc",
-        ]
+        sources += [ "../browser/chromeos/kiosk_next/kiosk_next_browser_factory_browsertest.cc" ]
       }
       sources -= [
         "../../apps/load_and_launch_browsertest.cc",
@@ -2711,7 +2707,7 @@
     "../browser/android/preferences/pref_service_bridge_unittest.cc",
     "../browser/android/preferences/prefs_unittest.cc",
     "../browser/android/shortcut_info_unittest.cc",
-    "../browser/android/signin/signin_manager_android_unittest.cc",
+    "../browser/android/signin/chrome_signin_manager_delegate_unittest.cc",
     "../browser/android/thumbnail/scoped_ptr_expiring_cache_unittest.cc",
     "../browser/android/usage_stats/usage_stats_database_unittest.cc",
     "../browser/android/webapk/webapk_icon_hasher_unittest.cc",
@@ -2917,7 +2913,9 @@
     "../browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc",
     "../browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc",
     "../browser/performance_manager/graph/frame_node_impl_unittest.cc",
+    "../browser/performance_manager/graph/graph_impl_operations_unittest.cc",
     "../browser/performance_manager/graph/graph_impl_unittest.cc",
+    "../browser/performance_manager/graph/graph_operations_unittest.cc",
     "../browser/performance_manager/graph/graph_test_harness.cc",
     "../browser/performance_manager/graph/graph_test_harness.h",
     "../browser/performance_manager/graph/mock_graphs.cc",
@@ -2938,6 +2936,7 @@
     "../browser/performance_manager/performance_manager_unittest.cc",
     "../browser/performance_manager/persistence/site_data/exponential_moving_average_unittest.cc",
     "../browser/performance_manager/persistence/site_data/leveldb_site_data_store_unittest.cc",
+    "../browser/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc",
     "../browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc",
     "../browser/performance_manager/persistence/site_data/site_data_cache_impl_unittest.cc",
     "../browser/performance_manager/persistence/site_data/site_data_impl_unittest.cc",
@@ -3226,6 +3225,10 @@
 
   configs += [ "//build/config:precompiled_headers" ]
 
+  if (is_android && notouch_build) {
+    configs += [ "//chrome/browser:notouch_config" ]
+  }
+
   data_deps = [
     "//chrome/test/data/media/engagement/preload:generate_preload_list",
     "//chrome/test/data/media/engagement/preload:test_data",
@@ -3627,6 +3630,7 @@
       "../browser/ui/extensions/extension_action_view_controller_unittest.cc",
       "../browser/ui/extensions/extension_message_bubble_bridge_unittest.cc",
       "../browser/ui/global_error/global_error_service_unittest.cc",
+      "../browser/ui/global_media_controls/media_dialog_controller_unittest.cc",
       "../browser/ui/global_media_controls/media_toolbar_button_controller_unittest.cc",
       "../browser/ui/hid/hid_chooser_controller_unittest.cc",
       "../browser/ui/in_product_help/active_tab_tracker_unittest.cc",
diff --git a/chrome/test/base/android/android_browser_test.cc b/chrome/test/base/android/android_browser_test.cc
index 61b67c7..816e32a 100644
--- a/chrome/test/base/android/android_browser_test.cc
+++ b/chrome/test/base/android/android_browser_test.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/test/base/android/android_browser_test.h"
 
+#include "base/command_line.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "chrome/test/base/test_launcher_utils.h"
@@ -16,6 +17,7 @@
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   SetUpCommandLine(command_line);
   SetUpDefaultCommandLine(command_line);
+  ASSERT_TRUE(test_launcher_utils::CreateUserDataDir(&temp_user_data_dir_));
 
   BrowserTestBase::SetUp();
 }
diff --git a/chrome/test/base/android/android_browser_test.h b/chrome/test/base/android/android_browser_test.h
index 58c6a65..e8ebd0ca 100644
--- a/chrome/test/base/android/android_browser_test.h
+++ b/chrome/test/base/android/android_browser_test.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
 #define CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
 
+#include "base/macros.h"
+#include "base/files/scoped_temp_dir.h"
+#include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_base.h"
 
 class AndroidBrowserTest : public content::BrowserTestBase {
@@ -23,6 +26,13 @@
   void SetUp() override;
   void PreRunTestOnMainThread() override;
   void PostRunTestOnMainThread() override;
+
+ private:
+  // Temporary user data directory. Used only when a user data directory is not
+  // specified in the command line.
+  base::ScopedTempDir temp_user_data_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(AndroidBrowserTest);
 };
 
 #endif  // CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 41a45ad..e7f371d 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -212,7 +212,7 @@
   SetUpDefaultCommandLine(command_line);
 
   // Create a temporary user data directory if required.
-  ASSERT_TRUE(CreateUserDataDirectory())
+  ASSERT_TRUE(test_launcher_utils::CreateUserDataDir(&temp_user_data_dir_))
       << "Could not create user data directory.";
 
   // Allow subclasses the opportunity to make changes to the default user data
@@ -312,23 +312,6 @@
     command_line->AppendSwitch(switches::kDisableZeroBrowsersOpenForTests);
 }
 
-bool InProcessBrowserTest::CreateUserDataDirectory() {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  base::FilePath user_data_dir =
-      command_line->GetSwitchValuePath(switches::kUserDataDir);
-  if (user_data_dir.empty()) {
-    if (temp_user_data_dir_.CreateUniqueTempDir() &&
-        temp_user_data_dir_.IsValid()) {
-      user_data_dir = temp_user_data_dir_.GetPath();
-    } else {
-      LOG(ERROR) << "Could not create temporary user data directory \""
-                 << temp_user_data_dir_.GetPath().value() << "\".";
-      return false;
-    }
-  }
-  return test_launcher_utils::OverrideUserDataDir(user_data_dir);
-}
-
 void InProcessBrowserTest::TearDown() {
   DCHECK(!g_browser_process);
 #if defined(OS_WIN)
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index bc1accf..b016d9b 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -258,10 +258,6 @@
  private:
   void Initialize();
 
-  // Creates a user data directory for the test if one is needed. Returns true
-  // if successful.
-  virtual bool CreateUserDataDirectory() WARN_UNUSED_RESULT;
-
   // Quits all open browsers and waits until there are no more browsers.
   void QuitBrowsers();
 
diff --git a/chrome/test/base/interactive_test_utils.h b/chrome/test/base/interactive_test_utils.h
index 924aa98..5bf1c12 100644
--- a/chrome/test/base/interactive_test_utils.h
+++ b/chrome/test/base/interactive_test_utils.h
@@ -195,6 +195,10 @@
 
 // Returns the center of |view| in screen coordinates.
 gfx::Point GetCenterInScreenCoordinates(const views::View* view);
+
+// Blocks until the given view is focused (or not focused, depending on
+// |focused|). Returns immediately if the state is already correct.
+void WaitForViewFocus(Browser* browser, ViewID vid, bool focused);
 #endif
 
 #if defined(OS_MACOSX)
diff --git a/chrome/test/base/interactive_test_utils_common_views.cc b/chrome/test/base/interactive_test_utils_common_views.cc
index ea12da64..d495ec6 100644
--- a/chrome/test/base/interactive_test_utils_common_views.cc
+++ b/chrome/test/base/interactive_test_utils_common_views.cc
@@ -4,16 +4,59 @@
 
 // Methods compiled on all toolkit-views platforms (including Mac).
 
-#include "chrome/test/base/interactive_test_utils.h"
-
 #include "base/bind.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/views/view.h"
+#include "ui/views/view_observer.h"
 #include "ui/views/widget/widget.h"
 
 namespace ui_test_utils {
 
+namespace {
+
+// A helper to wait until a view either gains or loses focus.
+class ViewFocusWaiter : public views::ViewObserver {
+ public:
+  ViewFocusWaiter(views::View* view, bool focused)
+      : view_(view), target_focused_(focused) {
+    view->AddObserver(this);
+  }
+
+  ~ViewFocusWaiter() override { view_->RemoveObserver(this); }
+
+  // views::ViewObserver:
+  void OnViewFocused(views::View* observed_view) override {
+    if (target_focused_ && run_loop_.running())
+      run_loop_.Quit();
+  }
+
+  void OnViewBlurred(views::View* observed_view) override {
+    if (!target_focused_ && run_loop_.running())
+      run_loop_.Quit();
+  }
+
+  void Wait() {
+    if (view_->HasFocus() != target_focused_)
+      run_loop_.Run();
+  }
+
+ private:
+  base::RunLoop run_loop_;
+  views::View* view_;
+  const bool target_focused_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewFocusWaiter);
+};
+
+}  // namespace
+
 void MoveMouseToCenterAndPress(views::View* view,
                                ui_controls::MouseButton button,
                                int button_state,
@@ -44,4 +87,13 @@
   return center;
 }
 
+void WaitForViewFocus(Browser* browser, ViewID vid, bool focused) {
+  views::View* view = views::Widget::GetWidgetForNativeWindow(
+                          browser->window()->GetNativeWindow())
+                          ->GetContentsView()
+                          ->GetViewByID(vid);
+  ASSERT_TRUE(view);
+  ViewFocusWaiter(view, focused).Wait();
+}
+
 }  // namespace ui_test_utils
diff --git a/chrome/test/base/test_launcher_utils.cc b/chrome/test/base/test_launcher_utils.cc
index f64744a..11dfe9d 100644
--- a/chrome/test/base/test_launcher_utils.cc
+++ b/chrome/test/base/test_launcher_utils.cc
@@ -9,6 +9,7 @@
 #include "base/command_line.h"
 #include "base/environment.h"
 #include "base/feature_list.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
@@ -93,6 +94,23 @@
   }
 }
 
+bool CreateUserDataDir(base::ScopedTempDir* temp_dir) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  base::FilePath user_data_dir =
+      command_line->GetSwitchValuePath(switches::kUserDataDir);
+  if (user_data_dir.empty()) {
+    DCHECK(temp_dir);
+    if (temp_dir->CreateUniqueTempDir() && temp_dir->IsValid()) {
+      user_data_dir = temp_dir->GetPath();
+    } else {
+      LOG(ERROR) << "Could not create temporary user data directory \""
+                 << temp_dir->GetPath().value() << "\".";
+      return false;
+    }
+  }
+  return OverrideUserDataDir(user_data_dir);
+}
+
 bool OverrideUserDataDir(const base::FilePath& user_data_dir) {
   bool success = true;
 
diff --git a/chrome/test/base/test_launcher_utils.h b/chrome/test/base/test_launcher_utils.h
index 476a478..44af68d 100644
--- a/chrome/test/base/test_launcher_utils.h
+++ b/chrome/test/base/test_launcher_utils.h
@@ -12,6 +12,7 @@
 
 namespace base {
 class CommandLine;
+class ScopedTempDir;
 }
 
 // A set of utilities for test code that launches separate processes.
@@ -34,9 +35,12 @@
                              const std::string& switch_to_remove,
                              base::CommandLine* out_command_line);
 
+// Creates and overrides the current process' user data dir.
+bool CreateUserDataDir(base::ScopedTempDir* temp_dir) WARN_UNUSED_RESULT;
+
 // Overrides the current process' user data dir.
-bool OverrideUserDataDir(
-    const base::FilePath& user_data_dir) WARN_UNUSED_RESULT;
+bool OverrideUserDataDir(const base::FilePath& user_data_dir)
+    WARN_UNUSED_RESULT;
 
 }  // namespace test_launcher_utils
 
diff --git a/chrome/test/chromedriver/BUILD.gn b/chrome/test/chromedriver/BUILD.gn
index a6c410f..0fd94e4b 100644
--- a/chrome/test/chromedriver/BUILD.gn
+++ b/chrome/test/chromedriver/BUILD.gn
@@ -114,6 +114,8 @@
     "chrome/devtools_http_client.h",
     "chrome/dom_tracker.cc",
     "chrome/dom_tracker.h",
+    "chrome/download_directory_override_manager.cc",
+    "chrome/download_directory_override_manager.h",
     "chrome/frame_tracker.cc",
     "chrome/frame_tracker.h",
     "chrome/geolocation_override_manager.cc",
@@ -372,6 +374,7 @@
     "chrome/devtools_client_impl_unittest.cc",
     "chrome/devtools_http_client_unittest.cc",
     "chrome/dom_tracker_unittest.cc",
+    "chrome/download_directory_override_manager_unittest.cc",
     "chrome/frame_tracker_unittest.cc",
     "chrome/geolocation_override_manager_unittest.cc",
     "chrome/heap_snapshot_taker_unittest.cc",
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index 44461892..89de48a 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -566,8 +566,6 @@
     parser_map["extensions"] = base::Bind(&ParseExtensions);
     parser_map["extensionLoadTimeout"] =
         base::Bind(&ParseTimeDelta, &capabilities->extension_load_timeout);
-    parser_map["forceDevToolsScreenshot"] = base::Bind(
-        &ParseBoolean, &capabilities->force_devtools_screenshot);
     parser_map["loadAsync"] = base::Bind(&IgnoreDeprecatedOption, "loadAsync");
     parser_map["localState"] =
         base::Bind(&ParseDict, &capabilities->local_state);
@@ -716,7 +714,6 @@
       android_use_running_app(false),
       detach(false),
       extension_load_timeout(base::TimeDelta::FromSeconds(10)),
-      force_devtools_screenshot(true),
       network_emulation_enabled(false),
       use_automation_extension(true) {}
 
diff --git a/chrome/test/chromedriver/capabilities.h b/chrome/test/chromedriver/capabilities.h
index 56e52ec..14e0b2a 100644
--- a/chrome/test/chromedriver/capabilities.h
+++ b/chrome/test/chromedriver/capabilities.h
@@ -164,10 +164,6 @@
   // Time to wait for extension background page to appear. If 0, no waiting.
   base::TimeDelta extension_load_timeout;
 
-  // True if should always use DevTools for taking screenshots.
-  // This is experimental and may be removed at a later point.
-  bool force_devtools_screenshot;
-
   std::unique_ptr<base::DictionaryValue> local_state;
 
   std::string log_path;
diff --git a/chrome/test/chromedriver/chrome/download_directory_override_manager.cc b/chrome/test/chromedriver/chrome/download_directory_override_manager.cc
new file mode 100644
index 0000000..c2a8629
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/download_directory_override_manager.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/chromedriver/chrome/download_directory_override_manager.h"
+
+#include "base/values.h"
+#include "chrome/test/chromedriver/chrome/browser_info.h"
+#include "chrome/test/chromedriver/chrome/devtools_client.h"
+#include "chrome/test/chromedriver/chrome/status.h"
+
+DownloadDirectoryOverrideManager::DownloadDirectoryOverrideManager(
+    DevToolsClient* client)
+    : client_(client), is_connected_(false) {
+  client_->AddListener(this);
+}
+
+DownloadDirectoryOverrideManager::~DownloadDirectoryOverrideManager() = default;
+
+Status DownloadDirectoryOverrideManager::OverrideDownloadDirectoryWhenConnected(
+    const std::string& new_download_directory) {
+  download_directory_ = std::make_unique<std::string>(new_download_directory);
+  if (is_connected_)
+    return ApplyOverride();
+  return Status(kOk);
+}
+
+Status DownloadDirectoryOverrideManager::OnConnected(DevToolsClient* client) {
+  is_connected_ = true;
+  if (download_directory_)
+    return ApplyOverride();
+  return Status(kOk);
+}
+
+Status DownloadDirectoryOverrideManager::ApplyOverride() {
+  base::DictionaryValue params;
+  params.SetString("behavior", "allow");
+  params.SetString("downloadPath", *download_directory_);
+  return client_->SendCommand("Page.setDownloadBehavior", params);
+}
diff --git a/chrome/test/chromedriver/chrome/download_directory_override_manager.h b/chrome/test/chromedriver/chrome/download_directory_override_manager.h
new file mode 100644
index 0000000..9e033b8e
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/download_directory_override_manager.h
@@ -0,0 +1,39 @@
+// 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 CHROME_TEST_CHROMEDRIVER_CHROME_DOWNLOAD_DIRECTORY_OVERRIDE_MANAGER_H_
+#define CHROME_TEST_CHROMEDRIVER_CHROME_DOWNLOAD_DIRECTORY_OVERRIDE_MANAGER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/test/chromedriver/chrome/browser_info.h"
+#include "chrome/test/chromedriver/chrome/devtools_event_listener.h"
+
+class DevToolsClient;
+class Status;
+
+// Overrides the default download directory, if requested, for the duration
+// of the given |DevToolsClient|'s lifetime.
+class DownloadDirectoryOverrideManager : public DevToolsEventListener {
+ public:
+  explicit DownloadDirectoryOverrideManager(DevToolsClient* client);
+  ~DownloadDirectoryOverrideManager() override;
+
+  Status OverrideDownloadDirectoryWhenConnected(
+      const std::string& new_download_directory);
+
+  // Overridden from DevToolsEventListener:
+  Status OnConnected(DevToolsClient* client) override;
+
+ private:
+  Status ApplyOverride();
+  DevToolsClient* client_;
+  bool is_connected_;
+  std::unique_ptr<std::string> download_directory_;
+};
+
+#endif  // CHROME_TEST_CHROMEDRIVER_CHROME_DOWNLOAD_DIRECTORY_OVERRIDE_MANAGER_H_
diff --git a/chrome/test/chromedriver/chrome/download_directory_override_manager_unittest.cc b/chrome/test/chromedriver/chrome/download_directory_override_manager_unittest.cc
new file mode 100644
index 0000000..47ef8d47
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/download_directory_override_manager_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/chromedriver/chrome/download_directory_override_manager.h"
+#include "base/values.h"
+#include "chrome/test/chromedriver/chrome/recorder_devtools_client.h"
+#include "chrome/test/chromedriver/chrome/status.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+void AssertDownloadDirectoryCommand(const Command& command,
+                                    const std::string& download_directory) {
+  std::string behavior;
+  std::string download_path;
+  ASSERT_EQ("Page.setDownloadBehavior", command.method);
+  ASSERT_TRUE(command.params.GetString("behavior", &behavior));
+  ASSERT_TRUE(command.params.GetString("downloadPath", &download_path));
+  ASSERT_EQ(download_directory, download_path);
+  ASSERT_EQ(behavior, "allow");
+}
+}  // namespace
+
+TEST(DownloadDirectoryOverrideManager,
+     OnConnectedSendsCommandIfDownloadDirectoryPopulated) {
+  RecorderDevToolsClient client;
+  DownloadDirectoryOverrideManager manager(&client);
+  ASSERT_EQ(kOk, manager.OnConnected(&client).code());
+  ASSERT_EQ(0u, client.commands_.size());
+}
+
+TEST(DownloadDirectoryOverrideManager, OverrideSendsCommand) {
+  RecorderDevToolsClient client;
+  DownloadDirectoryOverrideManager manager(&client);
+
+  // No command should be sent until OnConnect is called
+  const std::string directory = "download/directory";
+  ASSERT_EQ(kOk,
+            manager.OverrideDownloadDirectoryWhenConnected(directory).code());
+  ASSERT_EQ(0u, client.commands_.size());
+
+  // On connected is called and the directory should now
+  // be overridden to 'download/directory'
+  ASSERT_EQ(kOk, manager.OnConnected(&client).code());
+  ASSERT_EQ(1u, client.commands_.size());
+  ASSERT_NO_FATAL_FAILURE(
+      AssertDownloadDirectoryCommand(client.commands_[0], directory));
+
+  const std::string directory2 = "download/directory2";
+  ASSERT_EQ(kOk,
+            manager.OverrideDownloadDirectoryWhenConnected(directory2).code());
+  ASSERT_EQ(2u, client.commands_.size());
+  ASSERT_NO_FATAL_FAILURE(
+      AssertDownloadDirectoryCommand(client.commands_[1], directory2));
+}
diff --git a/chrome/test/chromedriver/chrome/heap_snapshot_taker.cc b/chrome/test/chromedriver/chrome/heap_snapshot_taker.cc
index 9ea56718..9c16fe4ef 100644
--- a/chrome/test/chromedriver/chrome/heap_snapshot_taker.cc
+++ b/chrome/test/chromedriver/chrome/heap_snapshot_taker.cc
@@ -28,13 +28,7 @@
 
   Status status3(kOk);
   if (status1.IsOk() && status2.IsOk()) {
-    std::unique_ptr<base::Value> value =
-        base::JSONReader::ReadDeprecated(snapshot_);
-    if (!value) {
-      status3 = Status(kUnknownError, "heap snapshot not in JSON format");
-    } else {
-      *snapshot = std::move(value);
-    }
+    *snapshot = std::make_unique<base::Value>(std::move(snapshot_));
   }
   snapshot_.clear();
   if (status1.IsError()) {
diff --git a/chrome/test/chromedriver/chrome/heap_snapshot_taker_unittest.cc b/chrome/test/chromedriver/chrome/heap_snapshot_taker_unittest.cc
index 977e3d0..24f868d 100644
--- a/chrome/test/chromedriver/chrome/heap_snapshot_taker_unittest.cc
+++ b/chrome/test/chromedriver/chrome/heap_snapshot_taker_unittest.cc
@@ -22,10 +22,8 @@
 const char* const chunks[] = {"{\"a\": 1,", "\"b\": 2}"};
 
 std::unique_ptr<base::Value> GetSnapshotAsValue() {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("a", 1);
-  dict->SetInteger("b", 2);
-  return std::move(dict);
+  std::string str_snapshot = "{\"a\": 1,\"b\": 2}";
+  return std::make_unique<base::Value>(std::move(str_snapshot));;
 }
 
 class DummyDevToolsClient : public StubDevToolsClient {
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.cc b/chrome/test/chromedriver/chrome/stub_web_view.cc
index 21c8ea3..e8670f28 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.cc
+++ b/chrome/test/chromedriver/chrome/stub_web_view.cc
@@ -174,6 +174,11 @@
   return Status(kOk);
 }
 
+Status StubWebView::OverrideDownloadDirectoryIfNeeded(
+    const std::string& download_directory) {
+  return Status(kOk);
+}
+
 Status StubWebView::CaptureScreenshot(
     std::string* screenshot,
     const base::DictionaryValue& params) {
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.h b/chrome/test/chromedriver/chrome/stub_web_view.h
index 9443b53..f2bc162 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.h
+++ b/chrome/test/chromedriver/chrome/stub_web_view.h
@@ -88,6 +88,8 @@
   Status OverrideGeolocation(const Geoposition& geoposition) override;
   Status OverrideNetworkConditions(
       const NetworkConditions& network_conditions) override;
+  Status OverrideDownloadDirectoryIfNeeded(
+      const std::string& download_directory) override;
   Status CaptureScreenshot(
       std::string* screenshot,
       const base::DictionaryValue& params) override;
diff --git a/chrome/test/chromedriver/chrome/web_view.h b/chrome/test/chromedriver/chrome/web_view.h
index 6468c832..83ec5a5 100644
--- a/chrome/test/chromedriver/chrome/web_view.h
+++ b/chrome/test/chromedriver/chrome/web_view.h
@@ -193,6 +193,10 @@
   virtual Status OverrideNetworkConditions(
       const NetworkConditions& network_conditions) = 0;
 
+  // Overrides normal download directory with given path.
+  virtual Status OverrideDownloadDirectoryIfNeeded(
+      const std::string& download_directory) = 0;
+
   // Captures the visible portions of the web view as a base64-encoded PNG.
   virtual Status CaptureScreenshot(
       std::string* screenshot,
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 383a493..3c498226 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -21,6 +21,7 @@
 #include "chrome/test/chromedriver/chrome/debugger_tracker.h"
 #include "chrome/test/chromedriver/chrome/devtools_client_impl.h"
 #include "chrome/test/chromedriver/chrome/dom_tracker.h"
+#include "chrome/test/chromedriver/chrome/download_directory_override_manager.h"
 #include "chrome/test/chromedriver/chrome/frame_tracker.h"
 #include "chrome/test/chromedriver/chrome/geolocation_override_manager.h"
 #include "chrome/test/chromedriver/chrome/heap_snapshot_taker.h"
@@ -167,6 +168,13 @@
           new NetworkConditionsOverrideManager(client_.get())),
       heap_snapshot_taker_(new HeapSnapshotTaker(client_.get())),
       debugger_(new DebuggerTracker(client_.get())) {
+  // Downloading in headless mode requires the setting of
+  // Page.setDownloadBehavior. This is handled by the
+  // DownloadDirectoryOverrideManager, which is only instantiated
+  // in headless chrome.
+  if (browser_info->browser_name == "headless chrome")
+    download_directory_override_manager_ =
+        std::make_unique<DownloadDirectoryOverrideManager>(client_.get());
   client_->SetOwner(this);
 }
 
@@ -714,6 +722,14 @@
       network_conditions);
 }
 
+Status WebViewImpl::OverrideDownloadDirectoryIfNeeded(
+    const std::string& download_directory) {
+  if (download_directory_override_manager_)
+    return download_directory_override_manager_
+        ->OverrideDownloadDirectoryWhenConnected(download_directory);
+  return Status(kOk);
+}
+
 Status WebViewImpl::CaptureScreenshot(
     std::string* screenshot,
     const base::DictionaryValue& params) {
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.h b/chrome/test/chromedriver/chrome/web_view_impl.h
index 802a99a..4778c96f 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.h
+++ b/chrome/test/chromedriver/chrome/web_view_impl.h
@@ -24,6 +24,7 @@
 struct DeviceMetrics;
 class DevToolsClient;
 class DomTracker;
+class DownloadDirectoryOverrideManager;
 class FrameTracker;
 class GeolocationOverrideManager;
 class MobileEmulationOverrideManager;
@@ -127,6 +128,8 @@
   Status OverrideGeolocation(const Geoposition& geoposition) override;
   Status OverrideNetworkConditions(
       const NetworkConditions& network_conditions) override;
+  Status OverrideDownloadDirectoryIfNeeded(
+      const std::string& download_directory) override;
   Status CaptureScreenshot(
       std::string* screenshot,
       const base::DictionaryValue& params) override;
@@ -197,6 +200,8 @@
   std::unique_ptr<GeolocationOverrideManager> geolocation_override_manager_;
   std::unique_ptr<NetworkConditionsOverrideManager>
       network_conditions_override_manager_;
+  std::unique_ptr<DownloadDirectoryOverrideManager>
+      download_directory_override_manager_;
   std::unique_ptr<HeapSnapshotTaker> heap_snapshot_taker_;
   std::unique_ptr<DebuggerTracker> debugger_;
   std::unique_ptr<CastTracker> cast_tracker_;
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index 453f0b0..1f43b6d 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -605,10 +605,6 @@
     params = {'parameters': {'type': connection_type}}
     return self.ExecuteCommand(Command.SET_NETWORK_CONNECTION, params)
 
-  def SendCommand(self, cmd, cmd_params):
-    params = {'parameters': {'cmd': cmd, 'params': cmd_params}};
-    return self.ExecuteCommand(Command.SEND_COMMAND, params)
-
   def SendCommandAndGetResult(self, cmd, cmd_params):
     params = {'cmd': cmd, 'params': cmd_params};
     return self.ExecuteCommand(Command.SEND_COMMAND_AND_GET_RESULT, params)
diff --git a/chrome/test/chromedriver/client/command_executor.py b/chrome/test/chromedriver/client/command_executor.py
index 3be37a2..9f883f7 100644
--- a/chrome/test/chromedriver/client/command_executor.py
+++ b/chrome/test/chromedriver/client/command_executor.py
@@ -171,8 +171,6 @@
   STATUS = (_Method.GET, '/status')
   SET_NETWORK_CONNECTION = (
       _Method.POST, '/session/:sessionId/network_connection')
-  SEND_COMMAND = (
-      _Method.POST, '/session/:sessionId/chromium/send_command')
   SEND_COMMAND_AND_GET_RESULT = (
       _Method.POST, '/session/:sessionId/chromium/send_command_and_get_result')
   GENERATE_TEST_REPORT = (
diff --git a/chrome/test/chromedriver/extension/background.js b/chrome/test/chromedriver/extension/background.js
index 686adb2..e3bafbf7 100644
--- a/chrome/test/chromedriver/extension/background.js
+++ b/chrome/test/chromedriver/extension/background.js
@@ -18,29 +18,6 @@
 }
 
 /**
- * Captures a screenshot of the visible tab.
- *
- * @param {function(string)} callback The callback to invoke with the base64
- *     encoded PNG.
- * @param {function(!Error)} errCallback The callback to invoke for error
- *     reporting.
- */
-function captureScreenshot(callback, errCallback) {
-  chrome.tabs.captureVisibleTab({format:'png'}, function(dataUrl) {
-    if (chrome.extension.lastError &&
-        chrome.extension.lastError.message.indexOf('permission') != -1) {
-      var error = new Error(chrome.extension.lastError.message);
-      error.code = 103;  // kForbidden
-      errCallback(error);
-      return;
-    }
-    checkForExtensionError(errCallback);
-    var base64 = ';base64,';
-    callback(dataUrl.substr(dataUrl.indexOf(base64) + base64.length))
-  });
-}
-
-/**
  * Launches an app with the specified id.
  *
  * @param {string} id The ID of the app to launch.
diff --git a/chrome/test/chromedriver/session.cc b/chrome/test/chromedriver/session.cc
index 8172fe8..50463cb 100644
--- a/chrome/test/chromedriver/session.cc
+++ b/chrome/test/chromedriver/session.cc
@@ -64,7 +64,6 @@
       w3c_compliant(kW3CDefault),
       quit(false),
       detach(false),
-      force_devtools_screenshot(false),
       sticky_modifiers(0),
       mouse_position(0, 0),
       pressed_mouse_button(kNoneMouseButton),
@@ -81,7 +80,6 @@
       w3c_compliant(kW3CDefault),
       quit(false),
       detach(false),
-      force_devtools_screenshot(false),
       chrome(std::move(chrome)),
       sticky_modifiers(0),
       mouse_position(0, 0),
diff --git a/chrome/test/chromedriver/session.h b/chrome/test/chromedriver/session.h
index 5d75126..90d9df9 100644
--- a/chrome/test/chromedriver/session.h
+++ b/chrome/test/chromedriver/session.h
@@ -86,7 +86,6 @@
   bool w3c_compliant;
   bool quit;
   bool detach;
-  bool force_devtools_screenshot;
   std::unique_ptr<Chrome> chrome;
   std::string window;
   int sticky_modifiers;
@@ -102,6 +101,9 @@
   // first frame element in the root document. If target frame is window.top,
   // this list will be empty.
   std::list<FrameInfo> frames;
+  // Download directory that the user specifies. Used only in headless mode.
+  // Defaults to current directory in headless mode if no directory specified
+  std::unique_ptr<std::string> headless_download_directory;
   WebPoint mouse_position;
   MouseButton pressed_mouse_button;
   base::TimeDelta implicit_wait;
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index 9d7728c..4e75d64 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -310,10 +310,24 @@
   if (status.IsError())
     return status;
   session->detach = capabilities.detach;
-  session->force_devtools_screenshot = capabilities.force_devtools_screenshot;
   session->capabilities =
       CreateCapabilities(session, capabilities, *desired_caps);
 
+  std::string download_directory;
+  if (capabilities.prefs &&
+      capabilities.prefs->GetString("download.default_directory",
+                                    &download_directory))
+    session->headless_download_directory =
+        std::make_unique<std::string>(download_directory);
+  else
+    session->headless_download_directory = std::make_unique<std::string>(".");
+  WebView* first_view;
+  session->chrome->GetWebViewById(session->window, &first_view);
+  status = first_view->OverrideDownloadDirectoryIfNeeded(
+      *session->headless_download_directory);
+  if (status.IsError())
+    return status;
+
   if (session->w3c_compliant) {
     std::unique_ptr<base::DictionaryValue> capabilities =
         std::unique_ptr<base::DictionaryValue>(
@@ -700,6 +714,19 @@
     if (status.IsError())
       return status;
   }
+  if (session->headless_download_directory) {
+    WebView* web_view;
+    Status status = session->chrome->GetWebViewById(web_view_id, &web_view);
+    if (status.IsError())
+      return status;
+    status = web_view->ConnectIfNecessary();
+    if (status.IsError())
+      return status;
+    status = web_view->OverrideDownloadDirectoryIfNeeded(
+        *session->headless_download_directory);
+    if (status.IsError())
+      return status;
+  }
 
   status = session->chrome->ActivateWebView(web_view_id);
   if (status.IsError())
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 8c38391..bdfa3244 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -105,6 +105,9 @@
     'ChromeDriverTest.testGetWindowHandles',
     'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
     'ChromeDriverTest.testSwitchToWindow',
+    'ChromeDownloadDirTest.testFileDownloadAfterTabHeadless',
+    'ChromeDownloadDirTest.testFileDownloadWithClickHeadless',
+    'ChromeDownloadDirTest.testFileDownloadWithGetHeadless',
 ]
 _OS_SPECIFIC_FILTER['linux'] = [
 ]
@@ -116,6 +119,9 @@
     'ChromeDriverTest.testTakeElementScreenshotInIframe',
     # https://bugs.chromium.org/p/chromium/issues/detail?id=946023
     'ChromeDriverTest.testWindowFullScreen',
+    'ChromeDownloadDirTest.testFileDownloadAfterTabHeadless',
+    'ChromeDownloadDirTest.testFileDownloadWithClickHeadless',
+    'ChromeDownloadDirTest.testFileDownloadWithGetHeadless'
 ]
 
 _DESKTOP_NEGATIVE_FILTER = [
@@ -779,6 +785,52 @@
     self._driver.PerformActions(actions)
     self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
 
+  def testActionsMouseDrag(self):
+    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/drag.html'))
+    target = self._driver.FindElement('css selector', '#target')
+
+    # Move to center of target element and drag it to a new location.
+    actions = ({'actions': [{
+      'type': 'pointer',
+      'actions': [
+          {'type': 'pointerMove', 'x': 100, 'y': 100},
+          {'type': 'pointerDown', 'button': 0},
+          {'type': 'pointerMove', 'x': 200, 'y': 250}
+      ],
+      'parameters': {'pointerType': 'mouse'},
+      'id': 'pointer1'}]})
+    self._driver.PerformActions(actions)
+    rect = target.GetRect()
+    self.assertEquals(150, rect['x'])
+    self.assertEquals(200, rect['y'])
+
+    # Without releasing mouse button, should continue the drag.
+    actions = ({'actions': [{
+      'type': 'pointer',
+      'actions': [
+          {'type': 'pointerMove', 'x': 30, 'y': 40, 'origin': 'pointer'}
+      ],
+      'parameters': {'pointerType': 'mouse'},
+      'id': 'pointer1'}]})
+    self._driver.PerformActions(actions)
+    rect = target.GetRect()
+    self.assertEquals(180, rect['x'])
+    self.assertEquals(240, rect['y'])
+
+    # Releasing mouse button stops the drag.
+    actions = ({'actions': [{
+      'type': 'pointer',
+      'actions': [
+          {'type': 'pointerUp', 'button': 0},
+          {'type': 'pointerMove', 'x': 50, 'y': 50, 'origin': 'pointer'}
+      ],
+      'parameters': {'pointerType': 'mouse'},
+      'id': 'pointer1'}]})
+    self._driver.PerformActions(actions)
+    rect = target.GetRect()
+    self.assertEquals(180, rect['x'])
+    self.assertEquals(240, rect['y'])
+
   def testActionsTouchTap(self):
     self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
     self._driver.ExecuteScript(
@@ -2595,6 +2647,36 @@
         ChromeDriverTest.GetHttpUrlForFile('/chromedriver/download.html'),
         driver.GetCurrentUrl())
 
+  def testFileDownloadWithClickHeadless(self):
+      download_dir = self.CreateTempDir()
+      download_name = os.path.join(download_dir, 'a_red_dot.png')
+      driver = self.CreateDriver(download_dir=download_dir,
+                                 chrome_switches=['--headless'])
+      driver.Load(ChromeDriverTest.GetHttpUrlForFile(
+          '/chromedriver/download.html'))
+      driver.FindElement('css selector', '#red-dot').Click()
+      self.WaitForFileToDownload(download_name)
+      self.assertEqual(
+          ChromeDriverTest.GetHttpUrlForFile('/chromedriver/download.html'),
+          driver.GetCurrentUrl())
+
+  def testFileDownloadAfterTabHeadless(self):
+      download_dir = self.CreateTempDir()
+      download_name = os.path.join(download_dir, 'a_red_dot.png')
+      driver = self.CreateDriver(download_dir=download_dir,
+                                 chrome_switches=['--headless'])
+      driver.Load(ChromeDriverTest.GetHttpUrlForFile(
+          '/chromedriver/empty.html'))
+      new_window = driver.NewWindow(window_type='tab')
+      driver.SwitchToWindow(new_window['handle'])
+      driver.Load(ChromeDriverTest.GetHttpUrlForFile(
+          '/chromedriver/download.html'))
+      driver.FindElement('css selector', '#red-dot').Click()
+      self.WaitForFileToDownload(download_name)
+      self.assertEqual(
+          ChromeDriverTest.GetHttpUrlForFile('/chromedriver/download.html'),
+          driver.GetCurrentUrl())
+
   def testFileDownloadWithGet(self):
     ChromeDriverTest._http_server.SetCallbackForPath(
         '/abc.csv', self.RespondWithCsvFile)
@@ -2605,6 +2687,17 @@
     self.WaitForFileToDownload(os.path.join(download_dir, 'abc.csv'))
     self.assertEqual(original_url, driver.GetCurrentUrl())
 
+  def testFileDownloadWithGetHeadless(self):
+    ChromeDriverTest._http_server.SetCallbackForPath(
+        '/abc.csv', self.RespondWithCsvFile)
+    download_dir = self.CreateTempDir()
+    driver = self.CreateDriver(download_dir=download_dir,
+                               chrome_switches=['--headless'])
+    original_url = driver.GetCurrentUrl()
+    driver.Load(ChromeDriverTest.GetHttpUrlForFile('/abc.csv'))
+    self.WaitForFileToDownload(os.path.join(download_dir, 'abc.csv'))
+    self.assertEqual(original_url, driver.GetCurrentUrl())
+
   def testDownloadDirectoryOverridesExistingPreferences(self):
     user_data_dir = self.CreateTempDir()
     download_dir = self.CreateTempDir()
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index b5b3570a..6d45925d 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -1284,15 +1284,9 @@
                              std::unique_ptr<base::Value>* value,
                              Timeout* timeout) {
   // extract action sequence
-  const base::DictionaryValue* actions_dict;
   const base::ListValue* actions_input;
 
-  // TODO(lanwei): The below line will be removed after this pull request is
-  // merged, https://github.com/web-platform-tests/wpt/pull/14345.
-  if (!params.GetDictionary("actions", &actions_dict))
-    actions_dict = &params;
-
-  if (!actions_dict->GetList("actions", &actions_input))
+  if (!params.GetList("actions", &actions_input))
     return Status(kInvalidArgument, "'actions' must be an array");
 
   // the processed actions
@@ -1387,8 +1381,8 @@
       std::vector<TouchEvent> touch_events;
       double x = 0;
       double y = 0;
-      bool has_touch_start = false;
-      int buttons = 0;
+      bool has_touch_start = input_state->FindKey("pressed")->GetInt() != 0;
+      int buttons = input_state->FindKey("pressed")->GetInt();
       std::string button_type;
       OriginType origin_type = kPointer;
       std::string element_id;
@@ -1852,24 +1846,9 @@
     return status;
 
   std::string screenshot;
-  ChromeDesktopImpl* desktop = NULL;
-  status = session->chrome->GetAsDesktop(&desktop);
-  if (status.IsOk() && !session->force_devtools_screenshot) {
-    AutomationExtension* extension = NULL;
-    status = desktop->GetAutomationExtension(&extension,
-                                             session->w3c_compliant);
-    if (status.IsError())
-      return status;
-    status = extension->CaptureScreenshot(&screenshot);
-  } else {
-    std::unique_ptr<base::DictionaryValue> screenshot_params(
-        const base::DictionaryValue&);
   status = web_view->CaptureScreenshot(&screenshot, base::DictionaryValue());
-  }
   if (status.IsError()) {
     LOG(WARNING) << "screenshot failed, retrying";
-    std::unique_ptr<base::DictionaryValue> screenshot_params(
-        new base::DictionaryValue);
     status = web_view->CaptureScreenshot(&screenshot, base::DictionaryValue());
   }
   if (status.IsError())
diff --git a/chrome/test/data/chromedriver/drag.html b/chrome/test/data/chromedriver/drag.html
new file mode 100644
index 0000000..bae645c
--- /dev/null
+++ b/chrome/test/data/chromedriver/drag.html
@@ -0,0 +1,14 @@
+<div id="target" style="width: 100px; height: 100px; left: 50px; top: 50px;
+                        background-color: blue; position:absolute;" />
+
+<script>
+target = document.getElementById('target');
+document.onmousemove = evt => {
+  // Do drag when the left mouse button is down.
+  if (evt.buttons === 1) {
+    target.style.left = (evt.clientX - 50) + 'px';
+    target.style.top  = (evt.clientY - 50) + 'px';
+    return false;
+  }
+}
+</script>
diff --git a/chrome/test/data/find_in_page/find_from_selection.html b/chrome/test/data/find_in_page/find_from_selection.html
deleted file mode 100644
index a5ad292..0000000
--- a/chrome/test/data/find_in_page/find_from_selection.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<html>
-<head>
-<script>
-  function focusInput() {
-    var input = document.getElementById('selectText');
-    input.focus();
-    return "";
-  }
-</script>
-</head>
-<body>
-  findMe some text
-  <br>
-  <input id="selectText" value="text"></input>
-  some text.. find .. find ..
-  <br>
-  more text findMe and text.
-  <br>
-</body>
-</html>
diff --git a/chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js b/chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js
index bbafece..7b8556e 100644
--- a/chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js
+++ b/chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js
@@ -20,16 +20,26 @@
   // Checks if an element is present, visible or enabled on the page.
   automation_helper.isElementReady = function(
       getElementFunction,
+      xpath = "",
       state_flags =
           this.DomElementReadyState.visible |
           this.DomElementReadyState.enabled |
           this.DomElementReadyState.on_top) {
-    const element = getElementFunction();
-    if (element) {
-      var isReady = true;
 
+    // Some sites override the console function locally,
+    // this ensures our function can write to log
+    var frame = document.createElement('iframe');
+    document.body.appendChild(frame);
+    console = frame.contentWindow.console;
+
+    const element = getElementFunction();
+    let isReady = true;
+    let logDataArr = [];
+    logDataArr.push('[Element (' + xpath + ')]');
+    if (element) {
+      logDataArr.push('[FOUND]');
+      let target = element;
       if (state_flags & this.DomElementReadyState.visible) {
-        var target = element;
         // In some custom select drop downs, like the ones on Amazon.com and
         // Zappos.com, the drop down options are hosted inside a span element
         // that is the immediate sibling, rather than the descendant, of the
@@ -37,36 +47,42 @@
         // In these cases, check if the span is visible instead.
         if (element.offsetParent === null &&
             element instanceof HTMLSelectElement &&
-            element.nextElementSibling instanceof HTMLSpanElement)
+            element.nextElementSibling instanceof HTMLSpanElement) {
+          logDataArr.push("[Moved to nextElementSibling]");
           target = element.nextElementSibling;
-
-        isReady =
-            (target.offsetParent !== null) &&
-            (target.offsetWidth > 0) &&
-            (target.offsetHeight > 0);
-
-        if (isReady && state_flags & this.DomElementReadyState.on_top) {
-          // The document.elementFromPoint function only acts on an element
-          // inside the viewport. Scroll the element into view first.
-          element.scrollIntoViewIfNeeded(true);
-          var rect = target.getBoundingClientRect();
-          // Check that the element is not concealed behind another element.
-          const topElement = document.elementFromPoint(
-              // As coordinates, use the center of the element, minus the
-              // window offset in case the element is outside the view.
-              rect.left + rect.width / 2, rect.top + rect.height / 2);
-          isReady = target.contains(topElement) ||
-                      target.isSameNode(topElement);
         }
+        const isVisible = (target.offsetParent !== null) &&
+          (target.offsetWidth > 0) && (target.offsetHeight > 0);
+        logDataArr.push('[isVisible:' + isVisible + ']');
+        isReady = isReady && isVisible;
       }
-
-      if (isReady && state_flags & this.DomElementReadyState.enabled) {
-        isReady = !element.disabled;
+      if (state_flags & this.DomElementReadyState.on_top) {
+        // The document.elementFromPoint function only acts on an element
+        // inside the viewport. Actively scroll the element into view first.
+        element.scrollIntoView({block:"center", inline:"center"});
+        const rect = target.getBoundingClientRect();
+        // Check that the element is not concealed behind another element.
+        const topElement = document.elementFromPoint(
+            // As coordinates, use the center of the element, minus the
+            // window offset in case the element is outside the view.
+            rect.left + rect.width / 2, rect.top + rect.height / 2);
+        const isOnTop = target.contains(topElement) ||
+                      target.isSameNode(topElement);
+        isReady = isReady && isOnTop;
+        logDataArr.push('[OnTop:' + isOnTop + ':' + topElement.localName + ']');
       }
-
-      return isReady;
+      if (state_flags & this.DomElementReadyState.enabled) {
+        const isEnabled = !element.disabled;
+        logDataArr.push('[Enabled:' + isEnabled + ']');
+        isReady = isReady && isEnabled;
+      }
+    } else {
+      isReady = false;
+      logDataArr.push('[NOT FOUND]');
     }
-    return false;
+    logDataArr.push('[FinalReady:' + isReady + ']');
+    console.log(logDataArr.join(""));
+    return isReady;
   };
 
   // Check if an element identified by a xpath is present, visible or
@@ -79,6 +95,7 @@
       function(){
         return automation_helper.getElementByXpath(xpath);
       },
+      xpath,
       state_flags);
   };
 
diff --git a/chrome/test/data/webrtc/peerconnection_getstats.js b/chrome/test/data/webrtc/peerconnection_getstats.js
index 1cd3926b1..88604a1 100644
--- a/chrome/test/data/webrtc/peerconnection_getstats.js
+++ b/chrome/test/data/webrtc/peerconnection_getstats.js
@@ -116,7 +116,9 @@
   receiverId: 'string',
   remoteId: 'string',
   framesDecoded: 'number',
+  keyFramesDecoded: 'number',
   qpSum: 'number',
+  totalDecodeTime: 'number',
   lastPacketReceivedTimestamp: 'number',
   averageRtcpInterval: 'number',
   fecPacketsReceived: 'number',
@@ -180,6 +182,7 @@
   targetBitrate: 'number',
   totalEncodedBytesTarget: 'number',
   framesEncoded: 'number',
+  keyFramesEncoded: 'number',
   qpSum: 'number',
   totalEncodeTime: 'number',
   totalPacketSendDelay: 'number',
@@ -335,7 +338,6 @@
   framesCaptured: 'number',
   framesSent: 'number',
   hugeFramesSent: 'number',
-  keyFramesSent: 'number',
 });
 // TODO(hbos): When sender is implemented, make presence MANDATORY.
 addRTCStatsToWhitelist(Presence.OPTIONAL, 'sender', kRTCVideoSenderStats);
@@ -360,7 +362,6 @@
   jitterBufferDelay: 'number',
   jitterBufferEmittedCount: 'number',
   framesReceived: 'number',
-  keyFramesReceived: 'number',
   framesDecoded: 'number',
   framesDropped: 'number',
   partialFramesLost: 'number',
diff --git a/chrome/test/data/webui/app_management/arc_permission_view_test.js b/chrome/test/data/webui/app_management/arc_permission_view_test.js
index 6784ac2..19cc65e 100644
--- a/chrome/test/data/webui/app_management/arc_permission_view_test.js
+++ b/chrome/test/data/webui/app_management/arc_permission_view_test.js
@@ -38,6 +38,8 @@
         ArcPermissionType.CAMERA,
         ArcPermissionType.LOCATION,
         ArcPermissionType.NOTIFICATIONS,
+        ArcPermissionType.CONTACTS,
+        ArcPermissionType.STORAGE,
       ])
     };
 
@@ -115,5 +117,7 @@
     await checkPermissionItemOnClick('LOCATION');
     await checkPermissionItemOnClick('CAMERA');
     await checkPermissionItemOnClick('NOTIFICATIONS');
+    await checkPermissionItemOnClick('CONTACTS');
+    await checkPermissionItemOnClick('STORAGE');
   });
 });
diff --git a/chrome/test/data/webui/chromeos/fake_network_config_mojom.js b/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
index a71ab238..08b85956 100644
--- a/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
+++ b/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
@@ -39,6 +39,49 @@
   }
 
   /**
+   * @param {string} guid
+   * @return {!Promise<{result:
+   *     !chromeos.networkConfig.mojom.NetworkStateProperties>>}
+   */
+  getNetworkState(guid) {
+    return new Promise(resolve => {
+      this.extensionApi_.getState(guid, network => {
+        resolve({result: this.networkStateToMojo_(network)});
+      });
+    });
+  }
+
+  /**
+   * @param {!chromeos.networkConfig.mojom.NetworkFilter} filter
+   * @return {!Promise<{result:
+   *     !Array<!chromeos.networkConfig.mojom.NetworkStateProperties>}>}
+   */
+  getNetworkStateList(filter) {
+    return new Promise(resolve => {
+      const extensionFilter = {
+        networkType: OncMojo.getNetworkTypeString(filter.networkType)
+      };
+      this.extensionApi_.getNetworks(extensionFilter, networks => {
+        let result = networks.map(network => this.networkStateToMojo_(network));
+        resolve({result: result});
+      });
+    });
+  }
+
+  /**
+   * @return {!Promise<{result:
+   *     !Array<!chromeos.networkConfig.mojom.DeviceStateProperties>}>}
+   */
+  getDeviceStateList() {
+    return new Promise(resolve => {
+      this.extensionApi_.getDeviceStates(devices => {
+        let result = devices.map(device => this.deviceToMojo_(device));
+        resolve({result: result});
+      });
+    });
+  }
+
+  /**
    * @param {!chromeos.networkConfig.mojom.NetworkType} type
    * @param {boolean} enabled
    * @return {!Promise<{success: boolean}>}
@@ -51,4 +94,45 @@
     }
     return Promise.resolve(true);
   }
+
+  /** @param { !chromeos.networkConfig.mojom.NetworkType } type */
+  requestNetworkScan(type) {
+    this.extensionApi_.requestNetworkScan();
+  }
+
+  /**
+   * @param {!chrome.networkingPrivate.DeviceStateProperties} device
+   * @return {!chromeos.networkConfig.mojom.DeviceStateProperties}
+   * @private
+   */
+  deviceToMojo_(device) {
+    return {
+      deviceState: OncMojo.getDeviceStateTypeFromString(device.State),
+      type: OncMojo.getNetworkTypeFromString(device.Type),
+    };
+  }
+
+  /**
+   * @param {!chrome.networkingPrivate.NetworkStateProperties} network
+   * @return {!chromeos.networkConfig.mojom.NetworkStateProperties}
+   * @private
+   */
+  networkStateToMojo_(network) {
+    const mojoNetwork = OncMojo.getDefaultNetworkState(
+        OncMojo.getNetworkTypeFromString(network.Type));
+    mojoNetwork.guid = network.GUID;
+    mojoNetwork.name = network.Name;
+    if (network.ConnectionState) {
+      mojoNetwork.connectionState =
+          OncMojo.getConnectionStateTypeFromString(network.ConnectionState);
+    }
+    if (network.VPN) {
+      mojoNetwork.vpn = {type: OncMojo.getVPNTypeFromString(network.VPN.Type)};
+      if (network.VPN.ThirdPartyVPN) {
+        mojoNetwork.vpn.providerId = network.VPN.ThirdPartyVPN.ExtensionID;
+        mojoNetwork.vpn.providerName = network.VPN.ThirdPartyVPN.ProviderName;
+      }
+    }
+    return mojoNetwork;
+  }
 }
diff --git a/chrome/test/data/webui/cr_elements/cr_dialog_test.js b/chrome/test/data/webui/cr_elements/cr_dialog_test.js
index ecc0f0d..f7d43ce4 100644
--- a/chrome/test/data/webui/cr_elements/cr_dialog_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_dialog_test.js
@@ -220,25 +220,32 @@
     assertTrue(clicked);
   });
 
-  test('enter keys from cr-inputs (only) are processed', function() {
+  test('enter keys from certain inputs only are processed', function() {
     document.body.innerHTML = `
       <cr-dialog>
         <div slot="title">title</div>
         <div slot="body">
-          <cr-input></cr-input>
+          <input></input>
+          <input type="text"></input>
+          <input type="password"></input>
+          <input type="checkbox"></input>
           <foobar></foobar>
           <button class="action-button">active</button>
+          <cr-input></cr-input>
         </div>
       </cr-dialog>`;
 
     const dialog = document.body.querySelector('cr-dialog');
 
-    const inputElement = document.body.querySelector('cr-input');
+    const inputElement = document.body.querySelector('input:not([type])');
+    const inputTextElement = document.body.querySelector('input[type="text"]');
+    const inputPasswordElement =
+        document.body.querySelector('input[type="password"]');
+    const inputCheckboxElement =
+        document.body.querySelector('input[type="checkbox"]');
     const otherElement = document.body.querySelector('foobar');
     const actionButton = document.body.querySelector('.action-button');
-    assertTrue(!!inputElement);
-    assertTrue(!!otherElement);
-    assertTrue(!!actionButton);
+    const crInputElement = document.body.querySelector('cr-input');
 
     // MockInteractions triggers event listeners synchronously.
     let clickedCounter = 0;
@@ -246,8 +253,59 @@
       clickedCounter++;
     });
 
+    // Only certain types of <input> trigger a dialog submit.
     pressEnter(otherElement);
     assertEquals(0, clickedCounter);
+    // "type" defaults to text, which triggers the click.
+    pressEnter(inputElement);
+    assertEquals(1, clickedCounter);
+    pressEnter(inputTextElement);
+    assertEquals(2, clickedCounter);
+    pressEnter(inputPasswordElement);
+    assertEquals(3, clickedCounter);
+    pressEnter(inputCheckboxElement);
+    assertEquals(3, clickedCounter);
+    // Also trigger dialog submit if code synthesizes enter on a cr-input
+    // without targeting the underlying input.
+    pressEnter(crInputElement);
+    assertEquals(4, clickedCounter);
+  });
+
+  // Test that enter key presses trigger an action button click, even if the
+  // even was retargeted, e.g. because the input was really a cr-input, the
+  // cr-input was part of another custom element, etc.
+  test('enter keys are processed even if event was retargeted', function() {
+    document.body.innerHTML = `
+      <dom-module id="test-element">
+        <template><input></input></template>
+      </dom-module>
+
+      <cr-dialog>
+        <div slot="title">title</div>
+        <div slot="body">
+          <test-element></test-element>
+          <button class="action-button">active</button>
+        </div>
+      </cr-dialog>`;
+
+    Polymer({
+      is: 'test-element',
+    });
+
+    const dialog = document.body.querySelector('cr-dialog');
+
+    const inputWrapper = document.body.querySelector('test-element');
+    assertTrue(!!inputWrapper);
+    const inputElement = inputWrapper.shadowRoot.querySelector('input');
+    const actionButton = document.body.querySelector('.action-button');
+    assertTrue(!!inputElement);
+    assertTrue(!!actionButton);
+
+    // MockInteractions triggers event listeners synchronously.
+    let clickedCounter = 0;
+    actionButton.addEventListener('click', function() {
+      clickedCounter++;
+    });
 
     pressEnter(inputElement);
     assertEquals(1, clickedCounter);
diff --git a/chrome/test/data/webui/icon_test.html b/chrome/test/data/webui/icon_test.html
index 555a61ae..8a9c9f5 100644
--- a/chrome/test/data/webui/icon_test.html
+++ b/chrome/test/data/webui/icon_test.html
@@ -7,10 +7,10 @@
 function testGetFavicon_NonFavicon() {
   var url = 'http://foo.com';
   var expectedDesktop = '-webkit-image-set(' +
-      'url("chrome://favicon/size/16@1x/http://foo.com") 1x, ' +
-      'url("chrome://favicon/size/16@2x/http://foo.com") 2x)';
+      'url("chrome://favicon2/?size=16&scale_factor=1x&url_type=page_url&url='+encodeURIComponent('http://foo.com')+'") 1x, ' +
+      'url("chrome://favicon2/?size=16&scale_factor=2x&url_type=page_url&url='+encodeURIComponent('http://foo.com')+'") 2x)';
   var expectedOther = '-webkit-image-set(' +
-      'url("chrome://favicon/size/16@1x/http://foo.com") ' +
+      'url("chrome://favicon2/?size=16&scale_factor=1x&url_type=page_url&url='+encodeURIComponent('http://foo.com')+'") ' +
       window.devicePixelRatio + 'x)';
 
   var isDesktop = cr.isMac || cr.isChromeOS || cr.isWindows || cr.isLinux;
@@ -22,10 +22,13 @@
 function testGetFavicon_IconUrl() {
   var url = 'http://foo.com/foo.ico';
   var expectedDesktop = '-webkit-image-set(' +
-      'url("chrome://favicon/size/16@1x/iconurl/http://foo.com/foo.ico") 1x, ' +
-      'url("chrome://favicon/size/16@2x/iconurl/http://foo.com/foo.ico") 2x)';
+      'url("chrome://favicon2/?size=16&scale_factor=1x&url_type=icon_url&url=' +
+      encodeURIComponent('http://foo.com/foo.ico')+'") 1x, ' +
+      'url("chrome://favicon2/?size=16&scale_factor=2x&url_type=icon_url&url=' +
+      encodeURIComponent('http://foo.com/foo.ico')+'") 2x)';
   var expectedOther = '-webkit-image-set(' +
-      'url("chrome://favicon/size/16@1x/iconurl/http://foo.com/foo.ico") ' +
+      'url("chrome://favicon2/?size=16&scale_factor=1x&url_type=icon_url&url=' +
+      encodeURIComponent('http://foo.com/foo.ico')+'") ' +
       window.devicePixelRatio + 'x)';
 
   var isDesktop = cr.isMac || cr.isChromeOS || cr.isWindows || cr.isLinux;
diff --git a/chrome/test/data/webui/settings/about_page_tests.js b/chrome/test/data/webui/settings/about_page_tests.js
index 38b7c4b..2f9ebdcf 100644
--- a/chrome/test/data/webui/settings/about_page_tests.js
+++ b/chrome/test/data/webui/settings/about_page_tests.js
@@ -32,34 +32,47 @@
       const SPINNER_ICON = 'chrome://resources/images/throbber_small.svg';
 
       setup(function() {
+        loadTimeData.overrideValues({
+          aboutObsoleteNowOrSoon: false,
+          aboutObsoleteEndOfTheLine: false,
+        });
+        if (cr.isChromeOS) {
+          loadTimeData.overrideValues({
+            showOSSettings: true,
+          });
+        }
         lifetimeBrowserProxy = new settings.TestLifetimeBrowserProxy();
         settings.LifetimeBrowserProxyImpl.instance_ = lifetimeBrowserProxy;
 
         aboutBrowserProxy = new TestAboutPageBrowserProxy();
         settings.AboutPageBrowserProxyImpl.instance_ = aboutBrowserProxy;
-        return initNewPage();
+        return initNewPage(true /* showOsSettings */);
       });
 
       teardown(function() {
         page.remove();
         page = null;
-        loadTimeData.overrideValues({
-          aboutObsoleteNowOrSoon: false,
-          aboutObsoleteEndOfTheLine: false,
-        });
       });
 
-      /** @return {!Promise} */
-      function initNewPage() {
+      /**
+       * TODO(aee): remove |showOsSettings| param after the SplitSettings
+       *            feature tag is removed.
+       * @param {boolean=} showOsSettings
+       * @return {!Promise}
+       */
+      function initNewPage(showOsSettings) {
         aboutBrowserProxy.reset();
         lifetimeBrowserProxy.reset();
         PolymerTest.clearBody();
+        if (cr.isChromeOS) {
+          loadTimeData.overrideValues({showOSSettings: showOsSettings});
+        }
         page = document.createElement('settings-about-page');
         settings.navigateTo(settings.routes.ABOUT);
         document.body.appendChild(page);
         if (!cr.isChromeOS) {
           return aboutBrowserProxy.whenCalled('refreshUpdateStatus');
-        } else {
+        } else if (showOsSettings) {
           return Promise.all([
             aboutBrowserProxy.whenCalled('getChannelInfo'),
             aboutBrowserProxy.whenCalled('refreshUpdateStatus'),
@@ -141,75 +154,73 @@
          * (but not end of the line) a deprecation warning message is displayed,
          * without interfering with the update status message and icon.
          */
-        test('ObsoleteSystem', function() {
+        test('ObsoleteSystem', async () => {
           loadTimeData.overrideValues({
             aboutObsoleteNowOrSoon: true,
             aboutObsoleteEndOfTheLine: false,
           });
 
-          return initNewPage().then(function() {
-            const icon = page.$$('iron-icon');
-            assertTrue(!!icon);
-            assertTrue(!!page.$.updateStatusMessage);
-            assertTrue(!!page.$.deprecationWarning);
-            assertFalse(page.$.deprecationWarning.hidden);
+          await initNewPage(true /* showOsSettings */);
+          const icon = page.$$('iron-icon');
+          assertTrue(!!icon);
+          assertTrue(!!page.$.updateStatusMessage);
+          assertTrue(!!page.$.deprecationWarning);
+          assertFalse(page.$.deprecationWarning.hidden);
 
-            fireStatusChanged(UpdateStatus.CHECKING);
-            assertEquals(SPINNER_ICON, icon.src);
-            assertEquals(null, icon.getAttribute('icon'));
-            assertFalse(page.$.deprecationWarning.hidden);
-            assertFalse(page.$.updateStatusMessage.hidden);
+          fireStatusChanged(UpdateStatus.CHECKING);
+          assertEquals(SPINNER_ICON, icon.src);
+          assertEquals(null, icon.getAttribute('icon'));
+          assertFalse(page.$.deprecationWarning.hidden);
+          assertFalse(page.$.updateStatusMessage.hidden);
 
-            fireStatusChanged(UpdateStatus.UPDATING);
-            assertEquals(SPINNER_ICON, icon.src);
-            assertEquals(null, icon.getAttribute('icon'));
-            assertFalse(page.$.deprecationWarning.hidden);
-            assertFalse(page.$.updateStatusMessage.hidden);
+          fireStatusChanged(UpdateStatus.UPDATING);
+          assertEquals(SPINNER_ICON, icon.src);
+          assertEquals(null, icon.getAttribute('icon'));
+          assertFalse(page.$.deprecationWarning.hidden);
+          assertFalse(page.$.updateStatusMessage.hidden);
 
-            fireStatusChanged(UpdateStatus.NEARLY_UPDATED);
-            assertEquals(null, icon.src);
-            assertEquals('settings:check-circle', icon.icon);
-            assertFalse(page.$.deprecationWarning.hidden);
-            assertFalse(page.$.updateStatusMessage.hidden);
-          });
+          fireStatusChanged(UpdateStatus.NEARLY_UPDATED);
+          assertEquals(null, icon.src);
+          assertEquals('settings:check-circle', icon.icon);
+          assertFalse(page.$.deprecationWarning.hidden);
+          assertFalse(page.$.updateStatusMessage.hidden);
         });
 
         /**
          * Test that when the current platform has reached the end of the line,
          * a deprecation warning message and an error icon is displayed.
          */
-        test('ObsoleteSystemEndOfLine', function() {
+        test('ObsoleteSystemEndOfLine', async () => {
           loadTimeData.overrideValues({
             aboutObsoleteNowOrSoon: true,
             aboutObsoleteEndOfTheLine: true,
           });
-          return initNewPage().then(function() {
-            const icon = page.$$('iron-icon');
-            assertTrue(!!icon);
-            assertTrue(!!page.$.deprecationWarning);
-            assertTrue(!!page.$.updateStatusMessage);
+          await initNewPage(true /* showOsSettings */);
+          const icon = page.$$('iron-icon');
+          assertTrue(!!icon);
+          assertTrue(!!page.$.deprecationWarning);
+          assertTrue(!!page.$.updateStatusMessage);
 
-            assertFalse(page.$.deprecationWarning.hidden);
-            assertFalse(page.$.deprecationWarning.hidden);
+          assertFalse(page.$.deprecationWarning.hidden);
+          assertFalse(page.$.deprecationWarning.hidden);
 
-            fireStatusChanged(UpdateStatus.CHECKING);
-            assertEquals(null, icon.src);
-            assertEquals('cr:error', icon.icon);
-            assertFalse(page.$.deprecationWarning.hidden);
-            assertTrue(page.$.updateStatusMessage.hidden);
+          fireStatusChanged(UpdateStatus.CHECKING);
+          assertEquals(null, icon.src);
+          assertEquals('cr:error', icon.icon);
+          assertFalse(page.$.deprecationWarning.hidden);
+          assertTrue(page.$.updateStatusMessage.hidden);
 
-            fireStatusChanged(UpdateStatus.FAILED);
-            assertEquals(null, icon.src);
-            assertEquals('cr:error', icon.icon);
-            assertFalse(page.$.deprecationWarning.hidden);
-            assertTrue(page.$.updateStatusMessage.hidden);
+          fireStatusChanged(UpdateStatus.FAILED);
+          assertEquals(null, icon.src);
+          assertEquals('cr:error', icon.icon);
+          assertFalse(page.$.deprecationWarning.hidden);
+          assertTrue(page.$.updateStatusMessage.hidden);
 
-            fireStatusChanged(UpdateStatus.UPDATED);
-            assertEquals(null, icon.src);
-            assertEquals('cr:error', icon.icon);
-            assertFalse(page.$.deprecationWarning.hidden);
-            assertTrue(page.$.updateStatusMessage.hidden);
-          });
+          fireStatusChanged(UpdateStatus.UPDATED);
+          assertEquals(null, icon.src);
+          assertEquals('cr:error', icon.icon);
+          assertFalse(page.$.deprecationWarning.hidden);
+          assertTrue(page.$.updateStatusMessage.hidden);
         });
       }
 
@@ -309,24 +320,20 @@
          * 'update-status-changed' events for the case where the target channel
          * is more stable than current channel.
          */
-        test('ButtonsUpdate_BetaToStable', function() {
+        test('ButtonsUpdate_BetaToStable', async () => {
           aboutBrowserProxy.setChannels(
               BrowserChannel.BETA, BrowserChannel.STABLE);
           aboutBrowserProxy.setUpdateStatus(UpdateStatus.NEARLY_UPDATED);
 
-          return initNewPage().then(function() {
-            assertTrue(!!page.$.relaunch);
-            assertTrue(!!page.$.relaunchAndPowerwash);
+          await initNewPage(true /* showOsSettings */);
+          assertTrue(!!page.$.relaunch);
+          assertTrue(!!page.$.relaunchAndPowerwash);
 
-            assertTrue(page.$.relaunch.hidden);
-            assertFalse(page.$.relaunchAndPowerwash.hidden);
+          assertTrue(page.$.relaunch.hidden);
+          assertFalse(page.$.relaunchAndPowerwash.hidden);
 
-            page.$.relaunchAndPowerwash.click();
-            return lifetimeBrowserProxy.whenCalled('factoryReset')
-                .then((requestTpmFirmwareUpdate) => {
-                  assertFalse(requestTpmFirmwareUpdate);
-                });
-          });
+          page.$.relaunchAndPowerwash.click();
+          assertFalse(await lifetimeBrowserProxy.whenCalled('factoryReset'));
         });
 
         /**
@@ -334,21 +341,20 @@
          * 'update-status-changed' events for the case where the target channel
          * is less stable than current channel.
          */
-        test('ButtonsUpdate_StableToBeta', function() {
+        test('ButtonsUpdate_StableToBeta', async () => {
           aboutBrowserProxy.setChannels(
               BrowserChannel.STABLE, BrowserChannel.BETA);
           aboutBrowserProxy.setUpdateStatus(UpdateStatus.NEARLY_UPDATED);
 
-          return initNewPage().then(function() {
-            assertTrue(!!page.$.relaunch);
-            assertTrue(!!page.$.relaunchAndPowerwash);
+          await initNewPage(true /* showOsSettings */);
+          assertTrue(!!page.$.relaunch);
+          assertTrue(!!page.$.relaunchAndPowerwash);
 
-            assertFalse(page.$.relaunch.hidden);
-            assertTrue(page.$.relaunchAndPowerwash.hidden);
+          assertFalse(page.$.relaunch.hidden);
+          assertTrue(page.$.relaunchAndPowerwash.hidden);
 
-            page.$.relaunch.click();
-            return lifetimeBrowserProxy.whenCalled('relaunch');
-          });
+          page.$.relaunch.click();
+          await lifetimeBrowserProxy.whenCalled('relaunch');
         });
 
         /**
@@ -356,27 +362,26 @@
          * 'target-channel-changed' event (normally fired from
          * <settings-channel-switcher-dialog>).
          */
-        test('ButtonsUpdate_TargetChannelChangedEvent', function() {
+        test('ButtonsUpdate_TargetChannelChangedEvent', async () => {
           aboutBrowserProxy.setChannels(
               BrowserChannel.BETA, BrowserChannel.BETA);
           aboutBrowserProxy.setUpdateStatus(UpdateStatus.NEARLY_UPDATED);
 
-          return initNewPage().then(function() {
-            assertFalse(page.$.relaunch.hidden);
-            assertTrue(page.$.relaunchAndPowerwash.hidden);
+          await initNewPage(true /* showOsSettings */);
+          assertFalse(page.$.relaunch.hidden);
+          assertTrue(page.$.relaunchAndPowerwash.hidden);
 
-            page.fire('target-channel-changed', BrowserChannel.DEV);
-            assertFalse(page.$.relaunch.hidden);
-            assertTrue(page.$.relaunchAndPowerwash.hidden);
+          page.fire('target-channel-changed', BrowserChannel.DEV);
+          assertFalse(page.$.relaunch.hidden);
+          assertTrue(page.$.relaunchAndPowerwash.hidden);
 
-            page.fire('target-channel-changed', BrowserChannel.STABLE);
-            assertTrue(page.$.relaunch.hidden);
-            assertFalse(page.$.relaunchAndPowerwash.hidden);
-          });
+          page.fire('target-channel-changed', BrowserChannel.STABLE);
+          assertTrue(page.$.relaunch.hidden);
+          assertFalse(page.$.relaunchAndPowerwash.hidden);
         });
 
-        test('RegulatoryInfo', function() {
-          let regulatoryInfo = null;
+        test('RegulatoryInfo', async () => {
+          const regulatoryInfo = {text: 'foo', url: 'bar'};
 
           /**
            * Checks the visibility of the "regulatory info" section.
@@ -384,113 +389,106 @@
            *     visible.
            * @return {!Promise}
            */
-          function checkRegulatoryInfo(isShowing) {
-            return aboutBrowserProxy.whenCalled('getRegulatoryInfo')
-                .then(function() {
-                  const regulatoryInfoEl = page.$.regulatoryInfo;
-                  assertTrue(!!regulatoryInfoEl);
-                  assertEquals(isShowing, !regulatoryInfoEl.hidden);
+          async function checkRegulatoryInfo(isShowing) {
+            await aboutBrowserProxy.whenCalled('getRegulatoryInfo');
+            const regulatoryInfoEl = page.$.regulatoryInfo;
+            assertTrue(!!regulatoryInfoEl);
+            assertEquals(isShowing, !regulatoryInfoEl.hidden);
 
-                  if (isShowing) {
-                    const img = regulatoryInfoEl.querySelector('img');
-                    assertTrue(!!img);
-                    assertEquals(regulatoryInfo.text, img.getAttribute('alt'));
-                    assertEquals(regulatoryInfo.url, img.getAttribute('src'));
-                  }
-                });
+            if (isShowing) {
+              const img = regulatoryInfoEl.querySelector('img');
+              assertTrue(!!img);
+              assertEquals(regulatoryInfo.text, img.getAttribute('alt'));
+              assertEquals(regulatoryInfo.url, img.getAttribute('src'));
+            }
           }
 
-          return checkRegulatoryInfo(false)
-              .then(function() {
-                regulatoryInfo = {text: 'foo', url: 'bar'};
-                aboutBrowserProxy.setRegulatoryInfo(regulatoryInfo);
-                return initNewPage();
-              })
-              .then(function() {
-                return checkRegulatoryInfo(true);
-              });
+          await checkRegulatoryInfo(false);
+          aboutBrowserProxy.setRegulatoryInfo(regulatoryInfo);
+          await initNewPage(true /* showOsSettings */);
+          await checkRegulatoryInfo(true);
         });
 
-        test('TPMFirmwareUpdate', function() {
-          return initNewPage()
-              .then(function() {
-                assertTrue(page.$.aboutTPMFirmwareUpdate.hidden);
-                aboutBrowserProxy.setTPMFirmwareUpdateStatus(
-                    {updateAvailable: true});
-                aboutBrowserProxy.refreshTPMFirmwareUpdateStatus();
-              })
-              .then(function() {
-                assertFalse(page.$.aboutTPMFirmwareUpdate.hidden);
-                page.$.aboutTPMFirmwareUpdate.click();
-              })
-              .then(function() {
-                const dialog = page.$$('settings-powerwash-dialog');
-                assertTrue(!!dialog);
-                assertTrue(dialog.$.dialog.open);
-                dialog.$$('#powerwash').click();
-                return lifetimeBrowserProxy.whenCalled('factoryReset')
-                    .then((requestTpmFirmwareUpdate) => {
-                      assertTrue(requestTpmFirmwareUpdate);
-                    });
-              });
+        test('TPMFirmwareUpdate', async () => {
+          await initNewPage(true /* showOsSettings */);
+          assertTrue(page.$.aboutTPMFirmwareUpdate.hidden);
+          aboutBrowserProxy.setTPMFirmwareUpdateStatus({updateAvailable: true});
+          aboutBrowserProxy.refreshTPMFirmwareUpdateStatus();
+          assertFalse(page.$.aboutTPMFirmwareUpdate.hidden);
+          page.$.aboutTPMFirmwareUpdate.click();
+          await PolymerTest.flushTasks();
+          const dialog = page.$$('settings-powerwash-dialog');
+          assertTrue(!!dialog);
+          assertTrue(dialog.$.dialog.open);
+          dialog.$$('#powerwash').click();
+          assertTrue(await lifetimeBrowserProxy.whenCalled('factoryReset'));
         });
 
-        test('DeviceEndOfLife', function() {
+        test('DeviceEndOfLife', async () => {
           /**
            * Checks the visibility of the end of life message and icon.
            * @param {boolean} isShowing Whether the end of life UI is expected
            *     to be visible.
            * @return {!Promise}
            */
-          function checkHasEndOfLife(isShowing) {
-            return aboutBrowserProxy.whenCalled('getHasEndOfLife')
-                .then(function() {
-                  const endOfLifeMessageContainer =
-                      page.$.endOfLifeMessageContainer;
-                  assertTrue(!!endOfLifeMessageContainer);
-                  assertEquals(isShowing, !endOfLifeMessageContainer.hidden);
+          async function checkHasEndOfLife(isShowing) {
+            await aboutBrowserProxy.whenCalled('getHasEndOfLife');
+            const endOfLifeMessageContainer = page.$.endOfLifeMessageContainer;
+            assertTrue(!!endOfLifeMessageContainer);
+            assertEquals(isShowing, !endOfLifeMessageContainer.hidden);
 
-                  // Update status message should be hidden before user has
-                  // checked for updates.
-                  assertTrue(page.$.updateStatusMessage.hidden);
+            // Update status message should be hidden before user has
+            // checked for updates.
+            assertTrue(page.$.updateStatusMessage.hidden);
 
-                  fireStatusChanged(UpdateStatus.CHECKING);
-                  assertEquals(isShowing, page.$.updateStatusMessage.hidden);
+            fireStatusChanged(UpdateStatus.CHECKING);
+            assertEquals(isShowing, page.$.updateStatusMessage.hidden);
 
-                  if (isShowing) {
-                    const icon = page.$$('iron-icon');
-                    assertTrue(!!icon);
-                    assertEquals(null, icon.src);
-                    assertEquals('settings:end-of-life', icon.icon);
+            if (isShowing) {
+              const icon = page.$$('iron-icon');
+              assertTrue(!!icon);
+              assertEquals(null, icon.src);
+              assertEquals('settings:end-of-life', icon.icon);
 
-                    const checkForUpdates = page.$.checkForUpdates;
-                    assertTrue(!!checkForUpdates);
-                    assertTrue(checkForUpdates.hidden);
-                  }
-                });
+              const checkForUpdates = page.$.checkForUpdates;
+              assertTrue(!!checkForUpdates);
+              assertTrue(checkForUpdates.hidden);
+            }
           }
 
           // Force test proxy to not respond to JS requests.
           // End of life message should still be hidden in this case.
           aboutBrowserProxy.setHasEndOfLife(new Promise(function(res, rej) {}));
-          return initNewPage()
-              .then(function() {
-                return checkHasEndOfLife(false);
-              })
-              .then(function() {
-                aboutBrowserProxy.setHasEndOfLife(true);
-                return initNewPage();
-              })
-              .then(function() {
-                return checkHasEndOfLife(true);
-              })
-              .then(function() {
-                aboutBrowserProxy.setHasEndOfLife(false);
-                return initNewPage();
-              })
-              .then(function() {
-                return checkHasEndOfLife(false);
-              });
+          await initNewPage(true /* showOsSettings */);
+          await checkHasEndOfLife(false);
+          aboutBrowserProxy.setHasEndOfLife(true);
+          await initNewPage(true /* showOsSettings */);
+          await checkHasEndOfLife(true);
+          aboutBrowserProxy.setHasEndOfLife(false);
+          await initNewPage(true /* showOsSettings */);
+          await checkHasEndOfLife(false);
+        });
+
+        test('showOsSettings=false, CrOS parts not visible', async () => {
+          aboutBrowserProxy.setHasEndOfLife(true);
+          aboutBrowserProxy.setRegulatoryInfo({text: 'text', url: 'url'});
+          await initNewPage(true /* showOsSettings */);
+          assertFalse(!!page.$.endOfLifeMessageContainer.hidden);
+          assertFalse(!!page.$['detailed-build-info-trigger'].hidden);
+          assertFalse(!!page.$.regulatoryInfo.hidden);
+          assertFalse(!!page.$.crostiniLicense.hidden);
+          await initNewPage(false /* showOsSettings */);
+          assertTrue(!!page.$.endOfLifeMessageContainer.hidden);
+          assertTrue(!!page.$['detailed-build-info-trigger'].hidden);
+          assertTrue(!!page.$.regulatoryInfo.hidden);
+          assertTrue(!!page.$.crostiniLicense.hidden);
+        });
+
+        test('detailed build info page', () => {
+          page.scroller = page.offsetParent;
+          assertTrue(!!page.$['detailed-build-info-trigger']);
+          page.$['detailed-build-info-trigger'].click();
+          assertTrue(!!page.$$('settings-detailed-build-info'));
         });
       }
 
@@ -676,7 +674,7 @@
           page = null;
         });
 
-        test('Initialization', function() {
+        test('Initialization', async () => {
           const versionInfo = {
             arcVersion: 'dummyArcVersion',
             osFirmware: 'dummyOsFirmware',
@@ -687,20 +685,14 @@
           page = document.createElement('settings-detailed-build-info');
           document.body.appendChild(page);
 
-          return Promise
-              .all([
-                browserProxy.whenCalled('pageReady'),
-                browserProxy.whenCalled('getVersionInfo'),
-                browserProxy.whenCalled('getChannelInfo'),
-              ])
-              .then(function() {
-                assertEquals(
-                    versionInfo.arcVersion, page.$.arcVersion.textContent);
-                assertEquals(
-                    versionInfo.osVersion, page.$.osVersion.textContent);
-                assertEquals(
-                    versionInfo.osFirmware, page.$.osFirmware.textContent);
-              });
+          await Promise.all([
+            browserProxy.whenCalled('pageReady'),
+            browserProxy.whenCalled('getVersionInfo'),
+            browserProxy.whenCalled('getChannelInfo'),
+          ]);
+          assertEquals(versionInfo.arcVersion, page.$.arcVersion.textContent);
+          assertEquals(versionInfo.osVersion, page.$.osVersion.textContent);
+          assertEquals(versionInfo.osFirmware, page.$.osFirmware.textContent);
         });
 
         /**
@@ -711,15 +703,14 @@
          *     changing channels is allowed.
          * @return {!Promise}
          */
-        function checkChangeChannelButton(canChangeChannel) {
+        async function checkChangeChannelButton(canChangeChannel) {
           browserProxy.setCanChangeChannel(canChangeChannel);
           page = document.createElement('settings-detailed-build-info');
           document.body.appendChild(page);
-          return browserProxy.whenCalled('getChannelInfo').then(function() {
-            const changeChannelButton = page.$$('cr-button');
-            assertTrue(!!changeChannelButton);
-            assertEquals(canChangeChannel, !changeChannelButton.disabled);
-          });
+          await browserProxy.whenCalled('getChannelInfo');
+          const changeChannelButton = page.$$('cr-button');
+          assertTrue(!!changeChannelButton);
+          assertEquals(canChangeChannel, !changeChannelButton.disabled);
         }
 
         test('ChangeChannel_Enabled', function() {
@@ -775,60 +766,52 @@
         });
 
         // Test case where user switches to a less stable channel.
-        test('ChangeChannel_LessStable', function() {
+        test('ChangeChannel_LessStable', async () => {
           assertEquals(BrowserChannel.DEV, radioButtons.item(2).name);
           radioButtons.item(2).click();
           Polymer.dom.flush();
 
-          return browserProxy.whenCalled('getChannelInfo').then(function() {
-            assertEquals(dialog.$.warningSelector.selected, 2);
-            // Check that only the "Change channel" button becomes visible.
-            assertTrue(dialog.$.changeChannelAndPowerwash.hidden);
-            assertFalse(dialog.$.changeChannel.hidden);
+          await browserProxy.whenCalled('getChannelInfo');
+          assertEquals(dialog.$.warningSelector.selected, 2);
+          // Check that only the "Change channel" button becomes visible.
+          assertTrue(dialog.$.changeChannelAndPowerwash.hidden);
+          assertFalse(dialog.$.changeChannel.hidden);
 
-            const whenTargetChannelChangedFired =
-                test_util.eventToPromise('target-channel-changed', dialog);
+          const whenTargetChannelChangedFired =
+              test_util.eventToPromise('target-channel-changed', dialog);
 
-            dialog.$.changeChannel.click();
-            return browserProxy.whenCalled('setChannel')
-                .then(function(args) {
-                  assertEquals(BrowserChannel.DEV, args[0]);
-                  assertFalse(args[1]);
-                  return whenTargetChannelChangedFired;
-                })
-                .then(function(event) {
-                  assertEquals(BrowserChannel.DEV, event.detail);
-                });
-          });
+          dialog.$.changeChannel.click();
+          const [channel, isPowerwashAllowed] =
+              await browserProxy.whenCalled('setChannel');
+          assertEquals(BrowserChannel.DEV, channel);
+          assertFalse(isPowerwashAllowed);
+          const {detail} = await whenTargetChannelChangedFired;
+          assertEquals(BrowserChannel.DEV, detail);
         });
 
         // Test case where user switches to a more stable channel.
-        test('ChangeChannel_MoreStable', function() {
+        test('ChangeChannel_MoreStable', async () => {
           assertEquals(BrowserChannel.STABLE, radioButtons.item(0).name);
           radioButtons.item(0).click();
           Polymer.dom.flush();
 
-          return browserProxy.whenCalled('getChannelInfo').then(function() {
-            assertEquals(dialog.$.warningSelector.selected, 1);
-            // Check that only the "Change channel and Powerwash" button becomes
-            // visible.
-            assertFalse(dialog.$.changeChannelAndPowerwash.hidden);
-            assertTrue(dialog.$.changeChannel.hidden);
+          await browserProxy.whenCalled('getChannelInfo');
+          assertEquals(dialog.$.warningSelector.selected, 1);
+          // Check that only the "Change channel and Powerwash" button becomes
+          // visible.
+          assertFalse(dialog.$.changeChannelAndPowerwash.hidden);
+          assertTrue(dialog.$.changeChannel.hidden);
 
-            const whenTargetChannelChangedFired =
-                test_util.eventToPromise('target-channel-changed', dialog);
+          const whenTargetChannelChangedFired =
+              test_util.eventToPromise('target-channel-changed', dialog);
 
-            dialog.$.changeChannelAndPowerwash.click();
-            return browserProxy.whenCalled('setChannel')
-                .then(function(args) {
-                  assertEquals(BrowserChannel.STABLE, args[0]);
-                  assertTrue(args[1]);
-                  return whenTargetChannelChangedFired;
-                })
-                .then(function(event) {
-                  assertEquals(BrowserChannel.STABLE, event.detail);
-                });
-          });
+          dialog.$.changeChannelAndPowerwash.click();
+          const [channel, isPowerwashAllowed] =
+              await browserProxy.whenCalled('setChannel');
+          assertEquals(BrowserChannel.STABLE, channel);
+          assertTrue(isPowerwashAllowed);
+          const {detail} = await whenTargetChannelChangedFired;
+          assertEquals(BrowserChannel.STABLE, detail);
         });
       });
     }
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 14cf5ccd..e915e74 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -9,6 +9,7 @@
 
 GEN('#if defined(OS_CHROMEOS)');
 GEN('#include "ash/public/cpp/ash_features.h"');
+GEN('#include "chromeos/constants/chromeos_switches.h"');
 GEN('#endif  // defined(OS_CHROMEOS)');
 
 GEN('#include "chrome/common/chrome_features.h"');
@@ -249,7 +250,8 @@
   ]),
 };
 
-TEST_F('CrSettingsAppManagementPageTest', 'All', function() {
+// TODO(https://crbug.com/979553) Disabled due to failures .
+TEST_F('CrSettingsAppManagementPageTest', 'DISABLED_All', function() {
   mocha.run();
 });
 GEN('#endif  // defined(OS_CHROMEOS)');
@@ -587,6 +589,9 @@
   browsePreload: 'chrome://settings/people_page/account_manager.html',
 
   /** @override */
+  featureList: {enabled: ['chromeos::switches::kAccountManager']},
+
+  /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../test_browser_proxy.js',
     'people_page_account_manager_test.js',
diff --git a/chrome/test/data/webui/settings/cups_printer_page_tests.js b/chrome/test/data/webui/settings/cups_printer_page_tests.js
index a506172..bb73e0d 100644
--- a/chrome/test/data/webui/settings/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/cups_printer_page_tests.js
@@ -760,6 +760,28 @@
 
     dialog = document.createElement('settings-cups-edit-printer-dialog');
 
+    dialog.activePrinter = {
+      ppdManufacturer: '',
+      ppdModel: '',
+      printerAddress: '',
+      printerAutoconf: false,
+      printerDescription: '',
+      printerId: '',
+      printerManufacturer: '',
+      printerModel: '',
+      printerMakeAndModel: '',
+      printerName: '',
+      printerPPDPath: '',
+      printerPpdReference: {
+        userSuppliedPpdUrl: '',
+        effectiveMakeAndModel: '',
+        autoconf: false,
+      },
+      printerProtocol: '',
+      printerQueue: '',
+      printerStatus: '',
+    };
+
     dialog.pendingPrinter_ = {
       ppdManufacturer: '',
       ppdModel: '',
@@ -782,6 +804,8 @@
       printerStatus: '',
     };
 
+    dialog.isOnline_ = true;
+
     document.body.appendChild(dialog);
   });
 
@@ -1280,4 +1304,55 @@
           assertFalse(urlElement.hidden);
         });
   });
+
+  test('OfflineEdit', function() {
+    dialog.pendingPrinter_ = {
+      printerAutoconf: false,
+      printerDescription: '',
+      printerId: 'id_123',
+      printerManufacturer: '',
+      printerModel: '',
+      printerMakeAndModel: '',
+      printerName: 'Test Printer',
+      printerPPDPath: '',
+      printerPpdReference: {
+        userSuppliedPpdUrl: '',
+        effectiveMakeAndModel: '',
+        autoconf: false,
+      },
+      ppdManufacturer: '',
+      ppdModel: '',
+      printerAddress: '03f0/e414?serial=CD4234',
+      printerProtocol: 'usb',
+      printerQueue: 'moreinfohere',
+      printerStatus: '',
+    };
+    setPpdManufacturerAndPpdModel('manufacture', 'model');
+
+    // Initializing activePrinter will set |needsReconfigured_| to true. Reset
+    // it so that any changes afterwards mimic user input.
+    dialog.needsReconfigured_ = false;
+
+    // Simulate offline.
+    dialog.isOnline_ = false;
+
+    const expectedName = 'editedName';
+    const nameField = dialog.$$('.printer-name-input');
+    assertTrue(!!nameField);
+    nameField.value = expectedName;
+    nameField.fire('input');
+
+    Polymer.dom.flush();
+
+    const saveButton = dialog.$$('.action-button');
+    assertTrue(!!saveButton);
+    assertFalse(saveButton.disabled);
+
+    clickSaveButton(dialog);
+
+    return cupsPrintersBrowserProxy.whenCalled('updateCupsPrinter')
+        .then(function() {
+          assertEquals(expectedName, dialog.activePrinter.printerName);
+        });
+  });
 });
diff --git a/chrome/test/data/webui/settings/internet_page_tests.js b/chrome/test/data/webui/settings/internet_page_tests.js
index 8d5d433..6e721e32 100644
--- a/chrome/test/data/webui/settings/internet_page_tests.js
+++ b/chrome/test/data/webui/settings/internet_page_tests.js
@@ -335,7 +335,12 @@
 
     test('WiFi Detail', function() {
       setNetworksForTest([
-        {GUID: 'wifi1_guid', Name: 'wifi1', Type: 'WiFi'},
+        {
+          GUID: 'wifi1_guid',
+          Name: 'wifi1',
+          Type: 'WiFi',
+          ConnectionState: 'Connected'
+        },
       ]);
       api_.enableNetworkType('WiFi');
       return flushAsync().then(() => {
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
index 8a8061e31..c7813a3 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
@@ -216,16 +216,12 @@
 
     const dialog = this.document.createElement('passwords-export-dialog');
     this.document.body.appendChild(dialog);
-
     Polymer.dom.flush();
 
     if (cr.isChromeOS) {
-      dialog.tokenRequestManager =
-          new settings.BlockingRequestManager(function() {
-            // |this| is expected to be the BlockingRequestManager instance.
-            this.resolve();
-          });
+      dialog.tokenRequestManager = new settings.BlockingRequestManager();
     }
+
     return dialog;
   }
 }
diff --git a/chrome/test/data/webui/settings/passwords_section_test_cros.js b/chrome/test/data/webui/settings/passwords_section_test_cros.js
index 0ff1f05..c90cdf2 100644
--- a/chrome/test/data/webui/settings/passwords_section_test_cros.js
+++ b/chrome/test/data/webui/settings/passwords_section_test_cros.js
@@ -12,13 +12,13 @@
 cr.define('settings_passwords_section_cros', function() {
   suite('PasswordsSection_Cros', function() {
     /**
-     * Promise resolved when a saved password is retrieved.
+     * Promise resolved when an auth token request is made.
      * @type {Promise}
      */
     let requestPromise = null;
 
     /**
-     * Promise resolved when an auth token request is made.
+     * Promise resolved when a saved password is retrieved.
      * @type {Promise}
      */
     let passwordPromise = null;
@@ -188,7 +188,7 @@
       const passwordsSection =
           elementFactory.createPasswordsSection(passwordManager);
       assertTrue(!passwordsSection.$$('settings-password-prompt-dialog'));
-      passwordsSection.tokenRequestManager_.request();
+      passwordsSection.tokenRequestManager_.request(fail);
       Polymer.dom.flush();
       assertTrue(!!passwordsSection.$$('settings-password-prompt-dialog'));
     });
@@ -213,5 +213,20 @@
           passwordsSection.tokenRequestManager_.request(done);
           passwordsSection.authToken_ = 'auth token';
         });
+
+    test(
+        'user is not prompted for password if they cannot enter it',
+        function(done) {
+          loadTimeData.overrideValues({userCannotManuallyEnterPassword: true});
+          const passwordsSection = document.createElement('passwords-section');
+          document.body.appendChild(passwordsSection);
+          Polymer.dom.flush();
+          assertTrue(!passwordsSection.$$('settings-password-prompt-dialog'));
+          passwordsSection.tokenRequestManager_.request(() => {
+            Polymer.dom.flush();
+            assertTrue(!passwordsSection.$$('settings-password-prompt-dialog'));
+            done();
+          });
+        });
   });
 });
diff --git a/chrome/test/data/webui/settings/people_page_change_picture_test.js b/chrome/test/data/webui/settings/people_page_change_picture_test.js
index 22f1eb0..0dae771 100644
--- a/chrome/test/data/webui/settings/people_page_change_picture_test.js
+++ b/chrome/test/data/webui/settings/people_page_change_picture_test.js
@@ -99,6 +99,25 @@
     let crPicturePane = null;
     let crPictureList = null;
 
+    const LEFT_KEY_CODE = 37;
+    const RIGHT_KEY_CODE = 39;
+
+    /**
+     * @return {Array<HTMLElement>} Traverses the DOM tree to find the lowest
+     *     level active element and returns an array of the node path down the
+     *     tree, skipping shadow roots.
+     */
+    function getActiveElementPath() {
+      let node = document.activeElement;
+      let path = [];
+      while (node) {
+        path.push(node);
+        node = (node.shadowRoot || node).activeElement;
+      }
+      return path;
+    }
+
+
     suiteSetup(function() {
       loadTimeData.overrideValues({
         profilePhoto: 'Fake Profile Photo description',
@@ -129,6 +148,57 @@
       changePicture.remove();
     });
 
+    test('TraverseCameraIconUsingArrows', function() {
+      // Force the camera to be present.
+      cr.webUIListenerCallback('camera-presence-changed', true);
+      Polymer.dom.flush();
+      assertTrue(crPictureList.cameraPresent);
+
+      // Click camera icon.
+      const cameraImage = crPictureList.$.cameraImage;
+      cameraImage.click();
+      Polymer.dom.flush();
+
+      assertTrue(crPictureList.cameraSelected_);
+      const crCamera = crPicturePane.$$('#camera');
+      assertTrue(!!crCamera);
+
+      // Mock camera's video stream beginning to play.
+      crCamera.$.cameraVideo.dispatchEvent(new Event('canplay'));
+      Polymer.dom.flush();
+
+      // "Take photo" button should be active.
+      let activeElementPath = getActiveElementPath();
+      assertTrue(activeElementPath.includes(crPicturePane));
+      assertFalse(activeElementPath.includes(crPictureList));
+
+      // Press 'Right' key on active element.
+      MockInteractions.pressAndReleaseKeyOn(
+          activeElementPath.pop(), RIGHT_KEY_CODE);
+      Polymer.dom.flush();
+
+      // A profile picture open should be active.
+      activeElementPath = getActiveElementPath();
+      assertFalse(crPictureList.cameraSelected_);
+      assertFalse(activeElementPath.includes(crPicturePane));
+      assertTrue(activeElementPath.includes(crPictureList));
+
+      // Press 'Left' key on active element.
+      MockInteractions.pressAndReleaseKeyOn(
+          activeElementPath.pop(), LEFT_KEY_CODE);
+      Polymer.dom.flush();
+
+      // Mock camera's video stream beginning to play.
+      crCamera.$.cameraVideo.dispatchEvent(new Event('canplay'));
+      Polymer.dom.flush();
+
+      // "Take photo" button should be active again.
+      activeElementPath = getActiveElementPath();
+      assertTrue(crPictureList.cameraSelected_);
+      assertTrue(activeElementPath.includes(crPicturePane));
+      assertFalse(activeElementPath.includes(crPictureList));
+    });
+
     test('ChangePictureSelectCamera', function() {
       // Force the camera to be absent, even if it's actually present.
       cr.webUIListenerCallback('camera-presence-changed', false);
@@ -301,7 +371,7 @@
             // Now verify that arrow keys actually select the new image.
             browserProxy.resetResolver('selectDefaultImage');
             MockInteractions.pressAndReleaseKeyOn(
-                changePicture.selectedItem_, 39 /* right */);
+                changePicture.selectedItem_, RIGHT_KEY_CODE);
             return browserProxy.whenCalled('selectDefaultImage');
           })
           .then(function(args) {
diff --git a/chrome/test/data/webui/settings/site_favicon_test.js b/chrome/test/data/webui/settings/site_favicon_test.js
index 2fd50ed..a9be1a3 100644
--- a/chrome/test/data/webui/settings/site_favicon_test.js
+++ b/chrome/test/data/webui/settings/site_favicon_test.js
@@ -18,8 +18,10 @@
 
   function formExpected(url) {
     return '-webkit-image-set(' +
-        'url("chrome://favicon/size/16@1x/' + url + '") 1x, ' +
-        'url("chrome://favicon/size/16@2x/' + url + '") 2x)';
+        'url("chrome://favicon2/?size=16&scale_factor=1x&url_type=page_url&url=' +
+        encodeURIComponent(url) + '") 1x, ' +
+        'url("chrome://favicon2/?size=16&scale_factor=2x&url_type=page_url&url=' +
+        encodeURIComponent(url) + '") 2x)';
   }
 
   test('normal URL', function() {
diff --git a/chrome/test/data/webui/user_manager/create_profile_tests.js b/chrome/test/data/webui/user_manager/create_profile_tests.js
index 47b560d..5703185a 100644
--- a/chrome/test/data/webui/user_manager/create_profile_tests.js
+++ b/chrome/test/data/webui/user_manager/create_profile_tests.js
@@ -23,7 +23,6 @@
 
         // Replace real proxy with mock proxy.
         signin.ProfileBrowserProxyImpl.instance_ = browserProxy;
-        browserProxy.setDefaultProfileInfo({name: 'profile name'});
         browserProxy.setIcons([
           {url: 'icon1.png', label: 'icon1'}, {url: 'icon2.png', label: 'icon2'}
         ]);
@@ -46,21 +45,21 @@
         });
       });
 
-      test('Name is non-empty by default', function() {
-        assertEquals('profile name', createProfileElement.$.nameInput.value);
+      test('Name is empty by default', function() {
+        assertEquals('', createProfileElement.$.nameInput.value);
       });
 
       test('Create button is disabled if name is empty or invalid', function() {
-        assertEquals('profile name', createProfileElement.$.nameInput.value);
-        assertFalse(createProfileElement.$.nameInput.invalid);
-        assertFalse(createProfileElement.$.save.disabled);
-
-        createProfileElement.$.nameInput.value = '';
+        assertEquals('', createProfileElement.$.nameInput.value);
         assertTrue(createProfileElement.$.save.disabled);
 
         createProfileElement.$.nameInput.value = ' ';
         assertTrue(createProfileElement.$.nameInput.invalid);
         assertTrue(createProfileElement.$.save.disabled);
+
+        createProfileElement.$.nameInput.value = 'profile name';
+        assertFalse(createProfileElement.$.nameInput.invalid);
+        assertFalse(createProfileElement.$.save.disabled);
       });
 
       test('Create a profile', function() {
@@ -69,6 +68,9 @@
             createProfileElement.$.createShortcutCheckbox;
         assertTrue(createShortcutCheckbox.clientHeight == 0);
 
+        // Enter a profile name.
+        createProfileElement.$.nameInput.value = 'profile name';
+
         // Simulate clicking 'Create'.
         MockInteractions.tap(createProfileElement.$.save);
 
@@ -102,6 +104,9 @@
             }
           });
 
+          // Enter a profile name.
+          createProfileElement.$.nameInput.value = 'profile name';
+
           // Simulate clicking 'Create'.
           MockInteractions.tap(createProfileElement.$.save);
 
@@ -120,6 +125,9 @@
       });
 
       test('Create profile error', function() {
+        // Enter a profile name.
+        createProfileElement.$.nameInput.value = 'profile name';
+
         // Simulate clicking 'Create'.
         MockInteractions.tap(createProfileElement.$.save);
 
@@ -183,7 +191,6 @@
         browserProxy = new TestProfileBrowserProxy();
         // Replace real proxy with mock proxy.
         signin.ProfileBrowserProxyImpl.instance_ = browserProxy;
-        browserProxy.setDefaultProfileInfo({name: 'profile name'});
         browserProxy.setIcons([{url: 'icon1.png', label: 'icon1'}]);
 
         // Enable profile shortcuts feature.
@@ -192,6 +199,8 @@
         });
 
         createProfileElement = createElement();
+        // Enter a profile name.
+        createProfileElement.$.nameInput.value = 'profile name';
 
         // Make sure DOM is up to date.
         Polymer.dom.flush();
diff --git a/chrome/test/data/webui/user_manager/test_profile_browser_proxy.js b/chrome/test/data/webui/user_manager/test_profile_browser_proxy.js
index e6e140c..eec9c8d 100644
--- a/chrome/test/data/webui/user_manager/test_profile_browser_proxy.js
+++ b/chrome/test/data/webui/user_manager/test_profile_browser_proxy.js
@@ -20,9 +20,6 @@
     /** @private {!Array<!AvatarIcon>} */
     this.icons_ = [];
 
-    /** @private {!ProfileInfo} */
-    this.defaultProfileInfo_ = {};
-
     /** @private {boolean} */
     this.allProfilesLocked_ = false;
   }
@@ -35,13 +32,6 @@
   }
 
   /**
-   * @param {!ProfileInfo} profileInfo
-   */
-  setDefaultProfileInfo(profileInfo) {
-    this.defaultProfileInfo_ = profileInfo;
-  }
-
-  /**
    * @param {boolean} allProfilesLocked
    */
   setAllProfilesLocked(allProfilesLocked) {
@@ -52,8 +42,6 @@
   getAvailableIcons() {
     this.methodCalled('getAvailableIcons');
     cr.webUIListenerCallback('profile-icons-received', this.icons_);
-    cr.webUIListenerCallback(
-        'profile-defaults-received', this.defaultProfileInfo_);
   }
 
   /** @override */
diff --git a/chromecast/OWNERS b/chromecast/OWNERS
index 87f0d4a..fa50461 100644
--- a/chromecast/OWNERS
+++ b/chromecast/OWNERS
@@ -8,6 +8,7 @@
 
 # For major changes, please use one of the reviewers above!
 alexst@chromium.org
+dnicoara@chromium.org
 spang@chromium.org
 
 # COMPONENT: Chromecast
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index b246697..f02b1ca8 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -82,6 +82,8 @@
     "cast_permission_manager.h",
     "cast_quota_permission_context.cc",
     "cast_quota_permission_context.h",
+    "cast_renderer_block_data.cc",
+    "cast_renderer_block_data.h",
     "cast_resource_dispatcher_host_delegate.cc",
     "cast_resource_dispatcher_host_delegate.h",
     "cast_session_id_map.cc",
@@ -502,6 +504,7 @@
   testonly = true
 
   sources = [
+    "application_media_info_manager_unittest.cc",
     "bluetooth/cast_bluetooth_chooser_unittest.cc",
     "cast_media_blocker_unittest.cc",
     "cast_network_delegate_unittest.cc",
diff --git a/chromecast/browser/application_media_info_manager.cc b/chromecast/browser/application_media_info_manager.cc
index 91721df..db1f0415 100644
--- a/chromecast/browser/application_media_info_manager.cc
+++ b/chromecast/browser/application_media_info_manager.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "chromecast/browser/application_media_info_manager.h"
+#include "chromecast/browser/cast_renderer_block_data.h"
+#include "content/public/browser/web_contents.h"
 
 #include <utility>
 
@@ -28,12 +30,39 @@
     bool mixer_audio_enabled)
     : FrameServiceBase(render_frame_host, std::move(request)),
       application_session_id_(std::move(application_session_id)),
-      mixer_audio_enabled_(mixer_audio_enabled) {}
+      mixer_audio_enabled_(mixer_audio_enabled),
+      renderer_blocked_(false) {
+  shell::CastRendererBlockData::SetApplicationMediaInfoManagerForWebContents(
+      content::WebContents::FromRenderFrameHost(render_frame_host), this);
+}
 
 ApplicationMediaInfoManager::~ApplicationMediaInfoManager() = default;
 
+void ApplicationMediaInfoManager::SetRendererBlock(bool renderer_blocked) {
+  LOG(INFO) << "Setting blocked to: " << renderer_blocked << " from "
+            << renderer_blocked_
+            << "(Pending call set: " << (!pending_call_.is_null()) << ")";
+  if (renderer_blocked_ && !renderer_blocked && pending_call_) {
+    // Move callbacks in case CanStartRenderer() is called.
+    std::move(pending_call_)
+        .Run(::media::mojom::CastApplicationMediaInfo::New(
+            application_session_id_, mixer_audio_enabled_));
+    pending_call_.Reset();
+  }
+
+  renderer_blocked_ = renderer_blocked;
+}
+
 void ApplicationMediaInfoManager::GetCastApplicationMediaInfo(
     GetCastApplicationMediaInfoCallback callback) {
+  LOG(INFO) << "GetCastApplicationMediaInfo called with blocked: "
+            << renderer_blocked_;
+  if (renderer_blocked_) {
+    DCHECK(!pending_call_);
+    pending_call_ = std::move(callback);
+    return;
+  }
+
   std::move(callback).Run(::media::mojom::CastApplicationMediaInfo::New(
       application_session_id_, mixer_audio_enabled_));
 }
diff --git a/chromecast/browser/application_media_info_manager.h b/chromecast/browser/application_media_info_manager.h
index 8496f7f..5edb14ec 100644
--- a/chromecast/browser/application_media_info_manager.h
+++ b/chromecast/browser/application_media_info_manager.h
@@ -6,8 +6,10 @@
 #define CHROMECAST_BROWSER_APPLICATION_MEDIA_INFO_MANAGER_H_
 
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "content/public/browser/frame_service_base.h"
 #include "media/mojo/interfaces/cast_application_media_info_manager.mojom.h"
 
@@ -18,9 +20,12 @@
 namespace chromecast {
 namespace media {
 
+class ApplicationMediaInfoManagerTest;
+
 class ApplicationMediaInfoManager
     : public ::content::FrameServiceBase<
-          ::media::mojom::CastApplicationMediaInfoManager> {
+          ::media::mojom::CastApplicationMediaInfoManager>,
+      public base::SupportsWeakPtr<ApplicationMediaInfoManager> {
  public:
   ApplicationMediaInfoManager(
       content::RenderFrameHost* render_frame_host,
@@ -29,13 +34,19 @@
       bool mixer_audio_enabled);
   ~ApplicationMediaInfoManager() override;
 
+  void SetRendererBlock(bool renderer_blocked);
+
  private:
+  friend ApplicationMediaInfoManagerTest;
   // ::media::mojom::CastApplicationMediaInfoManager implementation:
   void GetCastApplicationMediaInfo(
       GetCastApplicationMediaInfoCallback callback) final;
 
+  GetCastApplicationMediaInfoCallback pending_call_;
   const std::string application_session_id_;
   bool mixer_audio_enabled_;
+  // Flag to determine if renderer can start.
+  bool renderer_blocked_;
 
   DISALLOW_COPY_AND_ASSIGN(ApplicationMediaInfoManager);
 };
diff --git a/chromecast/browser/application_media_info_manager_unittest.cc b/chromecast/browser/application_media_info_manager_unittest.cc
new file mode 100644
index 0000000..e673fcb
--- /dev/null
+++ b/chromecast/browser/application_media_info_manager_unittest.cc
@@ -0,0 +1,96 @@
+// 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 "chromecast/browser/application_media_info_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "content/public/test/test_content_client_initializer.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+using ::testing::_;
+using ::testing::Invoke;
+
+namespace {
+
+const char kSessionId[] = "test-session-id";
+constexpr bool kMixedAudioEnabled = true;
+
+}  // namespace
+
+class ApplicationMediaInfoManagerTest
+    : public content::RenderViewHostTestHarness {
+ public:
+  ApplicationMediaInfoManagerTest() : started_(false) {}
+  ~ApplicationMediaInfoManagerTest() override {}
+
+  void SetUp() override {
+    initializer_ = std::make_unique<content::TestContentClientInitializer>();
+    content::RenderViewHostTestHarness::SetUp();
+    application_media_info_manager_ =
+        std::make_unique<ApplicationMediaInfoManager>(
+            main_rfh(), mojo::MakeRequest(&application_media_info_manager_ptr_),
+            kSessionId, kMixedAudioEnabled);
+  }
+
+  void OnCastApplicationMediaInfo(
+      ::media::mojom::CastApplicationMediaInfoPtr ptr) {
+    EXPECT_EQ(ptr->application_session_id, kSessionId);
+    EXPECT_EQ(ptr->mixer_audio_enabled, kMixedAudioEnabled);
+    started_ = true;
+  }
+
+  ::media::mojom::CastApplicationMediaInfoManagerPtr
+      application_media_info_manager_ptr_;
+  std::unique_ptr<content::TestContentClientInitializer> initializer_;
+  std::unique_ptr<ApplicationMediaInfoManager> application_media_info_manager_;
+  bool started_;
+};
+
+TEST_F(ApplicationMediaInfoManagerTest, NoBlock_GetMediaInfo) {
+  application_media_info_manager_ptr_->GetCastApplicationMediaInfo(
+      base::BindOnce(
+          &ApplicationMediaInfoManagerTest::OnCastApplicationMediaInfo,
+          base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(started_);
+}
+
+TEST_F(ApplicationMediaInfoManagerTest, Block_GetMediaInfo_Unblock) {
+  application_media_info_manager_->SetRendererBlock(true);
+  base::RunLoop().RunUntilIdle();
+  application_media_info_manager_ptr_->GetCastApplicationMediaInfo(
+      base::BindOnce(
+          &ApplicationMediaInfoManagerTest::OnCastApplicationMediaInfo,
+          base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(started_);
+  application_media_info_manager_->SetRendererBlock(false);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(started_);
+}
+
+TEST_F(ApplicationMediaInfoManagerTest, Block_Unblock_GetMediaInfo) {
+  application_media_info_manager_->SetRendererBlock(true);
+  base::RunLoop().RunUntilIdle();
+  application_media_info_manager_->SetRendererBlock(false);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(started_);
+  application_media_info_manager_ptr_->GetCastApplicationMediaInfo(
+      base::BindOnce(
+          &ApplicationMediaInfoManagerTest::OnCastApplicationMediaInfo,
+          base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(started_);
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_browser_context.cc b/chromecast/browser/cast_browser_context.cc
index 34ce087d..dc7e08a 100644
--- a/chromecast/browser/cast_browser_context.cc
+++ b/chromecast/browser/cast_browser_context.cc
@@ -204,7 +204,7 @@
 }
 
 const content::SharedCorsOriginAccessList*
-CastBrowserContext::GetSharedCorsOriginAccessList() const {
+CastBrowserContext::GetSharedCorsOriginAccessList() {
   return shared_cors_origin_access_list_.get();
 }
 
diff --git a/chromecast/browser/cast_browser_context.h b/chromecast/browser/cast_browser_context.h
index 55f97b5..890ab21 100644
--- a/chromecast/browser/cast_browser_context.h
+++ b/chromecast/browser/cast_browser_context.h
@@ -61,7 +61,7 @@
       std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
       base::OnceClosure closure) override;
   const content::SharedCorsOriginAccessList* GetSharedCorsOriginAccessList()
-      const override;
+      override;
 
  private:
   class CastResourceContext;
diff --git a/chromecast/browser/cast_media_blocker.cc b/chromecast/browser/cast_media_blocker.cc
index 96d8f92..01b1e50 100644
--- a/chromecast/browser/cast_media_blocker.cc
+++ b/chromecast/browser/cast_media_blocker.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/threading/thread_checker.h"
+#include "chromecast/browser/cast_renderer_block_data.h"
 #include "chromecast/common/mojom/media_playback_options.mojom.h"
 #include "content/public/browser/media_session.h"
 #include "content/public/browser/web_contents.h"
@@ -20,7 +21,8 @@
 }
 
 CastMediaBlocker::CastMediaBlocker(content::MediaSession* media_session)
-    : blocked_(false),
+    : media_loading_blocked_(false),
+      media_starting_blocked_(false),
       paused_by_user_(true),
       suspended_(true),
       controllable_(false),
@@ -34,18 +36,36 @@
 CastMediaBlocker::~CastMediaBlocker() {}
 
 void CastMediaBlocker::BlockMediaLoading(bool blocked) {
-  if (blocked_ == blocked)
+  if (media_loading_blocked_ == blocked)
     return;
 
-  blocked_ = blocked;
-  UpdateMediaBlockedState();
+  media_loading_blocked_ = blocked;
+  UpdateMediaLoadingBlockedState();
 
-  LOG(INFO) << __FUNCTION__ << " blocked=" << blocked_
+  UpdatePlayingState();
+}
+
+void CastMediaBlocker::BlockMediaStarting(bool blocked) {
+  if (media_starting_blocked_ == blocked)
+    return;
+
+  media_starting_blocked_ = blocked;
+
+  shell::CastRendererBlockData::SetRendererBlockForWebContents(
+      web_contents(), media_starting_blocked_);
+
+  UpdatePlayingState();
+}
+
+void CastMediaBlocker::UpdatePlayingState() {
+  LOG(INFO) << __FUNCTION__
+            << " media_loading_blocked=" << media_loading_blocked_
+            << " media_starting_blocked=" << media_starting_blocked_
             << " suspended=" << suspended_ << " controllable=" << controllable_
             << " paused_by_user=" << paused_by_user_;
 
   // If blocking media, suspend if possible.
-  if (blocked_) {
+  if (PlayingBlocked()) {
     if (!suspended_ && controllable_) {
       Suspend();
     }
@@ -67,12 +87,18 @@
   UpdateBackgroundVideoPlaybackState();
 }
 
+bool CastMediaBlocker::PlayingBlocked() const {
+  return (media_loading_blocked_ || media_starting_blocked_);
+}
+
 void CastMediaBlocker::MediaSessionInfoChanged(
     media_session::mojom::MediaSessionInfoPtr session_info) {
   bool is_suspended = session_info->playback_state ==
                       media_session::mojom::MediaPlaybackState::kPaused;
 
-  LOG(INFO) << __FUNCTION__ << " blocked=" << blocked_
+  LOG(INFO) << __FUNCTION__
+            << " media_loading_blocked=" << media_loading_blocked_
+            << " media_starting_blocked=" << media_starting_blocked_
             << " is_suspended=" << is_suspended
             << " is_controllable=" << session_info->is_controllable
             << " paused_by_user=" << paused_by_user_;
@@ -83,13 +109,14 @@
 
     // If not blocked, and we regain control and the media wasn't paused when
     // blocked, resume media if suspended.
-    if (!blocked_ && !paused_by_user_ && is_suspended && controllable_) {
+    if (!PlayingBlocked() && !paused_by_user_ && is_suspended &&
+        controllable_) {
       paused_by_user_ = true;
       Resume();
     }
 
     // Suspend if blocked and the session becomes controllable.
-    if (blocked_ && !is_suspended && controllable_) {
+    if (PlayingBlocked() && !is_suspended && controllable_) {
       // Only suspend if suspended_ doesn't change. Otherwise, this will be
       // handled in the suspended changed block.
       if (suspended_ == is_suspended)
@@ -101,7 +128,7 @@
   if (suspended_ != is_suspended) {
     suspended_ = is_suspended;
     // If blocking, suspend media whenever possible.
-    if (blocked_ && !suspended_) {
+    if (PlayingBlocked() && !suspended_) {
       // If media was resumed when blocked, the user tried to play music.
       paused_by_user_ = false;
       if (controllable_)
@@ -109,7 +136,7 @@
     }
 
     // If not blocking, cache the user's play intent.
-    if (!blocked_)
+    if (!PlayingBlocked())
       paused_by_user_ = suspended_;
   }
 }
@@ -132,22 +159,22 @@
 
 void CastMediaBlocker::RenderFrameCreated(
     content::RenderFrameHost* render_frame_host) {
-  UpdateRenderFrameMediaBlockedState(render_frame_host);
+  UpdateRenderFrameMediaLoadingBlockedState(render_frame_host);
   UpdateRenderFrameBackgroundVideoPlaybackState(render_frame_host);
 }
 
 void CastMediaBlocker::RenderViewReady() {
-  UpdateMediaBlockedState();
+  UpdateMediaLoadingBlockedState();
 }
 
-void CastMediaBlocker::UpdateMediaBlockedState() {
+void CastMediaBlocker::UpdateMediaLoadingBlockedState() {
   if (!web_contents())
     return;
 
   const std::vector<content::RenderFrameHost*> frames =
       web_contents()->GetAllFrames();
   for (content::RenderFrameHost* frame : frames) {
-    UpdateRenderFrameMediaBlockedState(frame);
+    UpdateRenderFrameMediaLoadingBlockedState(frame);
   }
 }
 
@@ -161,14 +188,14 @@
   }
 }
 
-void CastMediaBlocker::UpdateRenderFrameMediaBlockedState(
+void CastMediaBlocker::UpdateRenderFrameMediaLoadingBlockedState(
     content::RenderFrameHost* render_frame_host) {
   DCHECK(render_frame_host);
   chromecast::shell::mojom::MediaPlaybackOptionsAssociatedPtr
       media_playback_options;
   render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
       &media_playback_options);
-  media_playback_options->SetMediaLoadingBlocked(media_loading_blocked());
+  media_playback_options->SetMediaLoadingBlocked(media_loading_blocked_);
 }
 
 void CastMediaBlocker::UpdateRenderFrameBackgroundVideoPlaybackState(
@@ -180,4 +207,9 @@
       background_video_playback_enabled_);
 }
 
+void CastMediaBlocker::SetMediaSessionForTesting(
+    content::MediaSession* media_session) {
+  media_session_ = media_session;
+}
+
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_media_blocker.h b/chromecast/browser/cast_media_blocker.h
index a9687233..7d6f68e 100644
--- a/chromecast/browser/cast_media_blocker.h
+++ b/chromecast/browser/cast_media_blocker.h
@@ -18,6 +18,10 @@
 
 namespace chromecast {
 
+namespace shell {
+class CastMediaBlockerTest;
+}  // namespace shell
+
 // This class implements a blocking mode for web applications and is used in
 // Chromecast internal code. Media is unblocked by default.
 class CastMediaBlocker : public content::WebContentsObserver,
@@ -31,9 +35,17 @@
 
   ~CastMediaBlocker() override;
 
-  // Sets if the web contents is allowed to play media or not. If media is
-  // unblocked, previously suspended elements should begin playing again.
+  // Sets if the web contents is allowed to load and play media or not.
+  // If media is unblocked, previously suspended elements should begin playing
+  // again. Media is unblocked when both MediaLoading and MediaStarting blocks
+  // are off.
   void BlockMediaLoading(bool blocked);
+  // Sets if the web contents is allowed to play media or not. If media is
+  // unblocked, previously suspended elements should begin playing again.  Media
+  // is unblocked when both MediaLoading and MediaStarting blocks are off.
+  // This is a more relaxed block than BlockMediaLoading since the block doesn't
+  // block media from loading but it does block media from starting.
+  void BlockMediaStarting(bool blocked);
   void EnableBackgroundVideoPlayback(bool enabled);
 
   // media_session::mojom::MediaSessionObserver implementation:
@@ -50,8 +62,7 @@
       override {}
 
  private:
-  bool media_loading_blocked() const { return blocked_; }
-
+  friend shell::CastMediaBlockerTest;
   // content::WebContentsObserver implementation:
   void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
   void RenderViewReady() override;
@@ -62,17 +73,30 @@
 
   // Blocks or unblocks the render process from loading new media
   // according to |blocked_|.
-  void UpdateMediaBlockedState();
-  void UpdateRenderFrameMediaBlockedState(
+  void UpdateMediaLoadingBlockedState();
+  void UpdateRenderFrameMediaLoadingBlockedState(
       content::RenderFrameHost* render_frame_host);
 
+  void UpdatePlayingState();
+
   void UpdateBackgroundVideoPlaybackState();
   void UpdateRenderFrameBackgroundVideoPlaybackState(
       content::RenderFrameHost* frame);
 
-  // Whether or not media should be blocked. This value cache's the last call to
-  // BlockMediaLoading. Is false by default.
-  bool blocked_;
+  bool PlayingBlocked() const;
+
+  // MediaSession when initialized from WebContesnts is always a
+  // MediaSessionImpl type. This method allows to replace the MediaSession with
+  // mockable MediaSessions for testing.
+  void SetMediaSessionForTesting(content::MediaSession* media_session);
+
+  // Whether or not media loading should be blocked. This value cache's the last
+  // call to BlockMediaLoading. Is false by default.
+  bool media_loading_blocked_;
+
+  // Whether or not media starting should be blocked. This value cache's the
+  // last call to BlockMediaStarting. Is false by default.
+  bool media_starting_blocked_;
 
   // Whether or not the user paused media on the page.
   bool paused_by_user_;
@@ -87,7 +111,7 @@
   // be disabled.
   bool background_video_playback_enabled_;
 
-  content::MediaSession* const media_session_;
+  content::MediaSession* media_session_;
 
   mojo::Binding<media_session::mojom::MediaSessionObserver> observer_binding_{
       this};
diff --git a/chromecast/browser/cast_media_blocker_unittest.cc b/chromecast/browser/cast_media_blocker_unittest.cc
index 760d206..667fa4c 100644
--- a/chromecast/browser/cast_media_blocker_unittest.cc
+++ b/chromecast/browser/cast_media_blocker_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/time/time.h"
 #include "content/public/browser/media_session.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/test_content_client_initializer.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -66,8 +67,10 @@
     gl::GLSurfaceTestSupport::InitializeOneOff();
     initializer_ = std::make_unique<content::TestContentClientInitializer>();
     content::RenderViewHostTestHarness::SetUp();
+    web_contents_ = CreateTestWebContents();
     media_session_ = std::make_unique<MockMediaSession>();
-    media_blocker_ = std::make_unique<CastMediaBlocker>(media_session_.get());
+    media_blocker_ = std::make_unique<CastMediaBlocker>(web_contents_.get());
+    media_blocker_->SetMediaSessionForTesting(media_session_.get());
   }
 
   void MediaSessionChanged(bool controllable, bool suspended) {
@@ -81,10 +84,17 @@
     media_blocker_->MediaSessionInfoChanged(std::move(session_info));
   }
 
+  void TearDown() override {
+    media_blocker_.reset();
+    web_contents_.reset();
+    content::RenderViewHostTestHarness::TearDown();
+  }
+
  protected:
   std::unique_ptr<content::TestContentClientInitializer> initializer_;
   std::unique_ptr<MockMediaSession> media_session_;
   std::unique_ptr<CastMediaBlocker> media_blocker_;
+  std::unique_ptr<content::WebContents> web_contents_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(CastMediaBlockerTest);
@@ -289,5 +299,130 @@
   media_blocker_->BlockMediaLoading(false);
 }
 
+TEST_F(CastMediaBlockerTest, BlockStarting_UnblockStarting_Suspended) {
+  // Testing block/unblock operations do nothing if media never plays.
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaStarting(true);
+  media_blocker_->BlockMediaStarting(false);
+
+  MediaSessionChanged(true, true);
+  media_blocker_->BlockMediaStarting(true);
+  media_blocker_->BlockMediaStarting(false);
+
+  media_blocker_->BlockMediaStarting(true);
+  MediaSessionChanged(false, true);
+  media_blocker_->BlockMediaStarting(false);
+}
+
+TEST_F(CastMediaBlockerTest, BlockStarting_Before_Controllable) {
+  // Tests CastMediaBlocker only suspends when controllable.
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaStarting(true);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  // Session becomes controllable
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(1);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  MediaSessionChanged(true, false);
+}
+
+TEST_F(CastMediaBlockerTest, BlockStarting_After_Controllable) {
+  // Tests CastMediaBlocker suspends immediately on block if controllable.
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  MediaSessionChanged(true, false);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  // Block when media is playing
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(1);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaStarting(true);
+  MediaSessionChanged(true, true);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  // Unblock
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(1);
+  media_blocker_->BlockMediaStarting(false);
+}
+
+TEST_F(CastMediaBlockerTest, BlockStarting_Unblock_Suspended) {
+  // Testing block/unblock operations do nothing if media never plays.
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaStarting(true);
+  media_blocker_->BlockMediaStarting(false);
+
+  MediaSessionChanged(true, true);
+  media_blocker_->BlockMediaStarting(true);
+  media_blocker_->BlockMediaStarting(false);
+
+  media_blocker_->BlockMediaStarting(true);
+  MediaSessionChanged(false, true);
+  media_blocker_->BlockMediaStarting(false);
+}
+
+TEST_F(CastMediaBlockerTest, BlockLoading_BlockStarting_After_Controllable) {
+  // Tests CastMediaBlocker suspends immediately on block if controllable.
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  MediaSessionChanged(true, false);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  // Block when media is playing
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(1);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaLoading(true);
+  MediaSessionChanged(true, true);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaStarting(true);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  // Unblock
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaLoading(false);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(1);
+  media_blocker_->BlockMediaStarting(false);
+}
+
+TEST_F(CastMediaBlockerTest, BlockStarting_BlockLoading_After_Controllable) {
+  // Tests CastMediaBlocker suspends immediately on block if controllable.
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  MediaSessionChanged(true, false);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  // Block when media is playing
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(1);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaStarting(true);
+  MediaSessionChanged(true, true);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaLoading(true);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  // Unblock
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(0);
+  media_blocker_->BlockMediaStarting(false);
+  testing::Mock::VerifyAndClearExpectations(media_session_.get());
+
+  EXPECT_CALL(*media_session_, Suspend(_)).Times(0);
+  EXPECT_CALL(*media_session_, Resume(_)).Times(1);
+  media_blocker_->BlockMediaLoading(false);
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_renderer_block_data.cc b/chromecast/browser/cast_renderer_block_data.cc
new file mode 100644
index 0000000..62d77db
--- /dev/null
+++ b/chromecast/browser/cast_renderer_block_data.cc
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/browser/cast_renderer_block_data.h"
+
+#include "chromecast/browser/application_media_info_manager.h"
+#include "chromecast/browser/cast_session_id_map.h"
+#include "content/public/browser/web_contents.h"
+
+namespace chromecast {
+namespace shell {
+namespace {
+
+const char kUserDataKey[] = "chromecast.shell.RenderBlockUserData.key";
+
+CastRendererBlockData* GetOrCreateCastRendererBlockData(
+    content::WebContents* web_contents) {
+  CastRendererBlockData* data = static_cast<CastRendererBlockData*>(
+      web_contents->GetUserData(kUserDataKey));
+  if (!data) {
+    auto cast_renderer_block_data = std::make_unique<CastRendererBlockData>();
+    data = cast_renderer_block_data.get();
+    web_contents->SetUserData(kUserDataKey,
+                              std::move(cast_renderer_block_data));
+  }
+  return data;
+}
+
+}  // namespace
+
+// static
+void CastRendererBlockData::SetRendererBlockForWebContents(
+    content::WebContents* web_contents,
+    bool blocked) {
+  DCHECK(web_contents);
+  CastRendererBlockData* data = GetOrCreateCastRendererBlockData(web_contents);
+  data->SetBlocked(blocked);
+}
+
+// static
+void CastRendererBlockData::SetApplicationMediaInfoManagerForWebContents(
+    content::WebContents* web_contents,
+    media::ApplicationMediaInfoManager* application_media_info_manager) {
+  DCHECK(web_contents);
+  CastRendererBlockData* data = GetOrCreateCastRendererBlockData(web_contents);
+  data->SetApplicationMediaInfoManager(application_media_info_manager);
+}
+
+CastRendererBlockData::CastRendererBlockData() : blocked_(false) {}
+CastRendererBlockData::~CastRendererBlockData() = default;
+
+void CastRendererBlockData::SetBlocked(bool blocked) {
+  LOG(INFO) << "Setting blocked to: " << blocked << " from " << blocked_;
+  blocked_ = blocked;
+  if (application_media_info_manager_) {
+    application_media_info_manager_->SetRendererBlock(blocked);
+  }
+}
+
+void CastRendererBlockData::SetApplicationMediaInfoManager(
+    media::ApplicationMediaInfoManager* application_media_info_manager) {
+  DCHECK(application_media_info_manager);
+  application_media_info_manager_ =
+      base::AsWeakPtr(application_media_info_manager);
+  application_media_info_manager_->SetRendererBlock(blocked_);
+}
+
+}  // namespace shell
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_renderer_block_data.h b/chromecast/browser/cast_renderer_block_data.h
new file mode 100644
index 0000000..c5a7965
--- /dev/null
+++ b/chromecast/browser/cast_renderer_block_data.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 CHROMECAST_BROWSER_CAST_RENDERER_BLOCK_DATA_H_
+#define CHROMECAST_BROWSER_CAST_RENDERER_BLOCK_DATA_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/supports_user_data.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace chromecast {
+namespace media {
+class ApplicationMediaInfoManager;
+}
+
+namespace shell {
+
+class CastRendererBlockData : public base::SupportsUserData::Data {
+ public:
+  static void SetRendererBlockForWebContents(content::WebContents* web_contents,
+                                             bool blocked);
+  static void SetApplicationMediaInfoManagerForWebContents(
+      content::WebContents* web_contents,
+      media::ApplicationMediaInfoManager* application_media_info_manager);
+  CastRendererBlockData();
+  ~CastRendererBlockData() override;
+
+  bool blocked() const { return blocked_; }
+  void SetBlocked(bool blocked);
+  void SetApplicationMediaInfoManager(
+      media::ApplicationMediaInfoManager* application_media_info_manager);
+
+ private:
+  bool blocked_;
+  base::WeakPtr<media::ApplicationMediaInfoManager>
+      application_media_info_manager_;
+};
+
+}  // namespace shell
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_RENDERER_BLOCK_DATA_H_
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
index 3d9bfe0..320b743 100644
--- a/chromecast/browser/cast_web_contents.h
+++ b/chromecast/browser/cast_web_contents.h
@@ -266,6 +266,10 @@
 
   // Block/unblock media from loading in all RenderFrames for the WebContents.
   virtual void BlockMediaLoading(bool blocked) = 0;
+  // Block/unblock media from starting in all RenderFrames for the WebContents.
+  // As opposed to |BlockMediaLoading|,  |BlockMediaStarting| allows media to
+  // load while in blocking state.
+  virtual void BlockMediaStarting(bool blocked) = 0;
   virtual void EnableBackgroundVideoPlayback(bool enabled) = 0;
 
   // ===========================================================================
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index 2ff7edb..c3b3793 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -189,6 +189,11 @@
     media_blocker_->BlockMediaLoading(blocked);
 }
 
+void CastWebContentsImpl::BlockMediaStarting(bool blocked) {
+  if (media_blocker_)
+    media_blocker_->BlockMediaStarting(blocked);
+}
+
 void CastWebContentsImpl::EnableBackgroundVideoPlayback(bool enabled) {
   if (media_blocker_)
     media_blocker_->EnableBackgroundVideoPlayback(enabled);
diff --git a/chromecast/browser/cast_web_contents_impl.h b/chromecast/browser/cast_web_contents_impl.h
index e0394a9a..aef4fabb 100644
--- a/chromecast/browser/cast_web_contents_impl.h
+++ b/chromecast/browser/cast_web_contents_impl.h
@@ -55,6 +55,7 @@
       service_manager::InterfaceProvider* interface_provider) override;
   service_manager::BinderRegistry* binder_registry() override;
   void BlockMediaLoading(bool blocked) override;
+  void BlockMediaStarting(bool blocked) override;
   void EnableBackgroundVideoPlayback(bool enabled) override;
 
   // Observer interface:
diff --git a/chromecast/browser/exo/wm_helper_cast_shell.cc b/chromecast/browser/exo/wm_helper_cast_shell.cc
index 6a156778..f9ab15d 100644
--- a/chromecast/browser/exo/wm_helper_cast_shell.cc
+++ b/chromecast/browser/exo/wm_helper_cast_shell.cc
@@ -37,6 +37,13 @@
   }
 }
 
+std::vector<display::ManagedDisplayMode> GetDisplayModes(
+    const display::Display& display) {
+  display::ManagedDisplayMode mode(GetNativeBounds(display).size(), 60.f, false,
+                                   true, 1.f);
+  return std::vector<display::ManagedDisplayMode>(1, mode);
+}
+
 }  // namespace
 
 WMHelperCastShell::WMHelperCastShell(
@@ -126,6 +133,12 @@
   return no_data;
 }
 
+bool WMHelperCastShell::GetActiveModeForDisplayId(
+    int64_t display_id,
+    display::ManagedDisplayMode* mode) const {
+  return display_observer_.GetActiveModeForDisplayId(display_id, mode);
+}
+
 aura::Window* WMHelperCastShell::GetPrimaryDisplayContainer(int container_id) {
   return cast_window_manager_aura_->GetRootWindow();
 }
@@ -169,7 +182,7 @@
   cast_window_manager_aura_->GetRootWindow()->RemovePostTargetHandler(handler);
 }
 
-bool WMHelperCastShell::IsTabletModeWindowManagerEnabled() const {
+bool WMHelperCastShell::InTabletMode() const {
   NOTIMPLEMENTED();
   return false;
 }
@@ -179,6 +192,15 @@
   return 1.0;
 }
 
+void WMHelperCastShell::SetImeBlocked(aura::Window* window, bool ime_blocked) {
+  NOTIMPLEMENTED();
+}
+
+bool WMHelperCastShell::IsImeBlocked(aura::Window* window) const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
 WMHelper::LifetimeManager* WMHelperCastShell::GetLifetimeManager() {
   return &lifetime_manager_;
 }
@@ -201,6 +223,8 @@
   md.SetRotation(new_display.rotation(),
                  display::Display::RotationSource::ACTIVE);
   md.SetBounds(GetNativeBounds(new_display));
+  md.SetManagedDisplayModes(GetDisplayModes(new_display));
+  md.set_native(true);
   display_info_.emplace(new_display.id(), md);
 }
 
@@ -224,8 +248,25 @@
 WMHelperCastShell::CastDisplayObserver::GetDisplayInfo(
     int64_t display_id) const {
   auto iter = display_info_.find(display_id);
-  CHECK(iter != display_info_.end()) << display_id;
+  DCHECK(iter != display_info_.end())
+      << "Failed to find display " << display_id;
   return iter->second;
 }
 
+bool WMHelperCastShell::CastDisplayObserver::GetActiveModeForDisplayId(
+    int64_t display_id,
+    display::ManagedDisplayMode* mode) const {
+  auto iter = display_info_.find(display_id);
+  DCHECK(iter != display_info_.end())
+      << "Failed to find display " << display_id;
+  for (const auto& display_mode : iter->second.display_modes()) {
+    if (display_mode.native()) {
+      *mode = display_mode;
+      return true;
+    }
+  }
+
+  return false;
+}
+
 }  // namespace exo
diff --git a/chromecast/browser/exo/wm_helper_cast_shell.h b/chromecast/browser/exo/wm_helper_cast_shell.h
index a93af5c..ade7b234 100644
--- a/chromecast/browser/exo/wm_helper_cast_shell.h
+++ b/chromecast/browser/exo/wm_helper_cast_shell.h
@@ -78,6 +78,9 @@
       int64_t display_id) const override;
   const std::vector<uint8_t>& GetDisplayIdentificationData(
       int64_t display_id) const override;
+  bool GetActiveModeForDisplayId(
+      int64_t display_id,
+      display::ManagedDisplayMode* mode) const override;
 
   aura::Window* GetPrimaryDisplayContainer(int container_id) override;
   aura::Window* GetActiveWindow() const override;
@@ -89,8 +92,10 @@
   void RemovePreTargetHandler(ui::EventHandler* handler) override;
   void AddPostTargetHandler(ui::EventHandler* handler) override;
   void RemovePostTargetHandler(ui::EventHandler* handler) override;
-  bool IsTabletModeWindowManagerEnabled() const override;
+  bool InTabletMode() const override;
   double GetDefaultDeviceScaleFactor() const override;
+  void SetImeBlocked(aura::Window* window, bool ime_blocked) override;
+  bool IsImeBlocked(aura::Window* window) const override;
 
   LifetimeManager* GetLifetimeManager() override;
   aura::client::CaptureClient* GetCaptureClient() override;
@@ -120,6 +125,8 @@
                                  uint32_t changed_metrics) override;
 
     const display::ManagedDisplayInfo& GetDisplayInfo(int64_t display_id) const;
+    bool GetActiveModeForDisplayId(int64_t display_id,
+                                   display::ManagedDisplayMode* mode) const;
 
    private:
     std::map<int64_t, display::ManagedDisplayInfo> display_info_;
diff --git a/chromecast/cast_shell_sandbox_policy b/chromecast/cast_shell_sandbox_policy
index 2f1fa46..2f39636 100644
--- a/chromecast/cast_shell_sandbox_policy
+++ b/chromecast/cast_shell_sandbox_policy
@@ -14,6 +14,7 @@
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
+      "fuchsia.posix.socket.Provider",
       "fuchsia.process.Launcher",
       "fuchsia.sys.Launcher",
       "fuchsia.ui.input.ImeService",
diff --git a/chromecast/media/DEPS b/chromecast/media/DEPS
index ffba57ad..862d80c 100644
--- a/chromecast/media/DEPS
+++ b/chromecast/media/DEPS
@@ -16,6 +16,7 @@
   "+ui/gfx/geometry",
   "+ui/gfx/overlay_transform.h",
   "+services/service_manager/public",
+  "+third_party/blink/public/platform/audio/web_audio_device_source_type.h",
   "+third_party/widevine/cdm/buildflags.h",
   "+third_party/widevine/cdm/widevine_cdm_common.h",
 ]
diff --git a/chromecast/media/audio/cast_audio_device_factory.cc b/chromecast/media/audio/cast_audio_device_factory.cc
index 33e8f21..d8eddb4 100644
--- a/chromecast/media/audio/cast_audio_device_factory.cc
+++ b/chromecast/media/audio/cast_audio_device_factory.cc
@@ -104,7 +104,7 @@
 
 scoped_refptr<::media::AudioRendererSink>
 CastAudioDeviceFactory::CreateAudioRendererSink(
-    content::AudioDeviceFactory::SourceType source_type,
+    blink::WebAudioDeviceSourceType source_type,
     int render_frame_id,
     const ::media::AudioSinkParameters& params) {
   // Use default implementation.
@@ -113,7 +113,7 @@
 
 scoped_refptr<::media::SwitchableAudioRendererSink>
 CastAudioDeviceFactory::CreateSwitchableAudioRendererSink(
-    content::AudioDeviceFactory::SourceType source_type,
+    blink::WebAudioDeviceSourceType source_type,
     int render_frame_id,
     const ::media::AudioSinkParameters& params) {
   return base::MakeRefCounted<NonSwitchableAudioRendererSink>(NewOutputDevice(
@@ -129,4 +129,4 @@
 }
 
 }  // namespace media
-}  // namespace chromecast
\ No newline at end of file
+}  // namespace chromecast
diff --git a/chromecast/media/audio/cast_audio_device_factory.h b/chromecast/media/audio/cast_audio_device_factory.h
index 2be9cdb4..e9dfd8f 100644
--- a/chromecast/media/audio/cast_audio_device_factory.h
+++ b/chromecast/media/audio/cast_audio_device_factory.h
@@ -10,6 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "content/renderer/media/audio/audio_device_factory.h"
 #include "media/audio/audio_sink_parameters.h"
+#include "third_party/blink/public/platform/audio/web_audio_device_source_type.h"
 
 namespace media {
 class AudioCapturerSource;
@@ -31,13 +32,13 @@
       base::TimeDelta auth_timeout) override;
 
   scoped_refptr<::media::AudioRendererSink> CreateAudioRendererSink(
-      content::AudioDeviceFactory::SourceType source_type,
+      blink::WebAudioDeviceSourceType source_type,
       int render_frame_id,
       const ::media::AudioSinkParameters& params) override;
 
   scoped_refptr<::media::SwitchableAudioRendererSink>
   CreateSwitchableAudioRendererSink(
-      content::AudioDeviceFactory::SourceType source_type,
+      blink::WebAudioDeviceSourceType source_type,
       int render_frame_id,
       const ::media::AudioSinkParameters& params) override;
 
diff --git a/chromecast/media/base/supported_codec_profile_levels_memo.cc b/chromecast/media/base/supported_codec_profile_levels_memo.cc
index a851a15..897bda5 100644
--- a/chromecast/media/base/supported_codec_profile_levels_memo.cc
+++ b/chromecast/media/base/supported_codec_profile_levels_memo.cc
@@ -10,17 +10,16 @@
 namespace chromecast {
 namespace media {
 
-SupportedCodecProfileLevelsMemo::SupportedCodecProfileLevelsMemo()
-    : codec_profile_levels_(), thread_checker_() {}
+SupportedCodecProfileLevelsMemo::SupportedCodecProfileLevelsMemo() {}
 
 SupportedCodecProfileLevelsMemo::~SupportedCodecProfileLevelsMemo() {}
 
 void SupportedCodecProfileLevelsMemo::AddSupportedCodecProfileLevel(
     CodecProfileLevel codec_profile_level) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   DVLOG(2) << __func__ << "(" << codec_profile_level.codec << ", "
            << codec_profile_level.profile << ", " << codec_profile_level.level
            << ")";
+  base::AutoLock lock(lock_);
   codec_profile_levels_.push_back(codec_profile_level);
 }
 
@@ -28,9 +27,9 @@
     VideoCodec codec,
     VideoProfile profile,
     int level) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
   DVLOG(1) << __func__ << "(" << codec << ", " << profile << ", " << level
            << ")";
+  base::AutoLock lock(lock_);
   for (const auto& supported_profile_info : codec_profile_levels_) {
     if (codec == supported_profile_info.codec &&
         profile == supported_profile_info.profile &&
diff --git a/chromecast/media/base/supported_codec_profile_levels_memo.h b/chromecast/media/base/supported_codec_profile_levels_memo.h
index 855bae1..3374da0 100644
--- a/chromecast/media/base/supported_codec_profile_levels_memo.h
+++ b/chromecast/media/base/supported_codec_profile_levels_memo.h
@@ -7,7 +7,8 @@
 
 #include <vector>
 
-#include "base/threading/thread_checker.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
 #include "chromecast/public/media/decoder_config.h"
 
 namespace chromecast {
@@ -26,8 +27,10 @@
                               int level) const;
 
  private:
+  mutable base::Lock lock_;
   std::vector<CodecProfileLevel> codec_profile_levels_;
-  const base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(SupportedCodecProfileLevelsMemo);
 };
 
 }  // namespace media
diff --git a/chromecast/media/cma/backend/alsa/alsa_volume_control.cc b/chromecast/media/cma/backend/alsa/alsa_volume_control.cc
index 244e59c..100079a7 100644
--- a/chromecast/media/cma/backend/alsa/alsa_volume_control.cc
+++ b/chromecast/media/cma/backend/alsa/alsa_volume_control.cc
@@ -339,6 +339,8 @@
   }
 }
 
+void AlsaVolumeControl::SetLimit(float limit) {}
+
 bool AlsaVolumeControl::SetElementMuted(ScopedAlsaMixer* mixer, bool muted) {
   if (!mixer || !mixer->element ||
       !alsa_->MixerSelemHasPlaybackSwitch(mixer->element)) {
diff --git a/chromecast/media/cma/backend/alsa/alsa_volume_control.h b/chromecast/media/cma/backend/alsa/alsa_volume_control.h
index c6762e2..cf259a1 100644
--- a/chromecast/media/cma/backend/alsa/alsa_volume_control.h
+++ b/chromecast/media/cma/backend/alsa/alsa_volume_control.h
@@ -31,6 +31,7 @@
   bool IsMuted() override;
   void SetMuted(bool muted) override;
   void SetPowerSave(bool power_save_on) override;
+  void SetLimit(float limit) override;
 
  private:
   class ScopedAlsaMixer;
diff --git a/chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.cc b/chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.cc
index a458c96..87392f7 100644
--- a/chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.cc
+++ b/chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.cc
@@ -45,5 +45,9 @@
   NOTIMPLEMENTED();
 }
 
+void FuchsiaVolumeControl::SetLimit(float limit) {
+  NOTIMPLEMENTED();
+}
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.h b/chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.h
index c941f9f..0216179 100644
--- a/chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.h
+++ b/chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.h
@@ -25,6 +25,7 @@
   bool IsMuted() override;
   void SetMuted(bool muted) override;
   void SetPowerSave(bool power_save_on) override;
+  void SetLimit(float limit) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FuchsiaVolumeControl);
diff --git a/chromecast/media/cma/backend/system_volume_control.h b/chromecast/media/cma/backend/system_volume_control.h
index 45362e78..e1fac1cf 100644
--- a/chromecast/media/cma/backend/system_volume_control.h
+++ b/chromecast/media/cma/backend/system_volume_control.h
@@ -55,6 +55,9 @@
   // Sets the system power save state to |power_save_on|.
   virtual void SetPowerSave(bool power_save_on) = 0;
 
+  // Sets the volume limit to be applied to the system volume.
+  virtual void SetLimit(float limit) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(SystemVolumeControl);
 };
diff --git a/chromecast/media/cma/backend/volume_control.cc b/chromecast/media/cma/backend/volume_control.cc
index c93bd7a0..c7b4d36 100644
--- a/chromecast/media/cma/backend/volume_control.cc
+++ b/chromecast/media/cma/backend/volume_control.cc
@@ -190,12 +190,10 @@
       return;
     }
 
-    if (BUILDFLAG(SYSTEM_OWNS_VOLUME)) {
-      return;
-    }
-    limit = std::max(0.0f, std::min(limit, 1.0f));
-    StreamMixer::Get()->SetOutputLimit(
-        type, DbFsToScale(VolumeControl::VolumeToDbFS(limit)));
+    thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&VolumeControlInternal::SetOutputLimitOnThread,
+                       base::Unretained(this), type, limit));
   }
 
   void SetPowerSaveMode(bool power_save_on) {
@@ -316,6 +314,24 @@
     }
   }
 
+  void SetOutputLimitOnThread(AudioContentType type, float limit) {
+    if (type == AudioContentType::kOther) {
+      NOTREACHED() << "Can't set output limit for content type kOther";
+      return;
+    }
+
+    if (BUILDFLAG(SYSTEM_OWNS_VOLUME)) {
+      return;
+    }
+    limit = std::max(0.0f, std::min(limit, 1.0f));
+    StreamMixer::Get()->SetOutputLimit(
+        type, DbFsToScale(VolumeControl::VolumeToDbFS(limit)));
+
+    if (type == AudioContentType::kMedia) {
+      system_volume_control_->SetLimit(limit);
+    }
+  }
+
   void SetPowerSaveModeOnThread(bool power_save_on) {
     DCHECK(thread_.task_runner()->BelongsToCurrentThread());
     system_volume_control_->SetPowerSave(power_save_on);
diff --git a/chromecast/media/cma/base/demuxer_stream_for_test.cc b/chromecast/media/cma/base/demuxer_stream_for_test.cc
index 4e4c027d..40270bd 100644
--- a/chromecast/media/cma/base/demuxer_stream_for_test.cc
+++ b/chromecast/media/cma/base/demuxer_stream_for_test.cc
@@ -57,9 +57,10 @@
   gfx::Size natural_size(640, 480);
   return ::media::VideoDecoderConfig(
       ::media::kCodecH264, ::media::VIDEO_CODEC_PROFILE_UNKNOWN,
-      ::media::PIXEL_FORMAT_YV12, ::media::VideoColorSpace(),
-      ::media::kNoTransformation, coded_size, visible_rect, natural_size,
-      ::media::EmptyExtraData(), ::media::Unencrypted());
+      ::media::VideoDecoderConfig::AlphaMode::kIsOpaque,
+      ::media::VideoColorSpace(), ::media::kNoTransformation, coded_size,
+      visible_rect, natural_size, ::media::EmptyExtraData(),
+      ::media::Unencrypted());
 }
 
 ::media::DemuxerStream::Type DemuxerStreamForTest::type() const {
diff --git a/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc b/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc
index 73de848f..81b4acc 100644
--- a/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc
+++ b/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc
@@ -154,9 +154,9 @@
       std::vector<::media::VideoDecoderConfig> video_configs;
       video_configs.push_back(::media::VideoDecoderConfig(
           ::media::kCodecH264, ::media::H264PROFILE_MAIN,
-          ::media::PIXEL_FORMAT_I420, ::media::VideoColorSpace(),
-          ::media::kNoTransformation, gfx::Size(640, 480),
-          gfx::Rect(0, 0, 640, 480), gfx::Size(640, 480),
+          ::media::VideoDecoderConfig::AlphaMode::kIsOpaque,
+          ::media::VideoColorSpace(), ::media::kNoTransformation,
+          gfx::Size(640, 480), gfx::Rect(0, 0, 640, 480), gfx::Size(640, 480),
           ::media::EmptyExtraData(), ::media::EncryptionScheme()));
       VideoPipelineClient client;
       client.av_pipeline_client.eos_cb = base::Bind(
diff --git a/chromecast/media/cma/test/mock_frame_provider.cc b/chromecast/media/cma/test/mock_frame_provider.cc
index 7a23e77..6b4e19b9 100644
--- a/chromecast/media/cma/test/mock_frame_provider.cc
+++ b/chromecast/media/cma/test/mock_frame_provider.cc
@@ -81,9 +81,10 @@
     gfx::Size natural_size(640, 480);
     video_config = ::media::VideoDecoderConfig(
         ::media::kCodecH264, ::media::VIDEO_CODEC_PROFILE_UNKNOWN,
-        ::media::PIXEL_FORMAT_YV12, ::media::VideoColorSpace(),
-        ::media::kNoTransformation, coded_size, visible_rect, natural_size,
-        ::media::EmptyExtraData(), ::media::Unencrypted());
+        ::media::VideoDecoderConfig::AlphaMode::kIsOpaque,
+        ::media::VideoColorSpace(), ::media::kNoTransformation, coded_size,
+        visible_rect, natural_size, ::media::EmptyExtraData(),
+        ::media::Unencrypted());
 
     audio_config = ::media::AudioDecoderConfig(
       ::media::kCodecAAC,
diff --git a/chromecast/net/connectivity_checker_impl.cc b/chromecast/net/connectivity_checker_impl.cc
index 1c7ce7a5..f7c02192 100644
--- a/chromecast/net/connectivity_checker_impl.cc
+++ b/chromecast/net/connectivity_checker_impl.cc
@@ -47,6 +47,10 @@
 const char kDefaultConnectivityCheckUrl[] =
     "https://connectivitycheck.gstatic.com/generate_204";
 
+// Http url for connectivity checking.
+const char kHttpConnectivityCheckUrl[] =
+    "http://connectivitycheck.gstatic.com/generate_204";
+
 // Delay notification of network change events to smooth out rapid flipping.
 // Histogram "Cast.Network.Down.Duration.In.Seconds" shows 40% of network
 // downtime is less than 3 seconds.
@@ -119,6 +123,17 @@
     base::AutoLock auto_lock(connected_lock_);
     connected_ = connected;
   }
+
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  base::CommandLine::StringType check_url_str =
+      command_line->GetSwitchValueNative(switches::kConnectivityCheckUrl);
+  if (check_url_str.empty())
+  {
+    connectivity_check_url_.reset(new GURL(
+      connected ? kHttpConnectivityCheckUrl : kDefaultConnectivityCheckUrl));
+    LOG(INFO) << "Change check url=" << *connectivity_check_url_;
+  }
+
   Notify(connected);
   LOG(INFO) << "Global connection is: " << (connected ? "Up" : "Down");
 }
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index 23ee7f4..8430d15 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -74,7 +74,8 @@
 #endif  // defined(OS_ANDROID)
 
 CastContentRendererClient::CastContentRendererClient()
-    : supported_profiles_(new media::SupportedCodecProfileLevelsMemo()),
+    : supported_profiles_(
+          std::make_unique<media::SupportedCodecProfileLevelsMemo>()),
       app_media_capabilities_observer_binding_(this),
       supported_bitstream_audio_codecs_(kBitstreamAudioCodecNone) {
 #if defined(OS_ANDROID)
diff --git a/chromecast/renderer/cast_extensions_renderer_client.cc b/chromecast/renderer/cast_extensions_renderer_client.cc
index 9df020b..991a3744f 100644
--- a/chromecast/renderer/cast_extensions_renderer_client.cc
+++ b/chromecast/renderer/cast_extensions_renderer_client.cc
@@ -36,4 +36,10 @@
   return dispatcher_.get();
 }
 
+bool CastExtensionsRendererClient::ExtensionAPIEnabledForServiceWorkerScript(
+    const GURL& scope,
+    const GURL& script_url) const {
+  return false;
+}
+
 }  // namespace extensions
diff --git a/chromecast/renderer/cast_extensions_renderer_client.h b/chromecast/renderer/cast_extensions_renderer_client.h
index a6dfb67..b40fcf5 100644
--- a/chromecast/renderer/cast_extensions_renderer_client.h
+++ b/chromecast/renderer/cast_extensions_renderer_client.h
@@ -22,6 +22,9 @@
   bool IsIncognitoProcess() const override;
   int GetLowestIsolatedWorldId() const override;
   Dispatcher* GetDispatcher() override;
+  bool ExtensionAPIEnabledForServiceWorkerScript(
+      const GURL& scope,
+      const GURL& script_url) const override;
 
  private:
   std::unique_ptr<Dispatcher> dispatcher_;
diff --git a/chromecast/renderer/media/media_caps_observer_impl.h b/chromecast/renderer/media/media_caps_observer_impl.h
index 7d42939e..37904fae3 100644
--- a/chromecast/renderer/media/media_caps_observer_impl.h
+++ b/chromecast/renderer/media/media_caps_observer_impl.h
@@ -34,7 +34,7 @@
   void AddSupportedCodecProfileLevel(
       mojom::CodecProfileLevelPtr codec_profile_level) override;
 
-  std::unique_ptr<SupportedCodecProfileLevelsMemo> supported_profiles_;
+  SupportedCodecProfileLevelsMemo* supported_profiles_;
   mojo::Binding<mojom::MediaCapsObserver> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaCapsObserverImpl);
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index d4947c69..18369f10 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -62,8 +62,6 @@
     "printing/printer_translator.h",
     "printing/uri_components.cc",
     "printing/uri_components.h",
-    "printing/usb_printer_id.cc",
-    "printing/usb_printer_id.h",
     "process_proxy/process_output_watcher.cc",
     "process_proxy/process_output_watcher.h",
     "process_proxy/process_proxy.cc",
@@ -184,7 +182,6 @@
     "printing/ppd_provider_unittest.cc",
     "printing/printer_configuration_unittest.cc",
     "printing/printer_translator_unittest.cc",
-    "printing/usb_printer_id_unittest.cc",
     "process_proxy/process_output_watcher_unittest.cc",
     "process_proxy/process_proxy_unittest.cc",
     "test/run_all_unittests.cc",
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 03cc720..ac10f1fb 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12296.0.0
\ No newline at end of file
+12303.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 19a974b7..d949813 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -202,6 +202,35 @@
         Change password
       </message>
 
+      <!-- In-session password change -->
+      <message name="IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_TITLE" desc="Title for the dialog where a user corrects and confirms their old and new passwords in order to complete a password change">
+        Confirm change
+      </message>
+      <message name="IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_BOTH_PASSWORDS_PROMPT" desc="Text on the password-change confirmation dialog explaining that the user has to enter both their old and new passwords again to finish the password change.">
+        To finish, enter your old and new passwords
+      </message>
+      <message name="IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_OLD_PASSWORD_PROMPT" desc="Text on the password-change confirmation dialog explaining that the user has to enter their old password again to finish the password change.">
+        To finish, enter your old password
+      </message>
+      <message name="IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_NEW_PASSWORD_PROMPT" desc="Text on the password-change confirmation dialog explaining that the user has to enter their new password again to finish the password change.">
+        To finish, enter your new password
+      </message>
+      <message name="IDS_PASSWORD_CHANGE_OLD_PASSWORD_LABEL" desc="Label for a box where the user enters their old password">
+        Old password
+      </message>
+      <message name="IDS_PASSWORD_CHANGE_NEW_PASSWORD_LABEL" desc="Label for a box where the user enters their new password">
+        New password
+      </message>
+      <message name="IDS_PASSWORD_CHANGE_CONFIRM_NEW_PASSWORD_LABEL" desc="Label for the second box where the user enters their new password again, to ensure they don't mistype it">
+        Confirm new password
+      </message>
+      <message name="IDS_PASSWORD_CHANGE_CONFIRM_SAVE_BUTTON" desc="Text on a button that confirms the change to the password and saves the new password in place of the old one">
+        Save
+      </message>
+      <message name="IDS_PASSWORD_CHANGE_PASSWORDS_DONT_MATCH" desc="Error text in the case that the user entered their new password twice into two different boxes as required, but the two entries do not match">
+        Passwords do not match
+      </message>
+
       <message name="IDS_IME_SERVICE_DISPLAY_NAME" desc="The display name (in the system task manager, etc) of the service process providing the input methods.">
         Chrome OS Input Method Service
       </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_BOTH_PASSWORDS_PROMPT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_BOTH_PASSWORDS_PROMPT.png.sha1
new file mode 100644
index 0000000..462f0bd
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_BOTH_PASSWORDS_PROMPT.png.sha1
@@ -0,0 +1 @@
+d2cc3ab3fc722730919bc98f70418b7231b70538
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_NEW_PASSWORD_PROMPT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_NEW_PASSWORD_PROMPT.png.sha1
new file mode 100644
index 0000000..462f0bd
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_NEW_PASSWORD_PROMPT.png.sha1
@@ -0,0 +1 @@
+d2cc3ab3fc722730919bc98f70418b7231b70538
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_OLD_PASSWORD_PROMPT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_OLD_PASSWORD_PROMPT.png.sha1
new file mode 100644
index 0000000..462f0bd
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_OLD_PASSWORD_PROMPT.png.sha1
@@ -0,0 +1 @@
+d2cc3ab3fc722730919bc98f70418b7231b70538
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_TITLE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..6677b068
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+7621e82261b2a3be6f21e9bfed47a659f3b62309
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_NEW_PASSWORD_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_NEW_PASSWORD_LABEL.png.sha1
new file mode 100644
index 0000000..fca99c4
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_NEW_PASSWORD_LABEL.png.sha1
@@ -0,0 +1 @@
+d1688aa0f76618db03d1ba53332133bc1a8b9c6d
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_SAVE_BUTTON.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_SAVE_BUTTON.png.sha1
new file mode 100644
index 0000000..6fb5c2e
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_CONFIRM_SAVE_BUTTON.png.sha1
@@ -0,0 +1 @@
+5ee11284a96e335b3322d22edc998272cd9cea0b
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_NEW_PASSWORD_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_NEW_PASSWORD_LABEL.png.sha1
new file mode 100644
index 0000000..7e16565
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_NEW_PASSWORD_LABEL.png.sha1
@@ -0,0 +1 @@
+2317a57dd8bbe587ee2f1167d9d0ecccb36cda44
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_OLD_PASSWORD_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_OLD_PASSWORD_LABEL.png.sha1
new file mode 100644
index 0000000..7c0c8f0
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_OLD_PASSWORD_LABEL.png.sha1
@@ -0,0 +1 @@
+f7995864a578f3098c91fc014ec971c570adcc72
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_PASSWORDS_DONT_MATCH.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_PASSWORDS_DONT_MATCH.png.sha1
new file mode 100644
index 0000000..e928a65
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PASSWORD_CHANGE_PASSWORDS_DONT_MATCH.png.sha1
@@ -0,0 +1 @@
+2ec19a0b552ca7afda09c7d6013137b31d31dd3a
\ No newline at end of file
diff --git a/chromeos/components/drivefs/BUILD.gn b/chromeos/components/drivefs/BUILD.gn
index 00a04b8..72bf524 100644
--- a/chromeos/components/drivefs/BUILD.gn
+++ b/chromeos/components/drivefs/BUILD.gn
@@ -25,6 +25,7 @@
   deps = [
     "//base",
     "//chromeos/components/drivefs/mojom",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//chromeos/disks",
     "//components/account_id",
diff --git a/chromeos/components/drivefs/drivefs_host.cc b/chromeos/components/drivefs/drivefs_host.cc
index 099d06a8..17219ba 100644
--- a/chromeos/components/drivefs/drivefs_host.cc
+++ b/chromeos/components/drivefs/drivefs_host.cc
@@ -14,6 +14,7 @@
 #include "chromeos/components/drivefs/drivefs_bootstrap.h"
 #include "chromeos/components/drivefs/drivefs_host_observer.h"
 #include "chromeos/components/drivefs/drivefs_search.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "components/drive/drive_notification_manager.h"
 #include "components/drive/drive_notification_observer.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -72,9 +73,12 @@
       DriveFsHost::Delegate* delegate) {
     auto access_token = auth_delegate->GetCachedAccessToken();
     mojom::DriveFsConfigurationPtr config = {
-        base::in_place, auth_delegate->GetAccountId().GetUserEmail(),
-        std::move(access_token), auth_delegate->IsMetricsCollectionEnabled(),
-        delegate->GetLostAndFoundDirectoryName()};
+        base::in_place,
+        auth_delegate->GetAccountId().GetUserEmail(),
+        std::move(access_token),
+        auth_delegate->IsMetricsCollectionEnabled(),
+        delegate->GetLostAndFoundDirectoryName(),
+        base::FeatureList::IsEnabled(chromeos::features::kDriveFsMirroring)};
     return DriveFsConnection::Create(delegate->CreateMojoListener(),
                                      std::move(config));
   }
diff --git a/chromeos/components/drivefs/mojom/drivefs.mojom b/chromeos/components/drivefs/mojom/drivefs.mojom
index b37b7b8..9477e535 100644
--- a/chromeos/components/drivefs/mojom/drivefs.mojom
+++ b/chromeos/components/drivefs/mojom/drivefs.mojom
@@ -100,7 +100,7 @@
   OnHeartbeat();
 };
 
-// Next MinVersion: 4
+// Next MinVersion: 5
 struct DriveFsConfiguration {
   string user_email;
 
@@ -114,6 +114,9 @@
   // directory.
   [MinVersion=3]
   string? lost_and_found_directory_name;
+
+  [MinVersion=4]
+  bool enable_experimental_mirroring = false;
 };
 
 enum AccessTokenStatus {
diff --git a/chromeos/components/proximity_auth/proximity_auth_client.h b/chromeos/components/proximity_auth/proximity_auth_client.h
index 3b1de34..113348f6 100644
--- a/chromeos/components/proximity_auth/proximity_auth_client.h
+++ b/chromeos/components/proximity_auth/proximity_auth_client.h
@@ -22,9 +22,6 @@
  public:
   virtual ~ProximityAuthClient() {}
 
-  // Returns the authenticated username.
-  virtual std::string GetAuthenticatedUsername() const = 0;
-
   // Updates the user pod on the signin or lock screen to reflect the provided
   // screenlock state.
   virtual void UpdateScreenlockState(ScreenlockState state) = 0;
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 0e04189d..5fcf978a 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -52,6 +52,10 @@
 // If enabled, DriveFS will be used for Drive sync.
 const base::Feature kDriveFs{"DriveFS", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables DriveFS' experimental local files mirroring functionality.
+const base::Feature kDriveFsMirroring{"DriveFsMirroring",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled shows the visual signals feedback panel.
 const base::Feature kEnableFileManagerFeedbackPanel{
     "EnableFeedbackPanel", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 9d85ab24..7903111 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -35,6 +35,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kDiscoverApp;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kDriveFs;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kDriveFsMirroring;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kEnableFileManagerFeedbackPanel;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kEnableFileManagerFormatDialog;
diff --git a/chromeos/dbus/constants/BUILD.gn b/chromeos/dbus/constants/BUILD.gn
index 07664ee..7f163b4 100644
--- a/chromeos/dbus/constants/BUILD.gn
+++ b/chromeos/dbus/constants/BUILD.gn
@@ -13,6 +13,8 @@
   sources = [
     "attestation_constants.cc",
     "attestation_constants.h",
+    "cryptohome_key_delegate_constants.cc",
+    "cryptohome_key_delegate_constants.h",
     "dbus_paths.cc",
     "dbus_paths.h",
     "dbus_switches.cc",
diff --git a/chromeos/dbus/constants/cryptohome_key_delegate_constants.cc b/chromeos/dbus/constants/cryptohome_key_delegate_constants.cc
new file mode 100644
index 0000000..2d4bdcc9
--- /dev/null
+++ b/chromeos/dbus/constants/cryptohome_key_delegate_constants.cc
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/dbus/constants/cryptohome_key_delegate_constants.h"
+
+namespace cryptohome {
+
+const char kCryptohomeKeyDelegateServiceName[] =
+    "org.chromium.CryptohomeKeyDelegate";
+const char kCryptohomeKeyDelegateServicePath[] =
+    "/org/chromium/CryptohomeKeyDelegate";
+
+}  // namespace cryptohome
diff --git a/chromeos/dbus/constants/cryptohome_key_delegate_constants.h b/chromeos/dbus/constants/cryptohome_key_delegate_constants.h
new file mode 100644
index 0000000..d5b8602
--- /dev/null
+++ b/chromeos/dbus/constants/cryptohome_key_delegate_constants.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_DBUS_CONSTANTS_CRYPTOHOME_KEY_DELEGATE_CONSTANTS_H_
+#define CHROMEOS_DBUS_CONSTANTS_CRYPTOHOME_KEY_DELEGATE_CONSTANTS_H_
+
+#include "base/component_export.h"
+
+namespace cryptohome {
+
+// Name and path of the D-Bus service that is run by Chrome and implements the
+// org.chromium.CryptohomeKeyDelegateInterface interface. See the interface
+// definition in the Chrome OS repo in
+// src/platform2/cryptohome/dbus_bindings/
+//   org.chromium.CryptohomeKeyDelegateInterface.xml .
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
+extern const char kCryptohomeKeyDelegateServiceName[];
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
+extern const char kCryptohomeKeyDelegateServicePath[];
+
+}  // namespace cryptohome
+
+#endif  // CHROMEOS_DBUS_CONSTANTS_CRYPTOHOME_KEY_DELEGATE_CONSTANTS_H_
diff --git a/chromeos/dbus/cros_disks_client.cc b/chromeos/dbus/cros_disks_client.cc
index 8653797..59290b81 100644
--- a/chromeos/dbus/cros_disks_client.cc
+++ b/chromeos/dbus/cros_disks_client.cc
@@ -222,16 +222,19 @@
   // CrosDisksClient override.
   void Format(const std::string& device_path,
               const std::string& filesystem,
+              const std::string& label,
               VoidDBusMethodCallback callback) override {
     dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
                                  cros_disks::kFormat);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(device_path);
     writer.AppendString(filesystem);
-    // No format option is currently specified, but we can later use this
-    // argument to specify options for the format operation.
+
     std::vector<std::string> format_options;
+    format_options.push_back(cros_disks::kFormatLabelOption);
+    format_options.push_back(label);
     writer.AppendArrayOfStrings(format_options);
+
     proxy_->CallMethod(
         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
         base::BindOnce(&CrosDisksClientImpl::OnVoidMethod,
diff --git a/chromeos/dbus/cros_disks_client.h b/chromeos/dbus/cros_disks_client.h
index fd3a68f..abcff3e2b 100644
--- a/chromeos/dbus/cros_disks_client.h
+++ b/chromeos/dbus/cros_disks_client.h
@@ -358,6 +358,7 @@
   // success, or with |false| otherwise.
   virtual void Format(const std::string& device_path,
                       const std::string& filesystem,
+                      const std::string& label,
                       VoidDBusMethodCallback callback) = 0;
 
   // Calls Rename method. On completion, |callback| is called, with |true| on
diff --git a/chromeos/dbus/fake_cros_disks_client.cc b/chromeos/dbus/fake_cros_disks_client.cc
index 75ef085..91607f9 100644
--- a/chromeos/dbus/fake_cros_disks_client.cc
+++ b/chromeos/dbus/fake_cros_disks_client.cc
@@ -178,12 +178,14 @@
 
 void FakeCrosDisksClient::Format(const std::string& device_path,
                                  const std::string& filesystem,
+                                 const std::string& label,
                                  VoidDBusMethodCallback callback) {
   DCHECK(!callback.is_null());
 
   format_call_count_++;
   last_format_device_path_ = device_path;
   last_format_filesystem_ = filesystem;
+  last_format_label_ = label;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), format_success_));
 }
diff --git a/chromeos/dbus/fake_cros_disks_client.h b/chromeos/dbus/fake_cros_disks_client.h
index 00fd7b3c..36b1d5a 100644
--- a/chromeos/dbus/fake_cros_disks_client.h
+++ b/chromeos/dbus/fake_cros_disks_client.h
@@ -54,6 +54,7 @@
                              base::OnceClosure error_callback) override;
   void Format(const std::string& device_path,
               const std::string& filesystem,
+              const std::string& label,
               VoidDBusMethodCallback callback) override;
   void Rename(const std::string& device_path,
               const std::string& volume_name,
@@ -118,6 +119,9 @@
     return last_format_filesystem_;
   }
 
+  // Returns the |label| parameter from the last invocation of Format().
+  const std::string& last_format_label() const { return last_format_label_; }
+
   // Makes the subsequent Format() calls fail. Format() succeeds by default.
   void MakeFormatFail() {
     format_success_ = false;
@@ -156,6 +160,7 @@
   int format_call_count_;
   std::string last_format_device_path_;
   std::string last_format_filesystem_;
+  std::string last_format_label_;
   bool format_success_;
   int rename_call_count_;
   std::string last_rename_device_path_;
diff --git a/chromeos/disks/disk_mount_manager.cc b/chromeos/disks/disk_mount_manager.cc
index 77c587a7..dadd4f3 100644
--- a/chromeos/disks/disk_mount_manager.cc
+++ b/chromeos/disks/disk_mount_manager.cc
@@ -32,8 +32,6 @@
 
 namespace {
 
-constexpr char kDefaultFormattedDeviceName[] = "UNTITLED";
-constexpr char kDefaultFormatVFAT[] = "vfat";
 constexpr char kDeviceNotFound[] = "Device could not be found";
 DiskMountManager* g_disk_mount_manager = NULL;
 
@@ -136,7 +134,9 @@
   }
 
   // DiskMountManager override.
-  void FormatMountedDevice(const std::string& mount_path) override {
+  void FormatMountedDevice(const std::string& mount_path,
+                           const std::string& filesystem,
+                           const std::string& label) override {
     MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
     if (mount_point == mount_points_.end()) {
       LOG(ERROR) << "Mount point with path \"" << mount_path << "\" not found.";
@@ -160,7 +160,8 @@
 
     UnmountPath(disk->second->mount_path(), UNMOUNT_OPTIONS_NONE,
                 base::BindOnce(&DiskMountManagerImpl::OnUnmountPathForFormat,
-                               weak_ptr_factory_.GetWeakPtr(), device_path));
+                               weak_ptr_factory_.GetWeakPtr(), device_path,
+                               filesystem, label));
   }
 
   void RenameMountedDevice(const std::string& mount_path,
@@ -511,25 +512,28 @@
   }
 
   void OnUnmountPathForFormat(const std::string& device_path,
+                              const std::string& filesystem,
+                              const std::string& label,
                               MountError error_code) {
     if (error_code == MOUNT_ERROR_NONE &&
         disks_.find(device_path) != disks_.end()) {
-      FormatUnmountedDevice(device_path);
+      FormatUnmountedDevice(device_path, filesystem, label);
     } else {
       OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
     }
   }
 
   // Starts device formatting.
-  void FormatUnmountedDevice(const std::string& device_path) {
+  void FormatUnmountedDevice(const std::string& device_path,
+                             const std::string& filesystem,
+                             const std::string& label) {
     DiskMap::const_iterator disk = disks_.find(device_path);
     DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
 
-    pending_format_changes_[device_path] = {kDefaultFormatVFAT,
-                                            kDefaultFormattedDeviceName};
+    pending_format_changes_[device_path] = {filesystem, label};
 
     cros_disks_client_->Format(
-        device_path, kDefaultFormatVFAT,
+        device_path, filesystem, label,
         base::BindOnce(&DiskMountManagerImpl::OnFormatStarted,
                        weak_ptr_factory_.GetWeakPtr(), device_path));
   }
diff --git a/chromeos/disks/disk_mount_manager.h b/chromeos/disks/disk_mount_manager.h
index e05aff47..2d6fbc7 100644
--- a/chromeos/disks/disk_mount_manager.h
+++ b/chromeos/disks/disk_mount_manager.h
@@ -175,9 +175,14 @@
   // Devices that can be mounted only in its read-only mode will be ignored.
   virtual void RemountAllRemovableDrives(chromeos::MountAccessMode mode) = 0;
 
-  // Formats Device given its mount path. Unmounts the device.
+  // Formats device mounted at |mount_path| with the given filesystem and label.
+  // Also unmounts the device before formatting.
   // Example: mount_path: /media/VOLUME_LABEL
-  virtual void FormatMountedDevice(const std::string& mount_path) = 0;
+  //          filesystem: ntfs
+  //          label: MYUSB
+  virtual void FormatMountedDevice(const std::string& mount_path,
+                                   const std::string& filesystem,
+                                   const std::string& label) = 0;
 
   // Renames Device given its mount path.
   // Example: mount_path: /media/VOLUME_LABEL
diff --git a/chromeos/disks/disk_mount_manager_unittest.cc b/chromeos/disks/disk_mount_manager_unittest.cc
index 182d33e..e5979a0 100644
--- a/chromeos/disks/disk_mount_manager_unittest.cc
+++ b/chromeos/disks/disk_mount_manager_unittest.cc
@@ -37,6 +37,10 @@
 const char kReadOnlyDeviceSourcePath[] = "/device/read_only_source_path";
 const char kFileSystemType1[] = "ntfs";
 const char kFileSystemType2[] = "exfat";
+const char kFormatFileSystemType1[] = "vfat";
+const char kFormatFileSystemType2[] = "exfat";
+const char kFormatLabel1[] = "UNTITLED";
+const char kFormatLabel2[] = "TESTUSB";
 
 // Holds information needed to create a Disk instance.
 struct TestDiskInfo {
@@ -587,7 +591,8 @@
 // Tests that the observer gets notified on attempt to format non existent mount
 // point.
 TEST_F(DiskMountManagerTest, Format_NotMounted) {
-  DiskMountManager::GetInstance()->FormatMountedDevice("/mount/non_existent");
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      "/mount/non_existent", kFormatFileSystemType1, kFormatLabel1);
   ASSERT_EQ(1U, observer_->GetEventCount());
   EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
                         chromeos::FORMAT_ERROR_UNKNOWN, "/mount/non_existent"),
@@ -598,7 +603,7 @@
 // point.
 TEST_F(DiskMountManagerTest, Format_ReadOnly) {
   DiskMountManager::GetInstance()->FormatMountedDevice(
-      kReadOnlyDeviceMountPath);
+      kReadOnlyDeviceMountPath, kFormatFileSystemType1, kFormatLabel1);
   ASSERT_EQ(1U, observer_->GetEventCount());
   EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
                         chromeos::FORMAT_ERROR_DEVICE_NOT_ALLOWED,
@@ -608,7 +613,8 @@
 
 // Tests that it is not possible to format archive mount point.
 TEST_F(DiskMountManagerTest, Format_Archive) {
-  DiskMountManager::GetInstance()->FormatMountedDevice("/archive/mount_path");
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      "/archive/mount_path", kFormatFileSystemType1, kFormatLabel1);
   ASSERT_EQ(1U, observer_->GetEventCount());
   EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
                         chromeos::FORMAT_ERROR_UNKNOWN, "/archive/source_path"),
@@ -624,7 +630,8 @@
   fake_cros_disks_client_->MakeUnmountFail(
       chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS);
   // Start test.
-  DiskMountManager::GetInstance()->FormatMountedDevice(kDevice1MountPath);
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      kDevice1MountPath, kFormatFileSystemType1, kFormatLabel1);
 
   // Cros disks will respond asynchronoulsy, so let's drain the message loop.
   base::RunLoop().RunUntilIdle();
@@ -661,7 +668,8 @@
 
   fake_cros_disks_client_->MakeFormatFail();
   // Start the test.
-  DiskMountManager::GetInstance()->FormatMountedDevice(kDevice1MountPath);
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      kDevice1MountPath, kFormatFileSystemType1, kFormatLabel1);
 
   // Cros disks will respond asynchronoulsy, so let's drain the message loop.
   base::RunLoop().RunUntilIdle();
@@ -686,7 +694,9 @@
   EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
   EXPECT_EQ(kDevice1SourcePath,
             fake_cros_disks_client_->last_format_device_path());
-  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatFileSystemType1,
+            fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatLabel1, fake_cros_disks_client_->last_format_label());
 
   // The device mount should be gone.
   EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
@@ -704,8 +714,10 @@
                           base::Unretained(fake_cros_disks_client_),
                           chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS));
   // Start the test.
-  DiskMountManager::GetInstance()->FormatMountedDevice(kDevice1MountPath);
-  DiskMountManager::GetInstance()->FormatMountedDevice(kDevice1MountPath);
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      kDevice1MountPath, kFormatFileSystemType1, kFormatLabel1);
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      kDevice1MountPath, kFormatFileSystemType2, kFormatLabel2);
 
   // Cros disks will respond asynchronoulsy, so let's drain the message loop.
   base::RunLoop().RunUntilIdle();
@@ -740,8 +752,9 @@
   EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
   EXPECT_EQ(kDevice1SourcePath,
             fake_cros_disks_client_->last_format_device_path());
-  EXPECT_EQ("vfat",
+  EXPECT_EQ(kFormatFileSystemType1,
             fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatLabel1, fake_cros_disks_client_->last_format_label());
 
   // The device mount should be gone.
   EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
@@ -764,7 +777,8 @@
   // Both unmount and format device cals are successful in this test.
 
   // Start the test.
-  DiskMountManager::GetInstance()->FormatMountedDevice(kDevice1MountPath);
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      kDevice1MountPath, kFormatFileSystemType1, kFormatLabel1);
 
   // Wait for Unmount and Format calls to end.
   base::RunLoop().RunUntilIdle();
@@ -777,7 +791,9 @@
   EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
   EXPECT_EQ(kDevice1SourcePath,
             fake_cros_disks_client_->last_format_device_path());
-  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatFileSystemType1,
+            fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatLabel1, fake_cros_disks_client_->last_format_label());
 
   // The device should be unmounted by now.
   EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
@@ -813,7 +829,8 @@
   // Both unmount and format device cals are successful in this test.
 
   // Start the test.
-  DiskMountManager::GetInstance()->FormatMountedDevice(kDevice1MountPath);
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      kDevice1MountPath, kFormatFileSystemType1, kFormatLabel1);
 
   // Wait for Unmount and Format calls to end.
   base::RunLoop().RunUntilIdle();
@@ -826,7 +843,9 @@
   EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
   EXPECT_EQ(kDevice1SourcePath,
             fake_cros_disks_client_->last_format_device_path());
-  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatFileSystemType1,
+            fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatLabel1, fake_cros_disks_client_->last_format_label());
 
   // The device should be unmounted by now.
   EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
@@ -848,8 +867,10 @@
             observer_->GetFormatEvent(2));
 
   // Disk should have new values for file system type and device label name
-  EXPECT_EQ("vfat", disks.find(kDevice1SourcePath)->second->file_system_type());
-  EXPECT_EQ("UNTITLED", disks.find(kDevice1SourcePath)->second->device_label());
+  EXPECT_EQ(kFormatFileSystemType1,
+            disks.find(kDevice1SourcePath)->second->file_system_type());
+  EXPECT_EQ(kFormatLabel1,
+            disks.find(kDevice1SourcePath)->second->device_label());
 }
 
 // Tests that it's possible to format the device twice in a row (this may not be
@@ -859,7 +880,8 @@
   // Each of the should be made twice (once for each formatting task).
 
   // Start the test.
-  DiskMountManager::GetInstance()->FormatMountedDevice(kDevice1MountPath);
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      kDevice1MountPath, kFormatFileSystemType1, kFormatLabel1);
 
   // Wait for Unmount and Format calls to end.
   base::RunLoop().RunUntilIdle();
@@ -872,7 +894,9 @@
   EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
   EXPECT_EQ(kDevice1SourcePath,
             fake_cros_disks_client_->last_format_device_path());
-  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatFileSystemType1,
+            fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatLabel1, fake_cros_disks_client_->last_format_label());
 
   // The device should be unmounted by now.
   EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
@@ -889,7 +913,8 @@
   EXPECT_TRUE(HasMountPoint(kDevice1MountPath));
 
   // Try formatting again.
-  DiskMountManager::GetInstance()->FormatMountedDevice(kDevice1MountPath);
+  DiskMountManager::GetInstance()->FormatMountedDevice(
+      kDevice1MountPath, kFormatFileSystemType2, kFormatLabel2);
 
   // Wait for Unmount and Format calls to end.
   base::RunLoop().RunUntilIdle();
@@ -902,7 +927,9 @@
   EXPECT_EQ(2, fake_cros_disks_client_->format_call_count());
   EXPECT_EQ(kDevice1SourcePath,
             fake_cros_disks_client_->last_format_device_path());
-  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatFileSystemType2,
+            fake_cros_disks_client_->last_format_filesystem());
+  EXPECT_EQ(kFormatLabel2, fake_cros_disks_client_->last_format_label());
 
   // Simulate cros_disks reporting success.
   fake_cros_disks_client_->NotifyFormatCompleted(chromeos::FORMAT_ERROR_NONE,
diff --git a/chromeos/disks/mock_disk_mount_manager.cc b/chromeos/disks/mock_disk_mount_manager.cc
index 9cdf922..e58151a 100644
--- a/chromeos/disks/mock_disk_mount_manager.cc
+++ b/chromeos/disks/mock_disk_mount_manager.cc
@@ -141,8 +141,7 @@
   EXPECT_CALL(*this, UnmountPath(_, _, _))
       .Times(AnyNumber());
   EXPECT_CALL(*this, RemountAllRemovableDrives(_)).Times(AnyNumber());
-  EXPECT_CALL(*this, FormatMountedDevice(_))
-      .Times(AnyNumber());
+  EXPECT_CALL(*this, FormatMountedDevice(_, _, _)).Times(AnyNumber());
   EXPECT_CALL(*this, UnmountDeviceRecursively(_, _))
       .Times(AnyNumber());
 }
diff --git a/chromeos/disks/mock_disk_mount_manager.h b/chromeos/disks/mock_disk_mount_manager.h
index 73c210f5..b9a98160f 100644
--- a/chromeos/disks/mock_disk_mount_manager.h
+++ b/chromeos/disks/mock_disk_mount_manager.h
@@ -48,7 +48,10 @@
                     UnmountOptions,
                     DiskMountManager::UnmountPathCallback));
   MOCK_METHOD1(RemountAllRemovableDrives, void(MountAccessMode));
-  MOCK_METHOD1(FormatMountedDevice, void(const std::string&));
+  MOCK_METHOD3(FormatMountedDevice,
+               void(const std::string&,
+                    const std::string&,
+                    const std::string&));
   MOCK_METHOD2(RenameMountedDevice,
                void(const std::string&, const std::string&));
   MOCK_METHOD2(UnmountDeviceRecursively,
diff --git a/chromeos/hugepage_text/OWNERS b/chromeos/hugepage_text/OWNERS
index 670bf340..6f2d98e8 100644
--- a/chromeos/hugepage_text/OWNERS
+++ b/chromeos/hugepage_text/OWNERS
@@ -2,3 +2,4 @@
 cmtice@chromium.org
 gbiv@chromium.org
 manojgupta@chromium.org
+tcwang@chromium.org
diff --git a/chromeos/hugepage_text/hugepage_text.cc b/chromeos/hugepage_text/hugepage_text.cc
index 19a9a43..f8a35fa1 100644
--- a/chromeos/hugepage_text/hugepage_text.cc
+++ b/chromeos/hugepage_text/hugepage_text.cc
@@ -1,7 +1,7 @@
 // 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.
-// Author: Ken Chen <kenchen@google.com>
+// Author: Ken Chen <kenchen@google.com> Tiancong Wang <tcwang@google.com>
 //
 // hugepage text library to remap process executable segment with hugepages.
 
@@ -15,6 +15,33 @@
 #include "base/bit_cast.h"
 #include "base/logging.h"
 
+// CHROMEOS_ORDERFILE_USE is a flag intended to use orderfile
+// to link Chrome. The else part of macro check in this code is to
+// make sure when the flag is turned off, the code works the same
+// as before.
+// We plan to turn it on in the future for Chrome OS.
+// Therefore, before it's deployed, by default, we turn it off until
+// the testing is done.
+
+// These function are here to delimit the start and end of the symbols
+// ordered by orderfile.
+// Due to ICF (Identical Code Folding), the linker merges functions
+// that have the same code (or empty). So we need to give these functions
+// some unique body, using inline .word in assembly.
+// Note that .word means different sizes in different architectures.
+// So we choose 16-bit numbers.
+extern "C" {
+void chrome_end_ordered_code() {
+  asm(".word 0xd44d");
+  asm(".word 0xc5b0");
+}
+
+void chrome_begin_ordered_code() {
+  asm(".word 0xa073");
+  asm(".word 0xdda6");
+}
+}
+
 namespace chromeos {
 
 #ifndef MAP_HUGETLB
@@ -37,9 +64,10 @@
 const int kMremapFlags = (MREMAP_MAYMOVE | MREMAP_FIXED);
 
 // The number of hugepages we want to use to map chrome text section
-// to hugepages. With the help of AutoFDO, the hot functions are grouped
-// in to a small area of the binary.
-const int kNumHugePages = 15;
+// to hugepages. Map at least 8 hugepages because of hardware support.
+// Map at most 16 hugepages to avoid using too many hugepages.
+constexpr static int kMinNumHugePages = 8;
+constexpr static int kMaxNumHugePages = 16;
 
 // Get an anonymous mapping backed by explicit transparent hugepage
 // Return NULL if such mapping can not be established.
@@ -116,14 +144,90 @@
   }
 }
 
-// Top level text remapping function.
+// Utility function to get 2MB-aligned address smaller or larger than the
+// given address.
+// Inputs: address, the address to get 2MB-aligned address for.
+//         round_up, whether to get larger (true) or smaller (false) 2MB-aligned
+//         address.
+// Return: 2MB-aligned address rounded up or down. Or itself if it's
+//         already 2MB-aligned.
+static size_t RoundToHugepageAlignment(size_t address, bool round_up) {
+  // Whether it's round_up or not, if the address is exactly 2MB-aligned,
+  // just return itself
+  if (address % kHpageSize == 0)
+    return address;
+  return round_up ? (address / kHpageSize + 1) * kHpageSize
+                  : address / kHpageSize * kHpageSize;
+}
+
+// Top level text remapping function, when orderfile is enabled.
+//
+// Inputs: vaddr, the starting virtual address to remap to hugepage
+//         segsize, size of the memory segment to remap in bytes
+// Return: none
+// Effect: physical backing page changed from small page to hugepage. If there
+//         are error condition, the remapping operation is aborted.
+static void RemapHugetlbTextWithOrderfileLayout(void* vaddr,
+                                                const size_t segsize) {
+  auto text_start = reinterpret_cast<size_t>(vaddr);
+  auto text_end = text_start + segsize;
+  auto marker_start = reinterpret_cast<size_t>(chrome_begin_ordered_code);
+  auto marker_end = reinterpret_cast<size_t>(chrome_end_ordered_code);
+  // Check if the markers are ordered correctly by the orderfile
+  if (!(marker_start < marker_end && text_start <= marker_start &&
+        marker_end < text_end)) {
+    LOG(WARNING) << "The ordering seems incorrect, fall back to small page";
+    return;
+  }
+
+  // Try to map symbols from the 2MB-aligned address before marker_start
+  size_t mapping_start = RoundToHugepageAlignment(marker_start, false);
+  if (mapping_start < text_start) {
+    // If the address is outside of text section, start to map
+    // at the 2MB-aligned address after the marker_start
+    mapping_start = RoundToHugepageAlignment(marker_start, true);
+  }
+
+  // Try to map symbols to the 2MB-aligned address after the marker_end
+  size_t mapping_end = RoundToHugepageAlignment(marker_end, true);
+  if (mapping_end > text_end) {
+    // If the address is outside of text section, end mapping at
+    // the 2MB-aligned address before the marker_end
+    // Note that this is not expected to happen for current linker
+    // behavior, as the markers are placed in the front (for x86) or
+    // are placed in the middle (for ARM/ARM64/PowerPC)
+    mapping_end = RoundToHugepageAlignment(marker_end, false);
+  }
+
+  size_t hsize = mapping_end - mapping_start;
+
+  // Make sure the number of hugepages used is between kMinNumHugePages
+  // and kMaxNumHugePages.
+  if (hsize < kHpageSize * kMinNumHugePages) {
+    LOG(WARNING) << "Orderfile ordered fewer than " << kMinNumHugePages
+                 << " huge pages.";
+    hsize = kHpageSize * kMinNumHugePages;
+  } else if (hsize > kHpageSize * kMaxNumHugePages) {
+    LOG(WARNING) << "Orderfile ordered more than " << kMaxNumHugePages
+                 << " huge pages.";
+    hsize = kHpageSize * kMaxNumHugePages;
+  }
+
+  MremapHugetlbText(reinterpret_cast<void*>(mapping_start), hsize);
+}
+
+// Top level text remapping function, without orderfile (old code).
 //
 // Inputs: vaddr, the starting virtual address to remap to hugepage
 //         segsize, size of the memory segment to remap in bytes
 // Return: none
 // Effect: physical backing page changed from small page to hugepage. If there
 //         are error condition, the remaping operation is aborted.
-static void RemapHugetlbText(void* vaddr, const size_t segsize) {
+static void RemapHugetlbTextWithoutOrderfileLayout(void* vaddr,
+                                                   const size_t segsize) {
+  // The number of hugepages to use if no orderfile is specified
+  const int kNumHugePages = 15;
+
   // remove unaligned head regions
   uintptr_t head_gap =
       (kHpageSize - reinterpret_cast<uintptr_t>(vaddr) % kHpageSize) %
@@ -170,8 +274,15 @@
         info->dlpi_phdr[i].p_flags == (PF_R | PF_X)) {
       vaddr = bit_cast<void*>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
       segsize = info->dlpi_phdr[i].p_filesz;
-
-      RemapHugetlbText(vaddr, segsize);
+      // The following function is conditionally compiled, so use
+      // the statements to avoid compiler warnings of unused functions
+      (void)RemapHugetlbTextWithOrderfileLayout;
+      (void)RemapHugetlbTextWithoutOrderfileLayout;
+#ifdef CHROMEOS_ORDERFILE_USE
+      RemapHugetlbTextWithOrderfileLayout(vaddr, segsize);
+#else
+      RemapHugetlbTextWithoutOrderfileLayout(vaddr, segsize);
+#endif
       // Only re-map the first text segment.
       return 1;
     }
diff --git a/chromeos/login/auth/BUILD.gn b/chromeos/login/auth/BUILD.gn
index 3d960158..919ba70 100644
--- a/chromeos/login/auth/BUILD.gn
+++ b/chromeos/login/auth/BUILD.gn
@@ -19,6 +19,7 @@
     "//chromeos/cryptohome",
     "//chromeos/dbus/auth_policy",
     "//chromeos/dbus/auth_policy:authpolicy_proto",
+    "//chromeos/dbus/constants",
     "//chromeos/dbus/cryptohome",
     "//chromeos/dbus/cryptohome:cryptohome_proto",
     "//chromeos/dbus/session_manager",
@@ -47,6 +48,8 @@
     "authenticator.h",
     "challenge_response/cert_utils.cc",
     "challenge_response/cert_utils.h",
+    "challenge_response/key_label_utils.cc",
+    "challenge_response/key_label_utils.h",
     "cryptohome_authenticator.cc",
     "cryptohome_authenticator.h",
     "extended_authenticator.cc",
@@ -59,6 +62,8 @@
     "login_event_recorder.h",
     "login_performer.cc",
     "login_performer.h",
+    "password_visibility_utils.cc",
+    "password_visibility_utils.h",
     "saml_password_attributes.cc",
     "saml_password_attributes.h",
     "stub_authenticator.cc",
diff --git a/chromeos/login/auth/challenge_response/key_label_utils.cc b/chromeos/login/auth/challenge_response/key_label_utils.cc
new file mode 100644
index 0000000..8e5f5b8
--- /dev/null
+++ b/chromeos/login/auth/challenge_response/key_label_utils.cc
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/login/auth/challenge_response/key_label_utils.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "crypto/sha2.h"
+
+namespace chromeos {
+
+namespace {
+
+// Prefix to be used in the cryptohome key labels for challenge-response keys.
+// Prefixing allows to avoid accidental clashes between different keys (labels
+// are used to uniquely identify the needed key in requests to cryptohomed).
+constexpr char kKeyLabelPrefix[] = "challenge-response-";
+
+// Given a DER-encoded X.509 Subject Public Key Info, returns its hashed
+// representation that is suitable for inclusion into cryptohome key labels. The
+// label built this way is, practically, unique (short of a SHA-256 collision).
+std::string GenerateLabelSpkiPart(const std::string& public_key_spki_der) {
+  DCHECK(!public_key_spki_der.empty());
+  const std::string hash = crypto::SHA256HashString(public_key_spki_der);
+  return base::HexEncode(hash.data(), hash.size());
+}
+
+}  // namespace
+
+std::string GenerateChallengeResponseKeyLabel(
+    const std::vector<ChallengeResponseKey>& challenge_response_keys) {
+  // For now, only a single challenge-response key is supported.
+  DCHECK_EQ(challenge_response_keys.size(), 1ull);
+  return kKeyLabelPrefix +
+         GenerateLabelSpkiPart(
+             challenge_response_keys[0].public_key_spki_der());
+}
+
+}  // namespace chromeos
diff --git a/chromeos/login/auth/challenge_response/key_label_utils.h b/chromeos/login/auth/challenge_response/key_label_utils.h
new file mode 100644
index 0000000..98a04e6
--- /dev/null
+++ b/chromeos/login/auth/challenge_response/key_label_utils.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_LOGIN_AUTH_CHALLENGE_RESPONSE_KEY_LABEL_UTILS_H_
+#define CHROMEOS_LOGIN_AUTH_CHALLENGE_RESPONSE_KEY_LABEL_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "chromeos/login/auth/challenge_response_key.h"
+
+namespace chromeos {
+
+// Generates the cryptohome user key label for the given challenge-response key
+// information. Currently the constraint is that |challenge_response_keys| must
+// contain exactly one item.
+std::string GenerateChallengeResponseKeyLabel(
+    const std::vector<ChallengeResponseKey>& challenge_response_keys);
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_LOGIN_AUTH_CHALLENGE_RESPONSE_KEY_LABEL_UTILS_H_
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index 12c5b67f9..a4a7d48 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -20,8 +20,10 @@
 #include "chromeos/cryptohome/cryptohome_util.h"
 #include "chromeos/cryptohome/homedir_methods.h"
 #include "chromeos/cryptohome/system_salt_getter.h"
+#include "chromeos/dbus/constants/cryptohome_key_delegate_constants.h"
 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
 #include "chromeos/login/auth/auth_status_consumer.h"
+#include "chromeos/login/auth/challenge_response/key_label_utils.h"
 #include "chromeos/login/auth/key.h"
 #include "chromeos/login/auth/login_event_recorder.h"
 #include "chromeos/login/auth/user_context.h"
@@ -227,31 +229,50 @@
   // that returns directly would not generate 2 OnLoginSucces() calls.
   attempt->UsernameHashRequested();
 
+  cryptohome::KeyDefinition key_definition;
+  cryptohome::AuthorizationRequest auth;
+  cryptohome::Key* auth_key = auth.mutable_key();
+
+  if (!attempt->user_context.GetChallengeResponseKeys().empty()) {
+    // For the challenge-response key, no secret is passed, only public-key
+    // information. The authorization request consists of the same key as the
+    // persisted key, plus the additional KeyDelegate information that allows
+    // cryptohomed to call back to Chrome to perform challenges.
+    key_definition = cryptohome::KeyDefinition::CreateForChallengeResponse(
+        attempt->user_context.GetChallengeResponseKeys(),
+        GenerateChallengeResponseKeyLabel(
+            attempt->user_context.GetChallengeResponseKeys()),
+        cryptohome::PRIV_DEFAULT);
+    cryptohome::KeyDefinitionToKey(key_definition, auth_key);
+    auth.mutable_key_delegate()->set_dbus_service_name(
+        cryptohome::kCryptohomeKeyDelegateServiceName);
+    auth.mutable_key_delegate()->set_dbus_object_path(
+        cryptohome::kCryptohomeKeyDelegateServicePath);
+  } else {
+    key_definition = cryptohome::KeyDefinition::CreateForPassword(
+        key->GetSecret(), kCryptohomeGAIAKeyLabel, cryptohome::PRIV_DEFAULT);
+    // Don't set the authorization's key label, implicitly setting it to an
+    // empty string, which is a wildcard allowing any key to match. This is
+    // necessary because cryptohomes created by Chrome OS M38 and older will
+    // have a legacy key with no label while those created by Chrome OS M39 and
+    // newer will have a key with the label kCryptohomeGAIAKeyLabel.
+    //
+    // This logic does not apply to PIN and weak keys in general, as those do
+    // not authenticate against a wildcard label.
+    if (attempt->user_context.IsUsingPin())
+      auth_key->mutable_data()->set_label(key->GetLabel());
+    auth_key->set_secret(key->GetSecret());
+  }
+
   cryptohome::MountRequest mount;
   if (ephemeral)
     mount.set_require_ephemeral(true);
   if (create_if_nonexistent) {
-    cryptohome::KeyDefinitionToKey(
-        cryptohome::KeyDefinition::CreateForPassword(key->GetSecret(),
-                                                     kCryptohomeGAIAKeyLabel,
-                                                     cryptohome::PRIV_DEFAULT),
-        mount.mutable_create()->add_keys());
+    cryptohome::KeyDefinitionToKey(key_definition,
+                                   mount.mutable_create()->add_keys());
   }
   if (attempt->user_context.IsForcingDircrypto())
     mount.set_force_dircrypto_if_available(true);
-  cryptohome::AuthorizationRequest auth;
-  cryptohome::Key* auth_key = auth.mutable_key();
-  // Don't set the authorization's key label, implicitly setting it to an
-  // empty string, which is a wildcard allowing any key to match. This is
-  // necessary because cryptohomes created by Chrome OS M38 and older will have
-  // a legacy key with no label while those created by Chrome OS M39 and newer
-  // will have a key with the label kCryptohomeGAIAKeyLabel.
-  //
-  // This logic does not apply to PIN and weak keys in general, as those do not
-  // authenticate against a wildcard label.
-  if (attempt->user_context.IsUsingPin())
-    auth_key->mutable_data()->set_label(key->GetLabel());
-  auth_key->set_secret(key->GetSecret());
   CryptohomeClient::Get()->MountEx(
       cryptohome::CreateAccountIdentifierFromAccountId(
           attempt->user_context.GetAccountId()),
diff --git a/chromeos/login/auth/password_visibility_utils.cc b/chromeos/login/auth/password_visibility_utils.cc
new file mode 100644
index 0000000..6d7f09b
--- /dev/null
+++ b/chromeos/login/auth/password_visibility_utils.cc
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/login/auth/password_visibility_utils.h"
+
+#include "components/user_manager/known_user.h"
+
+namespace chromeos {
+
+namespace password_visibility {
+
+bool AccountHasUserFacingPassword(const AccountId& account_id) {
+  // TODO(emaxx): Maintain this bit as more cases (e.g. Smart Cards) arise or
+  // if/when the logic for determining accounts without a user facing password
+  // is refined to reduce false negatives.
+  return !user_manager::known_user::GetIsUsingSAMLPrincipalsAPI(account_id);
+}
+
+}  // namespace password_visibility
+
+}  // namespace chromeos
diff --git a/chromeos/login/auth/password_visibility_utils.h b/chromeos/login/auth/password_visibility_utils.h
new file mode 100644
index 0000000..222cf9f
--- /dev/null
+++ b/chromeos/login/auth/password_visibility_utils.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 CHROMEOS_LOGIN_AUTH_PASSWORD_VISIBILITY_UTILS_H_
+#define CHROMEOS_LOGIN_AUTH_PASSWORD_VISIBILITY_UTILS_H_
+
+#include "base/component_export.h"
+
+class AccountId;
+
+namespace chromeos {
+
+namespace password_visibility {
+
+// Whether the account has a user facing password that the user can enter for
+// security checks.
+bool COMPONENT_EXPORT(CHROMEOS_LOGIN_AUTH)
+    AccountHasUserFacingPassword(const AccountId& account_id);
+
+}  // namespace password_visibility
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_LOGIN_AUTH_PASSWORD_VISIBILITY_UTILS_H_
diff --git a/chromeos/network/device_state.cc b/chromeos/network/device_state.cc
index db6e4c22..53727c60 100644
--- a/chromeos/network/device_state.cc
+++ b/chromeos/network/device_state.cc
@@ -17,14 +17,7 @@
 namespace chromeos {
 
 DeviceState::DeviceState(const std::string& path)
-    : ManagedState(MANAGED_TYPE_DEVICE, path),
-      allow_roaming_(false),
-      provider_requires_roaming_(false),
-      support_network_scan_(false),
-      scanning_(false),
-      sim_retries_left_(0),
-      sim_present_(true),
-      eap_authentication_completed_(false) {}
+    : ManagedState(MANAGED_TYPE_DEVICE, path) {}
 
 DeviceState::~DeviceState() = default;
 
diff --git a/chromeos/network/device_state.h b/chromeos/network/device_state.h
index 8c6c155..a97dc91 100644
--- a/chromeos/network/device_state.h
+++ b/chromeos/network/device_state.h
@@ -92,15 +92,15 @@
   // Cellular specific properties
   std::string operator_name_;
   std::string country_code_;
-  bool allow_roaming_;
-  bool provider_requires_roaming_;
-  bool support_network_scan_;
-  bool scanning_;
+  bool allow_roaming_ = false;
+  bool provider_requires_roaming_ = false;
+  bool support_network_scan_ = false;
+  bool scanning_ = false;
   std::string technology_family_;
   std::string sim_lock_type_;
-  int sim_retries_left_;
-  bool sim_lock_enabled_;
-  bool sim_present_;
+  int sim_retries_left_ = 0;
+  bool sim_lock_enabled_ = false;
+  bool sim_present_ = true;
   std::string meid_;
   std::string imei_;
   std::string iccid_;
@@ -108,7 +108,7 @@
   CellularScanResults scan_results_;
 
   // Ethernet specific properties
-  bool eap_authentication_completed_;
+  bool eap_authentication_completed_ = false;
 
   // WiFi specific properties
   std::string available_managed_network_path_;
diff --git a/chromeos/network/managed_state.cc b/chromeos/network/managed_state.cc
index 4812dd7..e985f7af 100644
--- a/chromeos/network/managed_state.cc
+++ b/chromeos/network/managed_state.cc
@@ -34,11 +34,7 @@
 }
 
 ManagedState::ManagedState(ManagedType type, const std::string& path)
-    : managed_type_(type),
-      path_(path),
-      update_received_(false),
-      update_requested_(false) {
-}
+    : managed_type_(type), path_(path) {}
 
 ManagedState::~ManagedState() = default;
 
diff --git a/chromeos/network/managed_state.h b/chromeos/network/managed_state.h
index bb4058b..c767b8f7 100644
--- a/chromeos/network/managed_state.h
+++ b/chromeos/network/managed_state.h
@@ -131,10 +131,10 @@
   std::string type_;  // shill::kTypeProperty
 
   // Set to true when the an update has been received.
-  bool update_received_;
+  bool update_received_ = false;
 
   // Tracks when an update has been requested.
-  bool update_requested_;
+  bool update_requested_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(ManagedState);
 };
diff --git a/chromeos/network/network_state_test_helper.cc b/chromeos/network/network_state_test_helper.cc
index 27e7cc7..50034ce5 100644
--- a/chromeos/network/network_state_test_helper.cc
+++ b/chromeos/network/network_state_test_helper.cc
@@ -8,6 +8,7 @@
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "chromeos/dbus/shill/shill_clients.h"
+#include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/onc/onc_utils.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
@@ -23,7 +24,6 @@
                        const std::string& error_message) {}
 
 const char kUserHash[] = "user_hash";
-const char kProfilePathShared[] = "shared_profile_path";
 const char kProfilePathUser[] = "user_profile_path";
 
 }  // namespace
@@ -40,7 +40,7 @@
   device_test_ = ShillDeviceClient::Get()->GetTestInterface();
   service_test_ = ShillServiceClient::Get()->GetTestInterface();
 
-  profile_test_->AddProfile(kProfilePathShared,
+  profile_test_->AddProfile(NetworkProfileHandler::GetSharedProfilePath(),
                             std::string() /* shared profile */);
   profile_test_->AddProfile(kProfilePathUser, kUserHash);
   base::RunLoop().RunUntilIdle();
@@ -194,10 +194,6 @@
   return network;
 }
 
-const char* NetworkStateTestHelper::ProfilePathShared() {
-  return kProfilePathShared;
-}
-
 const char* NetworkStateTestHelper::ProfilePathUser() {
   return kProfilePathUser;
 }
diff --git a/chromeos/network/network_state_test_helper.h b/chromeos/network/network_state_test_helper.h
index c8078fc..d2326f29 100644
--- a/chromeos/network/network_state_test_helper.h
+++ b/chromeos/network/network_state_test_helper.h
@@ -67,7 +67,6 @@
       int signal_strength);
 
   // Returns the path used for the shared and user profiles.
-  const char* ProfilePathShared();
   const char* ProfilePathUser();
 
   // Returns the hash used for the user profile.
diff --git a/chromeos/printing/epson_driver_matching.cc b/chromeos/printing/epson_driver_matching.cc
index 8f1bd00d..d25b5a6 100644
--- a/chromeos/printing/epson_driver_matching.cc
+++ b/chromeos/printing/epson_driver_matching.cc
@@ -34,7 +34,7 @@
                             "application/octet-stream");
 
     case PrinterSearchData::PrinterDiscoveryType::kUsb:
-      return base::Contains(sd.printer_id.command_set(), "ESC/P-R");
+      return base::Contains(sd.usb_command_set, "ESC/P-R");
 
     case PrinterSearchData::PrinterDiscoveryType::kZeroconf:
       // For printers found through mDNS/DNS-SD discovery,
diff --git a/chromeos/printing/epson_driver_matching_unittest.cc b/chromeos/printing/epson_driver_matching_unittest.cc
index 8bf627a..42d274a 100644
--- a/chromeos/printing/epson_driver_matching_unittest.cc
+++ b/chromeos/printing/epson_driver_matching_unittest.cc
@@ -5,7 +5,6 @@
 #include "chromeos/printing/epson_driver_matching.h"
 
 #include <string>
-#include <vector>
 
 #include "chromeos/printing/ppd_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -32,7 +31,7 @@
 
     case PrinterDiscoveryType::kUsb:
       sd.discovery_type = PrinterDiscoveryType::kUsb;
-      sd.printer_id.set_command_set({kEscPr});
+      sd.usb_command_set.push_back(kEscPr);
       break;
 
     case PrinterDiscoveryType::kZeroconf:
@@ -97,7 +96,7 @@
   sd.supported_document_formats.push_back(std::string(kOctetStream) + "afds");
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
-  sd.printer_id.set_command_set({kOctetStream});
+  sd.usb_command_set.push_back(kOctetStream);
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
   sd.supported_document_formats.push_back(kOctetStream);
@@ -107,21 +106,18 @@
 // Simple PrinterDiscoveryType::kUsb checks.
 TEST(EpsonDriverMatchingTest, UsbDiscovery) {
   PrinterSearchData sd(GetTestPrinterSearchData(PrinterDiscoveryType::kUsb));
-  std::vector<std::string> command_set;
+  sd.usb_command_set.clear();
 
-  command_set.push_back("ESC");
-  sd.printer_id.set_command_set(command_set);
+  sd.usb_command_set.push_back("ESC");
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
-  command_set.push_back(std::string(kEscPr) + ":asfd");
-  sd.printer_id.set_command_set(command_set);
+  sd.usb_command_set.push_back(std::string(kEscPr) + ":asfd");
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
   sd.supported_document_formats.push_back(kEscPr);
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
-  command_set.push_back(kEscPr);
-  sd.printer_id.set_command_set(command_set);
+  sd.usb_command_set.push_back(kEscPr);
   EXPECT_TRUE(CanUseEpsonGenericPPD(sd));
 }
 
@@ -136,7 +132,7 @@
   sd.supported_document_formats.push_back(std::string(kEpsonEscpr) + ":asfd");
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
-  sd.printer_id.set_command_set({kEpsonEscpr});
+  sd.usb_command_set.push_back(kEpsonEscpr);
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
   sd.supported_document_formats.push_back(kEpsonEscpr);
diff --git a/chromeos/printing/ppd_provider.h b/chromeos/printing/ppd_provider.h
index b5110b03..51baa82a 100644
--- a/chromeos/printing/ppd_provider.h
+++ b/chromeos/printing/ppd_provider.h
@@ -16,7 +16,6 @@
 #include "base/version.h"
 #include "chromeos/chromeos_export.h"
 #include "chromeos/printing/printer_configuration.h"
-#include "chromeos/printing/usb_printer_id.h"
 
 namespace network {
 namespace mojom {
@@ -59,9 +58,9 @@
   // Set of MIME types supported by this printer.
   std::vector<std::string> supported_document_formats;
 
-  // Representation of IEEE1284 standard printing device ID.
-  // Contains a set of languages this printer understands.
-  UsbPrinterId printer_id;
+  // Stripped from IEEE1284 signaling method(from the device ID key 'CMD').
+  // Details a set of languages this printer understands.
+  std::vector<std::string> usb_command_set;
 };
 
 // PpdProvider is responsible for mapping printer descriptions to
diff --git a/chromeos/printing/usb_printer_id.cc b/chromeos/printing/usb_printer_id.cc
deleted file mode 100644
index 5c7a81e..0000000
--- a/chromeos/printing/usb_printer_id.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/printing/usb_printer_id.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/stl_util.h"
-#include "base/strings/string_split.h"
-
-namespace chromeos {
-
-// Device ID keys pulled from IEEE Standard 1284.
-const char kManufacturer[] = "MANUFACTURER";
-const char kManufacturerAbbr[] = "MFG";
-const char kModel[] = "MODEL";
-const char kModelAbbr[] = "MDL";
-const char kCommandSet[] = "COMMAND SET";
-const char kCommandSetAbbr[] = "CMD";
-
-UsbPrinterId::UsbPrinterId(const std::vector<uint8_t>& device_id_data) {
-  // Build mapping.
-  id_mappings_ = BuildDeviceIdMapping(device_id_data);
-
-  // Save required mappings.
-  // Save make_.
-  if (base::Contains(id_mappings_, kManufacturer)) {
-    make_ = id_mappings_[kManufacturer].front();
-  } else if (base::Contains(id_mappings_, kManufacturerAbbr)) {
-    make_ = id_mappings_[kManufacturerAbbr].front();
-  }
-
-  // Save model_.
-  if (base::Contains(id_mappings_, kModel)) {
-    model_ = id_mappings_[kModel].front();
-  } else if (base::Contains(id_mappings_, kModelAbbr)) {
-    model_ = id_mappings_[kModelAbbr].front();
-  }
-
-  // Save command_set_.
-  if (base::Contains(id_mappings_, kCommandSet)) {
-    command_set_ = id_mappings_[kCommandSet];
-  } else if (base::Contains(id_mappings_, kCommandSetAbbr)) {
-    command_set_ = id_mappings_[kCommandSetAbbr];
-  }
-}
-
-UsbPrinterId::UsbPrinterId() = default;
-UsbPrinterId::UsbPrinterId(const UsbPrinterId& other) = default;
-UsbPrinterId::~UsbPrinterId() = default;
-
-std::map<std::string, std::vector<std::string>> BuildDeviceIdMapping(
-    const std::vector<uint8_t>& data) {
-  // Must contain at least the length information.
-  if (data.size() < 2) {
-    return {};
-  }
-
-  std::map<std::string, std::vector<std::string>> ret;
-
-  // Convert to string to work on.
-  // Note: First two bytes contain the length, so we skip those.
-  std::string printer_id;
-  std::copy(data.begin() + 2, data.end(), std::back_inserter(printer_id));
-
-  // We filter out terms with empty keys or values.
-  base::StringPairs terms;
-  base::SplitStringIntoKeyValuePairs(printer_id, ':', ';', &terms);
-  for (const auto& term : terms) {
-    if (term.first.empty()) {
-      continue;
-    }
-
-    std::vector<std::string> values = base::SplitString(
-        term.second, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-    if (values.empty()) {
-      continue;
-    }
-
-    ret[term.first] = values;
-  }
-
-  return ret;
-}
-
-}  // namespace chromeos
diff --git a/chromeos/printing/usb_printer_id.h b/chromeos/printing/usb_printer_id.h
deleted file mode 100644
index 3df4257..0000000
--- a/chromeos/printing/usb_printer_id.h
+++ /dev/null
@@ -1,60 +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 CHROMEOS_PRINTING_USB_PRINTER_ID_H_
-#define CHROMEOS_PRINTING_USB_PRINTER_ID_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "chromeos/chromeos_export.h"
-
-namespace chromeos {
-
-// This class parses and holds the IEEE 1284 Device ID string as queried
-// from a USB-connected printer.
-class CHROMEOS_EXPORT UsbPrinterId {
- public:
-  UsbPrinterId();
-  UsbPrinterId(const UsbPrinterId& other);
-  ~UsbPrinterId();
-
-  // Expects |printer_id_data| to contain the data portion response to a USB
-  // Printer Class-Specific GET_DEVICE_ID Request.
-  explicit UsbPrinterId(const std::vector<uint8_t>& printer_id_data);
-
-  // Accessors.
-  std::string make() const { return make_; }
-  std::string model() const { return model_; }
-  std::vector<std::string> command_set() const { return command_set_; }
-
-  // Setters (only used in testing).
-  void set_make(std::string make) { make_ = make; }
-  void set_model(std::string model) { model_ = model; }
-  void set_command_set(std::vector<std::string> command_set) {
-    command_set_ = std::move(command_set);
-  }
-
- private:
-  std::string make_;
-  std::string model_;
-
-  // List of supported document formats (MIME types).
-  std::vector<std::string> command_set_;
-
-  // Holds the fully parsed IEEE 1284 Device ID.
-  std::map<std::string, std::vector<std::string>> id_mappings_;
-};
-
-// Expects data to hold a IEEE 1284 Device ID. Parses |data| and returns the
-// resulting key-value(s) pairs.
-CHROMEOS_EXPORT std::map<std::string, std::vector<std::string>>
-BuildDeviceIdMapping(const std::vector<uint8_t>& data);
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_PRINTING_USB_PRINTER_ID_H_
diff --git a/chromeos/printing/usb_printer_id_unittest.cc b/chromeos/printing/usb_printer_id_unittest.cc
deleted file mode 100644
index 7ad21a1..0000000
--- a/chromeos/printing/usb_printer_id_unittest.cc
+++ /dev/null
@@ -1,74 +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 "chromeos/printing/usb_printer_id.h"
-
-#include <algorithm>
-#include <map>
-#include <string>
-
-#include "base/strings/string_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos {
-namespace {
-
-using testing::IsEmpty;
-
-using MapType = std::map<std::string, std::vector<std::string>>;
-
-MapType GetDefaultDeviceId() {
-  MapType ret;
-
-  // Make.
-  ret["MFG"].push_back("EPSON");
-
-  // Model.
-  ret["MDL"].push_back("ET-2700");
-
-  // Command set.
-  ret["CMD"].push_back("ESCPL2");
-  ret["CMD"].push_back("BDC");
-  ret["CMD"].push_back("D4");
-  ret["CMD"].push_back("END4");
-  ret["CMD"].push_back("GENEP");
-
-  return ret;
-}
-
-std::string MapToString(const MapType& map) {
-  std::vector<std::string> terms;
-  for (auto& term : map) {
-    std::string values = base::JoinString(term.second, ",");
-    terms.push_back(base::JoinString({term.first, values}, ":"));
-  }
-
-  std::string device_id_str = "xx";  // Two unused bytes for the length.
-  device_id_str += base::JoinString(terms, ";") + ";";
-  return device_id_str;
-}
-
-std::vector<uint8_t> MapToBuffer(const MapType& map) {
-  std::string device_id_str = MapToString(map);
-
-  std::vector<uint8_t> ret;
-  std::copy(device_id_str.begin(), device_id_str.end(),
-            std::back_inserter(ret));
-  return ret;
-}
-
-TEST(UsbPrinterIdTest, EmptyDeviceId) {
-  EXPECT_THAT(BuildDeviceIdMapping({}), IsEmpty());
-}
-
-// Tests that we get the same map back after parsing.
-TEST(UsbPrinterIdTest, SimpleSanityTest) {
-  MapType mapping = GetDefaultDeviceId();
-  std::vector<uint8_t> buffer = MapToBuffer(mapping);
-  EXPECT_EQ(mapping, BuildDeviceIdMapping(buffer));
-}
-
-}  // namespace
-}  // namespace chromeos
diff --git a/chromeos/services/assistant/chromium_http_connection.cc b/chromeos/services/assistant/chromium_http_connection.cc
index ff84fdf..0624974 100644
--- a/chromeos/services/assistant/chromium_http_connection.cc
+++ b/chromeos/services/assistant/chromium_http_connection.cc
@@ -183,6 +183,8 @@
   auto factory =
       SharedURLLoaderFactory::Create(std::move(url_loader_factory_info_));
   if (handle_partial_response_) {
+    url_loader_->SetOnResponseStartedCallback(
+        base::BindOnce(&ChromiumHttpConnection::OnResponseStarted, this));
     url_loader_->DownloadAsStream(factory.get(), this);
   } else {
     url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
@@ -192,11 +194,21 @@
 }
 
 void ChromiumHttpConnection::Pause() {
-  NOTIMPLEMENTED();
+  ENSURE_MAIN_THREAD(&ChromiumHttpConnection::Pause);
+  is_paused_ = true;
 }
 
 void ChromiumHttpConnection::Resume() {
-  NOTIMPLEMENTED();
+  ENSURE_MAIN_THREAD(&ChromiumHttpConnection::Resume);
+  is_paused_ = false;
+
+  if (!partial_response_cache_.empty()) {
+    delegate_->OnPartialResponse(partial_response_cache_);
+    partial_response_cache_.clear();
+  }
+
+  if (on_resume_callback_)
+    std::move(on_resume_callback_).Run();
 }
 
 void ChromiumHttpConnection::Close() {
@@ -263,14 +275,18 @@
 void ChromiumHttpConnection::OnDataReceived(base::StringPiece string_piece,
                                             base::OnceClosure resume) {
   DCHECK(handle_partial_response_);
-  if (enable_header_response_) {
-    // Cache the partial responses, we need to send the headers back before
-    // any |OnPartialResposne| to honor the API contract.
-    partial_response_cache_.emplace_back(string_piece.as_string());
+
+  if (is_paused_) {
+    // If the connection is paused, stop sending |OnPartialResponse|
+    // notification to the delegate and cache the response part.
+    on_resume_callback_ = std::move(resume);
+    DCHECK(partial_response_cache_.empty());
+    partial_response_cache_ = string_piece.as_string();
   } else {
+    DCHECK(partial_response_cache_.empty());
     delegate_->OnPartialResponse(string_piece.as_string());
+    std::move(resume).Run();
   }
-  std::move(resume).Run();
 }
 
 void ChromiumHttpConnection::OnComplete(bool success) {
@@ -288,12 +304,6 @@
     response_code = url_loader_->ResponseInfo()->headers->response_code();
   }
 
-  if (enable_header_response_) {
-    delegate_->OnHeaderResponse(raw_headers);
-    for (auto& partial_response : partial_response_cache_)
-      delegate_->OnPartialResponse(partial_response);
-  }
-
   if (response_code != kResponseCodeInvalid) {
     delegate_->OnCompleteResponse(response_code, raw_headers, /*response=*/"");
     return;
@@ -381,5 +391,15 @@
   delegate_->OnCompleteResponse(response_code, raw_headers, *response_body);
 }
 
+void ChromiumHttpConnection::OnResponseStarted(
+    const GURL& final_url,
+    const network::ResourceResponseHead& response_header) {
+  if (enable_header_response_ && response_header.headers) {
+    // Only propagate |OnHeaderResponse()| once before any |OnPartialResponse()|
+    // invoked to honor the API contract.
+    delegate_->OnHeaderResponse(response_header.headers->raw_headers());
+  }
+}
+
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/chromium_http_connection.h b/chromeos/services/assistant/chromium_http_connection.h
index 26dc85d..0abf0ce 100644
--- a/chromeos/services/assistant/chromium_http_connection.h
+++ b/chromeos/services/assistant/chromium_http_connection.h
@@ -17,6 +17,7 @@
 #include "libassistant/shared/internal_api/http_connection.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "net/http/http_request_headers.h"
+#include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
 #include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h"
 #include "url/gurl.h"
@@ -87,6 +88,10 @@
   // URL loader completion callback.
   void OnURLLoadComplete(std::unique_ptr<std::string> response_body);
 
+  // Callback invoked when the response of the http connection has started.
+  void OnResponseStarted(const GURL& final_url,
+                         const network::ResourceResponseHead& response_header);
+
   Delegate* const delegate_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   State state_ = State::NEW;
@@ -113,7 +118,12 @@
   std::string chunked_upload_content_type_;
   bool handle_partial_response_ = false;
   bool enable_header_response_ = false;
-  std::vector<std::string> partial_response_cache_;
+
+  // Set to true if the response transfer of the connection is paused.
+  bool is_paused_ = false;
+
+  base::OnceClosure on_resume_callback_;
+  std::string partial_response_cache_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromiumHttpConnection);
 };
diff --git a/chromeos/services/ime/decoder/decoder_engine.cc b/chromeos/services/ime/decoder/decoder_engine.cc
index f657153..191738a6 100644
--- a/chromeos/services/ime/decoder/decoder_engine.cc
+++ b/chromeos/services/ime/decoder/decoder_engine.cc
@@ -91,14 +91,14 @@
     mojo::PendingRemote<mojom::InputChannel> remote,
     const std::vector<uint8_t>& extra) {
   // If the shared library supports this ime_spec.
-  if (IsImeSupported(ime_spec)) {
+  if (IsImeSupportedByDecoder(ime_spec)) {
     // Activates an IME engine via the shared library. Passing a
     // |ClientDelegate| for engine instance created by the shared library to
     // make safe calls on the client.
     if (engine_main_entry_->ActivateIme(
             ime_spec.c_str(),
             new ClientDelegate(ime_spec, std::move(remote)))) {
-      channel_receivers_.Add(this, std::move(receiver));
+      decoder_channel_receivers_.Add(this, std::move(receiver));
       // TODO(https://crbug.com/837156): Registry connection error handler.
       return true;
     }
@@ -110,7 +110,7 @@
                                   std::move(remote), extra);
 }
 
-bool DecoderEngine::IsImeSupported(const std::string& ime_spec) {
+bool DecoderEngine::IsImeSupportedByDecoder(const std::string& ime_spec) {
   return engine_main_entry_ &&
          engine_main_entry_->IsImeSupported(ime_spec.c_str());
 }
diff --git a/chromeos/services/ime/decoder/decoder_engine.h b/chromeos/services/ime/decoder/decoder_engine.h
index e211b8e3..ea1dbb7 100644
--- a/chromeos/services/ime/decoder/decoder_engine.h
+++ b/chromeos/services/ime/decoder/decoder_engine.h
@@ -33,12 +33,13 @@
                    mojo::PendingRemote<mojom::InputChannel> remote,
                    const std::vector<uint8_t>& extra) override;
 
-  // Returns whether the loaded shared library supports this ime_spec.
-  bool IsImeSupported(const std::string& ime_spec) override;
   void ProcessMessage(const std::vector<uint8_t>& message,
                       ProcessMessageCallback callback) override;
 
  private:
+  // Returns whether the decoder supports this ime_spec.
+  bool IsImeSupportedByDecoder(const std::string& ime_spec);
+
   // Shared library handle of the implementation for input logic with decoders.
   base::ScopedNativeLibrary library_;
 
@@ -46,7 +47,7 @@
 
   ImeCrosPlatform* platform_ = nullptr;
 
-  mojo::ReceiverSet<mojom::InputChannel> channel_receivers_;
+  mojo::ReceiverSet<mojom::InputChannel> decoder_channel_receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(DecoderEngine);
 };
diff --git a/chromeos/services/ime/ime_service.cc b/chromeos/services/ime/ime_service.cc
index 3037ba8..7a2d7bf 100644
--- a/chromeos/services/ime/ime_service.cc
+++ b/chromeos/services/ime/ime_service.cc
@@ -20,6 +20,16 @@
 namespace chromeos {
 namespace ime {
 
+namespace {
+
+enum SimpleDownloadError {
+  SIMPLE_DOWNLOAD_ERROR_OK = 0,
+  SIMPLE_DOWNLOAD_ERROR_FAILED = -1,
+  SIMPLE_DOWNLOAD_ERROR_ABORTED = -2,
+};
+
+}  // namespace
+
 ImeService::ImeService(
     mojo::PendingReceiver<service_manager::mojom::Service> receiver)
     : service_binding_(this, std::move(receiver)) {}
@@ -87,11 +97,21 @@
   }
 }
 
+void ImeService::SimpleDownloadFinished(SimpleDownloadCallback callback,
+                                        const base::FilePath& file) {
+  if (file.empty()) {
+    callback(SIMPLE_DOWNLOAD_ERROR_FAILED, "");
+  } else {
+    callback(SIMPLE_DOWNLOAD_ERROR_OK, file.MaybeAsASCII().c_str());
+  }
+}
+
 const char* ImeService::GetImeBundleDir() {
   return "";
 }
 
 const char* ImeService::GetImeGlobalDir() {
+  // Global IME data dir will not be supported yet.
   return "";
 }
 
@@ -102,14 +122,26 @@
 int ImeService::SimpleDownloadToFile(const char* url,
                                      const char* file_path,
                                      SimpleDownloadCallback callback) {
-  // TODO(https://crbug.com/837156): Create a download with PlatformAccess
-  // Mojo remote. Make sure the parent of |file_path| is ImeUserHomeDir.
+  if (!platform_access_.is_bound()) {
+    callback(SIMPLE_DOWNLOAD_ERROR_ABORTED, "");
+  } else {
+    GURL download_url(url);
+    // |file_path| must be relative.
+    base::FilePath relative_file_path(file_path);
+
+    platform_access_->DownloadImeFileTo(
+        download_url, relative_file_path,
+        base::BindOnce(&ImeService::SimpleDownloadFinished,
+                       base::Unretained(this), std::move(callback)));
+  }
+
+  // For |SimpleDownloadToFile|, always returns 0.
   return 0;
 }
 
 ImeCrosDownloader* ImeService::GetDownloader() {
-  // TODO(https://crbug.com/837156): Create a ImeCrosDownloader based on its
-  // specification in interfaces. The caller should free it after use.
+  // TODO(https://crbug.com/837156): Create an ImeCrosDownloader based on its
+  // specification defined in interfaces. The caller should free it after use.
   return nullptr;
 }
 
diff --git a/chromeos/services/ime/ime_service.h b/chromeos/services/ime/ime_service.h
index 0cb42afd..67377f5 100644
--- a/chromeos/services/ime/ime_service.h
+++ b/chromeos/services/ime/ime_service.h
@@ -74,6 +74,11 @@
   // happen when the input engine client exits or crashes.
   void OnConnectionLost();
 
+  // Callback used when a file download finishes by the |SimpleURLLoader|.
+  // On failure, |file| will be empty.
+  void SimpleDownloadFinished(SimpleDownloadCallback callback,
+                              const base::FilePath& file);
+
   service_manager::ServiceBinding service_binding_;
 
   // For the duration of this service lifetime, there should be only one
diff --git a/chromeos/services/ime/ime_service_unittest.cc b/chromeos/services/ime/ime_service_unittest.cc
index ddf51c8..00a89282 100644
--- a/chromeos/services/ime/ime_service_unittest.cc
+++ b/chromeos/services/ime/ime_service_unittest.cc
@@ -37,10 +37,10 @@
 }
 
 void TestProcessKeypressForRulebasedCallback(
-    mojom::KeypressResponse* res_out,
-    mojom::KeypressResponsePtr response) {
+    mojom::KeypressResponseForRulebased* res_out,
+    mojom::KeypressResponseForRulebasedPtr response) {
   res_out->result = response->result;
-  res_out->operations = std::vector<mojom::OperationPtr>(0);
+  res_out->operations = std::vector<mojom::OperationForRulebasedPtr>(0);
   for (int i = 0; i < (int)response->operations.size(); i++) {
     res_out->operations.push_back(std::move(response->operations[i]));
   }
@@ -64,7 +64,7 @@
                void(const std::vector<uint8_t>& message,
                     ProcessMessageCallback));
   MOCK_METHOD2(ProcessKeypressForRulebased,
-               void(const mojom::KeypressInfoPtr message,
+               void(const mojom::KeypressInfoForRulebasedPtr message,
                     ProcessKeypressForRulebasedCallback));
   MOCK_METHOD0(ResetForRulebased, void());
   MOCK_METHOD1(GetRulebasedKeypressCountForTesting,
@@ -314,18 +314,19 @@
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
 
-  mojom::KeypressResponse response;
-  mojom::KeypressInfoPtr keypress_info = mojom::KeypressInfo::New(
-      "keydown", "KeyA", true, false, false, false, false);
+  mojom::KeypressResponseForRulebased response;
+  mojom::KeypressInfoForRulebasedPtr keypress_info =
+      mojom::KeypressInfoForRulebased::New("keydown", "KeyA", true, false,
+                                           false, false, false);
   remote_engine_1->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "KeyA", true, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "KeyA", true, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   remote_engine_1.FlushForTesting();
 
   remote_engine_2->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "KeyA", true, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "KeyA", true, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   remote_engine_2.FlushForTesting();
 
@@ -355,45 +356,45 @@
   EXPECT_TRUE(success);
 
   // Test Shift+KeyA.
-  mojom::KeypressResponse response;
+  mojom::KeypressResponseForRulebased response;
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "KeyA", true, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "KeyA", true, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
 
   EXPECT_EQ(response.result, true);
-  std::vector<mojom::OperationPtr> expected_operations;
-  expected_operations.push_back(
-      {mojom::Operation::New(mojom::OperationMethod::COMMIT_TEXT, "\u0650")});
+  std::vector<mojom::OperationForRulebasedPtr> expected_operations;
+  expected_operations.push_back({mojom::OperationForRulebased::New(
+      mojom::OperationMethodForRulebased::COMMIT_TEXT, "\u0650")});
   EXPECT_EQ(response.operations.size(), expected_operations.size());
   EXPECT_EQ(response.operations, expected_operations);
 
   // Test KeyB
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "KeyB", false, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "KeyB", false, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
   EXPECT_EQ(response.result, true);
-  expected_operations = std::vector<mojom::OperationPtr>(0);
-  expected_operations.push_back({mojom::Operation::New(
-      mojom::OperationMethod::COMMIT_TEXT, "\u0644\u0627")});
+  expected_operations = std::vector<mojom::OperationForRulebasedPtr>(0);
+  expected_operations.push_back({mojom::OperationForRulebased::New(
+      mojom::OperationMethodForRulebased::COMMIT_TEXT, "\u0644\u0627")});
   EXPECT_EQ(response.operations.size(), expected_operations.size());
   EXPECT_EQ(response.operations, expected_operations);
 
   // Test unhandled key.
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "Enter", false, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "Enter", false, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
   EXPECT_EQ(response.result, false);
 
   // Test keyup.
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keyup", "Enter", false, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keyup", "Enter", false, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
   EXPECT_EQ(response.result, false);
@@ -403,8 +404,8 @@
 
   // Test invalid request.
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "", false, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "", false, false, false,
+                                           false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
   EXPECT_EQ(response.result, false);
@@ -423,66 +424,67 @@
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
 
-  mojom::KeypressResponse response;
-  std::vector<mojom::OperationPtr> expected_operations;
+  mojom::KeypressResponseForRulebased response;
+  std::vector<mojom::OperationForRulebasedPtr> expected_operations;
 
   // Test KeyN.
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "KeyN", false, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "KeyN", false, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
 
   EXPECT_EQ(response.result, true);
-  expected_operations = std::vector<mojom::OperationPtr>(0);
-  expected_operations.push_back({mojom::Operation::New(
-      mojom::OperationMethod::SET_COMPOSITION, "\u0928")});
+  expected_operations = std::vector<mojom::OperationForRulebasedPtr>(0);
+  expected_operations.push_back({mojom::OperationForRulebased::New(
+      mojom::OperationMethodForRulebased::SET_COMPOSITION, "\u0928")});
   EXPECT_EQ(response.operations.size(), expected_operations.size());
   EXPECT_EQ(response.operations, expected_operations);
 
   // Backspace.
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "Backspace", false, false, false,
-                               false, false),
+      mojom::KeypressInfoForRulebased::New("keydown", "Backspace", false, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
 
   EXPECT_EQ(response.result, true);
-  expected_operations = std::vector<mojom::OperationPtr>(0);
-  expected_operations.push_back(
-      {mojom::Operation::New(mojom::OperationMethod::SET_COMPOSITION, "")});
+  expected_operations = std::vector<mojom::OperationForRulebasedPtr>(0);
+  expected_operations.push_back({mojom::OperationForRulebased::New(
+      mojom::OperationMethodForRulebased::SET_COMPOSITION, "")});
   EXPECT_EQ(response.operations.size(), expected_operations.size());
   EXPECT_EQ(response.operations, expected_operations);
 
   // KeyN + KeyC.
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "KeyN", false, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "KeyN", false, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "KeyC", false, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "KeyC", false, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
 
   EXPECT_EQ(response.result, true);
-  expected_operations = std::vector<mojom::OperationPtr>(0);
-  expected_operations.push_back({mojom::Operation::New(
-      mojom::OperationMethod::SET_COMPOSITION, "\u091e\u094d\u091a")});
+  expected_operations = std::vector<mojom::OperationForRulebasedPtr>(0);
+  expected_operations.push_back({mojom::OperationForRulebased::New(
+      mojom::OperationMethodForRulebased::SET_COMPOSITION,
+      "\u091e\u094d\u091a")});
   EXPECT_EQ(response.operations.size(), expected_operations.size());
   EXPECT_EQ(response.operations, expected_operations);
 
   // Space.
   to_engine_ptr->ProcessKeypressForRulebased(
-      mojom::KeypressInfo::New("keydown", "Space", false, false, false, false,
-                               false),
+      mojom::KeypressInfoForRulebased::New("keydown", "Space", false, false,
+                                           false, false, false),
       base::BindOnce(&TestProcessKeypressForRulebasedCallback, &response));
   to_engine_ptr.FlushForTesting();
 
   EXPECT_EQ(response.result, true);
-  expected_operations = std::vector<mojom::OperationPtr>(0);
-  expected_operations.push_back({mojom::Operation::New(
-      mojom::OperationMethod::COMMIT_TEXT, "\u091e\u094d\u091a ")});
+  expected_operations = std::vector<mojom::OperationForRulebasedPtr>(0);
+  expected_operations.push_back({mojom::OperationForRulebased::New(
+      mojom::OperationMethodForRulebased::COMMIT_TEXT, "\u091e\u094d\u091a ")});
   EXPECT_EQ(response.operations.size(), expected_operations.size());
   EXPECT_EQ(response.operations, expected_operations);
 }
diff --git a/chromeos/services/ime/input_engine.cc b/chromeos/services/ime/input_engine.cc
index 1d999ea..7161352c 100644
--- a/chromeos/services/ime/input_engine.cc
+++ b/chromeos/services/ime/input_engine.cc
@@ -35,15 +35,16 @@
   return modifiers;
 }
 
-mojom::KeypressResponsePtr GenerateKeypressResponseForRulebased(
+mojom::KeypressResponseForRulebasedPtr GenerateKeypressResponseForRulebased(
     rulebased::ProcessKeyResult& process_key_result) {
-  mojom::KeypressResponsePtr keypress_response = mojom::KeypressResponse::New();
+  mojom::KeypressResponseForRulebasedPtr keypress_response =
+      mojom::KeypressResponseForRulebased::New();
   keypress_response->result = process_key_result.key_handled;
   if (!process_key_result.commit_text.empty()) {
     std::string commit_text;
     base::EscapeJSONString(process_key_result.commit_text, false, &commit_text);
-    keypress_response->operations.push_back(mojom::Operation::New(
-        mojom::OperationMethod::COMMIT_TEXT, commit_text));
+    keypress_response->operations.push_back(mojom::OperationForRulebased::New(
+        mojom::OperationMethodForRulebased::COMMIT_TEXT, commit_text));
   }
   // Need to add the setComposition operation to the result when the key is
   // handled and commit_text and composition_text are both empty.
@@ -55,8 +56,8 @@
     std::string composition_text;
     base::EscapeJSONString(process_key_result.composition_text, false,
                            &composition_text);
-    keypress_response->operations.push_back(mojom::Operation::New(
-        mojom::OperationMethod::SET_COMPOSITION, composition_text));
+    keypress_response->operations.push_back(mojom::OperationForRulebased::New(
+        mojom::OperationMethodForRulebased::SET_COMPOSITION, composition_text));
   }
   return keypress_response;
 }
@@ -83,7 +84,7 @@
     mojo::PendingReceiver<mojom::InputChannel> receiver,
     mojo::PendingRemote<mojom::InputChannel> remote,
     const std::vector<uint8_t>& extra) {
-  if (!IsImeSupported(ime_spec))
+  if (!IsImeSupportedByRulebased(ime_spec))
     return false;
 
   channel_receivers_.Add(this, std::move(receiver),
@@ -93,7 +94,7 @@
   // TODO(https://crbug.com/837156): Registry connection error handler.
 }
 
-bool InputEngine::IsImeSupported(const std::string& ime_spec) {
+bool InputEngine::IsImeSupportedByRulebased(const std::string& ime_spec) {
   return rulebased::Engine::IsImeSupported(GetIdFromImeSpec(ime_spec));
 }
 
@@ -222,15 +223,15 @@
 }
 
 void InputEngine::ProcessKeypressForRulebased(
-    mojom::KeypressInfoPtr keypress_info,
+    mojom::KeypressInfoForRulebasedPtr keypress_info,
     ProcessKeypressForRulebasedCallback callback) {
   auto& context = channel_receivers_.current_context();
   auto& engine = context.get()->engine;
 
   if (!engine || keypress_info->type.empty() ||
       keypress_info->type != "keydown") {
-    std::move(callback).Run(mojom::KeypressResponse::New(
-        false, std::vector<mojom::OperationPtr>(0)));
+    std::move(callback).Run(mojom::KeypressResponseForRulebased::New(
+        false, std::vector<mojom::OperationForRulebasedPtr>(0)));
     return;
   }
 
@@ -238,7 +239,7 @@
       keypress_info->code,
       GenerateModifierValueForRulebased(
           keypress_info->shift, keypress_info->altgr, keypress_info->caps));
-  mojom::KeypressResponsePtr keypress_response =
+  mojom::KeypressResponseForRulebasedPtr keypress_response =
       GenerateKeypressResponseForRulebased(process_key_result);
 
   std::move(callback).Run(std::move(keypress_response));
diff --git a/chromeos/services/ime/input_engine.h b/chromeos/services/ime/input_engine.h
index 55422ab..088d650 100644
--- a/chromeos/services/ime/input_engine.h
+++ b/chromeos/services/ime/input_engine.h
@@ -42,16 +42,13 @@
                            mojo::PendingRemote<mojom::InputChannel> remote,
                            const std::vector<uint8_t>& extra);
 
-  // Returns whether the given ime_spec is supported by this engine.
-  virtual bool IsImeSupported(const std::string& ime_spec);
-
-  // mojom::MessageChannel overrides:
+  // mojom::InputChannel overrides:
   void ProcessText(const std::string& message,
                    ProcessTextCallback callback) override;
   void ProcessMessage(const std::vector<uint8_t>& message,
                       ProcessMessageCallback callback) override;
   void ProcessKeypressForRulebased(
-      mojom::KeypressInfoPtr keypress_info,
+      mojom::KeypressInfoForRulebasedPtr keypress_info,
       ProcessKeypressForRulebasedCallback callback) override;
   void ResetForRulebased() override;
   void GetRulebasedKeypressCountForTesting(
@@ -60,6 +57,9 @@
   // TODO(https://crbug.com/837156): Implement a state for the interface.
 
  private:
+  // Returns whether the given ime_spec is supported by rulebased engine.
+  bool IsImeSupportedByRulebased(const std::string& ime_spec);
+
   std::string Process(const std::string& message,
                       const InputEngineContext* context);
 
diff --git a/chromeos/services/ime/public/mojom/input_engine.mojom b/chromeos/services/ime/public/mojom/input_engine.mojom
index 3a758eb..c4b1135 100644
--- a/chromeos/services/ime/public/mojom/input_engine.mojom
+++ b/chromeos/services/ime/public/mojom/input_engine.mojom
@@ -15,43 +15,43 @@
 //   between the IME extension and the IME service. It also provides additional
 //   functionality to the IME service (for downloading IME data as needed).
 
-// Method of an operation to apply to a text field
-enum OperationMethod {
+// Method of an operation to apply to a text field.
+enum OperationMethodForRulebased {
   COMMIT_TEXT = 0,
   SET_COMPOSITION = 1,
 };
 
-// Operation to apply to a text field, in response to a Keypress
-struct Operation {
-  // The operation we wish to apply to the text field
-  OperationMethod method;
+// Operation to apply to a text field, in response to a Keypress.
+struct OperationForRulebased {
+  // The operation we wish to apply to the text field.
+  OperationMethodForRulebased method;
 
-  // Additional arguments of the method to apply to the text field
+  // Additional arguments of the method to apply to the text field.
   string arguments;
 };
 
-// KeypressResponse represents the Rulebased engine response to a keypress
-struct KeypressResponse {
+// KeypressResponse represents the Rulebased engine response to a keypress.
+struct KeypressResponseForRulebased {
   // result indicates if the function call was successful
   // There must be a valid engine and the keypress must be valid for
-  // this to be the case
+  // this to be the case.
   bool result;
 
   // An array of operations to apply to the text field in respoonse to
-  // the Keypress
-  array<Operation> operations;
+  // the Keypress.
+  array<OperationForRulebased> operations;
 };
 
-// Information about a specific keypress
-struct KeypressInfo {
+// Information about a specific keypress.
+struct KeypressInfoForRulebased {
 
-  // Keypress Type (Eg; "keydown", "keyup", etc)
+  // Keypress Type (Eg; "keydown", "keyup", etc).
   string type;
 
-  // Keypress key code (eg; "KeyA", "KeyB", "Enter", etc )
+  // Keypress key code (eg; "KeyA", "KeyB", "Enter", etc ).
   string code;
 
-  // Booleans to indicate if an option key is active / pressed down
+  // Booleans to indicate if an option key is active / pressed down.
   bool shift;
   bool altgr;
   bool caps;
@@ -92,16 +92,16 @@
   // protobuf message.
   ProcessMessage(array<uint8> message) => (array<uint8> result);
 
-  // Process a KeypressInfo mojo object and return a KeypressResponse object
-  // with a list of operations to be handled by the caller's callback
-  // (for rulebased logic)
-  ProcessKeypressForRulebased(KeypressInfo message)
-                              => (KeypressResponse result);
+  // Process a KeypressInfoForRulebased mojo object and return a
+  // KeypressResponseForRulebased object with a list of operations to be
+  // handled by the caller's callback (for rulebased logic).
+  ProcessKeypressForRulebased(KeypressInfoForRulebased message)
+                              => (KeypressResponseForRulebased result);
 
-  // Resets the engine for rulebased logic
+  // Resets the engine for rulebased logic.
   ResetForRulebased();
 
-  // Gets the keypress count for rulebased logic
+  // Gets the keypress count for rulebased logic.
   GetRulebasedKeypressCountForTesting() => (int32 count);
 };
 
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index 915a7e9..76a4edd4e 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -16,6 +16,7 @@
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h"
 #include "components/device_event_log/device_event_log.h"
+#include "components/onc/onc_constants.h"
 #include "net/base/ip_address.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
@@ -24,7 +25,7 @@
 
 namespace {
 
-std::string ShillToONC(const std::string& shill_string,
+std::string ShillToOnc(const std::string& shill_string,
                        const onc::StringTranslationEntry table[]) {
   std::string onc_string;
   if (!shill_string.empty())
@@ -122,6 +123,26 @@
   return mojom::DeviceStateType::kUnavailable;
 }
 
+mojom::OncSource GetMojoOncSource(const NetworkState* network) {
+  ::onc::ONCSource source = network->onc_source();
+  switch (source) {
+    case ::onc::ONC_SOURCE_UNKNOWN:
+    case ::onc::ONC_SOURCE_NONE:
+      if (!network->IsInProfile())
+        return mojom::OncSource::kNone;
+      return network->IsPrivate() ? mojom::OncSource::kUser
+                                  : mojom::OncSource::kDevice;
+    case ::onc::ONC_SOURCE_USER_IMPORT:
+      return mojom::OncSource::kUser;
+    case ::onc::ONC_SOURCE_DEVICE_POLICY:
+      return mojom::OncSource::kDevicePolicy;
+    case ::onc::ONC_SOURCE_USER_POLICY:
+      return mojom::OncSource::kUserPolicy;
+  }
+  NOTREACHED();
+  return mojom::OncSource::kNone;
+}
+
 mojom::NetworkStatePropertiesPtr NetworkStateToMojo(const NetworkState* network,
                                                     bool technology_enabled) {
   mojom::NetworkType type = ShillTypeToMojo(network->type());
@@ -146,7 +167,7 @@
   result->name = network->name();
   result->priority = network->priority();
   result->prohibited_by_policy = network->blocked_by_policy();
-  result->source = mojom::ONCSource(network->onc_source());
+  result->source = GetMojoOncSource(network);
 
   // NetworkHandler and UIProxyConfigService may not exist in tests.
   UIProxyConfigService* ui_proxy_config_service =
@@ -172,7 +193,7 @@
     case mojom::NetworkType::kCellular: {
       auto cellular = mojom::CellularStateProperties::New();
       cellular->activation_state = network->GetMojoActivationState();
-      cellular->network_technology = ShillToONC(network->network_technology(),
+      cellular->network_technology = ShillToOnc(network->network_technology(),
                                                 onc::kNetworkTechnologyTable);
       cellular->roaming = network->IndicateRoaming();
       cellular->signal_strength = network->signal_strength();
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index a5bb415..8460bdd4 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "chromeos/dbus/shill/fake_shill_device_client.h"
 #include "chromeos/network/network_device_handler.h"
+#include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
 #include "chromeos/network/network_type_pattern.h"
@@ -64,7 +65,8 @@
             "Strength": 50})");
     helper().ConfigureService(
         R"({"GUID": "wifi2_guid", "Type": "wifi", "State": "idle",
-            "SecurityClass": "psk", "Strength": 100})");
+            "SecurityClass": "psk", "Strength": 100,
+            "Profile": "user_profile_path"})");
     helper().ConfigureService(
         R"({"GUID": "cellular_guid", "Type": "cellular",  "State": "idle",
             "Strength": 0, "Cellular.NetworkTechnology": "LTE",
@@ -75,9 +77,10 @@
 
     // Add a non visible configured wifi service.
     std::string wifi3_path = helper().ConfigureService(
-        R"({"GUID": "wifi3_guid", "Type": "wifi", "Visible": false})");
-    helper().profile_test()->AddService(helper().ProfilePathShared(),
-                                        wifi3_path);
+        R"({"GUID": "wifi3_guid", "Type": "wifi", "SecurityClass": "psk",
+            "Visible": false})");
+    helper().profile_test()->AddService(
+        NetworkProfileHandler::GetSharedProfilePath(), wifi3_path);
     base::RunLoop().RunUntilIdle();
   }
 
@@ -149,7 +152,7 @@
         EXPECT_EQ(mojom::NetworkType::kEthernet, result->type);
         EXPECT_EQ(mojom::ConnectionStateType::kOnline,
                   result->connection_state);
-        EXPECT_EQ(mojom::ONCSource::kUnknown, result->source);
+        EXPECT_EQ(mojom::OncSource::kNone, result->source);
       }));
   cros_network_config()->GetNetworkState(
       "wifi1_guid", base::BindOnce([](mojom::NetworkStatePropertiesPtr result) {
@@ -161,6 +164,7 @@
         ASSERT_TRUE(result->wifi);
         EXPECT_EQ(mojom::SecurityType::kNone, result->wifi->security);
         EXPECT_EQ(50, result->wifi->signal_strength);
+        EXPECT_EQ(mojom::OncSource::kNone, result->source);
       }));
   cros_network_config()->GetNetworkState(
       "wifi2_guid", base::BindOnce([](mojom::NetworkStatePropertiesPtr result) {
@@ -172,6 +176,19 @@
         ASSERT_TRUE(result->wifi);
         EXPECT_EQ(mojom::SecurityType::kWpaPsk, result->wifi->security);
         EXPECT_EQ(100, result->wifi->signal_strength);
+        EXPECT_EQ(mojom::OncSource::kUser, result->source);
+      }));
+  cros_network_config()->GetNetworkState(
+      "wifi3_guid", base::BindOnce([](mojom::NetworkStatePropertiesPtr result) {
+        ASSERT_TRUE(result);
+        EXPECT_EQ("wifi3_guid", result->guid);
+        EXPECT_EQ(mojom::NetworkType::kWiFi, result->type);
+        EXPECT_EQ(mojom::ConnectionStateType::kNotConnected,
+                  result->connection_state);
+        ASSERT_TRUE(result->wifi);
+        EXPECT_EQ(mojom::SecurityType::kWpaPsk, result->wifi->security);
+        EXPECT_EQ(0, result->wifi->signal_strength);
+        EXPECT_EQ(mojom::OncSource::kDevice, result->source);
       }));
   cros_network_config()->GetNetworkState(
       "cellular_guid",
@@ -186,6 +203,7 @@
         EXPECT_EQ("LTE", result->cellular->network_technology);
         EXPECT_EQ(mojom::ActivationStateType::kActivated,
                   result->cellular->activation_state);
+        EXPECT_EQ(mojom::OncSource::kNone, result->source);
       }));
   cros_network_config()->GetNetworkState(
       "vpn_guid", base::BindOnce([](mojom::NetworkStatePropertiesPtr result) {
@@ -196,6 +214,7 @@
                   result->connection_state);
         ASSERT_TRUE(result->vpn);
         EXPECT_EQ(mojom::VPNType::kL2TPIPsec, result->vpn->type);
+        EXPECT_EQ(mojom::OncSource::kNone, result->source);
       }));
   // TODO(919691): Test ProxyMode once UIProxyConfigService logic is improved.
 }
@@ -257,8 +276,9 @@
       filter.Clone(),
       base::BindOnce(
           [](std::vector<mojom::NetworkStatePropertiesPtr> networks) {
-            ASSERT_EQ(1u, networks.size());
-            EXPECT_EQ("wifi3_guid", networks[0]->guid);
+            ASSERT_EQ(2u, networks.size());
+            EXPECT_EQ("wifi2_guid", networks[0]->guid);
+            EXPECT_EQ("wifi3_guid", networks[1]->guid);
           }));
 }
 
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.mojom b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
index aae6a56..b94cacc 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.mojom
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
@@ -65,11 +65,11 @@
 };
 
 // The ONC source for policy or imported networks.
-enum ONCSource {
-  kUnknown,
+enum OncSource {
   kNone,
-  kUserImport,
+  kDevice,
   kDevicePolicy,
+  kUser,
   kUserPolicy,
 };
 
@@ -207,7 +207,7 @@
   ProxyMode proxy_mode;
   // True for visible networks that are blocked / disallowed by policy.
   bool prohibited_by_policy = false;
-  ONCSource source;
+  OncSource source;
   TetherStateProperties? tether;
   NetworkType type;
   VPNStateProperties? vpn;
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.typemap b/chromeos/services/network_config/public/mojom/cros_network_config.typemap
index 2410ad27..3425d2e3 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.typemap
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.typemap
@@ -6,16 +6,13 @@
     "//chromeos/services/network_config/public/mojom/cros_network_config.mojom"
 
 public_deps = [
-  "//components/onc",
   "//components/proxy_config",
 ]
 
-public_headers = [ "//components/onc/onc_constants.h" ]
-
 traits_headers = [ "//chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h" ]
 
 type_mappings =
-    [ "chromeos.cros_network_config.mojom.ONCSource=onc::ONCSource" ]
+    [ "chromeos.cros_network_config.mojom.ProxyMode=ProxyPrefs::ProxyMode" ]
 
 sources = [
   "//chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc",
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc b/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc
index 89895d0..2f60db3 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc
+++ b/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc
@@ -8,51 +8,6 @@
 
 namespace mojo {
 
-chromeos::network_config::mojom::ONCSource
-EnumTraits<chromeos::network_config::mojom::ONCSource, onc::ONCSource>::ToMojom(
-    onc::ONCSource input) {
-  switch (input) {
-    case onc::ONC_SOURCE_UNKNOWN:
-      return chromeos::network_config::mojom::ONCSource::kUnknown;
-    case onc::ONC_SOURCE_NONE:
-      return chromeos::network_config::mojom::ONCSource::kNone;
-    case onc::ONC_SOURCE_USER_IMPORT:
-      return chromeos::network_config::mojom::ONCSource::kUserImport;
-    case onc::ONC_SOURCE_DEVICE_POLICY:
-      return chromeos::network_config::mojom::ONCSource::kDevicePolicy;
-    case onc::ONC_SOURCE_USER_POLICY:
-      return chromeos::network_config::mojom::ONCSource::kUserPolicy;
-  }
-
-  NOTREACHED();
-  return chromeos::network_config::mojom::ONCSource::kUnknown;
-}
-
-bool EnumTraits<chromeos::network_config::mojom::ONCSource, onc::ONCSource>::
-    FromMojom(chromeos::network_config::mojom::ONCSource input,
-              onc::ONCSource* out) {
-  switch (input) {
-    case chromeos::network_config::mojom::ONCSource::kUnknown:
-      *out = onc::ONC_SOURCE_UNKNOWN;
-      return true;
-    case chromeos::network_config::mojom::ONCSource::kNone:
-      *out = onc::ONC_SOURCE_NONE;
-      return true;
-    case chromeos::network_config::mojom::ONCSource::kUserImport:
-      *out = onc::ONC_SOURCE_USER_IMPORT;
-      return true;
-    case chromeos::network_config::mojom::ONCSource::kDevicePolicy:
-      *out = onc::ONC_SOURCE_DEVICE_POLICY;
-      return true;
-    case chromeos::network_config::mojom::ONCSource::kUserPolicy:
-      *out = onc::ONC_SOURCE_USER_POLICY;
-      return true;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
 chromeos::network_config::mojom::ProxyMode
 EnumTraits<chromeos::network_config::mojom::ProxyMode,
            ProxyPrefs::ProxyMode>::ToMojom(ProxyPrefs::ProxyMode input) {
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h b/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h
index 0ae9185..7aad32b 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h
+++ b/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h
@@ -6,22 +6,12 @@
 #define CHROMEOS_SERVICES_NETWORK_CONFIG_PUBLIC_MOJOM_CROS_NETWORK_CONFIG_MOJOM_TRAITS_H_
 
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
-#include "components/onc/onc_constants.h"
 #include "components/proxy_config/proxy_prefs.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 
 namespace mojo {
 
 template <>
-class EnumTraits<chromeos::network_config::mojom::ONCSource, onc::ONCSource> {
- public:
-  static chromeos::network_config::mojom::ONCSource ToMojom(
-      onc::ONCSource input);
-  static bool FromMojom(chromeos::network_config::mojom::ONCSource input,
-                        onc::ONCSource* out);
-};
-
-template <>
 class EnumTraits<chromeos::network_config::mojom::ProxyMode,
                  ProxyPrefs::ProxyMode> {
  public:
diff --git a/chromeos/services/secure_channel/ble_connection_manager_impl.cc b/chromeos/services/secure_channel/ble_connection_manager_impl.cc
index df15f2dc..0d4df396 100644
--- a/chromeos/services/secure_channel/ble_connection_manager_impl.cc
+++ b/chromeos/services/secure_channel/ble_connection_manager_impl.cc
@@ -345,7 +345,8 @@
   std::unique_ptr<Connection> connection =
       weave::BluetoothLowEnergyWeaveClientConnection::Factory::NewInstance(
           remote_device, bluetooth_adapter_,
-          device::BluetoothUUID(kGattServerUuid), bluetooth_device,
+          device::BluetoothUUID(kGattServerUuid),
+          bluetooth_device->GetAddress(),
           true /* should_set_low_connection_latency */);
 
   SetAuthenticatingChannel(
diff --git a/chromeos/services/secure_channel/ble_connection_manager_impl_unittest.cc b/chromeos/services/secure_channel/ble_connection_manager_impl_unittest.cc
index 2539533..a22cc15f 100644
--- a/chromeos/services/secure_channel/ble_connection_manager_impl_unittest.cc
+++ b/chromeos/services/secure_channel/ble_connection_manager_impl_unittest.cc
@@ -218,7 +218,7 @@
       multidevice::RemoteDeviceRef remote_device,
       scoped_refptr<device::BluetoothAdapter> adapter,
       const device::BluetoothUUID remote_service_uuid,
-      device::BluetoothDevice* bluetooth_device,
+      const std::string& device_address,
       bool should_set_low_connection_latency) override {
     EXPECT_EQ(expected_mock_adapter_, adapter);
     EXPECT_EQ(device::BluetoothUUID(kGattServerUuid), remote_service_uuid);
diff --git a/chromeos/services/secure_channel/ble_weave_client_connection.cc b/chromeos/services/secure_channel/ble_weave_client_connection.cc
index 2acfe57..62497d4 100644
--- a/chromeos/services/secure_channel/ble_weave_client_connection.cc
+++ b/chromeos/services/secure_channel/ble_weave_client_connection.cc
@@ -61,13 +61,13 @@
     multidevice::RemoteDeviceRef remote_device,
     scoped_refptr<device::BluetoothAdapter> adapter,
     const device::BluetoothUUID remote_service_uuid,
-    device::BluetoothDevice* bluetooth_device,
+    const std::string& device_address,
     bool should_set_low_connection_latency) {
   if (!factory_instance_) {
     factory_instance_ = new Factory();
   }
   return factory_instance_->BuildInstance(remote_device, adapter,
-                                          remote_service_uuid, bluetooth_device,
+                                          remote_service_uuid, device_address,
                                           should_set_low_connection_latency);
 }
 
@@ -82,10 +82,10 @@
     multidevice::RemoteDeviceRef remote_device,
     scoped_refptr<device::BluetoothAdapter> adapter,
     const device::BluetoothUUID remote_service_uuid,
-    device::BluetoothDevice* bluetooth_device,
+    const std::string& device_address,
     bool should_set_low_connection_latency) {
   return std::make_unique<BluetoothLowEnergyWeaveClientConnection>(
-      remote_device, adapter, remote_service_uuid, bluetooth_device,
+      remote_device, adapter, remote_service_uuid, device_address,
       should_set_low_connection_latency);
 }
 
@@ -145,10 +145,10 @@
         multidevice::RemoteDeviceRef device,
         scoped_refptr<device::BluetoothAdapter> adapter,
         const device::BluetoothUUID remote_service_uuid,
-        device::BluetoothDevice* bluetooth_device,
+        const std::string& device_address,
         bool should_set_low_connection_latency)
     : Connection(device),
-      bluetooth_device_(bluetooth_device),
+      initial_device_address_(device_address),
       should_set_low_connection_latency_(should_set_low_connection_latency),
       adapter_(adapter),
       remote_service_({remote_service_uuid, std::string()}),
@@ -164,6 +164,7 @@
       timer_(std::make_unique<base::OneShotTimer>()),
       sub_status_(SubStatus::DISCONNECTED),
       weak_ptr_factory_(this) {
+  DCHECK(!initial_device_address_.empty());
   adapter_->AddObserver(this);
 }
 
@@ -866,14 +867,13 @@
   // |gatt_connection_|. Unpaired BLE device addresses are ephemeral and are
   // expected to change periodically.
   return gatt_connection_ ? gatt_connection_->GetDeviceAddress()
-                          : bluetooth_device_->GetAddress();
+                          : initial_device_address_;
 }
 
 void BluetoothLowEnergyWeaveClientConnection::GetConnectionRssi(
     base::OnceCallback<void(base::Optional<int32_t>)> callback) {
-  device::BluetoothDevice* device = GetBluetoothDevice();
-
-  if (!device || !device->IsConnected()) {
+  device::BluetoothDevice* bluetooth_device = GetBluetoothDevice();
+  if (!bluetooth_device || !bluetooth_device->IsConnected()) {
     std::move(callback).Run(base::nullopt);
     return;
   }
@@ -881,7 +881,7 @@
   // device::BluetoothDevice has not converted to using a base::OnceCallback
   // instead of a base::Callback, so use a wrapper for now.
   auto callback_holder = base::AdaptCallbackForRepeating(std::move(callback));
-  device->GetConnectionInfo(
+  bluetooth_device->GetConnectionInfo(
       base::Bind(&BluetoothLowEnergyWeaveClientConnection::OnConnectionInfo,
                  weak_ptr_factory_.GetWeakPtr(), callback_holder));
 }
@@ -897,11 +897,6 @@
   std::move(rssi_callback).Run(connection_info.rssi);
 }
 
-device::BluetoothDevice*
-BluetoothLowEnergyWeaveClientConnection::GetBluetoothDevice() {
-  return bluetooth_device_;
-}
-
 device::BluetoothRemoteGattService*
 BluetoothLowEnergyWeaveClientConnection::GetRemoteService() {
   device::BluetoothDevice* bluetooth_device = GetBluetoothDevice();
@@ -935,6 +930,11 @@
   return remote_service->GetCharacteristic(gatt_characteristic);
 }
 
+device::BluetoothDevice*
+BluetoothLowEnergyWeaveClientConnection::GetBluetoothDevice() {
+  return adapter_ ? adapter_->GetDevice(GetDeviceAddress()) : nullptr;
+}
+
 std::string BluetoothLowEnergyWeaveClientConnection::GetReasonForClose() {
   switch (packet_receiver_->GetReasonForClose()) {
     case ReasonForClose::CLOSE_WITHOUT_ERROR:
diff --git a/chromeos/services/secure_channel/ble_weave_client_connection.h b/chromeos/services/secure_channel/ble_weave_client_connection.h
index 236dbec..e6fef2e0 100644
--- a/chromeos/services/secure_channel/ble_weave_client_connection.h
+++ b/chromeos/services/secure_channel/ble_weave_client_connection.h
@@ -61,7 +61,7 @@
         multidevice::RemoteDeviceRef remote_device,
         scoped_refptr<device::BluetoothAdapter> adapter,
         const device::BluetoothUUID remote_service_uuid,
-        device::BluetoothDevice* bluetooth_device,
+        const std::string& device_address,
         bool should_set_low_connection_latency);
     static void SetInstanceForTesting(Factory* factory);
 
@@ -70,7 +70,7 @@
         multidevice::RemoteDeviceRef remote_device,
         scoped_refptr<device::BluetoothAdapter> adapter,
         const device::BluetoothUUID remote_service_uuid,
-        device::BluetoothDevice* bluetooth_device,
+        const std::string& device_address,
         bool should_set_low_connection_latency);
 
    private:
@@ -96,7 +96,7 @@
       multidevice::RemoteDeviceRef remote_device,
       scoped_refptr<device::BluetoothAdapter> adapter,
       const device::BluetoothUUID remote_service_uuid,
-      device::BluetoothDevice* bluetooth_device,
+      const std::string& device_address,
       bool should_set_low_connection_latency);
 
   ~BluetoothLowEnergyWeaveClientConnection() override;
@@ -395,8 +395,11 @@
   // connection.
   std::string GetReasonForClose();
 
-  // The device to which to connect.
-  device::BluetoothDevice* bluetooth_device_;
+  // The initial address of the Bluetooth device to which to connect. The
+  // address of the device we're connecting to may change over time because
+  // public addresses of BLE devices periodically rotate (we don't know its
+  // static private address because we're not paired to it.)
+  const std::string initial_device_address_;
 
   bool should_set_low_connection_latency_;
 
diff --git a/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc b/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
index 8c7b608..81fae7af 100644
--- a/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
+++ b/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
@@ -221,13 +221,13 @@
       multidevice::RemoteDeviceRef remote_device,
       scoped_refptr<device::BluetoothAdapter> adapter,
       const device::BluetoothUUID remote_service_uuid,
-      device::MockBluetoothDevice* mock_bluetooth_device,
+      const std::string& device_address,
       bool should_set_low_connection_latency)
       : BluetoothLowEnergyWeaveClientConnection(
             remote_device,
             adapter,
             remote_service_uuid,
-            mock_bluetooth_device,
+            device_address,
             should_set_low_connection_latency) {}
 
   ~TestBluetoothLowEnergyWeaveClientConnection() override {}
@@ -397,7 +397,8 @@
     std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
         new TestBluetoothLowEnergyWeaveClientConnection(
             remote_device_, adapter_, service_uuid_,
-            mock_bluetooth_device_.get(), should_set_low_connection_latency));
+            kTestRemoteDeviceBluetoothAddress,
+            should_set_low_connection_latency));
 
     EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
     EXPECT_EQ(connection->status(), Connection::Status::DISCONNECTED);
@@ -716,7 +717,8 @@
        CreateAndDestroyWithoutConnectCallDoesntCrash) {
   std::unique_ptr<BluetoothLowEnergyWeaveClientConnection> connection =
       std::make_unique<BluetoothLowEnergyWeaveClientConnection>(
-          remote_device_, adapter_, service_uuid_, mock_bluetooth_device_.get(),
+          remote_device_, adapter_, service_uuid_,
+          kTestRemoteDeviceBluetoothAddress,
           true /* should_set_low_connection_latency */);
 
   connection.reset();
diff --git a/chromeos/strings/chromeos_strings_am.xtb b/chromeos/strings/chromeos_strings_am.xtb
index ac6f8835f..7087d2e 100644
--- a/chromeos/strings/chromeos_strings_am.xtb
+++ b/chromeos/strings/chromeos_strings_am.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">እኔ ላግዝ የምችልበት ምንም ነገር በእርስዎ ማያ ገጽ ላይ አላገኘሁም።
 ማንኛውም ነገር እኔን ለመጠየቅ ማይክሮፎኑን መታ አድርገው ይሞክሩ።</translation>
 <translation id="5222676887888702881">ዘግተህ ውጣ</translation>
+<translation id="5317780077021120954">አስቀምጥ</translation>
 <translation id="5457599981699367932">እንደ እንግዳ ያስሱ</translation>
 <translation id="54609108002486618">የተቀናበረ</translation>
 <translation id="5733345267661125295">እባክዎ አሁን አዲስ የይለፍ ቃል ይምረጡ</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">የመሣሪያው አስተዳዳሪ የእርስዎን እንቅስቃሴ ሊከታተል ይችላል።</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{የአሁኑ ይለፍ ቃልዎ ጊዜው አልፎበታል!}=1{የአሁኑ ይለፍ ቃልዎ ከአንድ ቀን ባነሰ ጊዜ ውስጥ ጊዜው ያልፍበታል!}one{የአሁኑ ይለፍ ቃልዎ ከ# ቀኖች ባነሰ ጊዜ ውስጥ ጊዜው ያልፍበታል!}other{የአሁኑ ይለፍ ቃልዎ ከ# ቀኖች ባነሰ ጊዜ ውስጥ ጊዜው ያልፍበታል!}}</translation>
 <translation id="7658239707568436148">ይቅር</translation>
+<translation id="7690294790491645610">አዲስ የይለፍ ቃል ያረጋግጡ</translation>
 <translation id="808894953321890993">የይለፍ ቃል ለውጥ</translation>
 <translation id="9111102763498581341">ክፈት</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ar.xtb b/chromeos/strings/chromeos_strings_ar.xtb
index d6449de..f338c1b5 100644
--- a/chromeos/strings/chromeos_strings_ar.xtb
+++ b/chromeos/strings/chromeos_strings_ar.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">يتعذَّر عليَّ العثور على شيء مفيد في هذه الشاشة.
 يُرجى تجربة النقر على الميكروفون لمطالبتي بأي شيء.</translation>
 <translation id="5222676887888702881">تسجيل الخروج</translation>
+<translation id="5317780077021120954">حفظ</translation>
 <translation id="5457599981699367932">تصفَّح كزائر</translation>
 <translation id="54609108002486618">تتم إدارته</translation>
 <translation id="5733345267661125295">يُرجى اختيار كلمة مرور جديدة الآن.</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">قد يتمكَّن مشرف الجهاز من مراقبة نشاطك.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{لقد انتهت صلاحية كلمة مرورك الحالية.}=1{ستنتهي صلاحية كلمة مرورك الحالية بعد أقل من يوم واحد.}two{ستنتهي صلاحية كلمة مرورك الحالية بعد أقل من يومين (#).}few{ستنتهي صلاحية كلمة مرورك الحالية بعد أقل من # أيام.}many{ستنتهي صلاحية كلمة مرورك الحالية بعد أقل من # يومًا.}other{ستنتهي صلاحية كلمة مرورك الحالية بعد أقل من # يوم.}}</translation>
 <translation id="7658239707568436148">إلغاء</translation>
+<translation id="7690294790491645610">تأكيد كلمة المرور الجديدة</translation>
 <translation id="808894953321890993">تغيير كلمة المرور</translation>
 <translation id="9111102763498581341">فتح القفل</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_bg.xtb b/chromeos/strings/chromeos_strings_bg.xtb
index a04624b..c2fdb49 100644
--- a/chromeos/strings/chromeos_strings_bg.xtb
+++ b/chromeos/strings/chromeos_strings_bg.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">На екрана ви не намирам нищо, за което да мога да ви помогна.
 Докоснете иконата на микрофон, за да ме попитате каквото и да е.</translation>
 <translation id="5222676887888702881">Изход</translation>
+<translation id="5317780077021120954">Запазване</translation>
 <translation id="5457599981699367932">Сърфиране като гост</translation>
 <translation id="54609108002486618">Управляван</translation>
 <translation id="5733345267661125295">Моля, изберете нова парола сега</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Администраторът на устройството може да е в състояние да наблюдава активността ви.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Валидността на текущата ви парола изтече!}=1{Валидността на текущата ви парола ще изтече след по-малко от един ден!}other{Валидността на текущата ви парола ще изтече след по-малко от # дни!}}</translation>
 <translation id="7658239707568436148">Отказ</translation>
+<translation id="7690294790491645610">Потвърдете новата парола</translation>
 <translation id="808894953321890993">Промяна на паролата</translation>
 <translation id="9111102763498581341">Отключване</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_bn.xtb b/chromeos/strings/chromeos_strings_bn.xtb
index 08cab36d..e1e8008 100644
--- a/chromeos/strings/chromeos_strings_bn.xtb
+++ b/chromeos/strings/chromeos_strings_bn.xtb
@@ -14,6 +14,7 @@
 <translation id="476166673298332917">ডিভাইস প্রশাসক আপনার ব্রাউজিং অ্যাক্টিভিটি পর্যবেক্ষণ করতে পারেন।</translation>
 <translation id="5212543919916444558">আপনাকে সাহায্য করতে পারি এমন কিছুই আপনার স্ক্রিনে আমি খুঁজে পাইনি। আমাকে কিছু জিজ্ঞাসা করতে মাইকে ট্যাপ করুন।</translation>
 <translation id="5222676887888702881">সাইন-আউট করুন</translation>
+<translation id="5317780077021120954">সেভ করুন</translation>
 <translation id="5457599981699367932">অতিথি হিসাবে ব্রাউজ করুন</translation>
 <translation id="54609108002486618">পরিচালিত</translation>
 <translation id="5733345267661125295">এখন একটি নতুন পাসওয়ার্ড বেছে নিন</translation>
@@ -22,6 +23,7 @@
 <translation id="6574601967010742428">ডিভাইস অ্যাডমিনিস্ট্রেটর আপনার অ্যাক্টিভিটি হয়ত মনিটর করতে পারেন।</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{আপনার বর্তমান পাসওয়ার্ডের মেয়াদ শেষ হয়ে গেছে!}=1{আপনার বর্তমান পাসওয়ার্ডের মেয়াদ ১ দিনেরও কম সময়ে শেষ হয়ে যাবে!}one{আপনার বর্তমান পাসওয়ার্ডের মেয়াদ # দিনেরও কম সময়ে শেষ হয়ে যাবে!}other{আপনার বর্তমান পাসওয়ার্ডের মেয়াদ # দিনেরও কম সময়ে শেষ হয়ে যাবে!}}</translation>
 <translation id="7658239707568436148">বাতিল</translation>
+<translation id="7690294790491645610">নতুন পাসওয়ার্ড নিশ্চিত করুন</translation>
 <translation id="808894953321890993">পাসওয়ার্ড পরিবর্তন করুন</translation>
 <translation id="9111102763498581341">আনলক</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ca.xtb b/chromeos/strings/chromeos_strings_ca.xtb
index 30dc3c9..0dadfed 100644
--- a/chromeos/strings/chromeos_strings_ca.xtb
+++ b/chromeos/strings/chromeos_strings_ca.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">No he trobat res a la pantalla amb què et pugui ajudar.
 Toca el micròfon per demanar-me qualsevol cosa.</translation>
 <translation id="5222676887888702881">Tanca la sessió</translation>
+<translation id="5317780077021120954">Desa</translation>
 <translation id="5457599981699367932">Navega com a convidat</translation>
 <translation id="54609108002486618">Gestionat</translation>
 <translation id="5733345267661125295">Tria ja una contrasenya nova</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Pot ser que l'administrador del dispositiu pugui supervisar la teva activitat.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{La teva contrasenya actual ha caducat.}=1{La teva contrasenya actual caducarà d'aquí a menys d'un dia.}other{La teva contrasenya actual caducarà d'aquí a menys de # dies.}}</translation>
 <translation id="7658239707568436148">Cancel·la</translation>
+<translation id="7690294790491645610">Confirma la contrasenya nova</translation>
 <translation id="808894953321890993">Canvia la contrasenya</translation>
 <translation id="9111102763498581341">Desbloqueja</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_cs.xtb b/chromeos/strings/chromeos_strings_cs.xtb
index b848191..6dc11dd 100644
--- a/chromeos/strings/chromeos_strings_cs.xtb
+++ b/chromeos/strings/chromeos_strings_cs.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Na obrazovce nevidím nic, s čím vám můžu pomoct.
 Zkuste klepnout na mikrofon a zeptat se.</translation>
 <translation id="5222676887888702881">Odhlásit se</translation>
+<translation id="5317780077021120954">Uložit</translation>
 <translation id="5457599981699367932">Použít jako host</translation>
 <translation id="54609108002486618">Spravovaný</translation>
 <translation id="5733345267661125295">Nastavte si nové heslo</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Administrátor zařízení může mít možnost sledovat vaši aktivitu.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Platnost vašeho aktuálního hesla vypršela.}=1{Platnost vašeho aktuálního hesla vyprší za méně než jeden den.}few{Platnost vašeho aktuálního hesla vyprší za méně než # dny.}many{Platnost vašeho aktuálního hesla vyprší za méně než # dne.}other{Platnost vašeho aktuálního hesla vyprší za méně než # dní.}}</translation>
 <translation id="7658239707568436148">Zrušit</translation>
+<translation id="7690294790491645610">Potvrďte nové heslo</translation>
 <translation id="808894953321890993">Změnit heslo</translation>
 <translation id="9111102763498581341">Odemknout</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_da.xtb b/chromeos/strings/chromeos_strings_da.xtb
index d9ab15a..ae7248f 100644
--- a/chromeos/strings/chromeos_strings_da.xtb
+++ b/chromeos/strings/chromeos_strings_da.xtb
@@ -14,6 +14,7 @@
 <translation id="476166673298332917">Administratoren af enheden kan overvåge din browseraktivitet.</translation>
 <translation id="5212543919916444558">Jeg kan ikke finde noget på skærmen, jeg kan hjælpe med. Prøv at trykke på mikrofonen for at spørge mig om noget.</translation>
 <translation id="5222676887888702881">Log ud</translation>
+<translation id="5317780077021120954">Gem</translation>
 <translation id="5457599981699367932">Anvend som gæst</translation>
 <translation id="54609108002486618">Administreret</translation>
 <translation id="5733345267661125295">Vælg en ny adgangskode nu</translation>
@@ -22,6 +23,7 @@
 <translation id="6574601967010742428">Enhedsadministratoren kan muligvis overvåge din aktivitet.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Din nuværende adgangskode er udløbet.}=1{Der er mindre end én dag til din nuværende adgangskode udløber.}one{Der er mindre end # dag til din nuværende adgangskode udløber.}other{Der er mindre end # dage til din nuværende adgangskode udløber.}}</translation>
 <translation id="7658239707568436148">Annuller</translation>
+<translation id="7690294790491645610">Bekræft den nye adgangskode</translation>
 <translation id="808894953321890993">Skift adgangskode</translation>
 <translation id="9111102763498581341">Lås op</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_de.xtb b/chromeos/strings/chromeos_strings_de.xtb
index 380bb353..f854ad6 100644
--- a/chromeos/strings/chromeos_strings_de.xtb
+++ b/chromeos/strings/chromeos_strings_de.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Ich finde nichts auf deinem Display, wobei ich dir helfen könnte.
 Tippe auf das Mikrofon und sag mir, was du wissen möchtest.</translation>
 <translation id="5222676887888702881">Abmelden</translation>
+<translation id="5317780077021120954">Speichern</translation>
 <translation id="5457599981699367932">Als Gast nutzen</translation>
 <translation id="54609108002486618">Verwaltet</translation>
 <translation id="5733345267661125295">Bitte wählen Sie jetzt ein neues Passwort</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Der Geräteadministrator kann Ihre Aktivitäten möglicherweise überwachen.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Ihr aktuelles Passwort ist abgelaufen.}=1{Ihr aktuelles Passwort läuft in weniger als einem Tag ab.}other{Ihr aktuelles Passwort läuft in weniger als # Tagen ab.}}</translation>
 <translation id="7658239707568436148">Abbrechen</translation>
+<translation id="7690294790491645610">Neues Passwort bestätigen</translation>
 <translation id="808894953321890993">Passwort ändern</translation>
 <translation id="9111102763498581341">Entsperren</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_el.xtb b/chromeos/strings/chromeos_strings_el.xtb
index c4d6715..8374cb71 100644
--- a/chromeos/strings/chromeos_strings_el.xtb
+++ b/chromeos/strings/chromeos_strings_el.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Δεν βρέθηκε κάτι με το οποίο να μπορώ να σας βοηθήσω στην οθόνη σας.
 Πατήστε στο μικρόφωνο για να ρωτήσετε οτιδήποτε θέλετε.</translation>
 <translation id="5222676887888702881">Έξοδος</translation>
+<translation id="5317780077021120954">Αποθήκευση</translation>
 <translation id="5457599981699367932">Περιήγηση ως επισκέπτης</translation>
 <translation id="54609108002486618">Διαχειριζόμενη</translation>
 <translation id="5733345267661125295">Επιλέξτε τώρα έναν νέο κωδικό πρόσβασης</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Ο διαχειριστής της συσκευής ίσως έχει τη δυνατότητα να παρακολουθεί τη δραστηριότητά σας.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Ο τρέχων κωδικός πρόσβασής σας έληξε!}=1{Ο τρέχων κωδικός πρόσβασής σας θα λήξει σε λιγότερο από μία ημέρα!}other{Ο τρέχων κωδικός πρόσβασής σας θα λήξει σε λιγότερο από # ημέρες!}}</translation>
 <translation id="7658239707568436148">Ακύρωση</translation>
+<translation id="7690294790491645610">Επιβεβαίωση νέου κωδικού πρόσβασης</translation>
 <translation id="808894953321890993">Αλλαγή κωδικού πρόσβασης</translation>
 <translation id="9111102763498581341">Ξεκλείδωμα</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_en-GB.xtb b/chromeos/strings/chromeos_strings_en-GB.xtb
index 3ce8be5..99ee7ea 100644
--- a/chromeos/strings/chromeos_strings_en-GB.xtb
+++ b/chromeos/strings/chromeos_strings_en-GB.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">I can't find anything on your screen that I can help with.
 Try tapping the mic to ask me anything.</translation>
 <translation id="5222676887888702881">Sign out</translation>
+<translation id="5317780077021120954">Save</translation>
 <translation id="5457599981699367932">Browse as Guest</translation>
 <translation id="54609108002486618">Managed</translation>
 <translation id="5733345267661125295">Please choose a new password now</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">The device administrator may be able to monitor your activity.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Your current password has expired.}=1{Your current password will expire in less than one day.}other{Your current password will expire in less than # days.}}</translation>
 <translation id="7658239707568436148">Cancel</translation>
+<translation id="7690294790491645610">Confirm new password</translation>
 <translation id="808894953321890993">Change password</translation>
 <translation id="9111102763498581341">Unlock</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_es-419.xtb b/chromeos/strings/chromeos_strings_es-419.xtb
index 7c8fac3d..3af083b 100644
--- a/chromeos/strings/chromeos_strings_es-419.xtb
+++ b/chromeos/strings/chromeos_strings_es-419.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">No hay nada en la pantalla con lo que pueda ayudarte.
 Intenta presionar el micrófono para hacerme una pregunta.</translation>
 <translation id="5222676887888702881">Salir</translation>
+<translation id="5317780077021120954">Guardar</translation>
 <translation id="5457599981699367932">Navegar como invitado</translation>
 <translation id="54609108002486618">Gestionado</translation>
 <translation id="5733345267661125295">Elige una contraseña nueva ahora</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Es posible que el administrador del dispositivo supervise tu actividad.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Caducó tu contraseña actual.}=1{Tu contraseña actual caducará en menos de un día.}other{Tu contraseña actual caducará en menos de # días.}}</translation>
 <translation id="7658239707568436148">Cancelar</translation>
+<translation id="7690294790491645610">Confirmar contraseña nueva</translation>
 <translation id="808894953321890993">Cambiar contraseña</translation>
 <translation id="9111102763498581341">Desbloquear</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_es.xtb b/chromeos/strings/chromeos_strings_es.xtb
index 747d5e6..96537ec 100644
--- a/chromeos/strings/chromeos_strings_es.xtb
+++ b/chromeos/strings/chromeos_strings_es.xtb
@@ -14,6 +14,7 @@
 <translation id="476166673298332917">Es posible que el administrador del dispositivo supervise tu actividad de navegación.</translation>
 <translation id="5212543919916444558">No encuentro nada en la pantalla con lo que pueda ayudarte. Toca el micrófono para pedirme algo.</translation>
 <translation id="5222676887888702881">Cerrar sesión</translation>
+<translation id="5317780077021120954">Guardar</translation>
 <translation id="5457599981699367932">Navegar como invitado</translation>
 <translation id="54609108002486618">Administrado</translation>
 <translation id="5733345267661125295">Elige una contraseña nueva ahora</translation>
@@ -22,6 +23,7 @@
 <translation id="6574601967010742428">Es posible que el administrador del dispositivo supervise tu actividad.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Tu contraseña actual ha caducado.}=1{Tu contraseña actual caducará en menos de 1 día.}other{Tu contraseña actual caducará en # días.}}</translation>
 <translation id="7658239707568436148">Cancelar</translation>
+<translation id="7690294790491645610">Confirma la nueva contraseña</translation>
 <translation id="808894953321890993">Cambiar contraseña</translation>
 <translation id="9111102763498581341">Desbloquear</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_et.xtb b/chromeos/strings/chromeos_strings_et.xtb
index 86943a3..24407c53 100644
--- a/chromeos/strings/chromeos_strings_et.xtb
+++ b/chromeos/strings/chromeos_strings_et.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Ma ei leia teie ekraanilt midagi, mille puhul saaksin aidata.
 Kui soovite mulle küsimuse esitada, puudutage mikrofoni.</translation>
 <translation id="5222676887888702881">Logi välja</translation>
+<translation id="5317780077021120954">Salvesta</translation>
 <translation id="5457599981699367932">Sirvi külalisena</translation>
 <translation id="54609108002486618">Hallatud</translation>
 <translation id="5733345267661125295">Valige nüüd uus parool</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Seadme haldur võib saada teie tegevust jälgida.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Teie praegune parool on aegunud.}=1{Teie praegune parool aegub vähem kui ühe päeva pärast.}other{Teie praegune parool aegub vähem kui # päeva pärast.}}</translation>
 <translation id="7658239707568436148">Tühista</translation>
+<translation id="7690294790491645610">Kinnitage uus parool</translation>
 <translation id="808894953321890993">Muuda parooli</translation>
 <translation id="9111102763498581341">Ava lukk</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_fa.xtb b/chromeos/strings/chromeos_strings_fa.xtb
index 99bba6f..9b88c2ea 100644
--- a/chromeos/strings/chromeos_strings_fa.xtb
+++ b/chromeos/strings/chromeos_strings_fa.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">در صفحه‌‌تان چیزی پیدا نمی‌کنم تا درمورد آن کمکتان کنم.
 برای درخواست از من، روی میکروفون ضربه بزنید.</translation>
 <translation id="5222676887888702881">خروج از سیستم</translation>
+<translation id="5317780077021120954">ذخیره</translation>
 <translation id="5457599981699367932">مرور کردن به‌عنوان یک مهمان</translation>
 <translation id="54609108002486618">مدیریت شده</translation>
 <translation id="5733345267661125295">لطفاً اکنون گذرواژه جدیدی انتخاب کنید</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">سرپرست دستگاه ممکن است بر فعالیتتان نظارت داشته باشد.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{گذرواژه کنونی‌تان منقضی شده است!}=1{گذرواژه کنونی‌تان کمتر از یک روز دیگر منقضی خواهد شد!}one{گذرواژه کنونی‌تان کمتر از # روز دیگر منقضی خواهد شد!}other{گذرواژه کنونی‌تان کمتر از # روز دیگر منقضی خواهد شد!}}</translation>
 <translation id="7658239707568436148">لغو</translation>
+<translation id="7690294790491645610">تأیید گذرواژه جدید</translation>
 <translation id="808894953321890993">تغییر گذرواژه</translation>
 <translation id="9111102763498581341">باز کردن قفل</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_fi.xtb b/chromeos/strings/chromeos_strings_fi.xtb
index a06901f..874a78c0 100644
--- a/chromeos/strings/chromeos_strings_fi.xtb
+++ b/chromeos/strings/chromeos_strings_fi.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">En löydä näytöltä mitään, minkä kanssa voisin auttaa sinua.
 Napauta mikrofonia, niin voit kysyä mitä haluat.</translation>
 <translation id="5222676887888702881">Kirjaudu ulos</translation>
+<translation id="5317780077021120954">Tallenna</translation>
 <translation id="5457599981699367932">Selaa vierailijana</translation>
 <translation id="54609108002486618">Hallinnoitu</translation>
 <translation id="5733345267661125295">Valitse nyt uusi salasana</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Laitteen järjestelmänvalvoja voi ehkä seurata toimintaasi.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Salasanasi on vanhentunut.}=1{Salasanasi vanhenee alle päivän päästä.}other{Salasanasi vanhenee alle # päivän päästä.}}</translation>
 <translation id="7658239707568436148">Peruuta</translation>
+<translation id="7690294790491645610">Vahvista uusi salasana</translation>
 <translation id="808894953321890993">Vaihda salasana</translation>
 <translation id="9111102763498581341">Poista lukitus</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_fil.xtb b/chromeos/strings/chromeos_strings_fil.xtb
index ef08d8e..fdbf6511 100644
--- a/chromeos/strings/chromeos_strings_fil.xtb
+++ b/chromeos/strings/chromeos_strings_fil.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Wala akong makitang kahit ano sa iyong screen kung saan ako makakatulong.
 Subukang i-tap ang mikropono para humiling sa akin ng kahit ano.</translation>
 <translation id="5222676887888702881">Mag-sign out</translation>
+<translation id="5317780077021120954">I-save</translation>
 <translation id="5457599981699367932">Mag-browse bilang Bisita</translation>
 <translation id="54609108002486618">Pinamamahalaan</translation>
 <translation id="5733345267661125295">Pumili ng bagong password ngayon</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Maaaring masubaybayan ng administrator ng device ang iyong aktibidad.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Nag-expire na ang iyong kasalukuyang password!}=1{Mag-e-expire sa loob ng wala pang isang araw ang iyong kasalukuyang password!}one{Mag-e-expire sa loob ng wala pang # araw ang iyong kasalukuyang password!}other{Mag-e-expire sa loob ng wala pang # na araw ang iyong kasalukuyang password!}}</translation>
 <translation id="7658239707568436148">Kanselahin</translation>
+<translation id="7690294790491645610">Kumpirmahin ang bagong password</translation>
 <translation id="808894953321890993">Palitan ang password</translation>
 <translation id="9111102763498581341">I-unlock</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_fr.xtb b/chromeos/strings/chromeos_strings_fr.xtb
index f5eae8d..7f09188 100644
--- a/chromeos/strings/chromeos_strings_fr.xtb
+++ b/chromeos/strings/chromeos_strings_fr.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Je ne parviens pas à détecter d'éléments sur votre écran pour lesquels je peux vous aider.
 Veuillez appuyer sur le micro pour me poser une question.</translation>
 <translation id="5222676887888702881">Déconnexion</translation>
+<translation id="5317780077021120954">Enregistrer</translation>
 <translation id="5457599981699367932">Naviguer en tant qu'invité</translation>
 <translation id="54609108002486618">Géré</translation>
 <translation id="5733345267661125295">Veuillez choisir bientôt un nouveau mot de passe</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Il est possible que l'administrateur de l'appareil puisse surveiller votre activité.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Votre mot de passe actuel est arrivé à expiration !}=1{Votre mot de passe actuel arrive à expiration dans moins d'un jour !}one{Votre mot de passe actuel arrive à expiration dans moins de # jour !}other{Votre mot de passe actuel arrive à expiration dans moins de # jours !}}</translation>
 <translation id="7658239707568436148">Annuler</translation>
+<translation id="7690294790491645610">Confirmer le nouveau mot de passe</translation>
 <translation id="808894953321890993">Modifier le mot de passe</translation>
 <translation id="9111102763498581341">Déverrouiller</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_gu.xtb b/chromeos/strings/chromeos_strings_gu.xtb
index 0083e4a7..fadc4fbc 100644
--- a/chromeos/strings/chromeos_strings_gu.xtb
+++ b/chromeos/strings/chromeos_strings_gu.xtb
@@ -14,6 +14,7 @@
 <translation id="476166673298332917">ડિવાઇસ વ્યવસ્થાપક તમારી બ્રાઉઝિંગ પ્રવૃત્તિને મોનિટર કરી શકે છે.</translation>
 <translation id="5212543919916444558">મને તમારી સ્ક્રીન પર એવી કોઈપણ વસ્તુ મળી નથી કે જેમાં હું તમારી સહાય કરી શકું. મને કંઈપણ પૂછવા માટે માઇકને ટૅપ કરી જુઓ.</translation>
 <translation id="5222676887888702881">સાઇન આઉટ</translation>
+<translation id="5317780077021120954">સાચવો</translation>
 <translation id="5457599981699367932">અતિથિ તરીકે બ્રાઉઝ કરો</translation>
 <translation id="54609108002486618">મેનેજ કરેલું</translation>
 <translation id="5733345267661125295">કૃપા કરીને હવે નવો પાસવર્ડ પસંદ કરો</translation>
@@ -22,6 +23,7 @@
 <translation id="6574601967010742428">ડિવાઇસના વ્યવસ્થાપક તમારી પ્રવૃત્તિને મૉનિટર કરી શકે છે.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{તમારા હાલના પાસવર્ડની સમયસીમા સમાપ્ત થઈ ગઈ છે!}=1{તમારો વર્તમાન પાસવર્ડ સમાપ્ત થવામાં એક દિવસ કરતાં પણ ઓછો સમય બાકી!}one{તમારો વર્તમાન પાસવર્ડ સમાપ્ત થવામાં # દિવસ કરતાં પણ ઓછો સમય બાકી!}other{તમારો વર્તમાન પાસવર્ડ સમાપ્ત થવામાં # દિવસ કરતાં પણ ઓછો સમય બાકી!}}</translation>
 <translation id="7658239707568436148">રદ કરો</translation>
+<translation id="7690294790491645610">નવા પાસવર્ડની પુષ્ટિ કરો</translation>
 <translation id="808894953321890993">પાસવર્ડ બદલો</translation>
 <translation id="9111102763498581341">અનલોક કરો</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_hi.xtb b/chromeos/strings/chromeos_strings_hi.xtb
index e14be1f..0adfca27 100644
--- a/chromeos/strings/chromeos_strings_hi.xtb
+++ b/chromeos/strings/chromeos_strings_hi.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">मुझे आपकी स्क्रीन पर ऐसा कुछ नहीं मिला जिसमें मैं आपकी सहायता कर पाऊं.
 मुझे कुछ पूछने के लिए माइक टैप करके देखें.</translation>
 <translation id="5222676887888702881">साइन आउट करें</translation>
+<translation id="5317780077021120954">सेव करें</translation>
 <translation id="5457599981699367932">मेहमान के रूप में ब्राउज़ करें</translation>
 <translation id="54609108002486618">प्रबंधित</translation>
 <translation id="5733345267661125295">कृपया अभी कोई नया पासवर्ड चुनें</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">डिवाइस का एडमिन आपकी गतिविधि पर नज़र रख सकता है.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{आपके मौजूदा पासवर्ड की समय-सीमा खत्म हो चुकी है!}=1{आपके मौजूदा पासवर्ड की समय-सीमा एक दिन से कम समय में खत्म हो जाएगी!}one{आपके मौजूदा पासवर्ड की समय-सीमा # दिन से कम समय में खत्म हो जाएगी!}other{आपके मौजूदा पासवर्ड की समय-सीमा # दिन से कम समय में खत्म हो जाएगी!}}</translation>
 <translation id="7658239707568436148">अभी नहीं</translation>
+<translation id="7690294790491645610">नए पासवर्ड की पुष्टि करें</translation>
 <translation id="808894953321890993">पासवर्ड बदलें</translation>
 <translation id="9111102763498581341">अनलॉक करें</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_hr.xtb b/chromeos/strings/chromeos_strings_hr.xtb
index e9b5872a..e64ec398 100644
--- a/chromeos/strings/chromeos_strings_hr.xtb
+++ b/chromeos/strings/chromeos_strings_hr.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Na zaslonu ne mogu pronaći ništa u vezi s čime mogu pružiti pomoć.
 Dodirnite mikrofon da biste mi dali glasovnu naredbu.</translation>
 <translation id="5222676887888702881">Odjava</translation>
+<translation id="5317780077021120954">Spremi</translation>
 <translation id="5457599981699367932">Pregledavaj kao gost</translation>
 <translation id="54609108002486618">Upravljani</translation>
 <translation id="5733345267661125295">Odaberite novu zaporku sada</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Administrator uređaja možda može nadzirati vašu aktivnost.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Vaša je trenutačna zaporka istekla!}=1{Vaša trenutačna zaporka isteći će za manje od jednog dana!}one{Vaša trenutačna zaporka isteći će za manje od # dana!}few{Vaša trenutačna zaporka isteći će za manje od # dana!}other{Vaša trenutačna zaporka isteći će za manje od # dana!}}</translation>
 <translation id="7658239707568436148">Odustani</translation>
+<translation id="7690294790491645610">Potvrdite novu zaporku</translation>
 <translation id="808894953321890993">Promjena zaporke</translation>
 <translation id="9111102763498581341">Otključaj</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_hu.xtb b/chromeos/strings/chromeos_strings_hu.xtb
index 2016ef60..bee61c7 100644
--- a/chromeos/strings/chromeos_strings_hu.xtb
+++ b/chromeos/strings/chromeos_strings_hu.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Nem találok semmi olyat a képernyőn, amellyel kapcsolatban segíthetnék.
 Koppintson a mikrofonra, és kérdezzen tőlem.</translation>
 <translation id="5222676887888702881">Kijelentkezés</translation>
+<translation id="5317780077021120954">Mentés</translation>
 <translation id="5457599981699367932">Böngészés vendégként</translation>
 <translation id="54609108002486618">Kezelt</translation>
 <translation id="5733345267661125295">Válasszon új jelszót most</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Lehetséges, hogy tevékenységeit megfigyelheti az eszköz adminisztrátora.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Jelenlegi jelszava lejárt!}=1{Jelenlegi jelszava 1 napon belül lejár!}other{Jelenlegi jelszava # napon belül lejár!}}</translation>
 <translation id="7658239707568436148">Mégse</translation>
+<translation id="7690294790491645610">Új jelszó megerősítése</translation>
 <translation id="808894953321890993">Jelszó módosítása</translation>
 <translation id="9111102763498581341">Feloldás</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_id.xtb b/chromeos/strings/chromeos_strings_id.xtb
index 73a1294..adcbebf 100644
--- a/chromeos/strings/chromeos_strings_id.xtb
+++ b/chromeos/strings/chromeos_strings_id.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Di layar tidak ada apa pun yang membutuhkan bantuan saya.
 Tap mikrofon untuk bertanya apa saja.</translation>
 <translation id="5222676887888702881">Logout</translation>
+<translation id="5317780077021120954">Simpan</translation>
 <translation id="5457599981699367932">Login sebagai Tamu</translation>
 <translation id="54609108002486618">Terkelola</translation>
 <translation id="5733345267661125295">Harap pilih sandi baru sekarang</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Administrator perangkat mungkin dapat memantau aktivitas Anda.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Sandi Anda saat ini sudah tidak berlaku.}=1{Masa berlaku sandi Anda saat ini akan berakhir dalam waktu kurang dari sehari.}other{Masa berlaku sandi Anda akan berakhir dalam # hari.}}</translation>
 <translation id="7658239707568436148">Batal</translation>
+<translation id="7690294790491645610">Konfirmasi sandi baru</translation>
 <translation id="808894953321890993">Ubah sandi</translation>
 <translation id="9111102763498581341">Buka kunci</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_it.xtb b/chromeos/strings/chromeos_strings_it.xtb
index eba7606..a59b3d9 100644
--- a/chromeos/strings/chromeos_strings_it.xtb
+++ b/chromeos/strings/chromeos_strings_it.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Non riesco a trovare nulla sullo schermo per cui poterti essere utile.
 Prova a toccare il microfono per chiedermi qualcosa.</translation>
 <translation id="5222676887888702881">Esci</translation>
+<translation id="5317780077021120954">Salva</translation>
 <translation id="5457599981699367932">Esplora come ospite</translation>
 <translation id="54609108002486618">Gestito</translation>
 <translation id="5733345267661125295">Scegli una nuova password ora</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">L'amministratore del dispositivo potrebbe essere in grado di monitorare le tue attività.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{La tua attuale password è scaduta.}=1{La tua attuale password scadrà tra meno di un giorno.}other{La tua attuale password scadrà tra meno di # giorni.}}</translation>
 <translation id="7658239707568436148">Annulla</translation>
+<translation id="7690294790491645610">Conferma nuova password</translation>
 <translation id="808894953321890993">Cambia password</translation>
 <translation id="9111102763498581341">Sblocca</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_iw.xtb b/chromeos/strings/chromeos_strings_iw.xtb
index 67e892b..9e0466a 100644
--- a/chromeos/strings/chromeos_strings_iw.xtb
+++ b/chromeos/strings/chromeos_strings_iw.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">לא מצאתי במסך משהו שאוכל לעזור בו.
 אפשר להקיש על המיקרופון כדי לשאול אותי משהו.</translation>
 <translation id="5222676887888702881">יציאה</translation>
+<translation id="5317780077021120954">שמור</translation>
 <translation id="5457599981699367932">גלוש כאורח</translation>
 <translation id="54609108002486618">מנוהל</translation>
 <translation id="5733345267661125295">צריך לבחור עכשיו סיסמה חדשה</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">למנהל המכשיר תהיה יכולת לעקוב אחרי הפעילות שלך.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{פג התוקף של הסיסמה הנוכחית!}=1{התוקף של הסיסמה הנוכחית יפוג תוך פחות מיום!}two{התוקף של הסיסמה הנוכחית יפוג תוך פחות מיומיים!}many{התוקף של הסיסמה הנוכחית יפוג תוך פחות מ-# ימים!}other{התוקף של הסיסמה הנוכחית יפוג תוך פחות מ-# ימים!}}</translation>
 <translation id="7658239707568436148">ביטול</translation>
+<translation id="7690294790491645610">אישור הסיסמה החדשה</translation>
 <translation id="808894953321890993">שינוי הסיסמה</translation>
 <translation id="9111102763498581341">בטל נעילה</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ja.xtb b/chromeos/strings/chromeos_strings_ja.xtb
index c1115b2..0a50b825 100644
--- a/chromeos/strings/chromeos_strings_ja.xtb
+++ b/chromeos/strings/chromeos_strings_ja.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">画面からはサポートできる内容が見つかりませんでした。
 マイクをタップして質問してみてください。</translation>
 <translation id="5222676887888702881">ログアウト</translation>
+<translation id="5317780077021120954">保存</translation>
 <translation id="5457599981699367932">ゲストとしてブラウジング</translation>
 <translation id="54609108002486618">管理</translation>
 <translation id="5733345267661125295">早急に新しいパスワードを選択してください</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">デバイスの管理者によりアクティビティが監視される可能性があります。</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{現在のパスワードは有効期限が切れています。}=1{現在のパスワードは 1 日以内に有効期限が切れます。}other{現在のパスワードは # 日以内に有効期限が切れます。}}</translation>
 <translation id="7658239707568436148">キャンセル</translation>
+<translation id="7690294790491645610">新しいパスワードの確認入力</translation>
 <translation id="808894953321890993">パスワードの変更</translation>
 <translation id="9111102763498581341">ロック解除</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_kn.xtb b/chromeos/strings/chromeos_strings_kn.xtb
index 7a2cb1b..e1796b7 100644
--- a/chromeos/strings/chromeos_strings_kn.xtb
+++ b/chromeos/strings/chromeos_strings_kn.xtb
@@ -14,6 +14,7 @@
 <translation id="476166673298332917">ಈ ಡಿವೈಸ್‌ ನಿರ್ವಾಹಕರು ನಿಮ್ಮ ಬ್ರೌಸಿಂಗ್‌ ಚಟುವಟಿಕೆಯನ್ನು ಗಮನಿಸಬಹುದು.</translation>
 <translation id="5212543919916444558">ನಾನು ನಿಮಗೆ ಸಹಾಯ ಮಾಡಬಹುದಾದ ಯಾವುದೇ ವಿಷಯ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ನನಗೆ ಕಾಣಿಸಿಲ್ಲ. ನನ್ನ ಬಳಿ ಏನಾದರೂ ಕೇಳಬೇಕಿದ್ದರೆ, ಮೈಕ್ ಟ್ಯಾಪ್ ಮಾಡಿ.</translation>
 <translation id="5222676887888702881">ಸೈನ್ ಔಟ್</translation>
+<translation id="5317780077021120954">ಉಳಿಸು</translation>
 <translation id="5457599981699367932">ಅತಿಥಿಯಾಗಿ ಬ್ರೌಸ್ ಮಾಡಿ</translation>
 <translation id="54609108002486618">ನಿರ್ವಹಿಸಲಾಗಿದೆ</translation>
 <translation id="5733345267661125295">ಈಗಲೇ ಹೊಸ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ</translation>
@@ -22,6 +23,7 @@
 <translation id="6574601967010742428">ನಿಮ್ಮ ಚಟುವಟಿಕೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಸಾಧನ ನಿರ್ವಾಹಕರಿಗೆ ಸಾಧ್ಯವಾಗಬಹುದು.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಪಾಸ್‌ವರ್ಡ್ ಅವಧಿ ಮೀರಿದೆ!}=1{ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಪಾಸ್‌ವರ್ಡ್ ಒಂದು ದಿನಕ್ಕಿಂತಲೂ ಕಡಿಮೆ ಅವಧಿಯಲ್ಲಿ ಅವಧಿ ಮೀರುತ್ತದೆ!}one{ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಪಾಸ್‌ವರ್ಡ್ # ದಿನಗಳಿಗಿಂತಲೂ ಕಡಿಮೆ ಅವಧಿಯಲ್ಲಿ ಅವಧಿ ಮೀರುತ್ತದೆ!}other{ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಪಾಸ್‌ವರ್ಡ್ # ದಿನಗಳಿಗಿಂತಲೂ ಕಡಿಮೆ ಅವಧಿಯಲ್ಲಿ ಅವಧಿ ಮೀರುತ್ತದೆ!}}</translation>
 <translation id="7658239707568436148">ರದ್ದುಮಾಡಿ</translation>
+<translation id="7690294790491645610">ಹೊಸ ಪಾಸ್‌ವರ್ಡ್ ಖಚಿತಪಡಿಸಿ</translation>
 <translation id="808894953321890993">ಪಾಸ್‌ವರ್ಡ್ ಬದಲಿಸಿ</translation>
 <translation id="9111102763498581341">ಅನ್‌ಲಾಕ್</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ko.xtb b/chromeos/strings/chromeos_strings_ko.xtb
index 4a0c92f5..d5d96ea7 100644
--- a/chromeos/strings/chromeos_strings_ko.xtb
+++ b/chromeos/strings/chromeos_strings_ko.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">화면에 도움을 드릴 수 있는 항목이 보이지 않습니다.
 마이크를 탭하여 무엇이든지 물어보세요.</translation>
 <translation id="5222676887888702881">로그아웃</translation>
+<translation id="5317780077021120954">저장</translation>
 <translation id="5457599981699367932">게스트로 로그인</translation>
 <translation id="54609108002486618">관리</translation>
 <translation id="5733345267661125295">지금 새 비밀번호를 선택해 주세요.</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">기기 관리자가 내 활동을 모니터링할 수도 있습니다.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{현재 비밀번호가 만료되었습니다.}=1{1일 이내에 현재 비밀번호가 만료됩니다.}other{#일 이내에 현재 비밀번호가 만료됩니다.}}</translation>
 <translation id="7658239707568436148">취소</translation>
+<translation id="7690294790491645610">새 비밀번호 확인</translation>
 <translation id="808894953321890993">비밀번호 변경</translation>
 <translation id="9111102763498581341">잠금 해제</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_lt.xtb b/chromeos/strings/chromeos_strings_lt.xtb
index 466565a..44df8f90 100644
--- a/chromeos/strings/chromeos_strings_lt.xtb
+++ b/chromeos/strings/chromeos_strings_lt.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Ekrane nieko nerasta, dėl ko galėtume jums padėti.
 Palieskite mikrofoną, kad užduotumėte reikiamus klausimus.</translation>
 <translation id="5222676887888702881">Atsijungti</translation>
+<translation id="5317780077021120954">Išsaugoti</translation>
 <translation id="5457599981699367932">Naršyti kaip svečiui</translation>
 <translation id="54609108002486618">Valdoma</translation>
 <translation id="5733345267661125295">Naują slaptažodį pasirinkite dabar</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Įrenginio administratorius gali stebėti jūsų veiklą.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Jūsų dabartinis slaptažodis nebegalioja!}=1{Jūsų dabartinis slaptažodis baigs galioti po mažiau nei vienos dienos!}one{Jūsų dabartinis slaptažodis baigs galioti po mažiau nei # dienos!}few{Jūsų dabartinis slaptažodis baigs galioti po mažiau nei # dienų!}many{Jūsų dabartinis slaptažodis baigs galioti po mažiau nei # dienos!}other{Jūsų dabartinis slaptažodis baigs galioti po mažiau nei # dienų!}}</translation>
 <translation id="7658239707568436148">Atšaukti</translation>
+<translation id="7690294790491645610">Patvirtinkite naują slaptažodį</translation>
 <translation id="808894953321890993">Keisti slaptažodį</translation>
 <translation id="9111102763498581341">Atrakinti</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_lv.xtb b/chromeos/strings/chromeos_strings_lv.xtb
index 708c62f5..5090c63 100644
--- a/chromeos/strings/chromeos_strings_lv.xtb
+++ b/chromeos/strings/chromeos_strings_lv.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Ekrānā neredzu neko, ar ko varētu palīdzēt.
 Pieskarieties mikrofonam, lai uzdotu man jautājumu.</translation>
 <translation id="5222676887888702881">Izrakstīties</translation>
+<translation id="5317780077021120954">Saglabāt</translation>
 <translation id="5457599981699367932">Pārlūkot kā viesim</translation>
 <translation id="54609108002486618">Pārvaldītais</translation>
 <translation id="5733345267661125295">Lūdzu, izvēlieties jaunu paroli jau tagad.</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Jūsu darbības var pārraudzīt ierīces pārvaldnieks.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Jūsu pašreizējai parolei ir beidzies derīguma termiņš.}=1{Jūsu pašreizējās paroles derīguma termiņš beigsies pēc mazāk nekā vienas dienas.}zero{Jūsu pašreizējās paroles derīguma termiņš beigsies pēc mazāk nekā # dienām.}one{Jūsu pašreizējās paroles derīguma termiņš beigsies pēc mazāk nekā # dienas.}other{Jūsu pašreizējās paroles derīguma termiņš beigsies pēc mazāk nekā # dienām.}}</translation>
 <translation id="7658239707568436148">Atcelt</translation>
+<translation id="7690294790491645610">Apstipriniet jauno paroli</translation>
 <translation id="808894953321890993">Mainīt paroli</translation>
 <translation id="9111102763498581341">Atbloķēt</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ml.xtb b/chromeos/strings/chromeos_strings_ml.xtb
index 71416d51..5849449 100644
--- a/chromeos/strings/chromeos_strings_ml.xtb
+++ b/chromeos/strings/chromeos_strings_ml.xtb
@@ -14,6 +14,7 @@
 <translation id="476166673298332917">ഉപകരണ അഡ്‌മിൻ നിങ്ങളുടെ ബ്രൗസിംഗ് ആക്‌റ്റിവിറ്റി നിരീക്ഷിച്ചേക്കാം.</translation>
 <translation id="5212543919916444558">എനിക്ക് സഹായിക്കാനാവുന്ന ഒന്നും നിങ്ങളുടെ സ്‌ക്രീനിൽ കണ്ടെത്താനാവുന്നില്ല. എന്നോട് എന്തെങ്കിലും ആവശ്യപ്പെടാൻ മൈക്ക് ടാപ്പ് ചെയ്യുന്നത് പരീക്ഷിക്കൂ.</translation>
 <translation id="5222676887888702881">പുറത്തുകടക്കുക</translation>
+<translation id="5317780077021120954">സംരക്ഷിക്കുക</translation>
 <translation id="5457599981699367932">അതിഥിയായി ബ്രൌസ് ചെയ്യുക</translation>
 <translation id="54609108002486618">നിയന്ത്രിതം</translation>
 <translation id="5733345267661125295">പുതിയ പാസ്‌വേഡ് ഇപ്പോൾ തിരഞ്ഞെടുക്കുക</translation>
@@ -22,6 +23,7 @@
 <translation id="6574601967010742428">ഉപകരണ അഡ്‌മിനിസ്‌ട്രേറ്റർക്ക് നിങ്ങളുടെ ആക്‌റ്റിവിറ്റി നിരീക്ഷിക്കാനായേക്കും.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{നിങ്ങളുടെ നിലവിലെ പാസ്‌വേഡ് കാലഹരണപ്പെട്ടു!}=1{നിങ്ങളുടെ നിലവിലെ പാസ്‌വേഡ് ഒരു ദിവസത്തിനുള്ളിൽ കാലഹരണപ്പെടും!}other{നിങ്ങളുടെ നിലവിലെ പാസ്‌വേഡ് # ദിവസത്തിനുള്ളിൽ കാലഹരണപ്പെടും!}}</translation>
 <translation id="7658239707568436148">റദ്ദാക്കൂ</translation>
+<translation id="7690294790491645610">പുതിയ പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക</translation>
 <translation id="808894953321890993">പാസ്‌വേഡ് മാറ്റുക</translation>
 <translation id="9111102763498581341">അണ്‍ലോക്ക് ചെയ്യുക</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_mr.xtb b/chromeos/strings/chromeos_strings_mr.xtb
index 08072ee6..4c74c44 100644
--- a/chromeos/strings/chromeos_strings_mr.xtb
+++ b/chromeos/strings/chromeos_strings_mr.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">मी मदत करू शकेन असे मला तुमच्या स्क्रीनवर काही आढळले नाही. 
 मला काहीही विचारण्यासाठी माइकवर टॅप करून पाहा.</translation>
 <translation id="5222676887888702881">साइन आउट करा</translation>
+<translation id="5317780077021120954">सेव्ह करा</translation>
 <translation id="5457599981699367932">अतिथी म्हणून ब्राउझ करा</translation>
 <translation id="54609108002486618">व्यवस्‍थापित</translation>
 <translation id="5733345267661125295">कृपया आता नवीन पासवर्ड निवडा</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">डिव्हाइस ॲडमिनिस्ट्रेटर कदाचित तुमची ॲक्टिव्हिटी नियंत्रित करू शकेल.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{तुमचा सध्याचा पासवर्ड एक्स्पायर झाला आहे!}=1{तुमचा सध्याचा पासवर्ड एका दिवसापेक्षा कमी वेळेत एक्स्पायर होईल!}other{तुमचा सध्याचा पासवर्ड # दिवसांपेक्षा कमी वेळेत एक्स्पायर होईल!}}</translation>
 <translation id="7658239707568436148">रद्द करा</translation>
+<translation id="7690294790491645610">नवीन पासवर्ड कन्फर्म करा</translation>
 <translation id="808894953321890993">पासवर्ड बदला</translation>
 <translation id="9111102763498581341">अनलॉक करा</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ms.xtb b/chromeos/strings/chromeos_strings_ms.xtb
index bdf125c..3cff27d9 100644
--- a/chromeos/strings/chromeos_strings_ms.xtb
+++ b/chromeos/strings/chromeos_strings_ms.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Saya tidak menemui apa-apa pada skrin anda yang boleh saya bantu.
 Cuba ketik mikrofon untuk bertanyakan sebarang perkara kepada saya.</translation>
 <translation id="5222676887888702881">Log keluar</translation>
+<translation id="5317780077021120954">Simpan</translation>
 <translation id="5457599981699367932">Semak Imbas sebagai Tetamu</translation>
 <translation id="54609108002486618">Diuruskan</translation>
 <translation id="5733345267661125295">Sila pilih kata laluan baharu sekarang</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Pentadbir peranti mungkin dapat memantau aktiviti anda</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Kata laluan semasa anda telah tamat tempoh!}=1{Kata laluan semasa anda akan tamat tempoh kurang daripada satu hari!}other{Kata laluan semasa anda akan tamat tempoh kurang daripada # hari!}}</translation>
 <translation id="7658239707568436148">Batal</translation>
+<translation id="7690294790491645610">Sahkan kata laluan baharu</translation>
 <translation id="808894953321890993">Tukar kata laluan</translation>
 <translation id="9111102763498581341">Buka kunci</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_nl.xtb b/chromeos/strings/chromeos_strings_nl.xtb
index fb331f2..d4564c0 100644
--- a/chromeos/strings/chromeos_strings_nl.xtb
+++ b/chromeos/strings/chromeos_strings_nl.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Ik heb niets gevonden op je scherm waarmee ik kan helpen.
 Tik op de microfoon om me iets te vragen.</translation>
 <translation id="5222676887888702881">Uitloggen</translation>
+<translation id="5317780077021120954">Opslaan</translation>
 <translation id="5457599981699367932">Gebruiken als gast</translation>
 <translation id="54609108002486618">Beheerd</translation>
 <translation id="5733345267661125295">Kies nu een nieuw wachtwoord</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">De beheerder van het apparaat kan je activiteit mogelijk bekijken.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Je huidige wachtwoord is verlopen.}=1{Je huidige wachtwoord verloopt binnen één dag.}other{Je huidige wachtwoord verloopt binnen # dagen.}}</translation>
 <translation id="7658239707568436148">Annuleren</translation>
+<translation id="7690294790491645610">Nieuw wachtwoord bevestigen</translation>
 <translation id="808894953321890993">Wachtwoord wijzigen</translation>
 <translation id="9111102763498581341">Ontgrendelen</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_no.xtb b/chromeos/strings/chromeos_strings_no.xtb
index 6b0d1f24..a7026ff4 100644
--- a/chromeos/strings/chromeos_strings_no.xtb
+++ b/chromeos/strings/chromeos_strings_no.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Jeg finner ikke noe på skjermen jeg kan hjelpe deg med. 
 Prøv å trykke på mikrofonen for å spørre meg om noe.</translation>
 <translation id="5222676887888702881">Logg av</translation>
+<translation id="5317780077021120954">Lagre</translation>
 <translation id="5457599981699367932">Surf som gjest</translation>
 <translation id="54609108002486618">Administrert</translation>
 <translation id="5733345267661125295">Velg et nytt passord nå</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Enhetsadministratoren kan muligens overvåke aktiviteten din.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Det nåværende passordet ditt er utløpt!}=1{Det nåværende passordet ditt utløper om mindre enn én dag!}other{Det nåværende passordet ditt utløper om mindre enn # dager!}}</translation>
 <translation id="7658239707568436148">Avbryt</translation>
+<translation id="7690294790491645610">Bekreft det nye passordet</translation>
 <translation id="808894953321890993">Endre passord</translation>
 <translation id="9111102763498581341">Lås opp</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_pl.xtb b/chromeos/strings/chromeos_strings_pl.xtb
index 018d78b..d8cfc75 100644
--- a/chromeos/strings/chromeos_strings_pl.xtb
+++ b/chromeos/strings/chromeos_strings_pl.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Na Twoim ekranie nie ma nic, w czym mogę pomóc.
 Kliknij mikrofon i zadaj pytanie.</translation>
 <translation id="5222676887888702881">Wyloguj się</translation>
+<translation id="5317780077021120954">Zapisz</translation>
 <translation id="5457599981699367932">Przeglądaj jako gość</translation>
 <translation id="54609108002486618">Zarządzany</translation>
 <translation id="5733345267661125295">Już teraz zmień hasło</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Administrator urządzenia może monitorować Twoją aktywność.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Twoje obecne hasło wygasło.}=1{Twoje obecne hasło wygasa za mniej niż dzień.}few{Twoje obecne hasło wygasa za mniej niż # dni.}many{Twoje obecne hasło wygasa za mniej niż # dni.}other{Twoje obecne hasło wygasa za mniej niż # dni.}}</translation>
 <translation id="7658239707568436148">Anuluj</translation>
+<translation id="7690294790491645610">Potwierdź nowe hasło</translation>
 <translation id="808894953321890993">Zmień hasło</translation>
 <translation id="9111102763498581341">Odblokuj</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_pt-BR.xtb b/chromeos/strings/chromeos_strings_pt-BR.xtb
index 77d82c3..d1c07cf 100644
--- a/chromeos/strings/chromeos_strings_pt-BR.xtb
+++ b/chromeos/strings/chromeos_strings_pt-BR.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Não foi possível encontrar na sua tela nada com o que eu possa ajudar.
 Tente tocar no microfone para me pedir algo.</translation>
 <translation id="5222676887888702881">Sair</translation>
+<translation id="5317780077021120954">Salvar</translation>
 <translation id="5457599981699367932">Navegar como visitante</translation>
 <translation id="54609108002486618">Gerenciado</translation>
 <translation id="5733345267661125295">Escolha uma nova senha agora</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">O administrador do dispositivo pode conseguir monitorar sua atividade.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Sua senha atual expirou.}=1{Sua senha atual expirará em menos de um dia.}one{Sua senha atual expirará em menos de # dia.}other{Sua senha atual expirará em menos de # dias.}}</translation>
 <translation id="7658239707568436148">Cancelar</translation>
+<translation id="7690294790491645610">Confirmar nova senha</translation>
 <translation id="808894953321890993">Alterar senha</translation>
 <translation id="9111102763498581341">Desbloquear</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_pt-PT.xtb b/chromeos/strings/chromeos_strings_pt-PT.xtb
index f5c0f3b..29f5bb4 100644
--- a/chromeos/strings/chromeos_strings_pt-PT.xtb
+++ b/chromeos/strings/chromeos_strings_pt-PT.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Não consigo encontrar nada no ecrã com que possa ajudar.
 Experimente tocar no microfone para me perguntar o que quiser.</translation>
 <translation id="5222676887888702881">Terminar sessão</translation>
+<translation id="5317780077021120954">Guardar</translation>
 <translation id="5457599981699367932">Navegar como convidado</translation>
 <translation id="54609108002486618">Geridos</translation>
 <translation id="5733345267661125295">Escolha uma nova palavra-passe agora.</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">O administrador do dispositivo pode conseguir monitorizar a sua atividade.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{A sua palavra-passe atual expirou!}=1{A sua palavra-passe atual expira em menos de um dia!}other{A sua palavra-passe atual expira em menos de # dias!}}</translation>
 <translation id="7658239707568436148">Cancelar</translation>
+<translation id="7690294790491645610">Confirmar a nova palavra-passe</translation>
 <translation id="808894953321890993">Alterar palavra-passe</translation>
 <translation id="9111102763498581341">Desbloquear</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ro.xtb b/chromeos/strings/chromeos_strings_ro.xtb
index 29d115e..d379e7e 100644
--- a/chromeos/strings/chromeos_strings_ro.xtb
+++ b/chromeos/strings/chromeos_strings_ro.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Nu găsesc conținut pe ecran în privința căruia te-aș putea ajuta.
 Atinge microfonul și întreabă orice dorești.</translation>
 <translation id="5222676887888702881">Deconectează-te</translation>
+<translation id="5317780077021120954">Salvează</translation>
 <translation id="5457599981699367932">Navighează ca invitat</translation>
 <translation id="54609108002486618">Gestionat</translation>
 <translation id="5733345267661125295">Alege acum o parolă nouă</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Administratorul dispozitivului poate să îți monitorizeze activitatea.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Parola curentă a expirat!}=1{Parola curentă va expira în mai puțin de o zi!}few{Parola curentă va expira în mai puțin de # zile!}other{Parola curentă va expira în mai puțin de # de zile!}}</translation>
 <translation id="7658239707568436148">Anulează</translation>
+<translation id="7690294790491645610">Confirmă noua parolă</translation>
 <translation id="808894953321890993">Schimbă parola</translation>
 <translation id="9111102763498581341">Deblochează</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ru.xtb b/chromeos/strings/chromeos_strings_ru.xtb
index abd4b58..31b2e4a2 100644
--- a/chromeos/strings/chromeos_strings_ru.xtb
+++ b/chromeos/strings/chromeos_strings_ru.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Я не нашла на экране ничего подходящего.
 Попробуйте озвучить запрос, нажав на значок микрофона.</translation>
 <translation id="5222676887888702881">Выйти</translation>
+<translation id="5317780077021120954">Сохранить</translation>
 <translation id="5457599981699367932">Войти в гостевой режим</translation>
 <translation id="54609108002486618">Автоматическое управление</translation>
 <translation id="5733345267661125295">Смените его прямо сейчас.</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Администратор может отслеживать ваши действия на этом устройстве.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Срок действия текущего пароля истек.}=1{Срок действия текущего пароля истекает менее чем через 1 день.}one{Срок действия текущего пароля истекает менее чем через # день.}few{Срок действия текущего пароля истекает менее чем через # дня.}many{Срок действия текущего пароля истекает менее чем через # дней.}other{Срок действия текущего пароля истекает менее чем через # дня.}}</translation>
 <translation id="7658239707568436148">Отмена</translation>
+<translation id="7690294790491645610">Введите новый пароль ещё раз</translation>
 <translation id="808894953321890993">Сменить пароль</translation>
 <translation id="9111102763498581341">Разблокировать</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_sk.xtb b/chromeos/strings/chromeos_strings_sk.xtb
index d219b7e..e1fb1378 100644
--- a/chromeos/strings/chromeos_strings_sk.xtb
+++ b/chromeos/strings/chromeos_strings_sk.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Na obrazovke nie je nič, s čím by som mohol pomôcť.
 Skúste klepnúť na mikrofón a niečo prikázať.</translation>
 <translation id="5222676887888702881">Odhlásiť sa</translation>
+<translation id="5317780077021120954">Uložiť</translation>
 <translation id="5457599981699367932">Prehliadať ako hosť</translation>
 <translation id="54609108002486618">Spravovaný</translation>
 <translation id="5733345267661125295">Vyberte nové heslo</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Správca zariadenia môže sledovať vašu aktivitu.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Platnosť vášho aktuálneho hesla vypršala.}=1{Platnosť vášho aktuálneho hesla vyprší za menej ako deň.}few{Platnosť vášho aktuálneho hesla vyprší za menej ako # dni.}many{Platnosť vášho aktuálneho hesla vyprší za menej ako # dňa.}other{Platnosť vášho aktuálneho hesla vyprší za menej ako # dní.}}</translation>
 <translation id="7658239707568436148">Zrušiť</translation>
+<translation id="7690294790491645610">Potvrďte nové heslo</translation>
 <translation id="808894953321890993">Zmeniť heslo</translation>
 <translation id="9111102763498581341">Odomknúť</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_sl.xtb b/chromeos/strings/chromeos_strings_sl.xtb
index d3632233..d426ece 100644
--- a/chromeos/strings/chromeos_strings_sl.xtb
+++ b/chromeos/strings/chromeos_strings_sl.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Na zaslonu ni ničesar uporabnega.
 Poskusite vprašati tako, da se dotaknete mikrofona.</translation>
 <translation id="5222676887888702881">Odjava</translation>
+<translation id="5317780077021120954">Shrani</translation>
 <translation id="5457599981699367932">Brskajte kot gost</translation>
 <translation id="54609108002486618">Upravljan</translation>
 <translation id="5733345267661125295">Izberite novo geslo</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Upravitelj naprave lahko morda nadzira vašo dejavnost.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Trenutno geslo je poteklo.}=1{Trenutno geslo poteče čez manj kot en dan.}one{Trenutno geslo poteče čez manj kot # dan.}two{Trenutno geslo poteče čez manj kot # dneva.}few{Trenutno geslo poteče čez manj kot # dni.}other{Trenutno geslo poteče čez manj kot # dni.}}</translation>
 <translation id="7658239707568436148">Prekliči</translation>
+<translation id="7690294790491645610">Potrdite novo geslo</translation>
 <translation id="808894953321890993">Sprememba gesla</translation>
 <translation id="9111102763498581341">Odkleni</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_sr.xtb b/chromeos/strings/chromeos_strings_sr.xtb
index 52f24ca..d35d3e3eb 100644
--- a/chromeos/strings/chromeos_strings_sr.xtb
+++ b/chromeos/strings/chromeos_strings_sr.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Не могу да пронађем на екрану ништа око чега могу да помогнем.
 Пробајте да додирнете микрофон да бисте ме питали нешто.</translation>
 <translation id="5222676887888702881">Одјави ме</translation>
+<translation id="5317780077021120954">Сачувај</translation>
 <translation id="5457599981699367932">Прегледајте као гост</translation>
 <translation id="54609108002486618">Управљани</translation>
 <translation id="5733345267661125295">Одаберите нову лозинку</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Администратор уређаја ће можда моћи да надгледа ваше активности.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Актуелна лозинка је истекла!}=1{Актуелна лозинка истиче за мање од једног дана!}one{Актуелна лозинка истиче за мање од # дана!}few{Актуелна лозинка истиче за мање од # дана!}other{Актуелна лозинка истиче за мање од # дана!}}</translation>
 <translation id="7658239707568436148">Откажи</translation>
+<translation id="7690294790491645610">Потврдите нову лозинку</translation>
 <translation id="808894953321890993">Промени лозинку</translation>
 <translation id="9111102763498581341">Откључај</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_sv.xtb b/chromeos/strings/chromeos_strings_sv.xtb
index 933708d..a773afe7 100644
--- a/chromeos/strings/chromeos_strings_sv.xtb
+++ b/chromeos/strings/chromeos_strings_sv.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Det finns inget på skärmen som jag kan hjälpa till med.
 Tryck på mikrofonen och fråga om något.</translation>
 <translation id="5222676887888702881">Logga ut</translation>
+<translation id="5317780077021120954">Spara</translation>
 <translation id="5457599981699367932">Använd som gäst</translation>
 <translation id="54609108002486618">Hanterade</translation>
 <translation id="5733345267661125295">Välj ett nytt lösenord nu</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Enhetsadministratören kan ha möjlighet att övervaka det du gör.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Lösenordet har upphört att gälla.}=1{Lösenordet upphör att gälla om mindre än en dag.}other{Lösenordet upphör att gälla om mindre än # dagar.}}</translation>
 <translation id="7658239707568436148">Avbryt</translation>
+<translation id="7690294790491645610">Bekräfta det nya lösenordet</translation>
 <translation id="808894953321890993">Ändra lösenord</translation>
 <translation id="9111102763498581341">Lås upp</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_sw.xtb b/chromeos/strings/chromeos_strings_sw.xtb
index 99064bbd..871ad16 100644
--- a/chromeos/strings/chromeos_strings_sw.xtb
+++ b/chromeos/strings/chromeos_strings_sw.xtb
@@ -14,6 +14,7 @@
 <translation id="476166673298332917">Msimamizi wa kifaa hiki anaweza kufuatilia shughuli zako za kuvinjari.</translation>
 <translation id="5212543919916444558">Sijapata swali lolote kwenye skrini yako. Jaribu kugonga maikrofoni ili uniulize chochote.</translation>
 <translation id="5222676887888702881">Ondoka</translation>
+<translation id="5317780077021120954">Hifadhi</translation>
 <translation id="5457599981699367932">Vinjari kama Mgeni</translation>
 <translation id="54609108002486618">Imedhibitiwa</translation>
 <translation id="5733345267661125295">Tafadhali chagua nenosiri jipya sasa</translation>
@@ -22,6 +23,7 @@
 <translation id="6574601967010742428">Huenda msimamizi wa kifaa hiki atafuatilia shughuli zako.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Muda wa kutumia nenosiri lako la sasa umekwisha!}=1{Muda wa kutumia nenosiri lako la sasa unaisha ndani ya siku moja!}other{Muda wa kutumia nenosiri lako la sasa utaisha ndani ya siku #!}}</translation>
 <translation id="7658239707568436148">Ghairi</translation>
+<translation id="7690294790491645610">Thibitisha nenosiri jipya</translation>
 <translation id="808894953321890993">Badilisha nenosiri</translation>
 <translation id="9111102763498581341">Fungua</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_ta.xtb b/chromeos/strings/chromeos_strings_ta.xtb
index 93591eb..37af302 100644
--- a/chromeos/strings/chromeos_strings_ta.xtb
+++ b/chromeos/strings/chromeos_strings_ta.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">உங்களுக்கு நான் உதவும் வகையில், உங்கள் திரையில் எதுவுமில்லை.
 என்னிடம் ஏதேனும் கேட்க, மைக்கைத் தட்டவும்.</translation>
 <translation id="5222676887888702881">வெளியேறு</translation>
+<translation id="5317780077021120954">சேமி</translation>
 <translation id="5457599981699367932">விருந்தினராக உலாவுங்கள்</translation>
 <translation id="54609108002486618">நிர்வகிக்கப்பட்டது</translation>
 <translation id="5733345267661125295">இப்போதே புதிய கடவுச்சொல்லை அமைக்கவும்</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">உங்கள் செயல்பாடு சாதன நிர்வாகியால் கண்காணிக்கப்படலாம்.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{தற்போதைய கடவுச்சொல் காலாவதியானது!}=1{தற்போதைய கடவுச்சொல் ஒரு நாளுக்குள் காலாவதியாகும்!}other{தற்போதைய கடவுச்சொல் # நாட்களுக்குள் காலாவதியாகும்!}}</translation>
 <translation id="7658239707568436148">ரத்து செய்</translation>
+<translation id="7690294790491645610">புதிய கடவுச்சொல்லை உறுதிப்படுத்துக</translation>
 <translation id="808894953321890993">கடவுச்சொல்லை மாற்று</translation>
 <translation id="9111102763498581341">பூட்டைத் திற</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_te.xtb b/chromeos/strings/chromeos_strings_te.xtb
index e69e88c..8c291a53 100644
--- a/chromeos/strings/chromeos_strings_te.xtb
+++ b/chromeos/strings/chromeos_strings_te.xtb
@@ -14,6 +14,7 @@
 <translation id="476166673298332917">పరికర నిర్వాహకులు మీ బ్రౌజింగ్ కార్యకలాపాన్ని పర్యవేక్షించవచ్చు.</translation>
 <translation id="5212543919916444558">నేను సహాయం అందించగలిగేలా మీ స్క్రీన్‌పై నాకు ఏదీ కనిపించలేదు. మైక్‌ను నొక్కి, నన్ను ఏదైనా అడగడానికి ప్రయత్నించండి.</translation>
 <translation id="5222676887888702881">సైన్ ఔట్</translation>
+<translation id="5317780077021120954">సేవ్ చేయి</translation>
 <translation id="5457599981699367932">అతిథి లాగా బ్రౌజ్ చేయండి</translation>
 <translation id="54609108002486618">నిర్వహించబడింది</translation>
 <translation id="5733345267661125295">దయచేసి ఇప్పుడు ఒక కొత్త పాస్‌వర్డ్‌ను ఎంచుకోండి</translation>
@@ -22,6 +23,7 @@
 <translation id="6574601967010742428">మీ కార్యకలాపాన్ని పరికర నిర్వాహకులు పర్యవేక్షించవచ్చు.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{మీ ప్రస్తుత పాస్‌వర్డ్ గడువు ముగిసింది!}=1{మీ ప్రస్తుత పాస్‌వర్డ్ గడువు ఒక రోజులోపు ముగుస్తుంది!}other{మీ ప్రస్తుత పాస్‌వర్డ్ గడువు # రోజులలో ముగుస్తుంది!}}</translation>
 <translation id="7658239707568436148">రద్దు చేయి</translation>
+<translation id="7690294790491645610">కొత్త పాస్‌వర్డ్‌ను నిర్ధారించండి</translation>
 <translation id="808894953321890993">పాస్‌వర్డ్‌ను మార్చు</translation>
 <translation id="9111102763498581341">అన్‌లాక్ చేయి</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_th.xtb b/chromeos/strings/chromeos_strings_th.xtb
index 5d9718a..80605aff 100644
--- a/chromeos/strings/chromeos_strings_th.xtb
+++ b/chromeos/strings/chromeos_strings_th.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">ฉันไม่พบสิ่งที่จะช่วยได้ในหน้าจอ
 ลองแตะไมโครโฟนเพื่อถามเรื่องอะไรก็ได้</translation>
 <translation id="5222676887888702881">ออกจากระบบ</translation>
+<translation id="5317780077021120954">บันทึก</translation>
 <translation id="5457599981699367932">ท่องเว็บในฐานะผู้มาเยือน</translation>
 <translation id="54609108002486618">มีการจัดการ</translation>
 <translation id="5733345267661125295">โปรดเลือกรหัสผ่านใหม่ตอนนี้</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">ผู้ดูแลระบบของอุปกรณ์อาจมีสิทธิ์ตรวจสอบกิจกรรมของคุณได้</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{รหัสผ่านปัจจุบันของคุณหมดอายุแล้ว}=1{รหัสผ่านปัจจุบันของคุณจะหมดอายุในไม่ถึง 1 วัน}other{รหัสผ่านปัจจุบันของคุณจะหมดอายุในไม่ถึง # วัน}}</translation>
 <translation id="7658239707568436148">ยกเลิก</translation>
+<translation id="7690294790491645610">ยืนยันรหัสผ่านใหม่</translation>
 <translation id="808894953321890993">เปลี่ยนรหัสผ่าน</translation>
 <translation id="9111102763498581341">ปลดล็อก</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_tr.xtb b/chromeos/strings/chromeos_strings_tr.xtb
index d61879f..93488f7c 100644
--- a/chromeos/strings/chromeos_strings_tr.xtb
+++ b/chromeos/strings/chromeos_strings_tr.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Ekranınızda size yardımcı olabileceğim bir şey bulamıyorum.
 Bana bir şey sormak için mikrofona dokunmayı deneyin.</translation>
 <translation id="5222676887888702881">Çıkış</translation>
+<translation id="5317780077021120954">Kaydet</translation>
 <translation id="5457599981699367932">Misafir olarak Göz At</translation>
 <translation id="54609108002486618">Yönetilen</translation>
 <translation id="5733345267661125295">Lütfen şimdi yeni bir şifre seçin</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Cihaz yöneticisi, etkinliğinizi izleyebilir.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Mevcut şifrenizin süresi doldu!}=1{Mevcut şifrenizin süresi bir günden kısa bir süre içinde dolacak!}other{Mevcut şifrenizin süresi # günden kısa bir süre içinde dolacak}}</translation>
 <translation id="7658239707568436148">İptal</translation>
+<translation id="7690294790491645610">Yeni şifreyi doğrula</translation>
 <translation id="808894953321890993">Şifreyi değiştir</translation>
 <translation id="9111102763498581341">Kilidi Aç</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_uk.xtb b/chromeos/strings/chromeos_strings_uk.xtb
index 5d36af0..4bee3a35 100644
--- a/chromeos/strings/chromeos_strings_uk.xtb
+++ b/chromeos/strings/chromeos_strings_uk.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">На екрані не знайдено елементів, проблеми з якими я можу вирішити.
 Торкніться значка мікрофона, щоб поставити запитання.</translation>
 <translation id="5222676887888702881">Вийти</translation>
+<translation id="5317780077021120954">Зберегти</translation>
 <translation id="5457599981699367932">Переглядати в режимі гостя</translation>
 <translation id="54609108002486618">Керований</translation>
 <translation id="5733345267661125295">Виберіть новий пароль</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Адміністратор пристрою може відстежувати вашу активність.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Термін дії поточного пароля закінчився.}=1{Термін дії поточного пароля закінчиться менше ніж за один день.}one{Термін дії поточного пароля закінчиться менше ніж за # день.}few{Термін дії поточного пароля закінчиться менше ніж за # дні.}many{Термін дії поточного пароля закінчиться менше ніж за # днів.}other{Термін дії поточного пароля закінчиться менше ніж за # дня.}}</translation>
 <translation id="7658239707568436148">Скасувати</translation>
+<translation id="7690294790491645610">Підтвердьте новий пароль</translation>
 <translation id="808894953321890993">Змінити пароль</translation>
 <translation id="9111102763498581341">Розблокувати</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_vi.xtb b/chromeos/strings/chromeos_strings_vi.xtb
index ba0aecb..60d50b2c 100644
--- a/chromeos/strings/chromeos_strings_vi.xtb
+++ b/chromeos/strings/chromeos_strings_vi.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">Tôi không tìm thấy nội dung nào trên màn hình để có thể trợ giúp cho bạn.
 Hãy thử nhấn vào micrô để hỏi tôi bất cứ điều gì.</translation>
 <translation id="5222676887888702881">Đăng xuất</translation>
+<translation id="5317780077021120954">Lưu</translation>
 <translation id="5457599981699367932">Duyệt với tư cách Khách</translation>
 <translation id="54609108002486618">Được quản lý</translation>
 <translation id="5733345267661125295">Vui lòng chọn một mật khẩu mới ngay bây giờ</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">Quản trị viên thiết bị có thể theo dõi hoạt động của bạn.</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{Mật khẩu hiện tại của bạn đã hết hạn!}=1{Còn chưa đến 1 ngày nữa là mật khẩu hiện tại của bạn sẽ hết hạn!}other{Còn chưa đến # ngày nữa là mật khẩu hiện tại của bạn sẽ hết hạn!}}</translation>
 <translation id="7658239707568436148">Hủy</translation>
+<translation id="7690294790491645610">Xác nhận mật khẩu mới</translation>
 <translation id="808894953321890993">Đổi mật khẩu</translation>
 <translation id="9111102763498581341">Mở khóa</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_zh-CN.xtb b/chromeos/strings/chromeos_strings_zh-CN.xtb
index f294d43..3f16729 100644
--- a/chromeos/strings/chromeos_strings_zh-CN.xtb
+++ b/chromeos/strings/chromeos_strings_zh-CN.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">无法提供与您的屏幕上可以找到的任何内容相关的帮助。
 请尝试通过点按麦克风图标向我提问。</translation>
 <translation id="5222676887888702881">退出</translation>
+<translation id="5317780077021120954">保存</translation>
 <translation id="5457599981699367932">以访客身份浏览</translation>
 <translation id="54609108002486618">托管</translation>
 <translation id="5733345267661125295">请尽快选择新密码</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">设备管理员或许能监控您的活动。</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{您当前的密码已过期!}=1{您当前的密码将在 1 天内过期!}other{您当前的密码将在 # 天内过期!}}</translation>
 <translation id="7658239707568436148">取消</translation>
+<translation id="7690294790491645610">再次输入新密码</translation>
 <translation id="808894953321890993">更改密码</translation>
 <translation id="9111102763498581341">解锁</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/strings/chromeos_strings_zh-TW.xtb b/chromeos/strings/chromeos_strings_zh-TW.xtb
index 56b8ca19..1d8f594 100644
--- a/chromeos/strings/chromeos_strings_zh-TW.xtb
+++ b/chromeos/strings/chromeos_strings_zh-TW.xtb
@@ -15,6 +15,7 @@
 <translation id="5212543919916444558">系統在你的畫面上找不到任何能提供協助的內容。
 請輕觸麥克風圖示,直接說出你的問題。</translation>
 <translation id="5222676887888702881">登出</translation>
+<translation id="5317780077021120954">儲存</translation>
 <translation id="5457599981699367932">以訪客身分瀏覽</translation>
 <translation id="54609108002486618">受管理</translation>
 <translation id="5733345267661125295">請立即選擇新的密碼</translation>
@@ -23,6 +24,7 @@
 <translation id="6574601967010742428">裝置管理員或許可監控你的活動。</translation>
 <translation id="7505377169174526985">{NUM_DAYS,plural, =0{目前的密碼已過期!}=1{目前的密碼將於 1 天內過期!}other{目前的密碼將於 # 天內過期!}}</translation>
 <translation id="7658239707568436148">取消</translation>
+<translation id="7690294790491645610">確認新密碼</translation>
 <translation id="808894953321890993">變更密碼</translation>
 <translation id="9111102763498581341">解除鎖定</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/chromeos/system/statistics_provider.cc b/chromeos/system/statistics_provider.cc
index 2721d33..65bb425 100644
--- a/chromeos/system/statistics_provider.cc
+++ b/chromeos/system/statistics_provider.cc
@@ -21,7 +21,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/system/sys_info.h"
@@ -278,7 +278,7 @@
   bool load_statistics_started_;
   NameValuePairsParser::NameValueMap machine_info_;
   MachineFlags machine_flags_;
-  base::CancellationFlag cancellation_flag_;
+  base::AtomicFlag cancellation_flag_;
   bool oem_manifest_loaded_;
   std::string region_;
   std::unique_ptr<base::Value> regional_data_;
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 7478218..2131fd9d 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -139,6 +139,7 @@
     "//components/services/unzip:unit_tests",
     "//components/sessions:unit_tests",
     "//components/signin/core/browser:unit_tests",
+    "//components/signin/core/browser/webdata:unit_tests",
     "//components/ssl_errors:unit_tests",
     "//components/subresource_filter/core/browser:unit_tests",
     "//components/subresource_filter/core/common:unit_tests",
diff --git a/components/arc/camera/arc_camera_bridge.cc b/components/arc/camera/arc_camera_bridge.cc
index c12dfc71..e91c9ea 100644
--- a/components/arc/camera/arc_camera_bridge.cc
+++ b/components/arc/camera/arc_camera_bridge.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/files/scoped_file.h"
 #include "base/memory/singleton.h"
+#include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "chromeos/dbus/arc_camera_client.h"
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
@@ -43,6 +44,52 @@
 
 }  // namespace
 
+// Runs the callback after verifying the connection to the service.
+class ArcCameraBridge::PendingStartCameraServiceResult {
+ public:
+  PendingStartCameraServiceResult(
+      ArcCameraBridge* owner,
+      mojo::ScopedMessagePipeHandle pipe,
+      ArcCameraBridge::StartCameraServiceCallback callback)
+      : owner_(owner),
+        service_(mojom::CameraServicePtrInfo(std::move(pipe), 0u)),
+        callback_(std::move(callback)) {
+    service_.set_connection_error_handler(
+        base::Bind(&PendingStartCameraServiceResult::OnError,
+                   weak_ptr_factory_.GetWeakPtr()));
+    service_.QueryVersion(
+        base::Bind(&PendingStartCameraServiceResult::OnVersionReady,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  ~PendingStartCameraServiceResult() = default;
+
+ private:
+  void OnVersionReady(uint32_t version) { Finish(); }
+
+  void OnError() {
+    LOG(ERROR) << "Failed to query the camera service version.";
+    // Run the callback anyways. The same error will be delivered to the Android
+    // side error handler.
+    Finish();
+  }
+
+  // Runs the callback and removes this object from the owner.
+  void Finish() {
+    DCHECK(callback_);
+    std::move(callback_).Run(std::move(service_));
+    // Destructs |this|.
+    owner_->pending_start_camera_service_results_.erase(this);
+  }
+
+  ArcCameraBridge* const owner_;
+  mojom::CameraServicePtr service_;
+  ArcCameraBridge::StartCameraServiceCallback callback_;
+  base::WeakPtrFactory<PendingStartCameraServiceResult> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(PendingStartCameraServiceResult);
+};
+
 // static
 ArcCameraBridge* ArcCameraBridge::GetForBrowserContext(
     content::BrowserContext* context) {
@@ -68,11 +115,13 @@
   mojo::PlatformChannel channel;
   mojo::ScopedMessagePipeHandle server_pipe =
       invitation.AttachMessagePipe(token);
-  // Run the callback now.
-  // When an error occurs, it'll be communicated via the pipe.
-  mojom::CameraServicePtr service;
-  service.Bind(mojom::CameraServicePtrInfo(std::move(server_pipe), 0u));
-  std::move(callback).Run(std::move(service));
+
+  // Run the callback after verifying the connection to the service process.
+  auto pending_result = std::make_unique<PendingStartCameraServiceResult>(
+      this, std::move(server_pipe), std::move(callback));
+  auto* pending_result_ptr = pending_result.get();
+  pending_start_camera_service_results_[pending_result_ptr] =
+      std::move(pending_result);
 
   mojo::OutgoingInvitation::Send(std::move(invitation),
                                  base::kNullProcessHandle,
diff --git a/components/arc/camera/arc_camera_bridge.h b/components/arc/camera/arc_camera_bridge.h
index 03d76922..e9e6117 100644
--- a/components/arc/camera/arc_camera_bridge.h
+++ b/components/arc/camera/arc_camera_bridge.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_ARC_CAMERA_ARC_CAMERA_BRIDGE_H_
 #define COMPONENTS_ARC_CAMERA_ARC_CAMERA_BRIDGE_H_
 
+#include <map>
+#include <memory>
+
 #include "base/macros.h"
 #include "components/arc/common/camera.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -33,8 +36,14 @@
   void StartCameraService(StartCameraServiceCallback callback) override;
 
  private:
+  class PendingStartCameraServiceResult;
+
   ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
 
+  std::map<PendingStartCameraServiceResult*,
+           std::unique_ptr<PendingStartCameraServiceResult>>
+      pending_start_camera_service_results_;
+
   DISALLOW_COPY_AND_ASSIGN(ArcCameraBridge);
 };
 
diff --git a/components/arc/common/app_permissions.mojom b/components/arc/common/app_permissions.mojom
index c6d65527..0454b741 100644
--- a/components/arc/common/app_permissions.mojom
+++ b/components/arc/common/app_permissions.mojom
@@ -11,9 +11,11 @@
 [Extensible]
 enum AppPermission {
   CAMERA          = 0, // android.manifest.CAMERA
-  LOCATION        = 1, // android.manifest.ACCESS_FINE_LOCATION
+  LOCATION        = 1, // android.manifest.ACCESS_FINE_LOCATION or android.manifest.ACCESS_COARSE_LOCATION
   MICROPHONE      = 2, // android.manifest.RECORD_AUDIO
   NOTIFICATIONS   = 3, // Not a manifest permission
+  CONTACTS        = 4, // android.manifest.READ_CONTACTS and android.manifest.WRITE_CONTACTS
+  STORAGE         = 5, // android.manifest.WRITE_EXTERNAL_STORAGE and android.manifest.READ_EXTERNAL_STORAGE
 };
 
 struct PermissionState {
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index 314b059f..c883169e 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -184,7 +184,6 @@
       }
     }
   }
-  automatic_generation_element_.Reset();
   current_generation_item_.reset();
   last_focused_password_element_.Reset();
   generation_enabled_fields_.clear();
@@ -368,19 +367,8 @@
     return false;
   }
 
-  // Automatic generation depends on whether the new parser is on because the
-  // new parser sends now information to the renderer about fields for
-  // generation. In case when the new old parser is used the old path for
-  // automatic generation is used. So detecting which automatic generation
-  // depends on which parser is used.
-  // TODO(https://crbug.com/831123): Remove this variable when the old parser is
-  // gone.
-  bool automatic_generation_available_with_the_old_parser =
-      last_focused_password_element_ == automatic_generation_element_;
-
   current_generation_item_->is_manually_triggered_ =
-      !is_automatic_generation_available &&
-      !automatic_generation_available_with_the_old_parser;
+      !is_automatic_generation_available;
   return true;
 }
 
@@ -646,12 +634,4 @@
   logger.LogBoolean(message_id, truth_value);
 }
 
-void PasswordGenerationAgent::LogNumber(Logger::StringID message_id,
-                                        int number) {
-  if (!password_agent_->logging_state_active())
-    return;
-  RendererSavePasswordProgressLogger logger(GetPasswordManagerDriver().get());
-  logger.LogNumber(message_id, number);
-}
-
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/password_generation_agent.h b/components/autofill/content/renderer/password_generation_agent.h
index 22427d8c..f33653e 100644
--- a/components/autofill/content/renderer/password_generation_agent.h
+++ b/components/autofill/content/renderer/password_generation_agent.h
@@ -140,17 +140,9 @@
       blink::WebInputElement element,
       uint32_t confirmation_password_renderer_id);
 
-  // Runs HTML parsing based classifier and saves its outcome to proto.
-  // TODO(crbug.com/621442): Remove client-side form classifier when server-side
-  // classifier is ready.
-  void RunFormClassifierAndSaveVote(const blink::WebFormElement& web_form,
-                                    const PasswordForm& form);
-
   void LogMessage(autofill::SavePasswordProgressLogger::StringID message_id);
   void LogBoolean(autofill::SavePasswordProgressLogger::StringID message_id,
                   bool truth_value);
-  void LogNumber(autofill::SavePasswordProgressLogger::StringID message_id,
-                 int number);
 
   // Creates a password form to presave a generated password. It copies behavior
   // of CreatePasswordFormFromWebForm/FromUnownedInputElements, but takes
@@ -158,9 +150,6 @@
   // If a form creating is failed, returns an empty unique_ptr.
   std::unique_ptr<PasswordForm> CreatePasswordFormToPresave();
 
-  // Element where we want to trigger automatic password generation UI on.
-  blink::WebInputElement automatic_generation_element_;
-
   // Contains the current element where generation is offered at the moment. It
   // can be either automatic or manual password generation.
   std::unique_ptr<GenerationItemInfo> current_generation_item_;
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index dbac7929..63b2369 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -683,7 +683,6 @@
 
   enum class EnabledFeature { kNone, kDesktop, kMobile };
   EnabledFeature enabled_feature_;
-  std::string icon_;
   base::test::ScopedFeatureList features_;
 };
 
@@ -699,9 +698,6 @@
 void SuggestionMatchingTest::InitializeFeatures() {
   enabled_feature_ =
       GetParam() ? EnabledFeature::kDesktop : EnabledFeature::kNone;
-  if (enabled_feature_ == EnabledFeature::kDesktop) {
-    icon_ = "accountBoxIcon";
-  }
   features_.InitWithFeatureState(
       features::kAutofillUseImprovedLabelDisambiguation, GetParam());
 }
@@ -1065,8 +1061,8 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, icon_, 1),
-                   Suggestion("Elvis", label2, icon_, 2));
+  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, "", 1),
+                   Suggestion("Elvis", label2, "", 2));
 }
 
 // Test that we return only matching address profile suggestions when the
@@ -1095,7 +1091,7 @@
       label = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Elvis", label, icon_, 1));
+  CheckSuggestions(kDefaultPageID, Suggestion("Elvis", label, "", 1));
 }
 
 // Tests that we return address profile suggestions values when the section
@@ -1144,10 +1140,9 @@
 
   switch (enabled_feature_) {
     case EnabledFeature::kDesktop:
-      CheckSuggestions(
-          kDefaultPageID,
-          Suggestion("Googler", "1600 Amphitheater pkwy", icon_, 1),
-          Suggestion("Grimes", "1234 Smith Blvd.", icon_, 2));
+      CheckSuggestions(kDefaultPageID,
+                       Suggestion("Googler", "1600 Amphitheater pkwy", "", 1),
+                       Suggestion("Grimes", "1234 Smith Blvd.", "", 2));
       break;
     case EnabledFeature::kMobile:
       // TODO(crbug.com/963630)
@@ -1194,7 +1189,7 @@
       label = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Elvis", label, icon_, 1));
+  CheckSuggestions(kDefaultPageID, Suggestion("Elvis", label, "", 1));
 }
 
 // Test that we return no suggestions when the form has no relevant fields.
@@ -1256,8 +1251,8 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, icon_, 1),
-                   Suggestion("Elvis", label2, icon_, 2));
+  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, "", 1),
+                   Suggestion("Elvis", label2, "", 2));
 }
 
 // Test that we return no suggestions when autofill is disabled.
@@ -1749,8 +1744,8 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, icon_, 1),
-                   Suggestion("Elvis", label2, icon_, 2));
+  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, "", 1),
+                   Suggestion("Elvis", label2, "", 2));
 
   const int kPageID2 = 2;
   test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
@@ -1809,8 +1804,8 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, icon_, 1),
-                   Suggestion("Elvis", label2, icon_, 2));
+  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, "", 1),
+                   Suggestion("Elvis", label2, "", 2));
 
   test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
   const int kPageID2 = 2;
@@ -1861,8 +1856,8 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, icon_, 1),
-                   Suggestion("Elvis", label2, icon_, 2));
+  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, "", 1),
+                   Suggestion("Elvis", label2, "", 2));
 }
 
 TEST_F(AutofillManagerTest,
@@ -1936,8 +1931,8 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, icon_, 1),
-                   Suggestion("Elvis", label2, icon_, 2));
+  CheckSuggestions(kDefaultPageID, Suggestion("Charles", label1, "", 1),
+                   Suggestion("Elvis", label2, "", 2));
 }
 
 // Test that nothing breaks when there are autocomplete suggestions but no
@@ -2000,7 +1995,7 @@
       label = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion("Elvis", label, icon_, 1));
+  CheckSuggestions(kDefaultPageID, Suggestion("Elvis", label, "", 1));
 }
 
 TEST_P(SuggestionMatchingTest, GetProfileSuggestions_FancyPhone) {
@@ -2051,9 +2046,9 @@
       label3 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion(value1, label1, icon_, 1),
-                   Suggestion(value2, label2, icon_, 2),
-                   Suggestion(value3, label3, icon_, 3));
+  CheckSuggestions(kDefaultPageID, Suggestion(value1, label1, "", 1),
+                   Suggestion(value2, label2, "", 2),
+                   Suggestion(value3, label3, "", 3));
 }
 
 TEST_F(AutofillManagerTest, GetProfileSuggestions_ForPhonePrefixOrSuffix) {
@@ -4215,11 +4210,11 @@
         Suggestion("Charles",
                    MakeLabel({"123 Apple St., unit 6", "23456789012",
                               "buddy@gmail.com"}),
-                   icon_, 1),
+                   "", 1),
         Suggestion("Elvis",
                    MakeLabel({"3734 Elvis Presley Blvd., Apt. 10",
                               "(234) 567-8901", "theking@gmail.com"}),
-                   icon_, 2));
+                   "", 2));
   } else {
     // Test that we sent the right values to the external delegate. Inferred
     // labels include full first relevant field, which in this case is the
@@ -5833,9 +5828,8 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID,
-                   Suggestion("buddy@gmail.com", label1, icon_, 1),
-                   Suggestion("theking@gmail.com", label2, icon_, 2));
+  CheckSuggestions(kDefaultPageID, Suggestion("buddy@gmail.com", label1, "", 1),
+                   Suggestion("theking@gmail.com", label2, "", 2));
 }
 
 // Verify that typing "apple" will match "123 Apple St." when substring matching
@@ -5871,7 +5865,7 @@
       label = "123 Apple St.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(kDefaultPageID, Suggestion(value, label, icon_, 1));
+  CheckSuggestions(kDefaultPageID, Suggestion(value, label, "", 1));
 }
 
 // Verify that typing "mail" will not match any of the "@gmail.com" email
@@ -6183,8 +6177,8 @@
 
   if (enabled_feature_ == EnabledFeature::kDesktop) {
     CheckSuggestions(kDefaultPageID,
-                     Suggestion("Shawn Smith", "1234 Smith Blvd.", icon_, 1),
-                     Suggestion("Adam Smith", "1234 Smith Blvd.", icon_, 2));
+                     Suggestion("Shawn Smith", "1234 Smith Blvd.", "", 1),
+                     Suggestion("Adam Smith", "1234 Smith Blvd.", "", 2));
   } else {
     CheckSuggestions(
         kDefaultPageID,
diff --git a/components/autofill/core/browser/payments/full_card_request.cc b/components/autofill/core/browser/payments/full_card_request.cc
index f324f30..6322c935 100644
--- a/components/autofill/core/browser/payments/full_card_request.cc
+++ b/components/autofill/core/browser/payments/full_card_request.cc
@@ -84,6 +84,7 @@
   result_delegate_ = result_delegate;
   request_.reset(new payments::PaymentsClient::UnmaskRequestDetails);
   request_->card = card;
+  request_->reason = reason;
   should_unmask_card_ = card.record_type() == CreditCard::MASKED_SERVER_CARD ||
                         (card.record_type() == CreditCard::FULL_SERVER_CARD &&
                          card.ShouldUpdateExpiration(AutofillClock::Now()));
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index 15502b4..bb7fa1dc 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -14,6 +14,7 @@
 #include "base/command_line.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -286,7 +287,7 @@
         offer_fido_opt_in && offer_fido_opt_in->GetBool();
 
     const auto* dictionary_value = response.FindKeyOfType(
-        "request_options", base::Value::Type::DICTIONARY);
+        "fido_request_options", base::Value::Type::DICTIONARY);
     if (dictionary_value)
       unmask_details_.fido_request_options = dictionary_value->Clone();
 
@@ -384,6 +385,19 @@
         net::EscapeUrlEncodedData(
             base::UTF16ToASCII(request_details_.user_response.cvc), true)
             .c_str());
+
+    // Payments is reporting receiving blank or non-standard-length CVCs.
+    // Log CVC length being sent to gauge how often this is happening.
+    if (request_details_.reason == AutofillClient::UNMASK_FOR_AUTOFILL) {
+      base::UmaHistogramCounts1000("Autofill.CardUnmask.CvcLength.ForAutofill",
+                                   request_details_.user_response.cvc.length());
+    } else if (request_details_.reason ==
+               AutofillClient::UNMASK_FOR_PAYMENT_REQUEST) {
+      base::UmaHistogramCounts1000(
+          "Autofill.CardUnmask.CvcLength.ForPaymentRequest",
+          request_details_.user_response.cvc.length());
+    }
+
     VLOG(3) << "getrealpan request body: " << request_content;
     return request_content;
   }
@@ -842,6 +856,7 @@
 PaymentsClient::UnmaskRequestDetails::UnmaskRequestDetails(
     const UnmaskRequestDetails& other) {
   billing_customer_number = other.billing_customer_number;
+  reason = other.reason;
   card = other.card;
   risk_data = other.risk_data;
   user_response = other.user_response;
diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h
index 72ac874..d979a82f 100644
--- a/components/autofill/core/browser/payments/payments_client.h
+++ b/components/autofill/core/browser/payments/payments_client.h
@@ -82,6 +82,7 @@
     ~UnmaskRequestDetails();
 
     int64_t billing_customer_number = 0;
+    AutofillClient::UnmaskCardReason reason;
     CreditCard card;
     std::string risk_data;
     CardUnmaskDelegate::UnmaskResponse user_response;
diff --git a/components/autofill/core/browser/payments/payments_client_unittest.cc b/components/autofill/core/browser/payments/payments_client_unittest.cc
index 532876a..4b73ecc 100644
--- a/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -50,6 +51,31 @@
     CreditCardSaveManager::DetectedValue::COUNTRY_CODE |
     CreditCardSaveManager::DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT;
 
+struct CardUnmaskOptions {
+  CardUnmaskOptions& with_use_fido(bool b) {
+    use_fido = b;
+    return *this;
+  }
+
+  CardUnmaskOptions& with_cvc(std::string c) {
+    cvc = c;
+    return *this;
+  }
+
+  CardUnmaskOptions& with_reason(AutofillClient::UnmaskCardReason r) {
+    reason = r;
+    return *this;
+  }
+
+  // If true, use FIDO authentication instead of CVC authentication.
+  bool use_fido = false;
+  // If not using FIDO authentication, the CVC value the user entered, to be
+  // sent to Google Payments.
+  std::string cvc = "123";
+  // The reason for unmasking this card.
+  AutofillClient::UnmaskCardReason reason = AutofillClient::UNMASK_FOR_AUTOFILL;
+};
+
 }  // namespace
 
 class PaymentsClientTest : public testing::Test {
@@ -164,16 +190,18 @@
 
   // Issue an UnmaskCard request. This requires an OAuth token before starting
   // the request.
-  void StartUnmasking(bool use_fido = false) {
+  void StartUnmasking(CardUnmaskOptions options) {
     PaymentsClient::UnmaskRequestDetails request_details;
     request_details.billing_customer_number = 111222333444;
+    request_details.reason = options.reason;
+
     request_details.card = test::GetMaskedServerCard();
     request_details.risk_data = "some risk data";
-    if (use_fido) {
+    if (options.use_fido) {
       request_details.fido_assertion_info =
           base::Value(base::Value::Type::DICTIONARY);
     } else {
-      request_details.user_response.cvc = base::ASCIIToUTF16("123");
+      request_details.user_response.cvc = base::ASCIIToUTF16(options.cvc);
     }
     client_->UnmaskCard(request_details,
                         base::BindOnce(&PaymentsClientTest::OnDidGetRealPan,
@@ -340,7 +368,7 @@
 }
 
 TEST_F(PaymentsClientTest, OAuthError) {
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
       GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
   EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
@@ -349,7 +377,7 @@
 
 TEST_F(PaymentsClientTest,
        UnmaskRequestIncludesBillingCustomerNumberInRequest) {
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
 
   // Verify that the billing customer number is included in the request.
@@ -359,7 +387,7 @@
 }
 
 TEST_F(PaymentsClientTest, UnmaskSuccessViaCVC) {
-  StartUnmasking(/*use_fido=*/false);
+  StartUnmasking(CardUnmaskOptions().with_use_fido(false));
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{ \"pan\": \"1234\" }");
   EXPECT_EQ(AutofillClient::SUCCESS, result_);
@@ -367,7 +395,7 @@
 }
 
 TEST_F(PaymentsClientTest, UnmaskSuccessViaFIDO) {
-  StartUnmasking(/*use_fido=*/true);
+  StartUnmasking(CardUnmaskOptions().with_use_fido(true));
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{ \"pan\": \"1234\" }");
   EXPECT_EQ(AutofillClient::SUCCESS, result_);
@@ -376,7 +404,7 @@
 
 TEST_F(PaymentsClientTest, UnmaskSuccessAccountFromSyncTest) {
   EnableAutofillGetPaymentsIdentityFromSync();
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{ \"pan\": \"1234\" }");
   EXPECT_EQ(AutofillClient::SUCCESS, result_);
@@ -388,7 +416,7 @@
       {features::kAutofillGetPaymentsIdentityFromSync},  // Enabled
       {features::kAutofillEnableAccountWalletStorage});  // Disabled
 
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{}");
 
@@ -403,7 +431,7 @@
       {features::kAutofillEnableAccountWalletStorage},    // Enabled
       {features::kAutofillGetPaymentsIdentityFromSync});  // Disabled
 
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{}");
 
@@ -418,7 +446,7 @@
       {features::kAutofillEnableAccountWalletStorage,
        features::kAutofillGetPaymentsIdentityFromSync});  // Disabled
 
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{}");
 
@@ -428,6 +456,39 @@
   EXPECT_TRUE(GetUploadData().find("full_sync_enabled") == std::string::npos);
 }
 
+TEST_F(PaymentsClientTest, UnmaskLogsCvcLengthForAutofill) {
+  base::HistogramTester histogram_tester;
+  StartUnmasking(CardUnmaskOptions()
+                     .with_reason(AutofillClient::UNMASK_FOR_AUTOFILL)
+                     .with_cvc("1234"));
+  IssueOAuthToken();
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CardUnmask.CvcLength.ForAutofill", 4, 1);
+}
+
+TEST_F(PaymentsClientTest, UnmaskLogsCvcLengthForPaymentRequest) {
+  base::HistogramTester histogram_tester;
+  StartUnmasking(CardUnmaskOptions()
+                     .with_reason(AutofillClient::UNMASK_FOR_PAYMENT_REQUEST)
+                     .with_cvc("56789"));
+  IssueOAuthToken();
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CardUnmask.CvcLength.ForPaymentRequest", 5, 1);
+}
+
+TEST_F(PaymentsClientTest, UnmaskLogsBlankCvcLength) {
+  base::HistogramTester histogram_tester;
+  StartUnmasking(CardUnmaskOptions()
+                     .with_reason(AutofillClient::UNMASK_FOR_AUTOFILL)
+                     .with_cvc(""));
+  IssueOAuthToken();
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.CardUnmask.CvcLength.ForAutofill", 0, 1);
+}
+
 TEST_F(PaymentsClientTest, GetDetailsSuccess) {
   StartGettingUploadDetails();
   ReturnResponse(
@@ -743,7 +804,7 @@
   // DisableAutofillSendExperimentIdsInPaymentsRPCs().
   base::FieldTrialList field_trial_list_(nullptr);
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
 
   // Note that experiment information is stored in X-Client-Data.
@@ -759,7 +820,7 @@
   DisableAutofillSendExperimentIdsInPaymentsRPCs();
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
 
   // Note that experiment information is stored in X-Client-Data.
   EXPECT_FALSE(HasVariationsHeader());
@@ -1041,13 +1102,13 @@
 }
 
 TEST_F(PaymentsClientTest, UnmaskMissingPan) {
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   ReturnResponse(net::HTTP_OK, "{}");
   EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
 }
 
 TEST_F(PaymentsClientTest, RetryFailure) {
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{ \"error\": { \"code\": \"INTERNAL\" } }");
   EXPECT_EQ(AutofillClient::TRY_AGAIN_FAILURE, result_);
@@ -1055,7 +1116,7 @@
 }
 
 TEST_F(PaymentsClientTest, PermanentFailure) {
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK,
                  "{ \"error\": { \"code\": \"ANYTHING_ELSE\" } }");
@@ -1064,7 +1125,7 @@
 }
 
 TEST_F(PaymentsClientTest, MalformedResponse) {
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{ \"error_code\": \"WRONG_JSON_FORMAT\" }");
   EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
@@ -1073,7 +1134,7 @@
 
 TEST_F(PaymentsClientTest, ReauthNeeded) {
   {
-    StartUnmasking();
+    StartUnmasking(CardUnmaskOptions());
     IssueOAuthToken();
     ReturnResponse(net::HTTP_UNAUTHORIZED, "");
     // No response yet.
@@ -1091,7 +1152,7 @@
   real_pan_.clear();
 
   {
-    StartUnmasking();
+    StartUnmasking(CardUnmaskOptions());
     // NOTE: Don't issue an access token here: the issuing of an access token
     // first waits for the access token request to be received, but here there
     // should be no access token request because PaymentsClient should reuse the
@@ -1110,7 +1171,7 @@
 }
 
 TEST_F(PaymentsClientTest, NetworkError) {
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_REQUEST_TIMEOUT, std::string());
   EXPECT_EQ(AutofillClient::NETWORK_ERROR, result_);
@@ -1118,7 +1179,7 @@
 }
 
 TEST_F(PaymentsClientTest, OtherError) {
-  StartUnmasking();
+  StartUnmasking(CardUnmaskOptions());
   IssueOAuthToken();
   ReturnResponse(net::HTTP_FORBIDDEN, std::string());
   EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index d166c9db..6d49b8b 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1200,8 +1200,8 @@
                                                             nullptr);
   }
 
-  suggestion_selection::PrepareSuggestions(formatter != nullptr, labels,
-                                           &unique_suggestions, comparator);
+  suggestion_selection::PrepareSuggestions(labels, &unique_suggestions,
+                                           comparator);
 
   return unique_suggestions;
 }
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 15c88591..aec9843 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -2650,7 +2650,7 @@
               &Suggestion::label,
               ConstructLabelLine({base::ASCIIToUTF16("(978) 674-4120"),
                                   base::ASCIIToUTF16("hoa.pham@comcast.net")})),
-          testing::Field(&Suggestion::icon, "accountBoxIcon"))));
+          testing::Field(&Suggestion::icon, ""))));
 }
 #endif  // #if !defined(OS_ANDROID) && !defined(OS_IOS)
 
@@ -2675,7 +2675,7 @@
                   testing::Field(
                       &Suggestion::label,
                       base::ASCIIToUTF16("401 Merrimack St, Lowell, MA 01852")),
-                  testing::Field(&Suggestion::icon, "accountBoxIcon"))));
+                  testing::Field(&Suggestion::icon, ""))));
 }
 #endif  // #if !defined(OS_ANDROID) && !defined(OS_IOS)
 
@@ -2701,7 +2701,7 @@
               &Suggestion::label,
               ConstructLabelLine({base::ASCIIToUTF16("(978) 674-4120"),
                                   base::ASCIIToUTF16("401 Merrimack St")})),
-          testing::Field(&Suggestion::icon, "accountBoxIcon"))));
+          testing::Field(&Suggestion::icon, ""))));
 }
 #endif  // #if !defined(OS_ANDROID) && !defined(OS_IOS)
 
@@ -2727,7 +2727,7 @@
               &Suggestion::label,
               ConstructLabelLine({base::ASCIIToUTF16("401 Merrimack St"),
                                   base::ASCIIToUTF16("hoa.pham@comcast.net")})),
-          testing::Field(&Suggestion::icon, "accountBoxIcon"))));
+          testing::Field(&Suggestion::icon, ""))));
 }
 #endif  // #if !defined(OS_ANDROID) && !defined(OS_IOS)
 
@@ -2748,11 +2748,10 @@
           AutofillType(NAME_FULL), base::string16(), false,
           std::vector<ServerFieldType>{NAME_FULL, ADDRESS_HOME_STREET_ADDRESS,
                                        EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER}),
-      ElementsAre(AllOf(
-          testing::Field(
-              &Suggestion::label,
-              ConstructLabelLine({base::ASCIIToUTF16("401 Merrimack St")})),
-          testing::Field(&Suggestion::icon, "accountBoxIcon"))));
+      ElementsAre(AllOf(testing::Field(&Suggestion::label,
+                                       ConstructLabelLine({base::ASCIIToUTF16(
+                                           "401 Merrimack St")})),
+                        testing::Field(&Suggestion::icon, ""))));
 }
 #endif  // #if !defined(OS_ANDROID) && !defined(OS_IOS)
 
@@ -2800,13 +2799,13 @@
                                  {base::ASCIIToUTF16("401 Merrimack St"),
                                   base::ASCIIToUTF16("(978) 674-4120"),
                                   base::ASCIIToUTF16("hoa.pham@comcast.net")})),
-              testing::Field(&Suggestion::icon, "accountBoxIcon")),
+              testing::Field(&Suggestion::icon, "")),
           AllOf(testing::Field(
                     &Suggestion::label,
                     ConstructLabelLine({base::ASCIIToUTF16("216 Broadway St"),
                                         base::ASCIIToUTF16("(978) 452-3366"),
                                         base::ASCIIToUTF16("hp@aol.com")})),
-                testing::Field(&Suggestion::icon, "accountBoxIcon"))));
+                testing::Field(&Suggestion::icon, ""))));
 }
 #endif  // #if !defined(OS_ANDROID) && !defined(OS_IOS)
 
diff --git a/components/autofill/core/browser/ui/accessory_sheet_data.cc b/components/autofill/core/browser/ui/accessory_sheet_data.cc
index 03593913..36eb0fb 100644
--- a/components/autofill/core/browser/ui/accessory_sheet_data.cc
+++ b/components/autofill/core/browser/ui/accessory_sheet_data.cc
@@ -17,6 +17,17 @@
       is_obfuscated_(is_obfuscated),
       selectable_(selectable) {}
 
+UserInfo::Field::Field(base::string16 display_text,
+                       base::string16 a11y_description,
+                       std::string id,
+                       bool is_obfuscated,
+                       bool selectable)
+    : display_text_(std::move(display_text)),
+      a11y_description_(std::move(a11y_description)),
+      id_(std::move(id)),
+      is_obfuscated_(is_obfuscated),
+      selectable_(selectable) {}
+
 UserInfo::Field::Field(const Field& field) = default;
 
 UserInfo::Field::Field(Field&& field) = default;
@@ -29,7 +40,7 @@
 
 bool UserInfo::Field::operator==(const UserInfo::Field& field) const {
   return display_text_ == field.display_text_ &&
-         a11y_description_ == field.a11y_description_ &&
+         a11y_description_ == field.a11y_description_ && id_ == field.id_ &&
          is_obfuscated_ == field.is_obfuscated_ &&
          selectable_ == field.selectable_;
 }
@@ -37,6 +48,7 @@
 std::ostream& operator<<(std::ostream& os, const UserInfo::Field& field) {
   os << "(display text: \"" << field.display_text() << "\", "
      << "a11y_description: \"" << field.a11y_description() << "\", "
+     << "id: \"" << field.id() << "\", "
      << "is " << (field.selectable() ? "" : "not ") << "selectable, "
      << "is " << (field.is_obfuscated() ? "" : "not ") << "obfuscated)";
   return os;
@@ -198,6 +210,30 @@
   return *this;
 }
 
+AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendField(
+    base::string16 display_text,
+    base::string16 a11y_description,
+    std::string id,
+    bool is_obfuscated,
+    bool selectable) && {
+  // Calls AppendField(...)& since |this| is an lvalue.
+  return std::move(AppendField(std::move(display_text),
+                               std::move(a11y_description), std::move(id),
+                               is_obfuscated, selectable));
+}
+
+AccessorySheetData::Builder& AccessorySheetData::Builder::AppendField(
+    base::string16 display_text,
+    base::string16 a11y_description,
+    std::string id,
+    bool is_obfuscated,
+    bool selectable) & {
+  accessory_sheet_data_.mutable_user_info_list().back().add_field(
+      UserInfo::Field(std::move(display_text), std::move(a11y_description),
+                      std::move(id), is_obfuscated, selectable));
+  return *this;
+}
+
 AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendFooterCommand(
     base::string16 display_text,
     autofill::AccessoryAction action) && {
diff --git a/components/autofill/core/browser/ui/accessory_sheet_data.h b/components/autofill/core/browser/ui/accessory_sheet_data.h
index 5d3ef31..c4fc354d 100644
--- a/components/autofill/core/browser/ui/accessory_sheet_data.h
+++ b/components/autofill/core/browser/ui/accessory_sheet_data.h
@@ -25,6 +25,11 @@
           base::string16 a11y_description,
           bool is_obfuscated,
           bool selectable);
+    Field(base::string16 display_text,
+          base::string16 a11y_description,
+          std::string id,
+          bool is_obfuscated,
+          bool selectable);
     Field(const Field& field);
     Field(Field&& field);
 
@@ -37,6 +42,8 @@
 
     const base::string16& a11y_description() const { return a11y_description_; }
 
+    const std::string& id() const { return id_; }
+
     bool is_obfuscated() const { return is_obfuscated_; }
 
     bool selectable() const { return selectable_; }
@@ -46,6 +53,7 @@
    private:
     base::string16 display_text_;
     base::string16 a11y_description_;
+    std::string id_;  // Optional, if needed to complete filling.
     bool is_obfuscated_;
     bool selectable_;
   };
@@ -184,6 +192,17 @@
                        bool is_obfuscated,
                        bool selectable) &;
 
+  Builder&& AppendField(base::string16 display_text,
+                        base::string16 a11y_description,
+                        std::string id,
+                        bool is_obfuscated,
+                        bool selectable) &&;
+  Builder& AppendField(base::string16 display_text,
+                       base::string16 a11y_description,
+                       std::string id,
+                       bool is_obfuscated,
+                       bool selectable) &;
+
   // Appends a new footer command to |accessory_sheet_data_|.
   Builder&& AppendFooterCommand(base::string16 display_text,
                                 autofill::AccessoryAction action) &&;
diff --git a/components/autofill/core/browser/ui/suggestion_selection.cc b/components/autofill/core/browser/ui/suggestion_selection.cc
index e89743f..9a8723eb 100644
--- a/components/autofill/core/browser/ui/suggestion_selection.cc
+++ b/components/autofill/core/browser/ui/suggestion_selection.cc
@@ -245,8 +245,7 @@
       num_profiles_supressed);
 }
 
-void PrepareSuggestions(bool add_profile_icon,
-                        const std::vector<base::string16>& labels,
+void PrepareSuggestions(const std::vector<base::string16>& labels,
                         std::vector<Suggestion>* suggestions,
                         const AutofillProfileComparator& comparator) {
   DCHECK_EQ(suggestions->size(), labels.size());
@@ -284,12 +283,6 @@
       // ranking. Suggestions with lower indices have a higher ranking and
       // should be kept.
       (*suggestions)[index_to_add_suggestion].label = labels[i];
-
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
-      if (add_profile_icon) {
-        (*suggestions)[index_to_add_suggestion].icon = "accountBoxIcon";
-      }
-#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
       ++index_to_add_suggestion;
     }
   }
diff --git a/components/autofill/core/browser/ui/suggestion_selection.h b/components/autofill/core/browser/ui/suggestion_selection.h
index cac66719..d012faa 100644
--- a/components/autofill/core/browser/ui/suggestion_selection.h
+++ b/components/autofill/core/browser/ui/suggestion_selection.h
@@ -66,13 +66,11 @@
 
 // Prepares a collection of Suggestions to show to the user. Adds |labels| to
 // their corresponding |suggestions| and removes duplicates, if any. A label
-// corresponds to the suggestion with the same index. Adds an icon on desktop
-// platforms when |add_profile_icon| is true.
+// corresponds to the suggestion with the same index.
 //
 // NOTE: |suggestions| are assumed to have already been sorted from most to
 // least important.
-void PrepareSuggestions(bool add_profile_icon,
-                        const std::vector<base::string16>& labels,
+void PrepareSuggestions(const std::vector<base::string16>& labels,
                         std::vector<Suggestion>* suggestions,
                         const AutofillProfileComparator& comparator);
 
diff --git a/components/autofill/core/browser/ui/suggestion_selection_unittest.cc b/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
index 783cc0c..5f73b6cc 100644
--- a/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
+++ b/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
@@ -533,8 +533,7 @@
       base::ASCIIToUTF16("2 Beyond-the-Wall Rd"),
       base::ASCIIToUTF16("2 Beyond-the-Wall Rd.")};
 
-  PrepareSuggestions(/*add_profile_icon=*/false, labels, &suggestions,
-                     comparator_);
+  PrepareSuggestions(labels, &suggestions, comparator_);
 
   // Suggestions are sorted from highest to lowest rank, so check that
   // duplicates with a lower rank are removed.
@@ -560,8 +559,7 @@
       base::ASCIIToUTF16("1 Winterfell Ln"), base::ASCIIToUTF16(""),
       base::ASCIIToUTF16("1 Winterfell Ln")};
 
-  PrepareSuggestions(/*add_profile_icon=*/false, labels, &suggestions,
-                     comparator_);
+  PrepareSuggestions(labels, &suggestions, comparator_);
 
   EXPECT_THAT(
       suggestions,
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index c17f8d8..4408e13b 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -49,6 +49,8 @@
     "actions/highlight_element_action.h",
     "actions/navigate_action.cc",
     "actions/navigate_action.h",
+    "actions/popup_message_action.cc",
+    "actions/popup_message_action.h",
     "actions/prompt_action.cc",
     "actions/prompt_action.h",
     "actions/reset_action.cc",
@@ -138,6 +140,8 @@
     "ui_controller.cc",
     "ui_controller.h",
     "ui_delegate.h",
+    "user_action.cc",
+    "user_action.h",
     "web_controller.cc",
     "web_controller.h",
   ]
@@ -166,6 +170,7 @@
     "actions/configure_bottom_sheet_action_unittest.cc",
     "actions/mock_action_delegate.cc",
     "actions/mock_action_delegate.h",
+    "actions/popup_message_action_unittest.cc",
     "actions/prompt_action_unittest.cc",
     "actions/wait_for_dom_action_unittest.cc",
     "batch_element_checker_unittest.cc",
diff --git a/components/autofill_assistant/browser/actions/action.cc b/components/autofill_assistant/browser/actions/action.cc
index e82ad03..ddbcaea 100644
--- a/components/autofill_assistant/browser/actions/action.cc
+++ b/components/autofill_assistant/browser/actions/action.cc
@@ -121,6 +121,9 @@
     case ActionProto::ActionInfoCase::kShowForm:
       out << "ShowForm";
       break;
+    case ActionProto::ActionInfoCase::kPopupMessage:
+      out << "PopupMessage";
+      break;
     case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET:
       out << "ACTION_INFO_NOT_SET";
       break;
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index a0eec45..0a175da3 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -49,6 +49,14 @@
   // the action.
   virtual std::string GetStatusMessage() = 0;
 
+  // Show a bubble / tooltip on the bottom bar. Dismisses the bubble if
+  // |message| is empty.
+  virtual void SetBubbleMessage(const std::string& message) = 0;
+
+  // Returns the current bubble / status message. Usually used to restore a
+  // message after the action.
+  virtual std::string GetBubbleMessage() = 0;
+
   // Checks one or more elements.
   virtual void RunElementChecks(BatchElementChecker* checker) = 0;
 
@@ -84,15 +92,16 @@
       ClickAction::ClickType click_type,
       base::OnceCallback<void(const ClientStatus&)> callback) = 0;
 
-  // Ask user to select one of the given suggestions.
+  // Have the UI enter the prompt mode and make the given actions available.
   //
   // While a prompt is in progress, the UI looks the same as it does between
   // scripts, even though we're in the middle of a script. This includes
   // allowing access to the touchable elements set previously, in the same
   // script.
-  virtual void Prompt(std::unique_ptr<std::vector<Chip>> chips) = 0;
+  virtual void Prompt(
+      std::unique_ptr<std::vector<UserAction>> user_actions) = 0;
 
-  // Remove all chips from the UI.
+  // Have the UI leave the prompt state and go back to its previous state.
   virtual void CancelPrompt() = 0;
 
   // Asks the user to provide the data used by UseAddressAction and
@@ -139,7 +148,7 @@
 
   // Sets selector of areas that can be manipulated:
   // - after the end of the script and before the beginning of the next script.
-  // - during the next call to SetChips()
+  // - during the next call to SetUserActions()
   // whichever comes first.
   virtual void SetTouchableElementArea(
       const ElementAreaProto& touchable_element_area) = 0;
diff --git a/components/autofill_assistant/browser/actions/get_payment_information_action.cc b/components/autofill_assistant/browser/actions/get_payment_information_action.cc
index 15f6aec..9d19efe 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action.cc
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action.cc
@@ -17,7 +17,9 @@
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 #include "components/autofill_assistant/browser/client_memory.h"
 #include "components/autofill_assistant/browser/service.pb.h"
+#include "components/strings/grit/components_strings.h"
 #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace autofill_assistant {
 
@@ -55,8 +57,17 @@
       !get_payment_information.shipping_address_name().empty();
   payment_options->request_payment_method =
       get_payment_information.ask_for_payment();
-  payment_options->confirm_button_text =
-      get_payment_information.confirm_button_text();
+
+  std::string confirm_text = get_payment_information.confirm_button_text();
+  if (confirm_text.empty()) {
+    confirm_text =
+        l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_PAYMENT_INFO_CONFIRM);
+  }
+  payment_options->confirm_chip.set_text(confirm_text);
+  payment_options->confirm_chip.set_type(HIGHLIGHTED_ACTION);
+  payment_options->confirm_direct_action =
+      get_payment_information.confirm_direct_action();
+
   switch (get_payment_information.terms_and_conditions_state()) {
     case GetPaymentInformationProto::NOT_SELECTED:
       payment_options->initial_terms_and_conditions = NOT_SELECTED;
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 365b965..814ceb21 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -54,12 +54,15 @@
 
   MOCK_METHOD1(SetStatusMessage, void(const std::string& message));
   MOCK_METHOD0(GetStatusMessage, std::string());
+  MOCK_METHOD1(SetBubbleMessage, void(const std::string& message));
+  MOCK_METHOD0(GetBubbleMessage, std::string());
   MOCK_METHOD3(ClickOrTapElement,
                void(const Selector& selector,
                     ClickAction::ClickType click_type,
                     base::OnceCallback<void(const ClientStatus&)> callback));
 
-  MOCK_METHOD1(Prompt, void(std::unique_ptr<std::vector<Chip>> chips));
+  MOCK_METHOD1(Prompt,
+               void(std::unique_ptr<std::vector<UserAction>> user_actions));
   MOCK_METHOD0(CancelPrompt, void());
 
   void FillAddressForm(
@@ -161,7 +164,8 @@
   MOCK_METHOD0(ClearInfoBox, void());
   MOCK_METHOD1(SetProgress, void(int progress));
   MOCK_METHOD1(SetProgressVisible, void(bool visible));
-  MOCK_METHOD1(SetChips, void(std::unique_ptr<std::vector<Chip>> chips));
+  MOCK_METHOD1(SetUserActions,
+               void(std::unique_ptr<std::vector<UserAction>> user_action));
   MOCK_METHOD1(SetResizeViewport, void(bool resize_viewport));
   MOCK_METHOD0(GetResizeViewport, bool());
   MOCK_METHOD1(SetPeekMode,
diff --git a/components/autofill_assistant/browser/actions/popup_message_action.cc b/components/autofill_assistant/browser/actions/popup_message_action.cc
new file mode 100644
index 0000000..ad96c3b
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/popup_message_action.cc
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/actions/popup_message_action.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+
+namespace autofill_assistant {
+
+PopupMessageAction::PopupMessageAction(const ActionProto& proto)
+    : Action(proto) {}
+
+PopupMessageAction::~PopupMessageAction() {}
+
+void PopupMessageAction::InternalProcessAction(ActionDelegate* delegate,
+                                               ProcessActionCallback callback) {
+  delegate->SetBubbleMessage(proto_.popup_message().message());
+  UpdateProcessedAction(ACTION_APPLIED);
+  std::move(callback).Run(std::move(processed_action_proto_));
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/popup_message_action.h b/components/autofill_assistant/browser/actions/popup_message_action.h
new file mode 100644
index 0000000..746d72f
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/popup_message_action.h
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_POPUP_MESSAGE_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_POPUP_MESSAGE_ACTION_H_
+
+#include "base/macros.h"
+#include "components/autofill_assistant/browser/actions/action.h"
+
+namespace autofill_assistant {
+
+// TODO: Action documentation.
+// This action shows a popup message in the bottom bar anchored at the icon.
+// The popup is just a Chrome TextBubble that is hidden when clicked. Also, a
+// subsequent popup action will replace the message in the popup bubble, not add
+// a second one.
+class PopupMessageAction : public Action {
+ public:
+  explicit PopupMessageAction(const ActionProto& proto);
+  ~PopupMessageAction() override;
+
+ private:
+  // Overrides Action:
+  void InternalProcessAction(ActionDelegate* delegate,
+                             ProcessActionCallback callback) override;
+
+  DISALLOW_COPY_AND_ASSIGN(PopupMessageAction);
+};
+
+}  // namespace autofill_assistant
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_POPUP_MESSAGE_ACTION_H_
diff --git a/components/autofill_assistant/browser/actions/popup_message_action_unittest.cc b/components/autofill_assistant/browser/actions/popup_message_action_unittest.cc
new file mode 100644
index 0000000..2765527
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/popup_message_action_unittest.cc
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/actions/popup_message_action.h"
+
+#include <string>
+#include <utility>
+
+#include "base/test/mock_callback.h"
+#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+namespace {
+
+using ::testing::InSequence;
+using ::testing::Pointee;
+using ::testing::Property;
+
+class PopupMessageActionTest : public testing::Test {
+ public:
+  void SetUp() override { prompt_proto_ = proto_.mutable_popup_message(); }
+
+ protected:
+  MockActionDelegate mock_action_delegate_;
+  base::MockCallback<Action::ProcessActionCallback> callback_;
+  ActionProto proto_;
+  PopupMessageProto* prompt_proto_;
+};
+
+TEST_F(PopupMessageActionTest, NoMessage) {
+  {
+    InSequence seq;
+
+    EXPECT_CALL(mock_action_delegate_, SetBubbleMessage(""));
+    EXPECT_CALL(
+        callback_,
+        Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  }
+  PopupMessageAction action(proto_);
+  action.ProcessAction(&mock_action_delegate_, callback_.Get());
+}
+
+TEST_F(PopupMessageActionTest, WithMessage) {
+  const std::string message = "test bubble message";
+  {
+    InSequence seq;
+
+    EXPECT_CALL(mock_action_delegate_, SetBubbleMessage(message));
+    EXPECT_CALL(
+        callback_,
+        Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  }
+  prompt_proto_->set_message(message);
+  PopupMessageAction action(proto_);
+  action.ProcessAction(&mock_action_delegate_, callback_.Get());
+}
+
+}  // namespace
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/prompt_action.cc b/components/autofill_assistant/browser/actions/prompt_action.cc
index 2853c9f..ce085aa 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action.cc
@@ -38,7 +38,7 @@
   delegate->SetStatusMessage(proto_.prompt().message());
 
   SetupPreconditions();
-  UpdateChips();
+  UpdateUserActions();
 
   if (HasNonemptyPreconditions() || HasAutoSelect()) {
     RunPeriodicChecks();
@@ -98,41 +98,29 @@
 
 void PromptAction::OnPreconditionChecksDone() {
   if (precondition_changed_)
-    UpdateChips();
+    UpdateUserActions();
 }
 
-void PromptAction::UpdateChips() {
+void PromptAction::UpdateUserActions() {
   DCHECK(callback_);  // Make sure we're still waiting for a response
 
-  auto chips = std::make_unique<std::vector<Chip>>();
+  auto user_actions = std::make_unique<std::vector<UserAction>>();
   for (int i = 0; i < proto_.prompt().choices_size(); i++) {
     auto& choice_proto = proto_.prompt().choices(i);
-    // Don't show choices with no names, icon or types; they're likely just
-    // there for auto_select_if_element_exists.
-    if (!choice_proto.has_chip() && choice_proto.name().empty() &&
-        choice_proto.chip_icon() == NO_ICON &&
-        choice_proto.chip_type() == UNKNOWN_CHIP_TYPE)
+    UserAction user_action(choice_proto.chip(), choice_proto.direct_action());
+    if (!user_action.has_triggers())
       continue;
 
-    // Hide chips whose precondition don't match.
+    // Hide actions whose preconditions don't match.
     if (!precondition_results_[i] && !choice_proto.allow_disabling())
       continue;
 
-    if (choice_proto.has_chip()) {
-      chips->emplace_back(choice_proto.chip());
-    } else {
-      chips->emplace_back();
-      chips->back().text = choice_proto.name();
-      chips->back().type = choice_proto.chip_type();
-      chips->back().icon = choice_proto.chip_icon();
-    }
-
-    chips->back().disabled = !precondition_results_[i];
-    chips->back().callback = base::BindOnce(&PromptAction::OnSuggestionChosen,
-                                            weak_ptr_factory_.GetWeakPtr(), i);
+    user_action.enabled = precondition_results_[i];
+    user_action.callback = base::BindOnce(&PromptAction::OnSuggestionChosen,
+                                          weak_ptr_factory_.GetWeakPtr(), i);
+    user_actions->emplace_back(std::move(user_action));
   }
-  SetDefaultChipType(chips.get());
-  delegate_->Prompt(std::move(chips));
+  delegate_->Prompt(std::move(user_actions));
   precondition_changed_ = false;
 }
 
diff --git a/components/autofill_assistant/browser/actions/prompt_action.h b/components/autofill_assistant/browser/actions/prompt_action.h
index be61a69..e53398a 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.h
+++ b/components/autofill_assistant/browser/actions/prompt_action.h
@@ -17,6 +17,7 @@
 #include "components/autofill_assistant/browser/batch_element_checker.h"
 #include "components/autofill_assistant/browser/chip.h"
 #include "components/autofill_assistant/browser/element_precondition.h"
+#include "components/autofill_assistant/browser/user_action.h"
 
 namespace autofill_assistant {
 
@@ -37,7 +38,7 @@
   void CheckPreconditions();
   void OnPreconditionResult(size_t choice_index, bool result);
   void OnPreconditionChecksDone();
-  void UpdateChips();
+  void UpdateUserActions();
   bool HasAutoSelect();
   void CheckAutoSelect();
   void OnAutoSelectElementExists(int choice_index, bool exists);
@@ -56,7 +57,7 @@
   std::vector<bool> precondition_results_;
 
   // true if something in precondition_results_ has changed, which means that
-  // the set of chips must be updated.
+  // the set of user actions must be updated.
   bool precondition_changed_ = false;
 
   // Batch element checker for preconditions.
diff --git a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
index 76d7c3e0..63913b2 100644
--- a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
@@ -20,6 +20,7 @@
 
 using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::ElementsAre;
 using ::testing::Eq;
 using ::testing::Invoke;
 using ::testing::IsEmpty;
@@ -45,9 +46,10 @@
           checker->Run(&mock_web_controller_);
         }));
     ON_CALL(mock_action_delegate_, Prompt(_))
-        .WillByDefault(Invoke([this](std::unique_ptr<std::vector<Chip>> chips) {
-          chips_ = std::move(chips);
-        }));
+        .WillByDefault(Invoke(
+            [this](std::unique_ptr<std::vector<UserAction>> user_actions) {
+              user_actions_ = std::move(user_actions);
+            }));
     prompt_proto_ = proto_.mutable_prompt();
   }
 
@@ -61,7 +63,7 @@
   base::MockCallback<Action::ProcessActionCallback> callback_;
   ActionProto proto_;
   PromptProto* prompt_proto_;
-  std::unique_ptr<std::vector<Chip>> chips_;
+  std::unique_ptr<std::vector<UserAction>> user_actions_;
 };
 
 TEST_F(PromptActionTest, ChoicesMissing) {
@@ -80,20 +82,20 @@
   ok_proto->set_server_payload("ok");
 
   auto* cancel_proto = prompt_proto_->add_choices();
-  cancel_proto->set_name("Cancel");
-  cancel_proto->set_chip_type(NORMAL_ACTION);
+  cancel_proto->mutable_chip()->set_text("Cancel");
+  cancel_proto->mutable_chip()->set_type(NORMAL_ACTION);
   cancel_proto->set_server_payload("cancel");
 
   PromptAction action(proto_);
   action.ProcessAction(&mock_action_delegate_, callback_.Get());
 
-  ASSERT_THAT(chips_, Pointee(SizeIs(2)));
+  ASSERT_THAT(user_actions_, Pointee(SizeIs(2)));
 
-  EXPECT_EQ("Ok", (*chips_)[0].text);
-  EXPECT_EQ(HIGHLIGHTED_ACTION, (*chips_)[0].type);
+  EXPECT_EQ("Ok", (*user_actions_)[0].chip.text);
+  EXPECT_EQ(HIGHLIGHTED_ACTION, (*user_actions_)[0].chip.type);
 
-  EXPECT_EQ("Cancel", (*chips_)[1].text);
-  EXPECT_EQ(NORMAL_ACTION, (*chips_)[1].type);
+  EXPECT_EQ("Cancel", (*user_actions_)[1].chip.text);
+  EXPECT_EQ(NORMAL_ACTION, (*user_actions_)[1].chip.type);
 
   EXPECT_CALL(
       callback_,
@@ -101,39 +103,64 @@
           Property(&ProcessedActionProto::status, ACTION_APPLIED),
           Property(&ProcessedActionProto::prompt_choice,
                    Property(&PromptProto::Choice::server_payload, "ok"))))));
-  DCHECK((*chips_)[0].callback);
-  std::move((*chips_)[0].callback).Run();
+  DCHECK((*user_actions_)[0].callback);
+  std::move((*user_actions_)[0].callback).Run();
+}
+
+TEST_F(PromptActionTest, ReportDirectAction) {
+  // Ok has a chip and a direct action.
+  auto* ok_proto = prompt_proto_->add_choices();
+  ok_proto->mutable_chip()->set_text("Ok");
+  ok_proto->mutable_direct_action()->add_names("ok");
+  ok_proto->set_server_payload("ok");
+
+  // Maybe only has a mappings to direct actions.
+  auto* maybe_proto = prompt_proto_->add_choices();
+  maybe_proto->mutable_direct_action()->add_names("maybe");
+  maybe_proto->mutable_direct_action()->add_names("I_guess");
+  maybe_proto->set_server_payload("maybe");
+
+  PromptAction action(proto_);
+  action.ProcessAction(&mock_action_delegate_, callback_.Get());
+
+  ASSERT_THAT(user_actions_, Pointee(SizeIs(2)));
+
+  EXPECT_THAT((*user_actions_)[0].direct_action_names, ElementsAre("ok"));
+  EXPECT_FALSE((*user_actions_)[0].chip.empty());
+  EXPECT_THAT((*user_actions_)[1].direct_action_names,
+              ElementsAre("maybe", "I_guess"));
+  EXPECT_TRUE((*user_actions_)[1].chip.empty());
 }
 
 TEST_F(PromptActionTest, ShowOnlyIfElementExists) {
   auto* ok_proto = prompt_proto_->add_choices();
-  ok_proto->set_name("Ok");
-  ok_proto->set_chip_type(HIGHLIGHTED_ACTION);
+  ok_proto->mutable_chip()->set_text("Ok");
+  ok_proto->mutable_chip()->set_type(HIGHLIGHTED_ACTION);
   ok_proto->set_server_payload("ok");
   ok_proto->add_show_only_if_element_exists()->add_selectors("element");
 
   PromptAction action(proto_);
   action.ProcessAction(&mock_action_delegate_, callback_.Get());
 
-  ASSERT_THAT(chips_, Pointee(IsEmpty()));
+  ASSERT_THAT(user_actions_, Pointee(IsEmpty()));
 
   EXPECT_CALL(mock_web_controller_,
               OnElementCheck(Eq(Selector({"element"})), _))
       .WillRepeatedly(RunOnceCallback<1>(true));
   task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  ASSERT_THAT(chips_, Pointee(SizeIs(1)));
+  ASSERT_THAT(user_actions_, Pointee(SizeIs(1)));
 
   EXPECT_CALL(mock_web_controller_,
               OnElementCheck(Eq(Selector({"element"})), _))
       .WillRepeatedly(RunOnceCallback<1>(false));
   task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  ASSERT_THAT(chips_, Pointee(IsEmpty()));
+  ASSERT_THAT(user_actions_, Pointee(IsEmpty()));
 }
 
 TEST_F(PromptActionTest, DisabledUnlessElementExists) {
   auto* ok_proto = prompt_proto_->add_choices();
-  ok_proto->set_name("Ok");
-  ok_proto->set_chip_type(HIGHLIGHTED_ACTION);
+  ok_proto->mutable_chip()->set_text("Ok");
+  ok_proto->mutable_chip()->set_type(HIGHLIGHTED_ACTION);
   ok_proto->set_server_payload("ok");
   ok_proto->set_allow_disabling(true);
   ok_proto->add_show_only_if_element_exists()->add_selectors("element");
@@ -145,15 +172,15 @@
               OnElementCheck(Eq(Selector({"element"})), _))
       .WillRepeatedly(RunOnceCallback<1>(true));
   task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  ASSERT_THAT(chips_, Pointee(SizeIs(1)));
-  EXPECT_FALSE((*chips_)[0].disabled);
+  ASSERT_THAT(user_actions_, Pointee(SizeIs(1)));
+  EXPECT_TRUE((*user_actions_)[0].enabled);
 
   EXPECT_CALL(mock_web_controller_,
               OnElementCheck(Eq(Selector({"element"})), _))
       .WillRepeatedly(RunOnceCallback<1>(false));
   task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  ASSERT_THAT(chips_, Pointee(SizeIs(1)));
-  EXPECT_TRUE((*chips_)[0].disabled);
+  ASSERT_THAT(user_actions_, Pointee(SizeIs(1)));
+  EXPECT_FALSE((*user_actions_)[0].enabled);
 }
 
 TEST_F(PromptActionTest, AutoSelect) {
@@ -164,7 +191,7 @@
 
   PromptAction action(proto_);
   action.ProcessAction(&mock_action_delegate_, callback_.Get());
-  EXPECT_THAT(chips_, Pointee(SizeIs(0)));
+  EXPECT_THAT(user_actions_, Pointee(SizeIs(0)));
 
   EXPECT_CALL(mock_web_controller_,
               OnElementCheck(Eq(Selector({"element"})), _))
@@ -182,8 +209,8 @@
 
 TEST_F(PromptActionTest, AutoSelectWithButton) {
   auto* ok_proto = prompt_proto_->add_choices();
-  ok_proto->set_name("Ok");
-  ok_proto->set_chip_type(HIGHLIGHTED_ACTION);
+  ok_proto->mutable_chip()->set_text("Ok");
+  ok_proto->mutable_chip()->set_type(HIGHLIGHTED_ACTION);
   ok_proto->set_server_payload("ok");
 
   auto* choice_proto = prompt_proto_->add_choices();
@@ -194,7 +221,7 @@
   PromptAction action(proto_);
   action.ProcessAction(&mock_action_delegate_, callback_.Get());
 
-  ASSERT_THAT(chips_, Pointee(SizeIs(1)));
+  ASSERT_THAT(user_actions_, Pointee(SizeIs(1)));
 
   EXPECT_CALL(mock_web_controller_,
               OnElementCheck(Eq(Selector({"element"})), _))
@@ -210,8 +237,8 @@
 
 TEST_F(PromptActionTest, Terminate) {
   auto* ok_proto = prompt_proto_->add_choices();
-  ok_proto->set_name("Ok");
-  ok_proto->set_chip_type(HIGHLIGHTED_ACTION);
+  ok_proto->mutable_chip()->set_text("Ok");
+  ok_proto->mutable_chip()->set_type(HIGHLIGHTED_ACTION);
   ok_proto->set_server_payload("ok");
   {
     PromptAction action(proto_);
@@ -219,8 +246,8 @@
   }
 
   // Chips pointing to a deleted action do nothing.
-  ASSERT_THAT(chips_, Pointee(SizeIs(1)));
-  std::move((*chips_)[0].callback).Run();
+  ASSERT_THAT(user_actions_, Pointee(SizeIs(1)));
+  std::move((*user_actions_)[0].callback).Run();
 }
 
 }  // namespace
diff --git a/components/autofill_assistant/browser/actions/show_form_action.cc b/components/autofill_assistant/browser/actions/show_form_action.cc
index 1e4dffa..ad2256a 100644
--- a/components/autofill_assistant/browser/actions/show_form_action.cc
+++ b/components/autofill_assistant/browser/actions/show_form_action.cc
@@ -44,28 +44,21 @@
   *processed_action_proto_->mutable_form_result() = *form_result;
 
   // Show "Continue" chip.
-  // TODO(crbug.com/806868): Make this chip configurable.
-  auto chips = std::make_unique<std::vector<Chip>>();
-  bool form_is_valid = IsFormValid(proto_.show_form().form(), *form_result);
-
-  if (proto_.show_form().has_chip()) {
-    chips->emplace_back(proto_.show_form().chip());
-    SetDefaultChipType(chips.get());
-  } else {
-    chips->emplace_back();
-    chips->back().text =
+  UserAction user_action =
+      UserAction(proto_.show_form().chip(), proto_.show_form().direct_action());
+  if (user_action.chip.empty()) {
+    user_action.chip.text =
         l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_PAYMENT_INFO_CONFIRM);
-    chips->back().type = HIGHLIGHTED_ACTION;
+    user_action.chip.type = HIGHLIGHTED_ACTION;
   }
+  user_action.enabled = IsFormValid(proto_.show_form().form(), *form_result);
+  user_action.callback =
+      base::BindOnce(&ShowFormAction::OnButtonClicked,
+                     weak_ptr_factory_.GetWeakPtr(), delegate);
 
-  chips->back().disabled = !form_is_valid;
-  if (form_is_valid) {
-    chips->back().callback =
-        base::BindOnce(&ShowFormAction::OnButtonClicked,
-                       weak_ptr_factory_.GetWeakPtr(), delegate);
-  }
-
-  delegate->Prompt(std::move(chips));
+  std::unique_ptr<std::vector<UserAction>> user_actions;
+  user_actions->emplace_back(std::move(user_action));
+  delegate->Prompt(std::move(user_actions));
 }
 
 bool ShowFormAction::IsFormValid(const FormProto& form,
diff --git a/components/autofill_assistant/browser/chip.cc b/components/autofill_assistant/browser/chip.cc
index e336fe16..d6ba436d 100644
--- a/components/autofill_assistant/browser/chip.cc
+++ b/components/autofill_assistant/browser/chip.cc
@@ -3,33 +3,42 @@
 // found in the LICENSE file.
 
 #include "components/autofill_assistant/browser/chip.h"
+#include "components/autofill_assistant/browser/user_action.h"
 
 namespace autofill_assistant {
 
 Chip::Chip() = default;
 Chip::~Chip() = default;
-Chip::Chip(Chip&&) = default;
-Chip& Chip::operator=(Chip&&) = default;
+Chip::Chip(const ChipProto& proto)
+    : type(proto.type()),
+      icon(proto.icon()),
+      text(proto.text()),
+      sticky(proto.sticky()) {}
 
-Chip::Chip(const ChipProto& chip_proto)
-    : type(chip_proto.type()),
-      icon(chip_proto.icon()),
-      text(chip_proto.text()),
-      sticky(chip_proto.sticky()) {}
+bool Chip::empty() const {
+  return type == UNKNOWN_CHIP_TYPE && text.empty() && icon == NO_ICON;
+}
 
-void SetDefaultChipType(std::vector<Chip>* chips) {
+void SetDefaultChipType(std::vector<UserAction>* user_actions) {
   ChipType default_type = SUGGESTION;
-  for (const Chip& chip : *chips) {
-    if (chip.type != UNKNOWN_CHIP_TYPE && chip.type != SUGGESTION) {
+  for (const UserAction& user_action : *user_actions) {
+    if (user_action.chip.empty())
+      continue;
+
+    ChipType type = user_action.chip.type;
+    if (type != UNKNOWN_CHIP_TYPE && type != SUGGESTION) {
       // If there's an action chip, assume chips with unknown type are also
       // actions.
       default_type = NORMAL_ACTION;
       break;
     }
   }
-  for (Chip& chip : *chips) {
-    if (chip.type == UNKNOWN_CHIP_TYPE) {
-      chip.type = default_type;
+  for (UserAction& user_action : *user_actions) {
+    if (user_action.chip.empty())
+      continue;
+
+    if (user_action.chip.type == UNKNOWN_CHIP_TYPE) {
+      user_action.chip.type = default_type;
     }
   }
 }
diff --git a/components/autofill_assistant/browser/chip.h b/components/autofill_assistant/browser/chip.h
index 9e3352e..4d669aa7 100644
--- a/components/autofill_assistant/browser/chip.h
+++ b/components/autofill_assistant/browser/chip.h
@@ -12,14 +12,17 @@
 #include "components/autofill_assistant/browser/service.pb.h"
 
 namespace autofill_assistant {
+struct UserAction;  // For SetDefaultChipType
 
 // A structure to represent a Chip shown in the carousel.
+//
+// Might be empty.
 struct Chip {
+  explicit Chip(const ChipProto& proto);
   Chip();
-  Chip(const ChipProto& chip_proto);
   ~Chip();
-  Chip(Chip&&);
-  Chip& operator=(Chip&&);
+
+  bool empty() const;
 
   ChipType type = UNKNOWN_CHIP_TYPE;
 
@@ -28,19 +31,13 @@
   // Localized string to display.
   std::string text;
 
-  // Callback triggered when the chip is tapped.
-  base::OnceClosure callback;
-
-  // Whether this chip is disabled.
-  bool disabled = false;
-
   // Whether this chip is sticky. A sticky chip will be a candidate to be
   // displayed in the header if the peek mode of the sheet is HANDLE_HEADER.
   bool sticky = false;
 };
 
 // Guarantees that the Chip.type of all chips is set to a sensible value.
-void SetDefaultChipType(std::vector<Chip>* chips);
+void SetDefaultChipType(std::vector<UserAction>* chips);
 
 }  // namespace autofill_assistant
 
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 9202f4b5..75bbc3e 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -140,6 +140,15 @@
   return status_message_;
 }
 
+void Controller::SetBubbleMessage(const std::string& message) {
+  bubble_message_ = message;
+  GetUiController()->OnBubbleMessageChanged(message);
+}
+
+std::string Controller::GetBubbleMessage() const {
+  return bubble_message_;
+}
+
 void Controller::SetDetails(std::unique_ptr<Details> details) {
   details_ = std::move(details);
   GetUiController()->OnDetailsChanged(details_.get());
@@ -191,42 +200,18 @@
   return progress_visible_;
 }
 
-const std::vector<Chip>& Controller::GetSuggestions() const {
-  static const base::NoDestructor<std::vector<Chip>> no_suggestions_;
-  return suggestions_ ? *suggestions_ : *no_suggestions_;
+const std::vector<UserAction>& Controller::GetUserActions() const {
+  static const base::NoDestructor<std::vector<UserAction>> no_user_actions_;
+  return user_actions_ ? *user_actions_ : *no_user_actions_;
 }
 
-const std::vector<Chip>& Controller::GetActions() const {
-  static const base::NoDestructor<std::vector<Chip>> no_actions_;
-  return actions_ ? *actions_ : *no_actions_;
-}
-
-void Controller::SetChips(std::unique_ptr<std::vector<Chip>> chips) {
-  // We split the chips into suggestions and actions, that are displayed in
-  // different carousels.
-  actions_.reset();
-  suggestions_.reset();
-
-  if (chips && !chips->empty()) {
-    for (auto iter = chips->begin(); iter != chips->end(); iter++) {
-      if (iter->type == SUGGESTION) {
-        if (!suggestions_) {
-          suggestions_ = std::make_unique<std::vector<Chip>>();
-        }
-
-        suggestions_->emplace_back(std::move(*iter));
-      } else {
-        if (!actions_) {
-          actions_ = std::make_unique<std::vector<Chip>>();
-        }
-
-        actions_->emplace_back(std::move(*iter));
-      }
-    }
+void Controller::SetUserActions(
+    std::unique_ptr<std::vector<UserAction>> user_actions) {
+  if (user_actions) {
+    SetDefaultChipType(user_actions.get());
   }
-
-  GetUiController()->OnSuggestionsChanged(GetSuggestions());
-  GetUiController()->OnActionsChanged(GetActions());
+  user_actions_ = std::move(user_actions);
+  GetUiController()->OnUserActionsChanged(GetUserActions());
 }
 
 bool Controller::IsNavigatingToNewDocument() {
@@ -249,28 +234,22 @@
     listeners_.erase(found);
 }
 
-void Controller::SelectSuggestion(int index) {
-  SelectChip(suggestions_.get(), index);
-}
-
-void Controller::SelectAction(int index) {
-  SelectChip(actions_.get(), index);
-}
-
-void Controller::SelectChip(std::vector<Chip>* chips, int chip_index) {
-  if (!chips || chip_index < 0 ||
-      static_cast<size_t>(chip_index) >= chips->size()) {
-    NOTREACHED() << "Invalid chip index: " << chip_index;
-    return;
+bool Controller::PerformUserAction(int index) {
+  if (!user_actions_ || index < 0 ||
+      static_cast<size_t>(index) >= user_actions_->size()) {
+    NOTREACHED() << "Invalid user action index: " << index;
+    return false;
   }
 
-  if (!(*chips)[chip_index].callback) {
-    return;
+  UserAction* user_action = &(*user_actions_)[index];
+  if (!user_action->enabled || !user_action->callback) {
+    return false;
   }
 
-  auto callback = std::move((*chips)[chip_index].callback);
-  SetChips(nullptr);
+  auto callback = std::move(user_action->callback);
+  SetUserActions(nullptr);
   std::move(callback).Run();
+  return true;
 }
 
 void Controller::SetResizeViewport(bool resize_viewport) {
@@ -431,7 +410,7 @@
 void Controller::EnterStoppedState() {
   ClearInfoBox();
   SetDetails(nullptr);
-  SetChips(nullptr);
+  SetUserActions(nullptr);
   SetPaymentRequestOptions(nullptr);
   EnterState(AutofillAssistantState::STOPPED);
 }
@@ -596,7 +575,7 @@
   // Runnable scripts will be checked and reported if necessary after executing
   // the script.
   script_tracker_->ClearRunnableScripts();
-  SetChips(nullptr);
+  SetUserActions(nullptr);
   // TODO(crbug.com/806868): Consider making ClearRunnableScripts part of
   // ExecuteScripts to simplify the controller.
   script_tracker()->ExecuteScript(
@@ -896,9 +875,10 @@
 }
 
 void Controller::UpdatePaymentRequestActions() {
-  // TODO(crbug.com/806868): This method uses #SetChips(), which means that
-  // updating the PR actions will also clear the suggestions. We should update
-  // the actions only if there are use cases of PR + suggestions.
+  // TODO(crbug.com/806868): This method uses #SetUserActions(), which means
+  // that updating the PR action buttons will also clear the suggestions. We
+  // should update the action buttons only if there are use cases of PR +
+  // suggestions.
   if (!payment_request_options_ || !payment_request_info_) {
     return;
   }
@@ -919,26 +899,21 @@
   bool terms_ok = payment_request_info_->terms_and_conditions != NOT_SELECTED ||
                   !payment_request_options_->request_terms_and_conditions;
 
-  bool continue_button_enabled =
+  bool confirm_button_enabled =
       contact_info_ok && shipping_address_ok && payment_method_ok && terms_ok;
 
-  auto chips = std::make_unique<std::vector<Chip>>();
-  chips->emplace_back();
-  if (!payment_request_options_->confirm_button_text.empty()) {
-    chips->back().text = payment_request_options_->confirm_button_text;
-  } else {
-    chips->back().text =
-        l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_PAYMENT_INFO_CONFIRM);
-  }
-  chips->back().type = HIGHLIGHTED_ACTION;
-  chips->back().disabled = !continue_button_enabled;
-  if (continue_button_enabled) {
-    chips->back().callback =
+  UserAction confirm(payment_request_options_->confirm_chip,
+                     payment_request_options_->confirm_direct_action);
+  confirm.enabled = confirm_button_enabled;
+  if (confirm_button_enabled) {
+    confirm.callback =
         base::BindOnce(&Controller::OnPaymentRequestContinueButtonClicked,
                        weak_ptr_factory_.GetWeakPtr());
   }
 
-  SetChips(std::move(chips));
+  auto user_actions = std::make_unique<std::vector<UserAction>>();
+  user_actions->emplace_back(std::move(confirm));
+  SetUserActions(std::move(user_actions));
 }
 
 void Controller::GetTouchableArea(std::vector<RectF>* area) const {
@@ -1010,19 +985,21 @@
   }
 
   // Update the set of scripts in the UI.
-  auto chips = std::make_unique<std::vector<Chip>>();
+  auto user_actions = std::make_unique<std::vector<UserAction>>();
   for (const auto& script : runnable_scripts) {
-    if (!script.autostart && (!script.chip.text().empty() ||
-                              script.chip.icon() != ChipIcon::NO_ICON)) {
-      chips->emplace_back(script.chip);
-      chips->back().callback =
-          base::BindOnce(&Controller::OnScriptSelected,
-                         weak_ptr_factory_.GetWeakPtr(), script.path);
-    }
-  }
-  SetDefaultChipType(chips.get());
+    UserAction user_action;
+    user_action.chip = script.chip;
+    user_action.direct_action_names = script.direct_action_names;
+    if (!user_action.has_triggers())
+      continue;
 
-  if (chips->empty() && state_ == AutofillAssistantState::STARTING) {
+    user_action.callback =
+        base::BindOnce(&Controller::OnScriptSelected,
+                       weak_ptr_factory_.GetWeakPtr(), script.path);
+    user_actions->emplace_back(std::move(user_action));
+  }
+
+  if (user_actions->empty() && state_ == AutofillAssistantState::STARTING) {
     // Continue waiting
     return;
   }
@@ -1035,7 +1012,7 @@
   } else {
     EnterState(AutofillAssistantState::PROMPT);
   }
-  SetChips(std::move(chips));
+  SetUserActions(std::move(user_actions));
 }
 
 void Controller::DidAttachInterstitialPage() {
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 11445752..b59cf091 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -24,6 +24,7 @@
 #include "components/autofill_assistant/browser/state.h"
 #include "components/autofill_assistant/browser/trigger_context.h"
 #include "components/autofill_assistant/browser/ui_delegate.h"
+#include "components/autofill_assistant/browser/user_action.h"
 #include "components/autofill_assistant/browser/web_controller.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -80,12 +81,15 @@
   void SetTouchableElementArea(const ElementAreaProto& area) override;
   void SetStatusMessage(const std::string& message) override;
   std::string GetStatusMessage() const override;
+  void SetBubbleMessage(const std::string& message) override;
+  std::string GetBubbleMessage() const override;
   void SetDetails(std::unique_ptr<Details> details) override;
   void SetInfoBox(const InfoBox& info_box) override;
   void ClearInfoBox() override;
   void SetProgress(int progress) override;
   void SetProgressVisible(bool visible) override;
-  void SetChips(std::unique_ptr<std::vector<Chip>> chips) override;
+  void SetUserActions(
+      std::unique_ptr<std::vector<UserAction>> user_actions) override;
   void SetResizeViewport(bool resize_viewport) override;
   void SetPeekMode(ConfigureBottomSheetProto::PeekMode peek_mode) override;
   bool SetForm(std::unique_ptr<FormProto> form,
@@ -109,10 +113,8 @@
   const InfoBox* GetInfoBox() const override;
   int GetProgress() const override;
   bool GetProgressVisible() const override;
-  const std::vector<Chip>& GetSuggestions() const override;
-  void SelectSuggestion(int index) override;
-  const std::vector<Chip>& GetActions() const override;
-  void SelectAction(int index) override;
+  const std::vector<UserAction>& GetUserActions() const override;
+  bool PerformUserAction(int index) override;
   std::string GetDebugContext() override;
   const PaymentRequestOptions* GetPaymentRequestOptions() const override;
   const PaymentInformation* GetPaymentRequestInformation() const override;
@@ -212,7 +214,6 @@
                               const std::vector<RectF>& touchable_areas,
                               const std::vector<RectF>& restricted_areas);
 
-  void SelectChip(std::vector<Chip>* chips, int chip_index);
   void SetOverlayColors(std::unique_ptr<OverlayColors> colors);
   void ReportNavigationStateChanged();
 
@@ -275,6 +276,9 @@
   // Current status message, may be empty.
   std::string status_message_;
 
+  // Current bubble / tooltip message, may be empty.
+  std::string bubble_message_;
+
   // Current details, may be null.
   std::unique_ptr<Details> details_;
 
@@ -287,11 +291,8 @@
   // Current visibility of the progress bar. It is initially visible.
   bool progress_visible_ = true;
 
-  // Current set of suggestions. May be null, but never empty.
-  std::unique_ptr<std::vector<Chip>> suggestions_;
-
-  // Current set of actions. May be null, but never empty.
-  std::unique_ptr<std::vector<Chip>> actions_;
+  // Current set of user actions. May be null, but never empty.
+  std::unique_ptr<std::vector<UserAction>> user_actions_;
 
   // Whether the viewport should be resized.
   bool resize_viewport_ = false;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 001fb16c..2bce1c8c6 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -27,6 +27,7 @@
 
 using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::AllOf;
 using ::testing::AnyNumber;
 using ::testing::AtLeast;
 using ::testing::Contains;
@@ -41,6 +42,7 @@
 using ::testing::Not;
 using ::testing::Pair;
 using ::testing::Pointee;
+using ::testing::Property;
 using ::testing::Return;
 using ::testing::ReturnRef;
 using ::testing::SaveArg;
@@ -126,7 +128,7 @@
       const std::string& name_and_path) {
     SupportedScriptProto* script = response->add_scripts();
     script->set_path(name_and_path);
-    script->mutable_presentation()->set_name(name_and_path);
+    script->mutable_presentation()->mutable_chip()->set_text(name_and_path);
     return script;
   }
 
@@ -243,7 +245,7 @@
   events.emplace_back(state);
 }
 
-TEST_F(ControllerTest, FetchAndRunScripts) {
+TEST_F(ControllerTest, FetchAndRunScriptsWithChip) {
   SupportsScriptResponseProto script_response;
   AddRunnableScript(&script_response, "script1");
   auto* script2 = AddRunnableScript(&script_response, "script2");
@@ -257,19 +259,57 @@
   // Offering the choices: script1 and script2
   EXPECT_EQ(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT,
             controller_->GetState());
-  EXPECT_THAT(controller_->GetSuggestions(),
-              UnorderedElementsAre(Field(&Chip::text, StrEq("script1")),
-                                   Field(&Chip::text, StrEq("script2"))));
+  EXPECT_THAT(
+      controller_->GetUserActions(),
+      UnorderedElementsAre(
+          Field(&UserAction::chip, AllOf(Field(&Chip::text, StrEq("script1")),
+                                         Field(&Chip::type, SUGGESTION))),
+          Field(&UserAction::chip, AllOf(Field(&Chip::text, StrEq("script2")),
+                                         Field(&Chip::type, SUGGESTION)))));
 
   // Choose script2 and run it successfully.
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("script2"), _, _, _, _, _))
       .WillOnce(RunOnceCallback<5>(true, ""));
-  controller_->SelectSuggestion(1);
+  EXPECT_TRUE(controller_->PerformUserAction(1));
 
   // Offering the remaining choice: script1 as script2 can only run once.
   EXPECT_EQ(AutofillAssistantState::PROMPT, controller_->GetState());
-  EXPECT_THAT(controller_->GetSuggestions(),
-              ElementsAre(Field(&Chip::text, StrEq("script1"))));
+  EXPECT_THAT(controller_->GetUserActions(),
+              ElementsAre(Field(&UserAction::chip,
+                                Field(&Chip::text, StrEq("script1")))));
+}
+
+TEST_F(ControllerTest, ReportDirectActions) {
+  SupportsScriptResponseProto script_response;
+
+  // script1 is available as a chip and a direct action.
+  auto* script1 = AddRunnableScript(&script_response, "script1");
+  script1->mutable_presentation()->mutable_direct_action()->add_names(
+      "action_1");
+
+  // script1 is available only as a direct action.
+  auto* script2 = AddRunnableScript(&script_response, "script2");
+  script2->mutable_presentation()->mutable_direct_action()->add_names(
+      "action_2");
+  script2->mutable_presentation()->clear_chip();
+
+  SetNextScriptResponse(script_response);
+
+  testing::InSequence seq;
+
+  Start("http://a.example.com/path");
+
+  // Offering the choices: script1 and script2
+  EXPECT_EQ(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT,
+            controller_->GetState());
+  EXPECT_THAT(controller_->GetUserActions(),
+              UnorderedElementsAre(
+                  AllOf(Field(&UserAction::chip, Field(&Chip::text, "script1")),
+                        Field(&UserAction::direct_action_names,
+                              ElementsAre("action_1"))),
+                  AllOf(Field(&UserAction::chip, Property(&Chip::empty, true)),
+                        Field(&UserAction::direct_action_names,
+                              ElementsAre("action_2")))));
 }
 
 TEST_F(ControllerTest, NoScripts) {
@@ -310,14 +350,14 @@
   AddRunnableScript(&script_response, "script2");
   SetNextScriptResponse(script_response);
 
-  EXPECT_CALL(mock_ui_controller_, OnSuggestionsChanged(SizeIs(2)));
+  EXPECT_CALL(mock_ui_controller_, OnUserActionsChanged(SizeIs(2)));
   Start("http://a.example.com/path");
 
   EXPECT_EQ(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT,
             controller_->GetState());
 }
 
-TEST_F(ControllerTest, ClearChipsWhenRunning) {
+TEST_F(ControllerTest, ClearUserActionsWhenRunning) {
   SupportsScriptResponseProto script_response;
   AddRunnableScript(&script_response, "script1");
   AddRunnableScript(&script_response, "script2");
@@ -328,15 +368,15 @@
   {
     testing::InSequence seq;
     // Discover 2 scripts, script1 and script2.
-    EXPECT_CALL(mock_ui_controller_, OnSuggestionsChanged(SizeIs(2)));
+    EXPECT_CALL(mock_ui_controller_, OnUserActionsChanged(SizeIs(2)));
     // Set of chips is cleared while running script1.
-    EXPECT_CALL(mock_ui_controller_, OnSuggestionsChanged(SizeIs(0)));
+    EXPECT_CALL(mock_ui_controller_, OnUserActionsChanged(SizeIs(0)));
     // This test doesn't specify what happens after that.
-    EXPECT_CALL(mock_ui_controller_, OnSuggestionsChanged(_))
+    EXPECT_CALL(mock_ui_controller_, OnUserActionsChanged(_))
         .Times(AnyNumber());
   }
   Start("http://a.example.com/path");
-  controller_->SelectSuggestion(0);
+  EXPECT_TRUE(controller_->PerformUserAction(0));
 }
 
 TEST_F(ControllerTest, ShowFirstInitialStatusMessage) {
@@ -362,7 +402,7 @@
 
   Start("http://a.example.com/path");
 
-  EXPECT_THAT(controller_->GetSuggestions(), SizeIs(4));
+  EXPECT_THAT(controller_->GetUserActions(), SizeIs(4));
   // Script3, with higher priority (lower number), wins.
   EXPECT_EQ("script3 prompt", controller_->GetStatusMessage());
 }
@@ -380,11 +420,11 @@
       .WillOnce(RunOnceCallback<5>(true, actions_response_str));
 
   Start();
-  ASSERT_THAT(controller_->GetSuggestions(), SizeIs(1));
+  ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
 
   testing::InSequence seq;
   EXPECT_CALL(fake_client_, Shutdown(Metrics::SCRIPT_SHUTDOWN));
-  controller_->SelectSuggestion(0);
+  EXPECT_TRUE(controller_->PerformUserAction(0));
 
   // Simulates Client::Shutdown(SCRIPT_SHUTDOWN)
   EXPECT_CALL(mock_ui_controller_, WillShutdown(Metrics::SCRIPT_SHUTDOWN));
@@ -402,8 +442,9 @@
         .WillRepeatedly(RunOnceCallback<2>(true, script_response_str));
 
     Start("http://a.example.com/path");
-    EXPECT_THAT(controller_->GetSuggestions(),
-                ElementsAre(Field(&Chip::text, StrEq("reset"))));
+    EXPECT_THAT(controller_->GetUserActions(),
+                ElementsAre(Field(&UserAction::chip,
+                                  Field(&Chip::text, StrEq("reset")))));
 
     // 2. Execute the "reset" script, which contains a reset action.
     ActionsResponseProto actions_response;
@@ -417,15 +458,16 @@
         std::make_unique<autofill::CreditCard>());
     EXPECT_TRUE(controller_->GetClientMemory()->has_selected_card());
 
-    controller_->SelectSuggestion(0);
+    EXPECT_TRUE(controller_->PerformUserAction(0));
 
     // Resetting should have cleared the client memory
     EXPECT_FALSE(controller_->GetClientMemory()->has_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_->GetSuggestions(),
-                ElementsAre(Field(&Chip::text, StrEq("reset"))));
+    EXPECT_THAT(controller_->GetUserActions(),
+                ElementsAre(Field(&UserAction::chip,
+                                  Field(&Chip::text, StrEq("reset")))));
 }
 
 TEST_F(ControllerTest, RefreshScriptWhenDomainChanges) {
@@ -478,13 +520,13 @@
   RunOnce(autostart);
   SetRepeatedScriptResponse(script_response);
 
-  EXPECT_CALL(mock_ui_controller_, OnSuggestionsChanged(SizeIs(0u)))
+  EXPECT_CALL(mock_ui_controller_, OnUserActionsChanged(SizeIs(0u)))
       .Times(AnyNumber());
-  EXPECT_CALL(mock_ui_controller_, OnSuggestionsChanged(SizeIs(Gt(0u))))
+  EXPECT_CALL(mock_ui_controller_, OnUserActionsChanged(SizeIs(Gt(0u))))
       .Times(0);
 
   Start("http://a.example.com/path");
-  EXPECT_THAT(controller_->GetSuggestions(), SizeIs(0));
+  EXPECT_THAT(controller_->GetUserActions(), SizeIs(0));
 }
 
 TEST_F(ControllerTest, InitialUrlLoads) {
@@ -555,8 +597,8 @@
   // Run script1: State should become RUNNING, as there's another script, then
   // go back to prompt to propose that script.
   states_.clear();
-  ASSERT_THAT(controller_->GetSuggestions(), SizeIs(2));
-  controller_->SelectSuggestion(0);
+  ASSERT_THAT(controller_->GetUserActions(), SizeIs(2));
+  EXPECT_TRUE(controller_->PerformUserAction(0));
 
   EXPECT_EQ(AutofillAssistantState::PROMPT, GetUiDelegate()->GetState());
   EXPECT_THAT(states_, ElementsAre(AutofillAssistantState::RUNNING,
@@ -565,8 +607,8 @@
   // Run script2: State should become STOPPED, as there are no more runnable
   // scripts.
   states_.clear();
-  ASSERT_THAT(controller_->GetSuggestions(), SizeIs(1));
-  controller_->SelectSuggestion(0);
+  ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
+  EXPECT_TRUE(controller_->PerformUserAction(0));
 
   EXPECT_EQ(AutofillAssistantState::STOPPED, GetUiDelegate()->GetState());
   EXPECT_THAT(states_, ElementsAre(AutofillAssistantState::RUNNING,
@@ -574,7 +616,7 @@
                                    AutofillAssistantState::STOPPED));
 
   // The cancel button is removed.
-  EXPECT_TRUE(controller_->GetActions().empty());
+  EXPECT_TRUE(controller_->GetUserActions().empty());
 }
 
 TEST_F(ControllerTest, ShowUIWhenStarting) {
@@ -825,11 +867,11 @@
                       RunOnceCallback<4>(true, "")));
 
   Start("http://a.example.com/path");
-  EXPECT_THAT(controller_->GetSuggestions(), SizeIs(1));
+  EXPECT_THAT(controller_->GetUserActions(), SizeIs(1));
 
   // Start script, which waits for some navigation event to happen after the
   // expect_navigation action has run..
-  controller_->SelectSuggestion(0);
+  EXPECT_TRUE(controller_->PerformUserAction(0));
 
   // No navigation event happened within the action timeout and the script ends.
   EXPECT_THAT(processed_actions_capture, SizeIs(0));
@@ -858,11 +900,11 @@
                       RunOnceCallback<4>(true, "")));
 
   Start("http://a.example.com/path");
-  EXPECT_THAT(controller_->GetSuggestions(), SizeIs(1));
+  EXPECT_THAT(controller_->GetUserActions(), SizeIs(1));
 
   // Start script, which waits for some navigation event to happen after the
   // expect_navigation action has run..
-  controller_->SelectSuggestion(0);
+  EXPECT_TRUE(controller_->PerformUserAction(0));
 
   // Navigation starts, but does not end, within the timeout.
   EXPECT_THAT(processed_actions_capture, SizeIs(0));
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index ee6b0e7..81f0603 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -67,6 +67,14 @@
   return status_message_;
 }
 
+void FakeScriptExecutorDelegate::SetBubbleMessage(const std::string& message) {
+  status_message_ = message;
+}
+
+std::string FakeScriptExecutorDelegate::GetBubbleMessage() const {
+  return status_message_;
+}
+
 void FakeScriptExecutorDelegate::SetDetails(std::unique_ptr<Details> details) {
   details_ = std::move(details);
 }
@@ -83,9 +91,9 @@
 
 void FakeScriptExecutorDelegate::SetProgressVisible(bool visible) {}
 
-void FakeScriptExecutorDelegate::SetChips(
-    std::unique_ptr<std::vector<Chip>> chips) {
-  chips_ = std::move(chips);
+void FakeScriptExecutorDelegate::SetUserActions(
+    std::unique_ptr<std::vector<UserAction>> user_actions) {
+  user_actions_ = std::move(user_actions);
 }
 
 void FakeScriptExecutorDelegate::SetPaymentRequestOptions(
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
index d9d766f..398a9e4 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -39,12 +39,15 @@
   void SetTouchableElementArea(const ElementAreaProto& element) override;
   void SetStatusMessage(const std::string& message) override;
   std::string GetStatusMessage() const override;
+  void SetBubbleMessage(const std::string& message) override;
+  std::string GetBubbleMessage() const override;
   void SetDetails(std::unique_ptr<Details> details) override;
   void SetInfoBox(const InfoBox& info_box) override;
   void ClearInfoBox() override;
   void SetProgress(int progress) override;
   void SetProgressVisible(bool visible) override;
-  void SetChips(std::unique_ptr<std::vector<Chip>> chips) override;
+  void SetUserActions(
+      std::unique_ptr<std::vector<UserAction>> user_actions) override;
   void SetPaymentRequestOptions(
       std::unique_ptr<PaymentRequestOptions> options) override;
   void SetResizeViewport(bool resize_viewport) override;
@@ -83,7 +86,7 @@
 
   InfoBox* GetInfoBox() { return info_box_.get(); }
 
-  std::vector<Chip>* GetChips() { return chips_.get(); }
+  std::vector<UserAction>* GetUserActions() { return user_actions_.get(); }
 
   PaymentRequestOptions* GetOptions() { return payment_request_options_.get(); }
 
@@ -110,7 +113,7 @@
   std::string status_message_;
   std::unique_ptr<Details> details_;
   std::unique_ptr<InfoBox> info_box_;
-  std::unique_ptr<std::vector<Chip>> chips_;
+  std::unique_ptr<std::vector<UserAction>> user_actions_;
   std::unique_ptr<PaymentRequestOptions> payment_request_options_;
   bool navigating_to_new_document_ = false;
   bool navigation_error_ = false;
diff --git a/components/autofill_assistant/browser/mock_ui_controller.h b/components/autofill_assistant/browser/mock_ui_controller.h
index b312c3c..20b69b5 100644
--- a/components/autofill_assistant/browser/mock_ui_controller.h
+++ b/components/autofill_assistant/browser/mock_ui_controller.h
@@ -23,11 +23,11 @@
   ~MockUiController() override;
 
   MOCK_METHOD1(OnStatusMessageChanged, void(const std::string& message));
+  MOCK_METHOD1(OnBubbleMessageChanged, void(const std::string& message));
   MOCK_METHOD1(OnStateChanged, void(AutofillAssistantState));
   MOCK_METHOD1(WillShutdown, void(Metrics::DropOutReason));
-  MOCK_METHOD1(OnSuggestionsChanged,
-               void(const std::vector<Chip>& suggestions));
-  MOCK_METHOD1(OnActionsChanged, void(const std::vector<Chip>& actions));
+  MOCK_METHOD1(OnUserActionsChanged,
+               void(const std::vector<UserAction>& user_actions));
   MOCK_METHOD1(OnPaymentRequestChanged,
                void(const PaymentRequestOptions* options));
   MOCK_METHOD1(OnDetailsChanged, void(const Details* details));
diff --git a/components/autofill_assistant/browser/payment_request.h b/components/autofill_assistant/browser/payment_request.h
index 9df555f9..9deac865 100644
--- a/components/autofill_assistant/browser/payment_request.h
+++ b/components/autofill_assistant/browser/payment_request.h
@@ -56,7 +56,8 @@
   bool request_terms_and_conditions = true;
   std::vector<std::string> supported_basic_card_networks;
   std::string default_email;
-  std::string confirm_button_text;
+  ChipProto confirm_chip;
+  DirectActionProto confirm_direct_action;
   TermsAndConditionsState initial_terms_and_conditions = NOT_SELECTED;
 
   base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback;
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 95686a0b..624e911b 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -16,6 +16,7 @@
 #include "components/autofill_assistant/browser/actions/get_payment_information_action.h"
 #include "components/autofill_assistant/browser/actions/highlight_element_action.h"
 #include "components/autofill_assistant/browser/actions/navigate_action.h"
+#include "components/autofill_assistant/browser/actions/popup_message_action.h"
 #include "components/autofill_assistant/browser/actions/prompt_action.h"
 #include "components/autofill_assistant/browser/actions/reset_action.h"
 #include "components/autofill_assistant/browser/actions/select_option_action.h"
@@ -85,33 +86,33 @@
                               std::vector<std::unique_ptr<Script>>* scripts) {
   auto script = std::make_unique<Script>();
   script->handle.path = script_proto.path();
+  if (script->handle.path.empty())
+    return;
 
   const auto& presentation = script_proto.presentation();
-  script->handle.interrupt = presentation.interrupt();
+  script->precondition = ScriptPrecondition::FromProto(
+      script_proto.path(), presentation.precondition());
+  if (!script->precondition)
+    return;
+
+  script->priority = presentation.priority();
   if (presentation.interrupt()) {
     script->handle.interrupt = true;
   } else {
     script->handle.autostart = presentation.autostart();
   }
-  script->handle.initial_prompt = presentation.initial_prompt();
-
-  if (presentation.has_chip()) {
-    script->handle.chip = presentation.chip();
+  if (script->handle.autostart) {
+    // Autostartable scripts without chip text must be skipped,
+    // but these chips must never be shown.
+    if (presentation.chip().text().empty()) {
+      return;
+    }
   } else {
-    script->handle.chip.set_text(presentation.name());
-    script->handle.chip.set_type(presentation.chip_type());
-    script->handle.chip.set_icon(presentation.chip_icon());
+    script->handle.initial_prompt = presentation.initial_prompt();
+    script->handle.chip = Chip(presentation.chip());
   }
-
-  script->precondition = ScriptPrecondition::FromProto(
-      script_proto.path(), presentation.precondition());
-  script->priority = presentation.priority();
-
-  if (script->handle.path.empty() || !script->precondition ||
-      (script->handle.chip.text().empty() &&
-       script->handle.chip.icon() == ChipIcon::NO_ICON &&
-       !script->handle.interrupt)) {
-    return;
+  for (const auto& name : presentation.direct_action().names()) {
+    script->handle.direct_action_names.emplace_back(name);
   }
   scripts->emplace_back(std::move(script));
 }
@@ -290,6 +291,10 @@
         client_action = std::make_unique<ShowFormAction>(action);
         break;
       }
+      case ActionProto::ActionInfoCase::kPopupMessage: {
+        client_action = std::make_unique<PopupMessageAction>(action);
+        break;
+      }
       case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
         DVLOG(1) << "Encountered action with ACTION_INFO_NOT_SET";
         client_action = std::make_unique<UnsupportedAction>(action);
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index 79985a91..725bb4a 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -30,16 +30,7 @@
 
 TEST(ProtocolUtilsTest, ScriptMissingPath) {
   SupportedScriptProto script;
-  script.mutable_presentation()->set_name("missing path");
-  std::vector<std::unique_ptr<Script>> scripts;
-  ProtocolUtils::AddScript(script, &scripts);
-
-  EXPECT_THAT(scripts, IsEmpty());
-}
-
-TEST(ProtocolUtilsTest, ScriptMissingName) {
-  SupportedScriptProto script;
-  script.set_path("missing name");
+  script.mutable_presentation()->mutable_chip()->set_text("missing path");
   std::vector<std::unique_ptr<Script>> scripts;
   ProtocolUtils::AddScript(script, &scripts);
 
@@ -49,33 +40,13 @@
 TEST(ProtocolUtilsTest, MinimalValidScript) {
   SupportedScriptProto script;
   script.set_path("path");
-  script.mutable_presentation()->set_name("name");
+  script.mutable_presentation()->mutable_chip()->set_text("name");
   std::vector<std::unique_ptr<Script>> scripts;
   ProtocolUtils::AddScript(script, &scripts);
 
   ASSERT_THAT(scripts, SizeIs(1));
   EXPECT_EQ("path", scripts[0]->handle.path);
-  EXPECT_EQ("name", scripts[0]->handle.chip.text());
-  EXPECT_NE(nullptr, scripts[0]->precondition);
-}
-
-TEST(ProtocolUtilsTest, OneFullyFeaturedScript) {
-  SupportedScriptProto script_proto;
-  script_proto.set_path("path");
-  auto* presentation = script_proto.mutable_presentation();
-  presentation->set_name("name");
-  presentation->set_autostart(true);
-  presentation->set_initial_prompt("prompt");
-  presentation->mutable_precondition()->add_domain("www.example.com");
-
-  std::vector<std::unique_ptr<Script>> scripts;
-  ProtocolUtils::AddScript(script_proto, &scripts);
-
-  ASSERT_THAT(scripts, SizeIs(1));
-  EXPECT_EQ("path", scripts[0]->handle.path);
-  EXPECT_EQ("name", scripts[0]->handle.chip.text());
-  EXPECT_EQ("prompt", scripts[0]->handle.initial_prompt);
-  EXPECT_TRUE(scripts[0]->handle.autostart);
+  EXPECT_EQ("name", scripts[0]->handle.chip.text);
   EXPECT_NE(nullptr, scripts[0]->precondition);
 }
 
@@ -92,7 +63,7 @@
   ProtocolUtils::AddScript(script_proto, &scripts);
   ASSERT_THAT(scripts, SizeIs(1));
   EXPECT_EQ("path", scripts[0]->handle.path);
-  EXPECT_EQ("", scripts[0]->handle.chip.text());
+  EXPECT_EQ("", scripts[0]->handle.chip.text);
   EXPECT_TRUE(scripts[0]->handle.interrupt);
 }
 
@@ -186,12 +157,11 @@
   EXPECT_TRUE(scripts.empty());
 }
 
-TEST(ProtocolUtilsTest, AddScriptValid) {
+TEST(ProtocolUtilsTest, AddScriptWithChip) {
   SupportedScriptProto script_proto;
   script_proto.set_path("path");
   auto* presentation = script_proto.mutable_presentation();
-  presentation->set_name("name");
-  presentation->set_autostart(true);
+  presentation->mutable_chip()->set_text("name");
   presentation->set_initial_prompt("prompt");
   presentation->mutable_precondition()->add_domain("www.example.com");
 
@@ -201,12 +171,62 @@
 
   EXPECT_NE(nullptr, script);
   EXPECT_EQ("path", script->handle.path);
-  EXPECT_EQ("name", script->handle.chip.text());
+  EXPECT_EQ("name", script->handle.chip.text);
   EXPECT_EQ("prompt", script->handle.initial_prompt);
+  EXPECT_FALSE(script->handle.autostart);
+  EXPECT_NE(nullptr, script->precondition);
+}
+
+TEST(ProtocolUtilsTest, AddScriptWithDirectAction) {
+  SupportedScriptProto script_proto;
+  script_proto.set_path("path");
+  auto* presentation = script_proto.mutable_presentation();
+  presentation->mutable_direct_action()->add_names("action_name");
+  presentation->mutable_precondition()->add_domain("www.example.com");
+
+  std::vector<std::unique_ptr<Script>> scripts;
+  ProtocolUtils::AddScript(script_proto, &scripts);
+  std::unique_ptr<Script> script = std::move(scripts[0]);
+
+  EXPECT_NE(nullptr, script);
+  EXPECT_EQ("path", script->handle.path);
+  EXPECT_THAT(script->handle.direct_action_names, ElementsAre("action_name"));
+  EXPECT_TRUE(script->handle.chip.empty());
+  EXPECT_FALSE(script->handle.autostart);
+  EXPECT_NE(nullptr, script->precondition);
+}
+
+TEST(ProtocolUtilsTest, AddAutostartableScript) {
+  SupportedScriptProto script_proto;
+  script_proto.set_path("path");
+  auto* presentation = script_proto.mutable_presentation();
+  presentation->mutable_chip()->set_text("name");
+  presentation->set_autostart(true);
+  presentation->mutable_precondition()->add_domain("www.example.com");
+
+  std::vector<std::unique_ptr<Script>> scripts;
+  ProtocolUtils::AddScript(script_proto, &scripts);
+  std::unique_ptr<Script> script = std::move(scripts[0]);
+
+  EXPECT_NE(nullptr, script);
+  EXPECT_EQ("path", script->handle.path);
+  EXPECT_TRUE(script->handle.chip.empty());
   EXPECT_TRUE(script->handle.autostart);
   EXPECT_NE(nullptr, script->precondition);
 }
 
+TEST(ProtocolUtilsTest, SkipAutostartableScriptWithoutName) {
+  SupportedScriptProto script_proto;
+  script_proto.set_path("path");
+  auto* presentation = script_proto.mutable_presentation();
+  presentation->set_autostart(true);
+  presentation->mutable_precondition()->add_domain("www.example.com");
+
+  std::vector<std::unique_ptr<Script>> scripts;
+  ProtocolUtils::AddScript(script_proto, &scripts);
+  EXPECT_THAT(scripts, IsEmpty());
+}
+
 TEST(ProtocolUtilsTest, ParseActionsParseError) {
   bool unused;
   std::vector<std::unique_ptr<Action>> unused_actions;
@@ -265,7 +285,7 @@
   auto* script_a = script_list->add_scripts();
   script_a->set_path("a");
   auto* presentation = script_a->mutable_presentation();
-  presentation->set_name("name");
+  presentation->mutable_chip()->set_text("name");
   presentation->mutable_precondition();
   // One invalid script.
   script_list->add_scripts();
@@ -283,7 +303,7 @@
   EXPECT_TRUE(should_update_scripts);
   EXPECT_THAT(scripts, SizeIs(1));
   EXPECT_THAT("a", Eq(scripts[0]->handle.path));
-  EXPECT_THAT("name", Eq(scripts[0]->handle.chip.text()));
+  EXPECT_THAT("name", Eq(scripts[0]->handle.chip.text));
 }
 
 }  // namespace
diff --git a/components/autofill_assistant/browser/script.h b/components/autofill_assistant/browser/script.h
index ec76746..0d07145 100644
--- a/components/autofill_assistant/browser/script.h
+++ b/components/autofill_assistant/browser/script.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "components/autofill_assistant/browser/chip.h"
 #include "components/autofill_assistant/browser/script_precondition.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 
@@ -19,7 +20,8 @@
   ScriptHandle(const ScriptHandle& orig);
   ~ScriptHandle();
 
-  ChipProto chip;
+  Chip chip;
+  std::vector<std::string> direct_action_names;
   std::string path;
   std::string initial_prompt;
 
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 62beb8c2..4146eec8 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -187,6 +187,14 @@
   return delegate_->GetStatusMessage();
 }
 
+void ScriptExecutor::SetBubbleMessage(const std::string& message) {
+  delegate_->SetBubbleMessage(message);
+}
+
+std::string ScriptExecutor::GetBubbleMessage() {
+  return delegate_->GetBubbleMessage();
+}
+
 void ScriptExecutor::ClickOrTapElement(
     const Selector& selector,
     ClickAction::ClickType click_type,
@@ -233,9 +241,10 @@
   std::move(callback).Run(std::move(card), cvc);
 }
 
-void ScriptExecutor::Prompt(std::unique_ptr<std::vector<Chip>> chips) {
+void ScriptExecutor::Prompt(
+    std::unique_ptr<std::vector<UserAction>> user_actions) {
   if (touchable_element_area_) {
-    // SetChips reproduces the end-of-script appearance and behavior during
+    // Prompt() reproduces the end-of-script appearance and behavior during
     // script execution. This includes allowing access to touchable elements,
     // set through a previous call to the focus action with touchable_elements
     // set.
@@ -251,14 +260,17 @@
 
   // We change the chips callback with a callback that cleans up the state
   // before calling the initial callback.
-  for (auto& chip : *chips) {
-    chip.callback = base::BindOnce(&ScriptExecutor::OnChosen,
-                                   weak_ptr_factory_.GetWeakPtr(),
-                                   std::move(chip.callback));
+  for (auto& user_action : *user_actions) {
+    if (!user_action.callback)
+      continue;
+
+    user_action.callback = base::BindOnce(&ScriptExecutor::OnChosen,
+                                          weak_ptr_factory_.GetWeakPtr(),
+                                          std::move(user_action.callback));
   }
 
   delegate_->EnterState(AutofillAssistantState::PROMPT);
-  delegate_->SetChips(std::move(chips));
+  delegate_->SetUserActions(std::move(user_actions));
 }
 
 void ScriptExecutor::CancelPrompt() {
@@ -266,7 +278,7 @@
   if (on_terminate_prompt_)
     std::move(on_terminate_prompt_);
 
-  delegate_->SetChips(nullptr);
+  delegate_->SetUserActions(nullptr);
   CleanUpAfterPrompt();
 }
 
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index 05faccc8..1578fba 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -111,6 +111,8 @@
       base::OnceCallback<void(ProcessedActionStatusProto)> callback) override;
   void SetStatusMessage(const std::string& message) override;
   std::string GetStatusMessage() override;
+  void SetBubbleMessage(const std::string& message) override;
+  std::string GetBubbleMessage() override;
   void ClickOrTapElement(
       const Selector& selector,
       ClickAction::ClickType click_type,
@@ -118,7 +120,7 @@
   void GetPaymentInformation(
       std::unique_ptr<PaymentRequestOptions> options) override;
   void GetFullCard(GetFullCardCallback callback) override;
-  void Prompt(std::unique_ptr<std::vector<Chip>> chips) override;
+  void Prompt(std::unique_ptr<std::vector<UserAction>> user_actions) override;
   void CancelPrompt() override;
   void FillAddressForm(
       const autofill::AutofillProfile* profile,
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index 70c8c6b..e1ef336 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -10,11 +10,11 @@
 #include <string>
 #include <vector>
 
-#include "components/autofill_assistant/browser/chip.h"
 #include "components/autofill_assistant/browser/details.h"
 #include "components/autofill_assistant/browser/info_box.h"
 #include "components/autofill_assistant/browser/payment_request.h"
 #include "components/autofill_assistant/browser/state.h"
+#include "components/autofill_assistant/browser/user_action.h"
 #include "url/gurl.h"
 
 namespace autofill {
@@ -60,6 +60,8 @@
   virtual void SetTouchableElementArea(const ElementAreaProto& element) = 0;
   virtual void SetStatusMessage(const std::string& message) = 0;
   virtual std::string GetStatusMessage() const = 0;
+  virtual void SetBubbleMessage(const std::string& message) = 0;
+  virtual std::string GetBubbleMessage() const = 0;
   virtual void SetDetails(std::unique_ptr<Details> details) = 0;
   virtual void SetInfoBox(const InfoBox& info_box) = 0;
   virtual void ClearInfoBox() = 0;
@@ -67,7 +69,8 @@
       std::unique_ptr<PaymentRequestOptions> options) = 0;
   virtual void SetProgress(int progress) = 0;
   virtual void SetProgressVisible(bool visible) = 0;
-  virtual void SetChips(std::unique_ptr<std::vector<Chip>> chips) = 0;
+  virtual void SetUserActions(
+      std::unique_ptr<std::vector<UserAction>> user_action) = 0;
   virtual bool GetResizeViewport() = 0;
   virtual void SetResizeViewport(bool resize_viewport) = 0;
   virtual void SetPeekMode(ConfigureBottomSheetProto::PeekMode peek_mode) = 0;
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 3d8db2d..b22ed20c 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -834,7 +834,7 @@
       next_actions_response.mutable_update_script_list()->add_scripts();
   script->set_path("path");
   auto* presentation = script->mutable_presentation();
-  presentation->set_name("name");
+  presentation->mutable_chip()->set_text("name");
   presentation->mutable_precondition();
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _))
@@ -849,7 +849,7 @@
   EXPECT_THAT(scripts_update_, SizeIs(1));
   EXPECT_THAT(scripts_update_count_, Eq(1));
   EXPECT_THAT("path", scripts_update_[0]->handle.path);
-  EXPECT_THAT("name", scripts_update_[0]->handle.chip.text());
+  EXPECT_THAT("name", scripts_update_[0]->handle.chip.text);
 }
 
 TEST_F(ScriptExecutorTest, UpdateScriptListShouldNotifyMultipleTimes) {
@@ -862,7 +862,7 @@
   auto* script = actions_response.mutable_update_script_list()->add_scripts();
   script->set_path("path");
   auto* presentation = script->mutable_presentation();
-  presentation->set_name("name");
+  presentation->mutable_chip()->set_text("name");
   presentation->mutable_precondition();
 
   EXPECT_CALL(mock_service_, OnGetActions(StrEq(kScriptPath), _, _, _, _, _))
@@ -900,7 +900,7 @@
   auto* script = interrupt_actions.mutable_update_script_list()->add_scripts();
   script->set_path("path");
   auto* presentation = script->mutable_presentation();
-  presentation->set_name("update_from_interrupt");
+  presentation->mutable_chip()->set_text("update_from_interrupt");
   presentation->mutable_precondition();
 
   // We expect a call from the interrupt which will update the script list and a
@@ -924,7 +924,7 @@
   EXPECT_THAT(scripts_update_, SizeIs(1));
   EXPECT_THAT(scripts_update_count_, Eq(1));
   EXPECT_THAT("path", scripts_update_[0]->handle.path);
-  EXPECT_THAT("update_from_interrupt", scripts_update_[0]->handle.chip.text());
+  EXPECT_THAT("update_from_interrupt", scripts_update_[0]->handle.chip.text);
 }
 
 TEST_F(ScriptExecutorTest, RestorePreInterruptStatusMessage) {
diff --git a/components/autofill_assistant/browser/script_tracker.cc b/components/autofill_assistant/browser/script_tracker.cc
index 3a62d64..f3b256a 100644
--- a/components/autofill_assistant/browser/script_tracker.cc
+++ b/components/autofill_assistant/browser/script_tracker.cc
@@ -26,10 +26,9 @@
               // Order of scripts with the same priority is arbitrary. Fallback
               // to ordering by name and path, arbitrarily, for the behavior to
               // be consistent across runs.
-              return std::tie(a->priority, a->handle.chip.text(),
-                              a->handle.path) < std::tie(b->priority,
-                                                         b->handle.chip.text(),
-                                                         a->handle.path);
+              return std::tie(a->priority, a->handle.chip.text,
+                              a->handle.path) <
+                     std::tie(b->priority, b->handle.chip.text, a->handle.path);
             });
 }
 
@@ -79,9 +78,8 @@
   batch_element_checker_ = std::make_unique<BatchElementChecker>();
   for (const auto& entry : available_scripts_) {
     Script* script = entry.first;
-    if (script->handle.chip.text().empty() &&
-        script->handle.chip.icon() == ChipIcon::NO_ICON &&
-        !script->handle.autostart)
+    if (script->handle.chip.empty() &&
+        script->handle.direct_action_names.empty() && !script->handle.autostart)
       continue;
 
     script->precondition->Check(
@@ -158,11 +156,16 @@
   std::vector<base::Value> runnable_scripts_js;
   for (const auto& entry : runnable_scripts_) {
     base::Value script_js = base::Value(base::Value::Type::DICTIONARY);
-    script_js.SetKey("name", base::Value(entry.chip.text()));
+    script_js.SetKey("name", base::Value(entry.chip.text));
     script_js.SetKey("path", base::Value(entry.path));
     script_js.SetKey("initial_prompt", base::Value(entry.initial_prompt));
     script_js.SetKey("autostart", base::Value(entry.autostart));
-    script_js.SetKey("chip_type", base::Value(entry.chip.type()));
+    script_js.SetKey("chip_type", base::Value(entry.chip.type));
+    std::vector<base::Value> direct_action_names;
+    for (const auto& name : entry.direct_action_names) {
+      direct_action_names.emplace_back(base::Value(name));
+    }
+    script_js.SetKey("direct_action_names", base::Value(direct_action_names));
     runnable_scripts_js.push_back(std::move(script_js));
   }
   dict.SetKey("runnable-scripts", base::Value(runnable_scripts_js));
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index a666ca9c..1df47dc 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -85,7 +85,7 @@
                                   const std::string& selector) {
     SupportedScriptProto* script = AddScript();
     script->set_path(path);
-    script->mutable_presentation()->set_name(name);
+    script->mutable_presentation()->mutable_chip()->set_text(name);
     if (!selector.empty()) {
       script->mutable_presentation()
           ->mutable_precondition()
@@ -150,7 +150,7 @@
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
-  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text());
+  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text);
   EXPECT_EQ("runnable path", runnable_scripts()[0].path);
   EXPECT_EQ(0, no_runnable_scripts_anymore_);
 }
@@ -171,7 +171,7 @@
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
-  EXPECT_EQ("with name", runnable_scripts()[0].chip.text());
+  EXPECT_EQ("with name", runnable_scripts()[0].chip.text);
 }
 
 TEST_F(ScriptTrackerTest, OrderScriptsByPriority) {
@@ -182,12 +182,12 @@
 
   SupportedScriptProto* b = AddScript();
   b->set_path("b");
-  b->mutable_presentation()->set_name("b");
+  b->mutable_presentation()->mutable_chip()->set_text("b");
   b->mutable_presentation()->set_priority(3);
 
   SupportedScriptProto* c = AddScript();
   c->set_path("c");
-  c->mutable_presentation()->set_name("c");
+  c->mutable_presentation()->mutable_chip()->set_text("c");
   c->mutable_presentation()->set_priority(1);
 
   SetAndCheckScripts();
@@ -291,7 +291,7 @@
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
-  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text());
+  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text);
   EXPECT_EQ("runnable path", runnable_scripts()[0].path);
 
   // 2. Run the action and trigger a script list update.
@@ -318,9 +318,9 @@
   // 3. Verify that the runnable scripts have changed to the updated list.
   EXPECT_EQ(2, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(2));
-  EXPECT_EQ("update name", runnable_scripts()[0].chip.text());
+  EXPECT_EQ("update name", runnable_scripts()[0].chip.text);
   EXPECT_EQ("update path", runnable_scripts()[0].path);
-  EXPECT_EQ("update name 2", runnable_scripts()[1].chip.text());
+  EXPECT_EQ("update name 2", runnable_scripts()[1].chip.text);
   EXPECT_EQ("update path 2", runnable_scripts()[1].path);
 }
 
@@ -332,7 +332,7 @@
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
-  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text());
+  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text);
   EXPECT_EQ("runnable path", runnable_scripts()[0].path);
 
   // 2. Run the interrupt action and trigger a script list update from an
@@ -360,9 +360,9 @@
   // 3. Verify that the runnable scripts have changed to the updated list.
   EXPECT_EQ(2, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(2));
-  EXPECT_EQ("update name", runnable_scripts()[0].chip.text());
+  EXPECT_EQ("update name", runnable_scripts()[0].chip.text);
   EXPECT_EQ("update path", runnable_scripts()[0].path);
-  EXPECT_EQ("update name 2", runnable_scripts()[1].chip.text());
+  EXPECT_EQ("update name 2", runnable_scripts()[1].chip.text);
   EXPECT_EQ("update path 2", runnable_scripts()[1].path);
 }
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index c24aa88..42207d4 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -120,10 +120,6 @@
   optional string path = 1;
 
   message PresentationProto {
-    // Script name.
-    // Deprecated, use chip.text instead.
-    optional string name = 1 [deprecated = true];
-
     // Precondition contains a set of conditions that must hold for a script to
     // be executed. No precondition means that a script can run in any case.
     optional ScriptPreconditionProto precondition = 3;
@@ -138,21 +134,13 @@
     // priority 1.
     optional int32 priority = 5;
 
-    // Describes the chip to be shown. The name of the chip is set by the
-    // name field.
-    // Deprecated, use chip.type instead.
-    optional ChipType chip_type = 10 [deprecated = true];
-
-    // An icon the should be shown next to the chip text.
-    // Deprecated, use chip.icon instead.
-    optional ChipIcon chip_icon = 11 [deprecated = true];
-
-    // The chip to display. This chip has precedence over name, chip_type and
-    // chip_icon. Not required if autostart is true.
+    // An optional chip to display.
     optional ChipProto chip = 12;
 
-    // When set to true this script can be run in 'autostart mode'. Chip won't
-    // be shown; name and chip_type are ignored.
+    // Optionally map this script to a direct action.
+    optional DirectActionProto direct_action = 13;
+
+    // When set to true this script can be run in 'autostart mode'.
     optional bool autostart = 8;
 
     // When set to true this script will be run from WaitForDom actions with
@@ -406,6 +394,7 @@
     WaitForNavigationProto wait_for_navigation = 41;
     ConfigureBottomSheetProto configure_bottom_sheet = 42;
     ShowFormProto show_form = 43;
+    PopupMessageProto popup_message = 44;
   }
 
   // Set to true to make the client remove any contextual information if the
@@ -944,31 +933,20 @@
   // Display this message to the user.
   optional string message = 1;
 
-  // A choice that is made either directly by clicking on a chip or chip, or
-  // implicitly by making a change on the website that is then detected by
-  // looking for the existence of an element.
+  // A choice that can be triggered:
+  // - by clicking on a chip, if a chip is set
+  // - by triggering a direct action, if direct action names are set
+  // - indirectly, DOM change, if auto_select_if_element_exists if set
   //
   // One of these protos must is transmitted as-is back to the server as part of
   // ProcessedActionProto.
   message Choice {
-    // Localized text message to display. Not required if
-    // auto_select_if_element_exists is set.
-    // Deprecated, use chip.text instead.
-    optional string name = 2 [deprecated = true];
-
-    // Describes the chip to be shown. The name of the chip is set by the
-    // name field.
-    // Deprecated, use chip.type instead.
-    optional ChipType chip_type = 7 [deprecated = true];
-
-    // An icon the should be shown next to the chip text.
-    // Deprecated, use chip.icon instead.
-    optional ChipIcon chip_icon = 10 [deprecated = true];
-
-    // The chip to display. This chip has precedence over name, chip_type and
-    // chip_icon. Not required if auto_select_if_element_exists is set.
+    // The chip to display to the UI.
     optional ChipProto chip = 11;
 
+    // Map this choice to a direct action.
+    optional DirectActionProto direct_action = 12;
+
     // Auto-select this choice if the given element exist.
     optional ElementReferenceProto auto_select_if_element_exists = 4;
 
@@ -1033,6 +1011,8 @@
   optional TermsAndConditionsState terms_and_conditions_state = 8;
   // When 'false', hide the terms and conditions box in the UI.
   optional bool request_terms_and_conditions = 9 [default = true];
+  // Optionally allows confiriming through the given direct actions.
+  optional DirectActionProto confirm_direct_action = 10;
 }
 
 // Resets Autofill Assistant: clears any state and server payload.
@@ -1275,6 +1255,9 @@
   // The chip to display below the form. This chip will be enabled only if all
   // form inputs are valid.
   optional ChipProto chip = 2;
+
+  // Optionally allow confirming the prompt using given direct actions.
+  optional DirectActionProto direct_action = 3;
 }
 
 message FormProto {
@@ -1458,3 +1441,17 @@
   // input valid.
   optional int32 min_selected_choices = 4 [default = 1];
 }
+
+// Defines a mapping to an Android Q direct action.
+message DirectActionProto {
+  // Map to direct actions with the given names.
+  repeated string names = 12;
+}
+
+// Action to show a popup bubble on top of the bottom sheet, anchored at the
+// icon. Setting an empty message will remove any existing and visible popup
+// message. A second action replaces a previously shown message.
+message PopupMessageProto {
+  // Message to show in the popup.
+  optional string message = 1;
+}
diff --git a/components/autofill_assistant/browser/ui_controller.cc b/components/autofill_assistant/browser/ui_controller.cc
index 3c77a5ca..1dd1af2 100644
--- a/components/autofill_assistant/browser/ui_controller.cc
+++ b/components/autofill_assistant/browser/ui_controller.cc
@@ -14,9 +14,10 @@
 
 void UiController::OnStateChanged(AutofillAssistantState new_state) {}
 void UiController::OnStatusMessageChanged(const std::string& message) {}
+void UiController::OnBubbleMessageChanged(const std::string& message) {}
 void UiController::WillShutdown(Metrics::DropOutReason reason) {}
-void UiController::OnSuggestionsChanged(const std::vector<Chip>& suggestions) {}
-void UiController::OnActionsChanged(const std::vector<Chip>& actions) {}
+void UiController::OnUserActionsChanged(
+    const std::vector<UserAction>& user_actions) {}
 void UiController::OnPaymentRequestOptionsChanged(
     const PaymentRequestOptions* options) {}
 void UiController::OnPaymentRequestInformationChanged(
diff --git a/components/autofill_assistant/browser/ui_controller.h b/components/autofill_assistant/browser/ui_controller.h
index 808ec13..2d897d9 100644
--- a/components/autofill_assistant/browser/ui_controller.h
+++ b/components/autofill_assistant/browser/ui_controller.h
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "base/callback_forward.h"
-#include "components/autofill_assistant/browser/chip.h"
 #include "components/autofill_assistant/browser/details.h"
 #include "components/autofill_assistant/browser/info_box.h"
 #include "components/autofill_assistant/browser/metrics.h"
@@ -18,6 +17,7 @@
 #include "components/autofill_assistant/browser/script.h"
 #include "components/autofill_assistant/browser/state.h"
 #include "components/autofill_assistant/browser/ui_delegate.h"
+#include "components/autofill_assistant/browser/user_action.h"
 #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
 
 namespace autofill_assistant {
@@ -37,17 +37,18 @@
   // Report that the status message has changed.
   virtual void OnStatusMessageChanged(const std::string& message);
 
+  // Report that the bubble / tooltip message has changed.
+  virtual void OnBubbleMessageChanged(const std::string& message);
+
   // Autofill Assistant is about to be shut down for this tab.
   //
   // Pointer to UIDelegate will become invalid as soon as this method has
   // returned.
   virtual void WillShutdown(Metrics::DropOutReason reason);
 
-  // Report that the set of suggestions has changed.
-  virtual void OnSuggestionsChanged(const std::vector<Chip>& suggestions);
-
-  // Report that the set of actions has changed.
-  virtual void OnActionsChanged(const std::vector<Chip>& actions);
+  // Report that the set of user actions has changed.
+  virtual void OnUserActionsChanged(
+      const std::vector<UserAction>& user_actions);
 
   // Gets or clears request for payment information.
   virtual void OnPaymentRequestOptionsChanged(
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index 1931600..4502d5ef 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -14,6 +14,7 @@
 #include "components/autofill_assistant/browser/payment_request.h"
 #include "components/autofill_assistant/browser/rectf.h"
 #include "components/autofill_assistant/browser/state.h"
+#include "components/autofill_assistant/browser/user_action.h"
 
 namespace autofill_assistant {
 
@@ -50,6 +51,9 @@
   // Returns the current status message.
   virtual std::string GetStatusMessage() const = 0;
 
+  // Returns the current bubble / tooltip message.
+  virtual std::string GetBubbleMessage() const = 0;
+
   // Returns the current contextual information. May be null if empty.
   virtual const Details* GetDetails() const = 0;
 
@@ -62,18 +66,14 @@
   // Returns whether the progress bar is visible.
   virtual bool GetProgressVisible() const = 0;
 
-  // Returns the current set of suggestions.
-  virtual const std::vector<Chip>& GetSuggestions() const = 0;
+  // Returns the current set of user actions.
+  virtual const std::vector<UserAction>& GetUserActions() const = 0;
 
-  // Selects a suggestion, from the set of suggestions returned by
-  // GetSuggestions().
-  virtual void SelectSuggestion(int suggestion) = 0;
-
-  // Returns the current set of actions.
-  virtual const std::vector<Chip>& GetActions() const = 0;
-
-  // Selects an action, from the set of actions returned by GetActions().
-  virtual void SelectAction(int action) = 0;
+  // Performs an action, from the set of actions returned by GetUserAction().
+  //
+  // Returns true if the action was triggered, false if the index did not
+  // correspond to any enabled actions.
+  virtual bool PerformUserAction(int index) = 0;
 
   // If the controller is waiting for payment request information, this
   // field contains a non-null options describing the request.
diff --git a/components/autofill_assistant/browser/user_action.cc b/components/autofill_assistant/browser/user_action.cc
new file mode 100644
index 0000000..3ea6fb0b
--- /dev/null
+++ b/components/autofill_assistant/browser/user_action.cc
@@ -0,0 +1,27 @@
+// 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/autofill_assistant/browser/user_action.h"
+
+namespace autofill_assistant {
+
+UserAction::UserAction(UserAction&& other) = default;
+UserAction::UserAction() = default;
+UserAction::~UserAction() = default;
+UserAction& UserAction::operator=(UserAction&& other) = default;
+
+// Initializes user action from proto.
+UserAction::UserAction(const ChipProto& chip_proto,
+                       const DirectActionProto& direct_action)
+    : chip(chip_proto) {
+  for (const std::string& name : direct_action.names()) {
+    direct_action_names.emplace_back(name);
+  }
+}
+
+bool UserAction::has_triggers() const {
+  return !chip.empty() || !direct_action_names.empty();
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/user_action.h b/components/autofill_assistant/browser/user_action.h
new file mode 100644
index 0000000..6ff74df
--- /dev/null
+++ b/components/autofill_assistant/browser/user_action.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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_USER_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_USER_ACTION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "components/autofill_assistant/browser/chip.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+
+namespace autofill_assistant {
+
+// An action that the user can perform, through the UI or, on Android Q, through
+// a direct action.
+struct UserAction {
+  UserAction(UserAction&&);
+  UserAction();
+  ~UserAction();
+  UserAction& operator=(UserAction&&);
+
+  // Initializes user action from proto.
+  UserAction(const ChipProto& chip, const DirectActionProto& direct_action);
+
+  // Returns true if the action has no trigger, that is, there is no chip and no
+  // direct action.
+  bool has_triggers() const;
+
+  // Specifies how the user can perform the action through the UI. Might be
+  // empty.
+  Chip chip;
+
+  // Names of the direct action under which this action is available. Optional.
+  std::vector<std::string> direct_action_names;
+
+  // Whether the action is enabled. The chip for a disabled action might still
+  // be shown.
+  bool enabled = true;
+
+  // Callback triggered to trigger the action.
+  base::OnceClosure callback;
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_USER_ACTION_H_
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc
index b5087f2..32c479a 100644
--- a/components/autofill_assistant/browser/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -414,7 +414,15 @@
                           int initial_container_scroll_y) {
     EXPECT_TRUE(content::ExecJs(
         shell(), base::StringPrintf(
-                     R"(window.scrollTo(0, %d);
+                     R"(
+           // Make sure scrolling is necessary, no matter the screen height
+           let before = document.querySelector("#before_scroll_container");
+           before.style.height = window.innerHeight + "px";
+           let after = document.querySelector("#after_scroll_container");
+           after.style.height = window.innerHeight + "px";
+
+           // Initial scrolling position
+           window.scrollTo(0, %d);
            let container = document.querySelector("#scroll_container");
            container.scrollTo(0, %d);)",
                      initial_window_scroll_y, initial_container_scroll_y)));
@@ -861,16 +869,14 @@
   EXPECT_EQ(true, content::EvalJs(shell(), checkVisibleScript));
 }
 
-// Disabled: https://crbug.com/970219
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
-                       DISABLED_FocusElementWithScrollIntoViewNeeded) {
+                       FocusElementWithScrollIntoViewNeeded) {
   TestScrollIntoView(/* initial_window_scroll_y= */ 0,
                      /* initial_container_scroll_y=*/0);
 }
 
-// Disabled: https://crbug.com/970219
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
-                       DISABLED_FocusElementWithScrollIntoViewNotNeeded) {
+                       FocusElementWithScrollIntoViewNotNeeded) {
   TestScrollIntoView(/* initial_window_scroll_y= */ 0,
                      /* initial_container_scroll_y=*/200);
 }
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index adc8715..5a0fffef 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -259,6 +259,9 @@
     <message name="IDS_MANUAL_FILLING_CREDIT_CARD_SHEET_ALL_ADDRESSES_LINK" desc="The text of the link in the address sheet that opens the address management section.">
       Manage payment methods
     </message>
+    <message name="IDS_MANUAL_FILLING_CREDIT_CARD_SHEET_EMPTY_MESSAGE" desc="Message indicating that no payment methods (e.g., credit cards) are available for filling.">
+      No saved payment methods
+    </message>
     <message name="IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE" desc="Message indicating that no addresses are available for filling.">
       No saved addresses
     </message>
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskScheduler.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskScheduler.java
index efdf3aa..a729201 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskScheduler.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskScheduler.java
@@ -5,6 +5,7 @@
 package org.chromium.components.background_task_scheduler;
 
 import android.content.Context;
+import android.support.annotation.MainThread;
 
 /**
  * A BackgroundTaskScheduler is used to schedule jobs that run in the background.
@@ -23,6 +24,7 @@
      * @return true if the schedule operation succeeded, and false otherwise.
      * @see TaskInfo
      */
+    @MainThread
     boolean schedule(Context context, TaskInfo taskInfo);
 
     /**
@@ -31,6 +33,7 @@
      * @param context the current context.
      * @param taskId the ID of the task to cancel. See {@link TaskIds} for a list.
      */
+    @MainThread
     void cancel(Context context, int taskId);
 
     /**
@@ -40,11 +43,13 @@
      *
      * @param context the current context.
      */
+    @MainThread
     void checkForOSUpgrade(Context context);
 
     /**
      * Reschedules all the tasks currently scheduler through BackgroundTaskSheduler.
      * @param context the current context.
      */
+    @MainThread
     void reschedule(Context context);
 }
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerImpl.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerImpl.java
index b00f636..853180b 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerImpl.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerImpl.java
@@ -66,6 +66,7 @@
     @Override
     public void checkForOSUpgrade(Context context) {
         try (TraceEvent te = TraceEvent.scoped("BackgroundTaskScheduler.checkForOSUpgrade")) {
+            ThreadUtils.assertOnUiThread();
             int oldSdkInt = BackgroundTaskSchedulerPrefs.getLastSdkVersion();
             int newSdkInt = Build.VERSION.SDK_INT;
 
@@ -98,6 +99,7 @@
     @Override
     public void reschedule(Context context) {
         try (TraceEvent te = TraceEvent.scoped("BackgroundTaskScheduler.reschedule")) {
+            ThreadUtils.assertOnUiThread();
             Set<String> scheduledTasksClassNames = BackgroundTaskSchedulerPrefs.getScheduledTasks();
             BackgroundTaskSchedulerPrefs.removeAllTasks();
             for (String className : scheduledTasksClassNames) {
diff --git a/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc
index 1e33ee4..694231e 100644
--- a/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc
+++ b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc
@@ -125,6 +125,13 @@
   TRACE_EVENT0("jpeg", "MojoMjpegDecodeAcceleratorService::Decode");
 
   DCHECK_EQ(decode_cb_map_.count(input_buffer.id()), 0u);
+  if (decode_cb_map_.count(input_buffer.id()) != 0) {
+    NotifyDecodeStatus(
+        input_buffer.id(),
+        ::chromeos_camera::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT);
+    return;
+  }
+
   decode_cb_map_[input_buffer.id()] = std::move(callback);
 
   if (!VerifyDecodeParams(coded_size, &output_handle, output_buffer_size)) {
@@ -245,6 +252,11 @@
 
   auto iter = decode_cb_map_.find(bitstream_buffer_id);
   DCHECK(iter != decode_cb_map_.end());
+  if (iter == decode_cb_map_.end()) {
+    // Silently ignoring abnormal case.
+    return;
+  }
+
   DecodeCallback decode_cb = std::move(iter->second);
   decode_cb_map_.erase(iter);
   std::move(decode_cb).Run(bitstream_buffer_id, error);
diff --git a/components/component_updater/component_updater_paths.h b/components/component_updater/component_updater_paths.h
index d13b1dbb..4ed73d7 100644
--- a/components/component_updater/component_updater_paths.h
+++ b/components/component_updater/component_updater_paths.h
@@ -32,6 +32,7 @@
   DIR_RECOVERY_BASE,               // The Recovery.
   DIR_SWIFT_SHADER,                // The SwiftShader.
   DIR_SUPERVISED_USER_WHITELISTS,  // The Supervised user whitelists.
+  DIR_ON_DEVICE_HEAD_SUGGEST,      // The On Device Head Suggest model.
   PATH_END
 };
 
diff --git a/components/consent_auditor/consent_auditor.h b/components/consent_auditor/consent_auditor.h
index 187212da..ff46df2 100644
--- a/components/consent_auditor/consent_auditor.h
+++ b/components/consent_auditor/consent_auditor.h
@@ -30,7 +30,7 @@
   PLAY_STORE = 1,
   BACKUP_AND_RESTORE = 2,
   GOOGLE_LOCATION_SERVICE = 3,
-  CHROME_UNIFIED_CONSENT = 4,
+  // CHROME_UNIFIED_CONSENT = 4, (deprecated, not used)
   ASSISTANT_ACTIVITY_CONTROL = 5,
 
   FEATURE_LAST = ASSISTANT_ACTIVITY_CONTROL
@@ -74,12 +74,6 @@
       const std::string& account_id,
       const sync_pb::UserConsentTypes::SyncConsent& consent) = 0;
 
-  // Records the Chrome Unified |consent| for the signed-in GAIA account with
-  // the ID |accounts_id| (as defined in Account Info).
-  virtual void RecordUnifiedConsent(
-      const std::string& account_id,
-      const sync_pb::UserConsentTypes::UnifiedConsent& consent) = 0;
-
   // Records the Assistant activity control |consent| for the signed-in GAIA
   // account with the ID |accounts_id| (as defined in Account Info).
   virtual void RecordAssistantActivityControlConsent(
diff --git a/components/consent_auditor/consent_auditor_impl.cc b/components/consent_auditor/consent_auditor_impl.cc
index 860303d..455d433 100644
--- a/components/consent_auditor/consent_auditor_impl.cc
+++ b/components/consent_auditor/consent_auditor_impl.cc
@@ -118,18 +118,6 @@
   consent_sync_bridge_->RecordConsent(std::move(specifics));
 }
 
-void ConsentAuditorImpl::RecordUnifiedConsent(
-    const std::string& account_id,
-    const sync_pb::UserConsentTypes::UnifiedConsent& consent) {
-  std::unique_ptr<sync_pb::UserConsentSpecifics> specifics =
-      CreateUserConsentSpecifics(account_id, app_locale_, clock_);
-
-  sync_pb::UserConsentTypes::UnifiedConsent* unified_consent =
-      specifics->mutable_unified_consent();
-  unified_consent->CopyFrom(consent);
-  consent_sync_bridge_->RecordConsent(std::move(specifics));
-}
-
 void ConsentAuditorImpl::RecordAssistantActivityControlConsent(
     const std::string& account_id,
     const sync_pb::UserConsentTypes::AssistantActivityControlConsent& consent) {
diff --git a/components/consent_auditor/consent_auditor_impl.h b/components/consent_auditor/consent_auditor_impl.h
index e10671d..57bb5d44 100644
--- a/components/consent_auditor/consent_auditor_impl.h
+++ b/components/consent_auditor/consent_auditor_impl.h
@@ -56,9 +56,6 @@
   void RecordSyncConsent(
       const std::string& account_id,
       const sync_pb::UserConsentTypes::SyncConsent& consent) override;
-  void RecordUnifiedConsent(
-      const std::string& account_id,
-      const sync_pb::UserConsentTypes::UnifiedConsent& consent) override;
   void RecordAssistantActivityControlConsent(
       const std::string& account_id,
       const sync_pb::UserConsentTypes::AssistantActivityControlConsent& consent)
diff --git a/components/consent_auditor/fake_consent_auditor.cc b/components/consent_auditor/fake_consent_auditor.cc
index aaeade3..4543bfc4 100644
--- a/components/consent_auditor/fake_consent_auditor.cc
+++ b/components/consent_auditor/fake_consent_auditor.cc
@@ -43,12 +43,6 @@
                     ConvertConsentStatus(consent.status()));
 }
 
-void FakeConsentAuditor::RecordUnifiedConsent(
-    const std::string& account_id,
-    const sync_pb::UserConsentTypes::UnifiedConsent& consent) {
-  NOTIMPLEMENTED();
-}
-
 void FakeConsentAuditor::RecordAssistantActivityControlConsent(
     const std::string& account_id,
     const sync_pb::UserConsentTypes::AssistantActivityControlConsent& consent) {
diff --git a/components/consent_auditor/fake_consent_auditor.h b/components/consent_auditor/fake_consent_auditor.h
index 3fa4fcc..8a00db1 100644
--- a/components/consent_auditor/fake_consent_auditor.h
+++ b/components/consent_auditor/fake_consent_auditor.h
@@ -37,9 +37,6 @@
       RecordArcGoogleLocationServiceConsent,
       void(const std::string&,
            const sync_pb::UserConsentTypes::ArcGoogleLocationServiceConsent&));
-  void RecordUnifiedConsent(
-      const std::string& account_id,
-      const sync_pb::UserConsentTypes::UnifiedConsent& consent) override;
   void RecordAssistantActivityControlConsent(
       const std::string& account_id,
       const sync_pb::UserConsentTypes::AssistantActivityControlConsent& consent)
diff --git a/components/constrained_window/constrained_window_views.cc b/components/constrained_window/constrained_window_views.cc
index 3b6fd9f9..d818daec 100644
--- a/components/constrained_window/constrained_window_views.cc
+++ b/components/constrained_window/constrained_window_views.cc
@@ -26,11 +26,6 @@
 #import "components/constrained_window/native_web_contents_modal_dialog_manager_views_mac.h"
 #endif
 
-#if defined(USE_AURA)
-#include "ui/aura/window.h"
-#include "ui/compositor/dip_util.h"
-#endif
-
 using web_modal::ModalDialogHost;
 using web_modal::ModalDialogHostObserver;
 
@@ -138,19 +133,6 @@
   }
 
   widget->SetBounds(gfx::Rect(position, size));
-
-#if defined(USE_AURA)
-  if (!widget->is_top_level()) {
-    // Toplevel windows are automatiacally snapped, but CHILD windows
-    // may not. If it's not toplevel, snap the widget's layer to pixel
-    // based on the parent toplevel window, which should be snapped.
-    gfx::NativeView window = widget->GetNativeView();
-    views::Widget* toplevel =
-        views::Widget::GetTopLevelWidgetForNativeView(window->parent());
-    ui::SnapLayerToPhysicalPixelBoundary(toplevel->GetLayer(),
-                                         widget->GetLayer());
-  }
-#endif
 }
 
 }  // namespace
diff --git a/components/content_settings/core/browser/content_settings_registry.cc b/components/content_settings/core/browser/content_settings_registry.cc
index 5c5030e..0d5adff 100644
--- a/components/content_settings/core/browser/content_settings_registry.cc
+++ b/components/content_settings/core/browser/content_settings_registry.cc
@@ -176,7 +176,7 @@
                          CONTENT_SETTING_DETECT_IMPORTANT_CONTENT),
            WebsiteSettingsInfo::SINGLE_ORIGIN_WITH_EMBEDDED_EXCEPTIONS_SCOPE,
            WebsiteSettingsRegistry::DESKTOP,
-           ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+           ContentSettingsInfo::INHERIT_IN_INCOGNITO,
            ContentSettingsInfo::EPHEMERAL,
            ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS);
 
diff --git a/components/content_settings/core/browser/content_settings_registry_unittest.cc b/components/content_settings/core/browser/content_settings_registry_unittest.cc
index 0aaa9ed..155cdbc5 100644
--- a/components/content_settings/core/browser/content_settings_registry_unittest.cc
+++ b/components/content_settings/core/browser/content_settings_registry_unittest.cc
@@ -132,6 +132,7 @@
   // disable features like popup blocking, download blocking or ad blocking.
   // They do not allow access to user data.
   const ContentSettingsType whitelist[] = {
+      CONTENT_SETTINGS_TYPE_PLUGINS,              //
       CONTENT_SETTINGS_TYPE_POPUPS,               //
       CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS,  //
       CONTENT_SETTINGS_TYPE_ADS,                  //
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index 44f6a5c..fec5c3a3b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -290,7 +290,8 @@
   ui_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&DataReductionProxyService::SetConfiguredProxiesOnUI,
-                     service_, config_->GetAllConfiguredProxies()));
+                     service_, config_->GetAllConfiguredProxies(),
+                     config_->GetProxiesForHttp()));
   UpdateCustomProxyConfig();
   UpdateThrottleConfig();
 }
@@ -426,6 +427,12 @@
   drp_throttle_config_observers_.AddPtr(std::move(observer));
 }
 
+void DataReductionProxyIOData::AddThrottleConfigObserverInfo(
+    mojom::DataReductionProxyThrottleConfigObserverPtrInfo observer) {
+  AddThrottleConfigObserver(
+      mojom::DataReductionProxyThrottleConfigObserverPtr(std::move(observer)));
+}
+
 void DataReductionProxyIOData::Clone(mojom::DataReductionProxyRequest request) {
   drp_bindings_.AddBinding(this, std::move(request));
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
index c57abd5f..fc4e5890 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -164,6 +164,8 @@
   void AddThrottleConfigObserver(
       mojom::DataReductionProxyThrottleConfigObserverPtr observer) override;
   void Clone(mojom::DataReductionProxyRequest request) override;
+  void AddThrottleConfigObserverInfo(
+      mojom::DataReductionProxyThrottleConfigObserverPtrInfo observer);
 
  private:
   friend class TestDataReductionProxyIOData;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index 9e8f49f..bdb5fc2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -107,11 +107,10 @@
   return header_value_;
 }
 
-
+// static
 void DataReductionProxyRequestOptions::AddPageIDRequestHeader(
     net::HttpRequestHeaders* request_headers,
-    uint64_t page_id) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+    uint64_t page_id) {
   std::string header_value;
   if (request_headers->HasHeader(chrome_proxy_header())) {
     request_headers->GetHeader(chrome_proxy_header(), &header_value);
@@ -135,6 +134,14 @@
     net::HttpRequestHeaders* request_headers,
     base::Optional<uint64_t> page_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  AddRequestHeader(request_headers, page_id, header_value_);
+}
+
+// static
+void DataReductionProxyRequestOptions::AddRequestHeader(
+    net::HttpRequestHeaders* request_headers,
+    base::Optional<uint64_t> page_id,
+    const std::string& session_header_value) {
   DCHECK(!page_id || page_id.value() > 0u);
   std::string header_value;
   if (request_headers->HasHeader(chrome_proxy_header())) {
@@ -143,7 +150,7 @@
     header_value += ", ";
   }
   request_headers->SetHeader(chrome_proxy_header(),
-                             header_value + header_value_);
+                             header_value + session_header_value);
   if (page_id)
     AddPageIDRequestHeader(request_headers, page_id.value());
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
index 38980230..d8b2debc 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -63,11 +63,14 @@
   // main frame requests.
   void AddRequestHeader(net::HttpRequestHeaders* request_headers,
                         base::Optional<uint64_t> page_id);
+  static void AddRequestHeader(net::HttpRequestHeaders* request_headers,
+                               base::Optional<uint64_t> page_id,
+                               const std::string& session_header_value);
 
   // Adds |page_id| to the 'Chrome-Proxy' header, merging with existing value if
   // it exists.
-  void AddPageIDRequestHeader(net::HttpRequestHeaders* request_headers,
-                              uint64_t page_id) const;
+  static void AddPageIDRequestHeader(net::HttpRequestHeaders* request_headers,
+                                     uint64_t page_id);
 
   // Stores the supplied key and sets up credentials suitable for authenticating
   // with the data reduction proxy.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
index 66e8a9d..8b4f9c4f 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -50,6 +50,7 @@
       settings_(settings),
       prefs_(prefs),
       db_data_owner_(new DBDataOwner(std::move(store))),
+      ui_task_runner_(ui_task_runner),
       io_task_runner_(io_task_runner),
       db_task_runner_(db_task_runner),
       initialized_(false),
@@ -281,9 +282,11 @@
 }
 
 void DataReductionProxyService::SetConfiguredProxiesOnUI(
-    const net::ProxyList& proxies) {
+    const net::ProxyList& proxies,
+    const std::vector<DataReductionProxyServer>& proxies_for_http) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   settings_->SetConfiguredProxies(proxies);
+  settings_->SetProxiesForHttp(proxies_for_http);
 }
 
 void DataReductionProxyService::SetIgnoreLongTermBlackListRules(
@@ -396,4 +399,32 @@
   }
 }
 
+void DataReductionProxyService::MarkProxiesAsBad(
+    base::TimeDelta bypass_duration,
+    const net::ProxyList& bad_proxies,
+    MarkProxiesAsBadCallback callback) {
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &DataReductionProxyIOData::MarkProxiesAsBad, io_data_,
+          bypass_duration, bad_proxies,
+          base::BindOnce(base::IgnoreResult(&base::TaskRunner::PostTask),
+                         ui_task_runner_, FROM_HERE, std::move(callback))));
+}
+
+void DataReductionProxyService::AddThrottleConfigObserver(
+    mojom::DataReductionProxyThrottleConfigObserverPtr observer) {
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&DataReductionProxyIOData::AddThrottleConfigObserverInfo,
+                     io_data_, observer.PassInterface()));
+}
+
+void DataReductionProxyService::Clone(
+    mojom::DataReductionProxyRequest request) {
+  io_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&DataReductionProxyIOData::Clone, io_data_,
+                                std::move(request)));
+}
+
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
index 81210891c..104236b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
@@ -20,6 +20,7 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.h"
 #include "components/data_reduction_proxy/core/browser/db_data_owner.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy.mojom.h"
 #include "components/data_use_measurement/core/data_use_measurement.h"
 #include "net/nqe/effective_connection_type.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
@@ -46,6 +47,7 @@
 class DataReductionProxyIOData;
 class DataReductionProxyServiceObserver;
 class DataReductionProxySettings;
+class DataReductionProxyServer;
 
 // Contains and initializes all Data Reduction Proxy objects that have a
 // lifetime based on the UI thread.
@@ -53,7 +55,8 @@
     : public data_use_measurement::DataUseMeasurement::ServicesDataUseObserver,
       public network::NetworkQualityTracker::EffectiveConnectionTypeObserver,
       public network::NetworkQualityTracker::RTTAndThroughputEstimatesObserver,
-      public network::NetworkConnectionTracker::NetworkConnectionObserver {
+      public network::NetworkConnectionTracker::NetworkConnectionObserver,
+      public mojom::DataReductionProxy {
  public:
   // The caller must ensure that |settings|, |prefs|, |request_context|, and
   // |io_task_runner| remain alive for the lifetime of the
@@ -155,8 +158,11 @@
   // Sends the given |headers| to |DataReductionProxySettings|.
   void SetProxyRequestHeadersOnUI(const net::HttpRequestHeaders& headers);
 
-  // Sends the given |proxies| to |DataReductionProxySettings|.
-  void SetConfiguredProxiesOnUI(const net::ProxyList& proxies);
+  // Sends the given |proxies| and |proxies_for_http| to
+  // |DataReductionProxySettings|.
+  void SetConfiguredProxiesOnUI(
+      const net::ProxyList& proxies,
+      const std::vector<DataReductionProxyServer>& proxies_for_http);
 
   // Sets a config client that can be used to update Data Reduction Proxy
   // settings when the network service is enabled.
@@ -202,6 +208,14 @@
   // NetworkConnectionTracker::NetworkConnectionObserver
   void OnConnectionChanged(network::mojom::ConnectionType type) override;
 
+  // mojom::DataReductionProxy implementation:
+  void MarkProxiesAsBad(base::TimeDelta bypass_duration,
+                        const net::ProxyList& bad_proxies,
+                        MarkProxiesAsBadCallback callback) override;
+  void AddThrottleConfigObserver(
+      mojom::DataReductionProxyThrottleConfigObserverPtr observer) override;
+  void Clone(mojom::DataReductionProxyRequest request) override;
+
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   // Tracks compression statistics to be displayed to the user.
@@ -216,6 +230,8 @@
 
   std::unique_ptr<DBDataOwner> db_data_owner_;
 
+  scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+
   // Used to post tasks to |io_data_|.
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
index ec69d4d..a0b22172 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -328,6 +328,12 @@
   configured_proxies_ = proxies;
 }
 
+void DataReductionProxySettings::SetProxiesForHttp(
+    const std::vector<DataReductionProxyServer>& proxies_for_http) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  proxies_for_http_ = proxies_for_http;
+}
+
 bool DataReductionProxySettings::IsConfiguredDataReductionProxy(
     const net::ProxyServer& proxy_server) const {
   if (proxy_server.is_direct() || !proxy_server.is_valid())
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
index 2a75c57..a638cfb 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -19,6 +19,7 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service_observer.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_member.h"
 #include "net/http/http_request_headers.h"
@@ -168,6 +169,8 @@
   void SetProxyRequestHeaders(const net::HttpRequestHeaders& headers);
 
   void SetConfiguredProxies(const net::ProxyList& proxies);
+  void SetProxiesForHttp(
+      const std::vector<DataReductionProxyServer>& proxies_for_http);
 
   // Returns headers to use for requests to the compression server.
   const net::HttpRequestHeaders& GetProxyRequestHeaders() const;
@@ -197,6 +200,10 @@
     return config_;
   }
 
+  const std::vector<DataReductionProxyServer> proxies_for_http() const {
+    return proxies_for_http_;
+  }
+
   // Permits changing the underlying |DataReductionProxyConfig| without running
   // the initialization loop.
   void ResetConfigForTest(DataReductionProxyConfig* config) {
@@ -316,6 +323,7 @@
   net::HttpRequestHeaders proxy_request_headers_;
 
   net::ProxyList configured_proxies_;
+  std::vector<DataReductionProxyServer> proxies_for_http_;
 
   network::mojom::CustomProxyConfigClientPtrInfo proxy_config_client_;
 
diff --git a/components/device_event_log/device_event_log.cc b/components/device_event_log/device_event_log.cc
index 1b68fb6..7e46b54 100644
--- a/components/device_event_log/device_event_log.cc
+++ b/components/device_event_log/device_event_log.cc
@@ -77,6 +77,11 @@
                                          max_events);
 }
 
+void ClearAll() {
+  if (g_device_event_log)
+    g_device_event_log->ClearAll();
+}
+
 void Clear(const base::Time& begin, const base::Time& end) {
   if (g_device_event_log)
     g_device_event_log->Clear(begin, end);
diff --git a/components/device_event_log/device_event_log.h b/components/device_event_log/device_event_log.h
index 417f743..482d6a89 100644
--- a/components/device_event_log/device_event_log.h
+++ b/components/device_event_log/device_event_log.h
@@ -174,6 +174,9 @@
                                                 LogLevel max_level,
                                                 size_t max_events);
 
+// Clear all entries from the device event log.
+void DEVICE_EVENT_LOG_EXPORT ClearAll();
+
 // Clear entries from the device event log between the given times.
 void DEVICE_EVENT_LOG_EXPORT Clear(const base::Time& begin,
                                    const base::Time& end);
diff --git a/components/device_event_log/device_event_log_impl.cc b/components/device_event_log/device_event_log_impl.cc
index 2b6e8f7..fa62718 100644
--- a/components/device_event_log/device_event_log_impl.cc
+++ b/components/device_event_log/device_event_log_impl.cc
@@ -404,6 +404,10 @@
   return result;
 }
 
+void DeviceEventLogImpl::ClearAll() {
+  entries_.clear();
+}
+
 void DeviceEventLogImpl::Clear(const base::Time& begin, const base::Time& end) {
   auto begin_it = std::find_if(
       entries_.begin(), entries_.end(),
diff --git a/components/device_event_log/device_event_log_impl.h b/components/device_event_log/device_event_log_impl.h
index f127f05..f176aba 100644
--- a/components/device_event_log/device_event_log_impl.h
+++ b/components/device_event_log/device_event_log_impl.h
@@ -72,6 +72,7 @@
                           size_t max_events);
 
   // Implements device_event_log::Clear.
+  void ClearAll();
   void Clear(const base::Time& begin, const base::Time& end);
 
   // Called from device_event_log::AddEntry if the global instance has not been
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index 4ade4f0..5cfd976 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -1145,7 +1145,7 @@
   // we're getting the same file.
   bool restart_required =
       (GetFullPath().empty() ||
-       (etag_.empty() && last_modified_time_.empty() &&
+       (!HasStrongValidators() &&
         !base::FeatureList::IsEnabled(
             features::kAllowDownloadResumptionWithoutStrongValidators)));
   // We won't auto-restart if we've used up our attempts or the
@@ -1157,6 +1157,10 @@
                                user_action_required);
 }
 
+bool DownloadItemImpl::HasStrongValidators() const {
+  return !etag_.empty() || !last_modified_time_.empty();
+}
+
 void DownloadItemImpl::UpdateValidatorsOnResumption(
     const DownloadCreateInfo& new_create_info) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -1189,8 +1193,13 @@
   }
 
   if (destination_info_.received_bytes > 0 && new_create_info.offset == 0) {
-    RecordResumptionRestartCount(
-        ResumptionRestartCountTypes::kRequestedByServerCount);
+    if (!base::FeatureList::IsEnabled(
+            features::kAllowDownloadResumptionWithoutStrongValidators) ||
+        GetDownloadValidationLengthConfig() >
+            destination_info_.received_bytes) {
+      RecordResumptionRestartCount(
+          ResumptionRestartCountTypes::kRequestedByServerCount);
+    }
   }
 
   request_info_.url_chain.insert(request_info_.url_chain.end(), chain_iter,
@@ -1863,7 +1872,8 @@
 
   bool is_parallelizable = job_ && job_->IsParallelizable();
   RecordDownloadCompleted(GetReceivedBytes(), is_parallelizable,
-                          download_source_);
+                          download_source_, has_resumed_,
+                          HasStrongValidators());
   if (!delegate_->IsOffTheRecord()) {
     RecordDownloadCountWithSource(COMPLETED_COUNT_NORMAL_PROFILE,
                                   download_source_);
@@ -2344,7 +2354,7 @@
     LOG_IF(ERROR, !GetFullPath().empty())
         << "Download full path should be empty before resumption";
     if (destination_info_.received_bytes > 0) {
-      if (etag_.empty() && last_modified_time_.empty()) {
+      if (!HasStrongValidators()) {
         RecordResumptionRestartCount(
             ResumptionRestartCountTypes::kMissingStrongValidatorsCount);
       }
@@ -2401,7 +2411,7 @@
   download_params->set_hash_of_partial_file(GetHash());
   download_params->set_hash_state(std::move(hash_state_));
   download_params->set_guid(guid_);
-  if (etag_.empty() && last_modified_time_.empty() &&
+  if (!HasStrongValidators() && download_params->offset() > 0 &&
       base::FeatureList::IsEnabled(
           features::kAllowDownloadResumptionWithoutStrongValidators)) {
     download_params->set_use_if_range(false);
@@ -2440,10 +2450,12 @@
 
   DownloadUkmHelper::RecordDownloadResumed(ukm_download_id_, GetResumeMode(),
                                            time_since_start);
+  RecordDownloadResumed(HasStrongValidators());
 
   delegate_->ResumeInterruptedDownload(std::move(download_params),
                                        request_info_.site_url);
 
+  has_resumed_ = true;
   if (job_)
     job_->Resume(false);
 }
diff --git a/components/download/internal/common/download_response_handler.cc b/components/download/internal/common/download_response_handler.cc
index bfc3ad640..d203a3cf 100644
--- a/components/download/internal/common/download_response_handler.cc
+++ b/components/download/internal/common/download_response_handler.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/metrics/histogram_functions.h"
 #include "components/download/public/common/download_stats.h"
 #include "components/download/public/common/download_url_parameters.h"
 #include "components/download/public/common/download_utils.h"
@@ -229,6 +230,11 @@
         ConvertInterruptReasonToMojoNetworkRequestStatus(reason));
   }
 
+  if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) {
+    base::UmaHistogramSparse("Download.MapErrorNetworkFailed.NetworkService",
+                             std::abs(status.error_code));
+  }
+
   if (started_) {
     delegate_->OnResponseCompleted();
     return;
diff --git a/components/download/internal/common/download_stats.cc b/components/download/internal/common/download_stats.cc
index 1fe8b19..a566609f 100644
--- a/components/download/internal/common/download_stats.cc
+++ b/components/download/internal/common/download_stats.cc
@@ -567,7 +567,9 @@
 
 void RecordDownloadCompleted(int64_t download_len,
                              bool is_parallelizable,
-                             DownloadSource download_source) {
+                             DownloadSource download_source,
+                             bool has_resumed,
+                             bool has_strong_validators) {
   RecordDownloadCountWithSource(COMPLETED_COUNT, download_source);
   int64_t max = 1024 * 1024 * 1024;  // One Terabyte.
   download_len /= 1024;              // In Kilobytes
@@ -577,6 +579,11 @@
     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.DownloadSize.Parallelizable",
                                 download_len, 1, max, 256);
   }
+
+  if (has_resumed) {
+    base::UmaHistogramBoolean("Download.ResumptionComplete.HasStrongValidators",
+                              has_strong_validators);
+  }
 }
 
 void RecordDownloadDeletion(base::Time completion_time,
@@ -1269,6 +1276,11 @@
   base::UmaHistogramEnumeration("Download.ResumptionRestart.Counts", type);
 }
 
+void RecordDownloadResumed(bool has_strong_validators) {
+  base::UmaHistogramBoolean("Download.ResumptionStart.HasStrongValidators",
+                            has_strong_validators);
+}
+
 #if defined(OS_ANDROID)
 void RecordFirstBackgroundDownloadInterruptReason(
     DownloadInterruptReason reason,
diff --git a/components/download/public/common/download_item_impl.h b/components/download/public/common/download_item_impl.h
index a33d25d..0da7255e 100644
--- a/components/download/public/common/download_item_impl.h
+++ b/components/download/public/common/download_item_impl.h
@@ -645,6 +645,9 @@
   // INTERRUPTED state.
   ResumeMode GetResumeMode() const;
 
+  // Whether strong validators are present.
+  bool HasStrongValidators() const;
+
   DownloadItem::DownloadRenameResult RenameDownloadedFile(
       const std::string& name);
   void RenameDownloadedFileDone(RenameDownloadCallback callback,
@@ -823,6 +826,9 @@
   // UKM ID for reporting, default to 0 if uninitialized.
   uint64_t ukm_download_id_ = 0;
 
+  // Whether download has been resumed.
+  bool has_resumed_ = false;
+
   THREAD_CHECKER(thread_checker_);
 
   base::WeakPtrFactory<DownloadItemImpl> weak_ptr_factory_;
diff --git a/components/download/public/common/download_stats.h b/components/download/public/common/download_stats.h
index 447e8fd..7e434c0 100644
--- a/components/download/public/common/download_stats.h
+++ b/components/download/public/common/download_stats.h
@@ -250,7 +250,9 @@
 COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadCompleted(
     int64_t download_len,
     bool is_parallelizable,
-    DownloadSource download_source);
+    DownloadSource download_source,
+    bool has_resumed,
+    bool has_strong_validators);
 
 // Record download deletion event.
 COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadDeletion(
@@ -445,6 +447,10 @@
 COMPONENTS_DOWNLOAD_EXPORT void RecordResumptionRestartCount(
     ResumptionRestartCountTypes type);
 
+// Records that download was resumed.
+COMPONENTS_DOWNLOAD_EXPORT void RecordDownloadResumed(
+    bool has_strong_validators);
+
 #if defined(OS_ANDROID)
 // Records the download interrupt reason for the first background download.
 // If |download_started| is true, this records the last interrupt reason
diff --git a/components/drive/chromeos/change_list_loader.cc b/components/drive/chromeos/change_list_loader.cc
index 1e8717e..eb0570d 100644
--- a/components/drive/chromeos/change_list_loader.cc
+++ b/components/drive/chromeos/change_list_loader.cc
@@ -17,7 +17,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "components/drive/chromeos/change_list_loader_observer.h"
@@ -213,7 +213,7 @@
     const base::FilePath& root_entry_path)
     : logger_(logger),
       blocking_task_runner_(blocking_task_runner),
-      in_shutdown_(new base::CancellationFlag),
+      in_shutdown_(new base::AtomicFlag),
       resource_metadata_(resource_metadata),
       scheduler_(scheduler),
       root_folder_id_loader_(root_folder_id_loader),
diff --git a/components/drive/chromeos/change_list_loader.h b/components/drive/chromeos/change_list_loader.h
index 470d120..e35dcbb 100644
--- a/components/drive/chromeos/change_list_loader.h
+++ b/components/drive/chromeos/change_list_loader.h
@@ -21,7 +21,7 @@
 #include "google_apis/drive/drive_common_callbacks.h"
 
 namespace base {
-class CancellationFlag;
+class AtomicFlag;
 class SequencedTaskRunner;
 class Time;
 }  // namespace base
@@ -154,7 +154,7 @@
 
   EventLogger* logger_;  // Not owned.
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
-  std::unique_ptr<base::CancellationFlag> in_shutdown_;
+  std::unique_ptr<base::AtomicFlag> in_shutdown_;
   ResourceMetadata* resource_metadata_;  // Not owned.
   JobScheduler* scheduler_;  // Not owned.
   RootFolderIdLoader* root_folder_id_loader_;      // Not owned.
diff --git a/components/drive/chromeos/change_list_processor.cc b/components/drive/chromeos/change_list_processor.cc
index 8c741a9..42fbcbf 100644
--- a/components/drive/chromeos/change_list_processor.cc
+++ b/components/drive/chromeos/change_list_processor.cc
@@ -12,7 +12,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "components/drive/chromeos/drive_file_util.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/drive.pb.h"
@@ -129,7 +129,7 @@
 ChangeListProcessor::ChangeListProcessor(const std::string& team_drive_id,
                                          const base::FilePath& root_entry_path,
                                          ResourceMetadata* resource_metadata,
-                                         base::CancellationFlag* in_shutdown)
+                                         base::AtomicFlag* in_shutdown)
     : resource_metadata_(resource_metadata),
       in_shutdown_(in_shutdown),
       changed_files_(new FileChange),
diff --git a/components/drive/chromeos/change_list_processor.h b/components/drive/chromeos/change_list_processor.h
index 67a94583..133315b 100644
--- a/components/drive/chromeos/change_list_processor.h
+++ b/components/drive/chromeos/change_list_processor.h
@@ -19,7 +19,7 @@
 #include "url/gurl.h"
 
 namespace base {
-class CancellationFlag;
+class AtomicFlag;
 }  // namespace base
 
 namespace google_apis {
@@ -126,7 +126,7 @@
   ChangeListProcessor(const std::string& team_drive_id,
                       const base::FilePath& root_entry_path,
                       ResourceMetadata* resource_metadata,
-                      base::CancellationFlag* in_shutdown);
+                      base::AtomicFlag* in_shutdown);
   ~ChangeListProcessor();
 
   // Applies user's change lists or full resource lists to
@@ -203,7 +203,7 @@
   void UpdateChangedDirs(const ResourceEntry& entry);
 
   ResourceMetadata* resource_metadata_;  // Not owned.
-  base::CancellationFlag* in_shutdown_;  // Not owned.
+  base::AtomicFlag* in_shutdown_;        // Not owned.
 
   ResourceEntryMap entry_map_;
   ParentResourceIdMap parent_resource_id_map_;
diff --git a/components/drive/chromeos/file_cache.h b/components/drive/chromeos/file_cache.h
index a06bccb..93e3338 100644
--- a/components/drive/chromeos/file_cache.h
+++ b/components/drive/chromeos/file_cache.h
@@ -15,7 +15,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/threading/thread_checker.h"
 #include "build/build_config.h"
 #include "components/drive/file_errors.h"
@@ -211,7 +211,7 @@
 
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
 
-  base::CancellationFlag in_shutdown_;
+  base::AtomicFlag in_shutdown_;
 
   ResourceMetadataStorage* storage_;
 
diff --git a/components/drive/chromeos/team_drive_list_loader.cc b/components/drive/chromeos/team_drive_list_loader.cc
index 669fe743..3fee614 100644
--- a/components/drive/chromeos/team_drive_list_loader.cc
+++ b/components/drive/chromeos/team_drive_list_loader.cc
@@ -12,7 +12,7 @@
 
 #include "base/bind.h"
 #include "base/sequenced_task_runner.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "components/drive/chromeos/change_list_loader.h"
 #include "components/drive/chromeos/change_list_processor.h"
 #include "components/drive/chromeos/loader_controller.h"
@@ -32,7 +32,7 @@
 // metadata.
 FileError AddOrUpdateTeamDrives(ResourceEntryVector team_drives,
                                 ResourceMetadata* metadata,
-                                base::CancellationFlag* in_shutdown) {
+                                base::AtomicFlag* in_shutdown) {
   DCHECK(metadata);
   DCHECK(in_shutdown);
   for (const auto& entry : team_drives) {
@@ -77,7 +77,7 @@
 // Remove the supplied list of team drives from the resource metadata.
 FileError RemoveTeamDrives(ResourceEntryVector team_drives,
                            ResourceMetadata* metadata,
-                           base::CancellationFlag* in_shutdown) {
+                           base::AtomicFlag* in_shutdown) {
   DCHECK(metadata);
   DCHECK(in_shutdown);
   FileError result = FILE_ERROR_OK;
@@ -110,7 +110,7 @@
     LoaderController* apply_task_controller)
     : logger_(logger),
       blocking_task_runner_(blocking_task_runner),
-      in_shutdown_(new base::CancellationFlag),
+      in_shutdown_(new base::AtomicFlag),
       pending_load_callbacks_(),
       resource_metadata_(resource_metadata),
       scheduler_(scheduler),
diff --git a/components/drive/chromeos/team_drive_list_loader.h b/components/drive/chromeos/team_drive_list_loader.h
index e295478..7fa57179 100644
--- a/components/drive/chromeos/team_drive_list_loader.h
+++ b/components/drive/chromeos/team_drive_list_loader.h
@@ -19,7 +19,7 @@
 #include "google_apis/drive/drive_api_requests.h"
 
 namespace base {
-class CancellationFlag;
+class AtomicFlag;
 class SequencedTaskRunner;
 }  // namespace base
 
@@ -85,7 +85,7 @@
 
   EventLogger* logger_;  // Not owned.
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
-  std::unique_ptr<base::CancellationFlag> in_shutdown_;
+  std::unique_ptr<base::AtomicFlag> in_shutdown_;
   std::vector<std::unique_ptr<ChangeList>> change_lists_;
   std::vector<FileOperationCallback> pending_load_callbacks_;
   bool loaded_ = false;
diff --git a/components/drive/drive_api_util.cc b/components/drive/drive_api_util.cc
index 59fb1a0..05376bd1 100644
--- a/components/drive/drive_api_util.cc
+++ b/components/drive/drive_api_util.cc
@@ -17,7 +17,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task_runner_util.h"
 #include "base/values.h"
 #include "google_apis/drive/drive_api_parser.h"
@@ -139,7 +139,7 @@
 }
 
 std::string GetMd5Digest(const base::FilePath& file_path,
-                         const base::CancellationFlag* cancellation_flag) {
+                         const base::AtomicFlag* cancellation_flag) {
   base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
   if (!file.IsValid())
     return std::string();
diff --git a/components/drive/drive_api_util.h b/components/drive/drive_api_util.h
index fc562ce9..cc94229 100644
--- a/components/drive/drive_api_util.h
+++ b/components/drive/drive_api_util.h
@@ -14,7 +14,7 @@
 #include "google_apis/drive/drive_common_callbacks.h"
 
 namespace base {
-class CancellationFlag;
+class AtomicFlag;
 class Location;
 class FilePath;
 class TaskRunner;
@@ -58,7 +58,7 @@
 // Returns the (base-16 encoded) MD5 digest of the file content at |file_path|,
 // or an empty string if an error is found.
 std::string GetMd5Digest(const base::FilePath& file_path,
-                         const base::CancellationFlag* cancellation_flag);
+                         const base::AtomicFlag* cancellation_flag);
 
 // Returns preferred file extension for hosted documents which have given mime
 // type.
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index e736618..e0f9f56 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -42,7 +42,6 @@
 #include "ui/base/class_property.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/compositor.h"
-#include "ui/compositor/dip_util.h"
 #include "ui/compositor_extra/shadow.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -989,11 +988,6 @@
       root_surface_origin().OffsetFromOrigin(), 1.f / GetScale()));
 
   host_window()->SetBounds(gfx::Rect(origin, host_window()->bounds().size()));
-  // The host window might have not been added to the widget yet.
-  if (host_window()->parent()) {
-    ui::SnapLayerToPhysicalPixelBoundary(widget_->GetNativeWindow()->layer(),
-                                         host_window()->layer());
-  }
 }
 
 void ShellSurfaceBase::UpdateShadow() {
diff --git a/components/exo/test/exo_test_base_views.cc b/components/exo/test/exo_test_base_views.cc
index 4612ec4..4fdd3fa 100644
--- a/components/exo/test/exo_test_base_views.cc
+++ b/components/exo/test/exo_test_base_views.cc
@@ -47,6 +47,11 @@
     static std::vector<uint8_t> no_data;
     return no_data;
   }
+  bool GetActiveModeForDisplayId(
+      int64_t display_id,
+      display::ManagedDisplayMode* mode) const override {
+    return false;
+  }
 
   aura::Window* GetPrimaryDisplayContainer(int container_id) override {
     return root_window_;
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index f9bdd210..e0c731e9 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -63,6 +63,8 @@
     "wp_presentation.h",
     "wp_viewporter.cc",
     "wp_viewporter.h",
+    "zaura_shell.cc",
+    "zaura_shell.h",
     "zcr_alpha_compositing.cc",
     "zcr_alpha_compositing.h",
     "zcr_secure_output.cc",
@@ -85,8 +87,6 @@
       "wayland_positioner.h",
       "wl_shell.cc",
       "wl_shell.h",
-      "zaura_shell.cc",
-      "zaura_shell.h",
       "zcr_cursor_shapes.cc",
       "zcr_cursor_shapes.h",
       "zcr_gaming_input.cc",
diff --git a/components/exo/wayland/fuzzer/BUILD.gn b/components/exo/wayland/fuzzer/BUILD.gn
index e928745..37051bb 100644
--- a/components/exo/wayland/fuzzer/BUILD.gn
+++ b/components/exo/wayland/fuzzer/BUILD.gn
@@ -49,6 +49,13 @@
   protocols = kDefaultWaylandProtocols
 }
 
+wayland_templater("protocol_docs") {
+  sources = [
+    "misc/docs.md.tmpl",
+  ]
+  protocols = kDefaultWaylandProtocols
+}
+
 wayland_templater("actions_tmpl") {
   sources = [
     "actions.proto.tmpl",
diff --git a/components/exo/wayland/fuzzer/harness.cc.tmpl b/components/exo/wayland/fuzzer/harness.cc.tmpl
index 80c85273..7707b93 100644
--- a/components/exo/wayland/fuzzer/harness.cc.tmpl
+++ b/components/exo/wayland/fuzzer/harness.cc.tmpl
@@ -62,6 +62,12 @@
         {% endif %}
         harness->add_{{event.constructed}}(new_object);
       {% endif %}
+      {% for arg in event.args if arg.type == 'fd' %}
+        FILE* _f = fdopen({{arg.name}}, "w");
+	CHECK(_f);
+	fwrite(".", 1, 1, _f);
+        fclose(_f);
+      {% endfor %}
     }
   {% endfor %}
   {% for request in interface.requests %}
@@ -88,10 +94,13 @@
         {% endif %}
       {% endfor %}
 
+      {% if request.is_constructor %}
+        struct {{request.constructed}}* new_object =
+      {% endif %}
       // Invoke the wayland method.  We need ::method_name in order to
       // disambiguate methods which might collide with
       // interface/namespace names.
-      {% if request.is_constructor %} struct {{request.constructed}}* new_object = {% endif %}::{{interface.name}}_{{request.name}}(receiver
+      ::{{interface.name}}_{{request.name}}(receiver
         {% for arg in request.args %}
           {% if arg.type == 'object' or arg.type == 'fd' %}
             , {{arg.name}}
diff --git a/components/exo/wayland/fuzzer/misc/docs.md.tmpl b/components/exo/wayland/fuzzer/misc/docs.md.tmpl
new file mode 100644
index 0000000..c7b9242
--- /dev/null
+++ b/components/exo/wayland/fuzzer/misc/docs.md.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.
+
+{% for interface in interfaces %}
+# {{interface.name}}
+
+{% for line in interface.doc %}
+{{line}}
+{% endfor %}
+
+{% for message in interface.requests + interface.events %}
+## {{interface.name}} :: {{message.name}} ({{message.args | join(', ', attribute='name')}}) - {{message.tag}}
+
+   {% for line in message.doc %}
+   {{line}}
+   {% endfor %}
+
+{% for arg in message.args %}
+  {{arg.name}}: {{arg.type}} {{arg.interface | default('', true)}}
+    {% for line in arg.doc %}
+    {{line}}
+    {% endfor %}
+{% endfor %}
+
+{% endfor %}
+
+{% endfor %}
diff --git a/components/exo/wayland/fuzzer/wayland_sequencer.py b/components/exo/wayland/fuzzer/wayland_sequencer.py
index a487349..3ad572b 100644
--- a/components/exo/wayland/fuzzer/wayland_sequencer.py
+++ b/components/exo/wayland/fuzzer/wayland_sequencer.py
@@ -24,9 +24,6 @@
 # most a small number of each interface. So the index has to be in this map.
 small_value = {0: 'ZERO', 1: 'ONE', 2: 'TWO', 3: 'THREE'}
 
-# The default (empty) message is a wl_display_roundtrip.
-round_trip = ('', '', [])
-
 
 def ToNode(interface, message):
   return (interface.attrib['name'], message.attrib['name'])
@@ -62,86 +59,157 @@
   return dep_graph
 
 
-class SequenceContext(object):
+class SequenceBuilder(object):
   """Store for data used when building one sequence.
 
-  This class is used to store the data which will help build the sequences but
-  which does not actually appear in the sequences.  You should make a new
-  SequenceContext for every sequence you want to generate.
+  This class is used to build the sequences.  You should make a new
+  SequenceBuilder for every sequence you want to generate.
   """
 
-  def __init__(self, dep_graph):
+  def __init__(self, name, dep_graph):
+    self.name = name
+    self.dep_graph = dep_graph
     self.counts = {}
     self.prevented = set()
-    self.dep_graph = dep_graph
+    self.sequence = []
     # To simulate what the harness itself does, we make a singleton wl_display.
     self.RecordInterfaceCreated('wl_display')
     self.Prevent('wl_display')
 
-  def IsPrevented(self, interface_name):
-    return interface_name in self.prevented
+  def IsPrevented(self, interface):
+    return interface in self.prevented
 
-  def Prevent(self, interface_name):
-    self.prevented.add(interface_name)
+  def Prevent(self, interface):
+    self.prevented.add(interface)
 
-  def RecordInterfaceCreated(self, interface_name):
-    self.counts[interface_name] = self.counts.get(interface_name, -1) + 1
+  def RecordInterfaceCreated(self, interface):
+    self.counts[interface] = self.counts.get(interface, -1) + 1
 
-  def GetLastInterfaceCreated(self, interface_name):
+  def GetLastInterfaceCreated(self, interface):
     """Return the small_value index for the currently available interface.
 
     Args:
-      interface_name: the name of the interface you want an index of.
+      interface: the name of the interface you want an index of.
 
     Returns:
       A small_value index for the topmost-version of the interface.
     """
-    return small_value[self.counts[interface_name]]
+    return small_value[self.counts[interface]]
+
+  def AppendCall(self, interface, message, args):
+    self.sequence.append((interface, message, args))
+
+  def AppendRoundTrip(self):
+    self.AppendCall('', '', [])
+
+  def BuildInterface(self, interface):
+    if not self.IsPrevented(interface):
+      if interface == 'wl_registry' or self.dep_graph[interface] == (
+          'wl_registry', 'bind'):
+        self.Prevent(interface)
+      (cons_i, cons_m) = self.dep_graph[interface]
+      self.BuildMessage(cons_i, cons_m, interface)
+      self.RecordInterfaceCreated(interface)
+    return self.GetLastInterfaceCreated(interface)
+
+  def BuildMessage(self, interface, message, target_i):
+    """Build the message sequence up to and including the supplied message.
+
+    Args:
+      interface: the name of the interface that defines the message you want to
+        send.
+      message: the name of the message you want to send.
+      target_i: the interface you expect to be created by this message (if there
+        is one, otherwise use '').
+    """
+    args = [(arg_name, self.BuildInterface(arg_type))
+            for arg_name, arg_type in self.dep_graph[(interface, message)]]
+    if interface == 'wl_registry' and message == 'bind' and target_i:
+      args.append(('global', target_i))
+    self.AppendCall(interface, message, args)
+    # We need to do a round-trip after binding the registry so that we have the
+    # globals available for binding.
+    if interface == 'wl_display' and message == 'get_registry' and target_i:
+      self.AppendRoundTrip()
+
+  def TemplateData(self):
+    return {'sequence': self.sequence}
 
 
-def GetSequenceForInterface(i_name, context):
-  if context.IsPrevented(i_name):
-    return []
-  if i_name == 'wl_registry' or context.dep_graph[i_name] == ('wl_registry',
-                                                              'bind'):
-    context.Prevent(i_name)
-  (cons_i, cons_m) = context.dep_graph[i_name]
-  seq = GetSequenceForMessage(cons_i, cons_m, context, i_name)
-  context.RecordInterfaceCreated(i_name)
-  return seq
+def GetManualSequences(dep_graph):
+  """Get the sequences which can't be automated.
 
-
-def GetSequenceForMessage(i_name, m_name, context, target_i):
-  """Return the message sequence up to and including the supplied message.
+  Some sequences of messages are interesting, and involve more than what can be
+  automatically generated by looking at the dependency graph. In such cases we
+  define the sequence here so that it will still be automatically generated as
+  part of the corpus.
 
   Args:
-    i_name: the name of the interface that defines the message you want to
-      send.
-    m_name: the name of the message you want to send.
-    context: the global context state,
-    target_i: the interface you expect to be created by this message (if there
-      is one, otherwise use '').
+    dep_graph: the dependency graph between messages and interfaces.
 
   Returns:
-    A list of (i, m, [args...]) tuples, each having an interface name, message
-    name, and arguments for invoking that message. The ('', '', []) tuple
-    indicates the default message (which sends wl_display_roundtrip()).
+    A list of SequenceBuilder objects, each one containing a manually-created
+    sequence.
   """
-  seq = []
-  args = []
-  # get the message sequence needed to create each argument for the target
-  # message.
-  for arg_n, arg_t in context.dep_graph[(i_name, m_name)]:
-    seq += GetSequenceForInterface(arg_t, context)
-    args.append((arg_n, context.GetLastInterfaceCreated(arg_t)))
-  if i_name == 'wl_registry' and m_name == 'bind' and target_i:
-    args.append(('global', target_i))
-  seq.append((i_name, m_name, args))
-  # We need to do a round-trip after binding the registry so that we have the
-  # globals available for binding.
-  if i_name == 'wl_display' and m_name == 'get_registry' and target_i:
-    seq.append(round_trip)
-  return seq
+  c = SequenceBuilder('copy_paste', dep_graph)
+  c_device = c.BuildInterface('wl_data_device')
+  c_source = c.BuildInterface('wl_data_source')
+  # TODO(crbug/979456): make a fuzz-dictionary with all the mime-types.
+  c.AppendCall('wl_data_source', 'offer', [('receiver', c_source),
+                                           ('mime_type', '"text/plain"')])
+  c.AppendCall('wl_data_device', 'set_selection', [('receiver', c_device),
+                                                   ('source', c_source)])
+  c.AppendRoundTrip()
+  c_surface = c.BuildInterface('wl_shell_surface')
+  c.AppendCall('wl_shell_surface', 'set_toplevel', [('receiver', c_surface)])
+  c_shm = c.BuildInterface('wl_shm')
+  c.AppendRoundTrip()  # Round trip so server can send us formats.
+  c.AppendCall('wl_shm', 'create_pool', [('receiver', c_shm), ('size', '64')])
+  c.RecordInterfaceCreated('wl_shm_pool')
+  c_shm_pool = c.GetLastInterfaceCreated('wl_shm_pool')
+  c.AppendCall('wl_shm_pool', 'create_buffer', [('receiver', c_shm_pool),
+                                                ('width', 1), ('stride', 4),
+                                                ('height', 1)])
+  c.RecordInterfaceCreated('wl_buffer')
+  c_buffer = c.GetLastInterfaceCreated('wl_buffer')
+  c.AppendCall('wl_surface', 'attach', [('receiver', c_surface),
+                                        ('buffer', c_buffer)])
+  c.AppendCall('wl_surface', 'damage', [('receiver', c_surface), ('width', 1),
+                                        ('height', 1)])
+  c.AppendCall('wl_surface', 'commit', [('receiver', c_surface)])
+  c.AppendRoundTrip()  # Round trip so server can make the data_offer.
+  c.RecordInterfaceCreated('wl_data_offer')
+
+  e = SequenceBuilder('empty', dep_graph)
+
+  p = SequenceBuilder('popup_configuration', dep_graph)
+  p_positioner = p.BuildInterface('zxdg_positioner_v6')
+  p_parent = p.BuildInterface('zxdg_toplevel_v6')
+  p.AppendCall('zxdg_surface_v6', 'set_window_geometry',
+               [('receiver', p_parent), ('x', 0), ('y', 0), ('width', 10),
+                ('height', 10)])
+  p.AppendRoundTrip()
+  p.AppendCall('wl_surface', 'commit', [('receiver', p_parent)])
+  p.AppendRoundTrip()
+  p.AppendCall('zxdg_surface_v6', 'ack_configure', [('receiver', p_parent),
+                                                    ('serial', 1)])
+  p_child = p.BuildInterface('zxdg_surface_v6')
+  p.AppendCall('zxdg_surface_v6', 'get_popup', [('receiver', p_child),
+                                                ('parent', p_parent),
+                                                ('positioner', p_positioner)])
+  p.AppendRoundTrip()
+
+  return [c, e, p]
+
+
+def SequenceToTemplate(parsed_arguments, builder):
+  out_dir = parsed_arguments.output
+  if not os.path.exists(out_dir):
+    os.mkdir(out_dir)
+  out_path = os.path.join(out_dir, builder.name + '.asciipb')
+  wayland_templater.InstantiateTemplate(parsed_arguments.input,
+                                        builder.TemplateData(), out_path,
+                                        parsed_arguments.directory)
 
 
 def Main(argv):
@@ -151,9 +219,6 @@
     argv: command-line arguments used to run the sequencer.
   """
   parsed = wlu.ParseOpts(argv)
-  out_dir = parsed.output
-  if not os.path.exists(out_dir):
-    os.mkdir(out_dir)
 
   protocols = wlu.ReadProtocols(parsed.spec)
   dep_graph = GetDependencyGraph(protocols)
@@ -161,16 +226,19 @@
     for req in interface.findall('request'):
       interface_name = interface.attrib['name']
       message_name = req.attrib['name']
-      sequence = GetSequenceForMessage(interface_name, message_name,
-                                       SequenceContext(dep_graph), '')
+      builder = SequenceBuilder(interface_name + '_' + message_name, dep_graph)
+      builder.BuildMessage(interface_name, message_name, '')
+
       # Add a round-trip to the sequence in case the server wants to do
       # something funky.
-      sequence += [round_trip]
+      builder.AppendRoundTrip()
 
-      out_path = os.path.join(out_dir,
-                              '%s_%s.asciipb' % (interface_name, message_name))
-      wayland_templater.InstantiateTemplate(
-          parsed.input, {'sequence': sequence}, out_path, parsed.directory)
+      SequenceToTemplate(parsed, builder)
+
+  # For sequences which are more complicated than a dependency search, we have
+  # a manual backup.
+  for sequence_builder in GetManualSequences(dep_graph):
+    SequenceToTemplate(parsed, sequence_builder)
 
 
 if __name__ == '__main__':
diff --git a/components/exo/wayland/fuzzer/wayland_templater.py b/components/exo/wayland/fuzzer/wayland_templater.py
index 65e6d90d..bac497c 100644
--- a/components/exo/wayland/fuzzer/wayland_templater.py
+++ b/components/exo/wayland/fuzzer/wayland_templater.py
@@ -11,8 +11,8 @@
 from __future__ import absolute_import
 from __future__ import print_function
 
-import sys
 import os
+import sys
 
 import jinja2
 import wayland_utils as wlu
@@ -104,6 +104,7 @@
       'proto_type': proto_type_conversions[ty],
       'cpp_type': GetCppType(arg),
       'interface': arg.get('interface'),
+      'doc': wlu.GetDocumentation(arg),
   }
 
 
@@ -113,6 +114,8 @@
   return {
       'name':
           name,
+      'tag':
+          message.tag,
       'idx':
           context.GetAndIncrementCount('message_index'),
       'args': [GetArg(a) for a in message.findall('arg')],
@@ -124,6 +127,8 @@
           constructed,
       'constructed_has_listener':
           constructed in context.interfaces_with_listeners,
+      'doc':
+          wlu.GetDocumentation(message),
   }
 
 
@@ -143,7 +148,9 @@
           GetMessage(m, context) for m in interface.findall('request')
       ],
       'has_listener':
-          wlu.NeedsListener(interface)
+          wlu.NeedsListener(interface),
+      'doc':
+          wlu.GetDocumentation(interface),
   }
 
 
@@ -154,7 +161,7 @@
   for p in protocols:
     for i in p.findall('interface'):
       interfaces.append(GetInterface(i, context))
-  assert all(p.endswith(".xml") for p in protocol_paths)
+  assert all(p.endswith('.xml') for p in protocol_paths)
   return {
       'protocol_names': [str(os.path.basename(p))[:-4] for p in protocol_paths],
       'interfaces':
diff --git a/components/exo/wayland/fuzzer/wayland_utils.py b/components/exo/wayland/fuzzer/wayland_utils.py
index e590dc0b..1aa7ca5 100644
--- a/components/exo/wayland/fuzzer/wayland_utils.py
+++ b/components/exo/wayland/fuzzer/wayland_utils.py
@@ -107,6 +107,29 @@
   return interface.find('event') is not None
 
 
+def GetDocumentation(element):
+  """Return this element's documentation as a list of strings.
+
+  Args:
+    element: the xml.etree.ElementTree node you want the documentation for.
+
+  Returns:
+    A list of strings, containing the data from this node's "summary" attribute,
+    or its "description" subelement.
+  """
+  doc = element.find('description')
+  if doc is None:
+    summary = element.get('summary')
+    return [summary] if summary is not None else []
+  ret = [doc.get('summary')]
+  if doc.text:
+    ret += [l.strip() for l in doc.text.split('\n')]
+    # Remove blank lines from the tail only.
+    while not ret[-1]:
+      ret.pop()
+  return ret
+
+
 def ParseOpts(argv):
   """Parses the given command line arguments for the templater.
 
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 6d2fdaa..c2baa3e 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -44,6 +44,7 @@
 #include "components/exo/wayland/wl_subcompositor.h"
 #include "components/exo/wayland/wp_presentation.h"
 #include "components/exo/wayland/wp_viewporter.h"
+#include "components/exo/wayland/zaura_shell.h"
 #include "components/exo/wayland/zcr_alpha_compositing.h"
 #include "components/exo/wayland/zcr_secure_output.h"
 #include "components/exo/wayland/zcr_stylus.h"
@@ -54,7 +55,6 @@
 
 #if defined(OS_CHROMEOS)
 #include "components/exo/wayland/wl_shell.h"
-#include "components/exo/wayland/zaura_shell.h"
 #include "components/exo/wayland/zcr_cursor_shapes.h"
 #include "components/exo/wayland/zcr_gaming_input.h"
 #include "components/exo/wayland/zcr_keyboard_configuration.h"
@@ -136,11 +136,11 @@
   wl_global_create(wl_display_.get(),
                    &zwp_linux_explicit_synchronization_v1_interface, 1,
                    display_, bind_linux_explicit_synchronization);
+  wl_global_create(wl_display_.get(), &zaura_shell_interface,
+                   kZAuraShellVersion, display_, bind_aura_shell);
 #if defined(OS_CHROMEOS)
   wl_global_create(wl_display_.get(), &wl_shell_interface, 1, display_,
                    bind_shell);
-  wl_global_create(wl_display_.get(), &zaura_shell_interface,
-                   kZAuraShellVersion, display_, bind_aura_shell);
   wl_global_create(wl_display_.get(), &zcr_cursor_shapes_v1_interface, 1,
                    display_, bind_cursor_shapes);
   wl_global_create(wl_display_.get(), &zcr_gaming_input_v2_interface, 1,
diff --git a/components/exo/wayland/zaura_shell.cc b/components/exo/wayland/zaura_shell.cc
index 95872439..a912432 100644
--- a/components/exo/wayland/zaura_shell.cc
+++ b/components/exo/wayland/zaura_shell.cc
@@ -13,11 +13,10 @@
 #include <utility>
 #include <vector>
 
-#include "ash/session/session_controller_impl.h"
-#include "ash/shell.h"
 #include "components/exo/wayland/server_util.h"
 #include "components/exo/wayland/wayland_display_observer.h"
 #include "components/exo/wayland/wl_output.h"
+#include "components/exo/wm_helper.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_occlusion_tracker.h"
 #include "ui/display/manager/display_manager.h"
@@ -26,6 +25,11 @@
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/public/activation_client.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#endif  // defined(OS_CHROMEOS)
+
 namespace exo {
 namespace wayland {
 
@@ -143,11 +147,11 @@
     : surface_(surface), resource_(resource) {
   surface_->AddSurfaceObserver(this);
   surface_->SetProperty(kSurfaceHasAuraSurfaceKey, true);
-  ash::Shell::Get()->activation_client()->AddObserver(this);
+  WMHelper::GetInstance()->AddActivationObserver(this);
 }
 
 AuraSurface::~AuraSurface() {
-  ash::Shell::Get()->activation_client()->RemoveObserver(this);
+  WMHelper::GetInstance()->RemoveActivationObserver(this);
   if (surface_) {
     surface_->RemoveSurfaceObserver(this);
     surface_->SetProperty(kSurfaceHasAuraSurfaceKey, false);
@@ -260,6 +264,7 @@
 void AuraSurface::ComputeAndSendOcclusionFraction(
     const aura::Window::OcclusionState occlusion_state,
     const SkRegion& occluded_region) {
+#if defined(OS_CHROMEOS)
   // Should re-write in locked case - we don't want to trigger PIP upon
   // locking the screen.
   // TODO(afakhry): We may also want to have special behaviour here for virtual
@@ -268,6 +273,7 @@
     SendOcclusionFraction(0.0f);
     return;
   }
+#endif  // defined(OS_CHROMEOS)
 
   auto* window = surface_->window();
   float fraction_occluded = 0.0f;
@@ -311,16 +317,15 @@
 
   // Overridden from WaylandDisplayObserver::ScaleObserver:
   void OnDisplayScalesChanged(const display::Display& display) override {
-    display::DisplayManager* display_manager =
-        ash::Shell::Get()->display_manager();
+    const WMHelper* wm_helper = WMHelper::GetInstance();
     const display::ManagedDisplayInfo& display_info =
-        display_manager->GetDisplayInfo(display.id());
+        wm_helper->GetDisplayInfo(display.id());
 
     if (wl_resource_get_version(resource_) >=
         ZAURA_OUTPUT_SCALE_SINCE_VERSION) {
       display::ManagedDisplayMode active_mode;
-      bool rv = display_manager->GetActiveModeForDisplayId(display.id(),
-                                                           &active_mode);
+      bool rv =
+          wm_helper->GetActiveModeForDisplayId(display.id(), &active_mode);
       DCHECK(rv);
       const int32_t current_output_scale =
           std::round(display_info.zoom_factor() * 1000.f);
diff --git a/components/exo/wayland/zaura_shell.h b/components/exo/wayland/zaura_shell.h
index da571a2..0cdcffdcf 100644
--- a/components/exo/wayland/zaura_shell.h
+++ b/components/exo/wayland/zaura_shell.h
@@ -19,6 +19,9 @@
 
 constexpr uint32_t kZAuraShellVersion = 8;
 
+// Adds bindings to the Aura Shell. Normally this implies Ash on ChromeOS
+// builds. On non-ChromeOS builds the protocol provides access to Aura windowing
+// system.
 void bind_aura_shell(wl_client* client,
                      void* data,
                      uint32_t version,
diff --git a/components/exo/wm_helper.h b/components/exo/wm_helper.h
index baf1b24..3a65d52 100644
--- a/components/exo/wm_helper.h
+++ b/components/exo/wm_helper.h
@@ -28,6 +28,7 @@
 
 namespace display {
 class ManagedDisplayInfo;
+class ManagedDisplayMode;
 }
 
 namespace ui {
@@ -106,6 +107,9 @@
       int64_t display_id) const = 0;
   virtual const std::vector<uint8_t>& GetDisplayIdentificationData(
       int64_t display_id) const = 0;
+  virtual bool GetActiveModeForDisplayId(
+      int64_t display_id,
+      display::ManagedDisplayMode* mode) const = 0;
 
   virtual aura::Window* GetPrimaryDisplayContainer(int container_id) = 0;
   virtual aura::Window* GetActiveWindow() const = 0;
diff --git a/components/exo/wm_helper_chromeos.cc b/components/exo/wm_helper_chromeos.cc
index aa0d4322..cc470fa 100644
--- a/components/exo/wm_helper_chromeos.cc
+++ b/components/exo/wm_helper_chromeos.cc
@@ -150,6 +150,13 @@
   return no_data;
 }
 
+bool WMHelperChromeOS::GetActiveModeForDisplayId(
+    int64_t display_id,
+    display::ManagedDisplayMode* mode) const {
+  return ash::Shell::Get()->display_manager()->GetActiveModeForDisplayId(
+      display_id, mode);
+}
+
 aura::Window* WMHelperChromeOS::GetPrimaryDisplayContainer(int container_id) {
   return ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
                                   container_id);
diff --git a/components/exo/wm_helper_chromeos.h b/components/exo/wm_helper_chromeos.h
index 854630cf..8ce2a99 100644
--- a/components/exo/wm_helper_chromeos.h
+++ b/components/exo/wm_helper_chromeos.h
@@ -77,6 +77,9 @@
       int64_t display_id) const override;
   const std::vector<uint8_t>& GetDisplayIdentificationData(
       int64_t display_id) const override;
+  bool GetActiveModeForDisplayId(
+      int64_t display_id,
+      display::ManagedDisplayMode* mode) const override;
 
   aura::Window* GetPrimaryDisplayContainer(int container_id) override;
   aura::Window* GetActiveWindow() const override;
diff --git a/components/favicon_base/favicon_url_parser.cc b/components/favicon_base/favicon_url_parser.cc
index f7de810..3cbd044 100644
--- a/components/favicon_base/favicon_url_parser.cc
+++ b/components/favicon_base/favicon_url_parser.cc
@@ -6,17 +6,14 @@
 
 #include "base/strings/string_number_conversions.h"
 #include "components/favicon_base/favicon_types.h"
+#include "net/base/url_util.h"
 #include "net/url_request/url_request.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/gfx/favicon_size.h"
 
-namespace {
+namespace chrome {
 
-// Parameters which can be used in chrome://favicon path. See file
-// "chrome/browser/ui/webui/favicon_source.h" for a description of
-// what each does.
-const char kIconURLParameter[] = "iconurl/";
-const char kSizeParameter[] = "size/";
+namespace {
 
 // Returns true if |search| is a substring of |path| which starts at
 // |start_index|.
@@ -26,12 +23,28 @@
   return path.compare(start_index, search.length(), search) == 0;
 }
 
-}  // namespace
+bool ParseIsIconUrl(const std::string& url_type, bool* is_icon_url) {
+  if (url_type == "page_url") {
+    *is_icon_url = false;
+    return true;
+  }
 
-namespace chrome {
+  if (url_type == "icon_url") {
+    *is_icon_url = true;
+    return true;
+  }
 
-bool ParseFaviconPath(const std::string& path,
-                      ParsedFaviconPath* parsed) {
+  return false;
+}
+
+// Parse with legacy FaviconUrlFormat::kFavicon format.
+bool ParseFaviconPathWithLegacyFormat(const std::string& path,
+                                      chrome::ParsedFaviconPath* parsed) {
+  // Parameters which can be used in chrome://favicon path. See file
+  // "favicon_url_parser.h" for a description of what each one does.
+  const char kIconURLParameter[] = "iconurl/";
+  const char kSizeParameter[] = "size/";
+
   parsed->is_icon_url = false;
   parsed->url = "";
   parsed->size_in_dip = gfx::kFaviconSize;
@@ -89,4 +102,56 @@
   return true;
 }
 
+// Parse with FaviconUrlFormat::kFavicon2 format.
+bool ParseFaviconPathWithFavicon2Format(const std::string& path,
+                                        chrome::ParsedFaviconPath* parsed) {
+  // Parameters which can be used in chrome://favicon2 path. See file
+  // "favicon_url_parser.h" for a description of what each one does.
+  const std::string kSizeParameter = "size";
+  const std::string kScaleParameter = "scale_factor";
+  const std::string kUrlTypeParameter = "url_type";
+  const std::string kUrlParameter = "url";
+
+  if (path.empty())
+    return false;
+
+  GURL query_url("chrome://favicon2/" + path);
+
+  std::string size_str;
+  if (!net::GetValueForKeyInQuery(query_url, kSizeParameter, &size_str))
+    parsed->size_in_dip = gfx::kFaviconSize;
+  else if (!base::StringToInt(size_str, &parsed->size_in_dip))
+    return false;
+
+  std::string scale_str;
+  if (!net::GetValueForKeyInQuery(query_url, kScaleParameter, &scale_str))
+    parsed->device_scale_factor = 1.0f;
+  else if (!webui::ParseScaleFactor(scale_str, &parsed->device_scale_factor))
+    return false;
+
+  std::string url_type;
+  if (!net::GetValueForKeyInQuery(query_url, kUrlTypeParameter, &url_type))
+    parsed->is_icon_url = false;
+  else if (!ParseIsIconUrl(url_type, &parsed->is_icon_url))
+    return false;
+
+  net::GetValueForKeyInQuery(query_url, kUrlParameter, &parsed->url);
+
+  return true;
+}
+}  // namespace
+
+bool ParseFaviconPath(const std::string& path,
+                      FaviconUrlFormat format,
+                      ParsedFaviconPath* parsed) {
+  switch (format) {
+    case FaviconUrlFormat::kFaviconLegacy:
+      return ParseFaviconPathWithLegacyFormat(path, parsed);
+    case FaviconUrlFormat::kFavicon2:
+      return ParseFaviconPathWithFavicon2Format(path, parsed);
+  }
+  NOTREACHED();
+  return false;
+}
+
 }  // namespace chrome
diff --git a/components/favicon_base/favicon_url_parser.h b/components/favicon_base/favicon_url_parser.h
index fdedc0ca..4a6ff65 100644
--- a/components/favicon_base/favicon_url_parser.h
+++ b/components/favicon_base/favicon_url_parser.h
@@ -24,16 +24,73 @@
   // The device scale factor of the requested favicon.
   float device_scale_factor;
 
+  // TODO(victorvianna): Remove this parameter.
   // The index of the first character (relative to the path) where the the URL
   // from which the favicon is being requested is located.
   size_t path_index;
 };
 
-// Parses |path|, which should be in the format described at the top of the
-// file "chrome/browser/ui/webui/favicon_source.h". Returns true if |path| could
-// be parsed. The result of the parsing will be stored in a ParsedFaviconPath
-// struct.
-bool ParseFaviconPath(const std::string& path, ParsedFaviconPath* parsed);
+// Enum describing the two possible url formats: the legacy chrome://favicon
+// and chrome://favicon2.
+// - chrome://favicon format:
+//   chrome://favicon/size&scalefactor/iconurl/url
+// Some parameters are optional as described below. However, the order of the
+// parameters is not interchangeable.
+//
+// Parameter:
+//  'url'               Required
+//    Specifies the page URL of the requested favicon. If the 'iconurl'
+//    parameter is specified, the URL refers to the URL of the favicon image
+//    instead.
+//  'size&scalefactor'  Optional
+//    Values: ['size/aa@bx/']
+//      Specifies the requested favicon's size in DIP (aa) and the requested
+//      favicon's scale factor. (b).
+//      The supported requested DIP sizes are: 16x16, 32x32 and 64x64.
+//      If the parameter is unspecified, the requested favicon's size defaults
+//      to 16 and the requested scale factor defaults to 1x.
+//      Example: chrome://favicon/size/16@2x/https://www.google.com/
+//  'iconurl'           Optional
+//    Values: ['iconurl']
+//    'iconurl': Specifies that the url parameter refers to the URL of
+//    the favicon image as opposed to the URL of the page that the favicon is
+//    on.
+//    Example: chrome://favicon/iconurl/https://www.google.com/favicon.ico
+//
+// - chrome://favicon2 format:
+//   chrome://favicon2/?query_parameters
+// Standard URL query parameters are used as described below.
+//
+// Parameter:
+//  'url' Required
+//    The url whose favicon we want.
+//  'url_type' Optional
+//   Values: ['icon_url', 'page_url']
+//    Specifies whether the |url| parameter refers to the URL of the favicon
+//    image directly or to the page whose favicon we want (respectively). If
+//    unspecified, defaults to 'page_url'.
+//  'size'  Optional
+//      Specifies the requested favicon's size in DIP. If unspecified, defaults
+//      to 16.
+//    Example: chrome://favicon2/?size=32
+// TODO(victorvianna): Refactor to remove scale_factor parameter.
+//  'scale_factor'  Optional
+//      Values: ['SCALEx']
+//      Specifies the requested favicon's scale factor. If unspecified, defaults
+//      to 1x.
+//    Example: chrome://favicon2/?scale_factor=1.2x
+enum class FaviconUrlFormat {
+  // Legacy chrome://favicon format.
+  kFaviconLegacy,
+  // chrome://favicon2 format.
+  kFavicon2,
+};
+
+// Parses |path| according to |format|, returning true if successful. The result
+// of the parsing will be stored in the struct pointed by |parsed|.
+bool ParseFaviconPath(const std::string& path,
+                      FaviconUrlFormat format,
+                      ParsedFaviconPath* parsed);
 
 }  // namespace chrome
 
diff --git a/components/favicon_base/favicon_url_parser_unittest.cc b/components/favicon_base/favicon_url_parser_unittest.cc
index 48eae90..0ea6e4a8 100644
--- a/components/favicon_base/favicon_url_parser_unittest.cc
+++ b/components/favicon_base/favicon_url_parser_unittest.cc
@@ -34,12 +34,13 @@
 };
 
 // Test parsing path with no extra parameters.
-TEST_F(FaviconUrlParserTest, ParsingNoExtraParams) {
+TEST_F(FaviconUrlParserTest, LegacyParsingNoExtraParams) {
   const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
   chrome::ParsedFaviconPath parsed;
 
   const std::string path1 = url;
-  EXPECT_TRUE(chrome::ParseFaviconPath(path1, &parsed));
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path1, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   EXPECT_FALSE(parsed.is_icon_url);
   EXPECT_EQ(url, parsed.url);
   EXPECT_EQ(16, parsed.size_in_dip);
@@ -47,13 +48,14 @@
 }
 
 // Test parsing path with a 'size' parameter.
-TEST_F(FaviconUrlParserTest, ParsingSizeParam) {
+TEST_F(FaviconUrlParserTest, LegacyParsingSizeParam) {
   const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
   chrome::ParsedFaviconPath parsed;
 
   // Test that we can still parse the legacy 'size' parameter format.
   const std::string path2 = "size/32/" + url;
-  EXPECT_TRUE(chrome::ParseFaviconPath(path2, &parsed));
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path2, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   EXPECT_FALSE(parsed.is_icon_url);
   EXPECT_EQ(url, parsed.url);
   EXPECT_EQ(32, parsed.size_in_dip);
@@ -61,7 +63,8 @@
 
   // Test parsing current 'size' parameter format.
   const std::string path3 = "size/32@1.4x/" + url;
-  EXPECT_TRUE(chrome::ParseFaviconPath(path3, &parsed));
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path3, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   EXPECT_FALSE(parsed.is_icon_url);
   EXPECT_EQ(url, parsed.url);
   EXPECT_EQ(32, parsed.size_in_dip);
@@ -70,7 +73,8 @@
   // Test that we pick the ui::ScaleFactor which is closest to the passed in
   // scale factor.
   const std::string path4 = "size/16@1.41x/" + url;
-  EXPECT_TRUE(chrome::ParseFaviconPath(path4, &parsed));
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path4, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   EXPECT_FALSE(parsed.is_icon_url);
   EXPECT_EQ(url, parsed.url);
   EXPECT_EQ(16, parsed.size_in_dip);
@@ -78,15 +82,19 @@
 
   // Invalid cases.
   const std::string path5 = "size/" + url;
-  EXPECT_FALSE(chrome::ParseFaviconPath(path5, &parsed));
+  EXPECT_FALSE(chrome::ParseFaviconPath(
+      path5, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   const std::string path6 = "size/@1x/" + url;
-  EXPECT_FALSE(chrome::ParseFaviconPath(path6, &parsed));
+  EXPECT_FALSE(chrome::ParseFaviconPath(
+      path6, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   const std::string path7 = "size/abc@1x/" + url;
-  EXPECT_FALSE(chrome::ParseFaviconPath(path7, &parsed));
+  EXPECT_FALSE(chrome::ParseFaviconPath(
+      path7, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
 
   // Part of url looks like 'size' parameter.
   const std::string path8 = "http://www.google.com/size/32@1.4x";
-  EXPECT_TRUE(chrome::ParseFaviconPath(path8, &parsed));
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path8, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   EXPECT_FALSE(parsed.is_icon_url);
   EXPECT_EQ(path8, parsed.url);
   EXPECT_EQ(16, parsed.size_in_dip);
@@ -94,12 +102,13 @@
 }
 
 // Test parsing path with 'iconurl' parameter.
-TEST_F(FaviconUrlParserTest, ParsingIconUrlParam) {
+TEST_F(FaviconUrlParserTest, LegacyParsingIconUrlParam) {
   const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
   chrome::ParsedFaviconPath parsed;
 
   const std::string path10 = "iconurl/http://www.google.com/favicon.ico";
-  EXPECT_TRUE(chrome::ParseFaviconPath(path10, &parsed));
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path10, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   EXPECT_TRUE(parsed.is_icon_url);
   EXPECT_EQ("http://www.google.com/favicon.ico", parsed.url);
   EXPECT_EQ(16, parsed.size_in_dip);
@@ -108,14 +117,60 @@
 
 // Test parsing paths with both a 'size' parameter and a 'url modifier'
 // parameter.
-TEST_F(FaviconUrlParserTest, ParsingSizeParamAndUrlModifier) {
+TEST_F(FaviconUrlParserTest, LegacyParsingSizeParamAndUrlModifier) {
   const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
   chrome::ParsedFaviconPath parsed;
 
   const std::string path14 =
       "size/32/iconurl/http://www.google.com/favicon.ico";
-  EXPECT_TRUE(chrome::ParseFaviconPath(path14, &parsed));
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path14, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed));
   EXPECT_TRUE(parsed.is_icon_url);
   EXPECT_EQ("http://www.google.com/favicon.ico", parsed.url);
   EXPECT_EQ(32, parsed.size_in_dip);
 }
+
+TEST_F(FaviconUrlParserTest, Favicon2ParsingSizeParam) {
+  chrome::ParsedFaviconPath parsed;
+
+  std::string path = "?size=32";
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path, chrome::FaviconUrlFormat::kFavicon2, &parsed));
+  EXPECT_FALSE(parsed.is_icon_url);
+  EXPECT_EQ(32, parsed.size_in_dip);
+
+  path = "?size=abc";
+  EXPECT_FALSE(chrome::ParseFaviconPath(
+      path, chrome::FaviconUrlFormat::kFavicon2, &parsed));
+}
+
+TEST_F(FaviconUrlParserTest, Favicon2ParsingScaleFactorParam) {
+  chrome::ParsedFaviconPath parsed;
+
+  std::string path = "?scale_factor=2.1x";
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path, chrome::FaviconUrlFormat::kFavicon2, &parsed));
+  EXPECT_EQ(2.1f, parsed.device_scale_factor);
+
+  path = "?scale_factor=-1";
+  EXPECT_FALSE(chrome::ParseFaviconPath(
+      path, chrome::FaviconUrlFormat::kFavicon2, &parsed));
+}
+
+TEST_F(FaviconUrlParserTest, Favicon2ParsingIconUrlParam) {
+  chrome::ParsedFaviconPath parsed;
+
+  std::string path = "?url_type=icon_url";
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path, chrome::FaviconUrlFormat::kFavicon2, &parsed));
+  EXPECT_TRUE(parsed.is_icon_url);
+
+  path = "?url_type=page_url";
+  EXPECT_TRUE(chrome::ParseFaviconPath(
+      path, chrome::FaviconUrlFormat::kFavicon2, &parsed));
+  EXPECT_FALSE(parsed.is_icon_url);
+
+  path = "?url_type=invalid";
+  EXPECT_FALSE(chrome::ParseFaviconPath(
+      path, chrome::FaviconUrlFormat::kFavicon2, &parsed));
+}
diff --git a/components/gcm_driver/BUILD.gn b/components/gcm_driver/BUILD.gn
index fe711178..7830e3d 100644
--- a/components/gcm_driver/BUILD.gn
+++ b/components/gcm_driver/BUILD.gn
@@ -40,6 +40,8 @@
     "registration_info.h",
     "system_encryptor.cc",
     "system_encryptor.h",
+    "web_push_sender.cc",
+    "web_push_sender.h",
   ]
 
   public_deps = [
@@ -194,6 +196,15 @@
     "//third_party/protobuf:protobuf_lite",
   ]
 
+  if (!is_ios) {
+    sources += [ "web_push_sender_unittest.cc" ]
+
+    deps += [
+      "//content/test:test_support",
+      "//third_party/re2",
+    ]
+  }
+
   if (!use_gcm_from_platform) {
     sources += [
       "account_tracker_unittest.cc",
@@ -202,6 +213,7 @@
       "gcm_channel_status_request_unittest.cc",
       "gcm_client_impl_unittest.cc",
       "gcm_driver_desktop_unittest.cc",
+      "gcm_driver_unittest.cc",
       "gcm_stats_recorder_impl_unittest.cc",
     ]
   }
diff --git a/components/gcm_driver/DEPS b/components/gcm_driver/DEPS
index d89782b6..8bc87bd 100644
--- a/components/gcm_driver/DEPS
+++ b/components/gcm_driver/DEPS
@@ -10,6 +10,8 @@
   "+components/timers",  # Only used for Chrome OS builds.
   "+components/version_info",
   "+content/public/browser/browser_context.h",
+  "+content/public/test",  # Only used for tests.
+  "+crypto",  # Only used for tests.
 
   # Whitelist specific headers from //google_apis/gaia. Contact
   # blundell@chromium.org if looking to add more.
@@ -22,4 +24,5 @@
   "+services/network/public/mojom",
   "+services/network/test",
   "+third_party/leveldatabase",  # Only used for tests.
+  "+third_party/re2",  # Only used for tests.
 ]
diff --git a/components/gcm_driver/common/BUILD.gn b/components/gcm_driver/common/BUILD.gn
index ec7530f..9f36716 100644
--- a/components/gcm_driver/common/BUILD.gn
+++ b/components/gcm_driver/common/BUILD.gn
@@ -10,4 +10,8 @@
   ]
 
   defines = [ "GCM_DRIVER_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+  ]
 }
diff --git a/components/gcm_driver/common/gcm_message.cc b/components/gcm_driver/common/gcm_message.cc
index 14b14cf..6e79990 100644
--- a/components/gcm_driver/common/gcm_message.cc
+++ b/components/gcm_driver/common/gcm_message.cc
@@ -8,6 +8,7 @@
 
 // static
 const int OutgoingMessage::kMaximumTTL = 24 * 60 * 60;  // 1 day.
+const int WebPushMessage::kMaximumTTL = 24 * 60 * 60;   // 1 day.
 
 OutgoingMessage::OutgoingMessage() : time_to_live(kMaximumTTL) {}
 
@@ -21,4 +22,10 @@
 
 IncomingMessage::~IncomingMessage() = default;
 
+WebPushMessage::WebPushMessage() : time_to_live(kMaximumTTL) {}
+
+WebPushMessage::WebPushMessage(WebPushMessage&& other) = default;
+
+WebPushMessage::~WebPushMessage() = default;
+
 }  // namespace gcm
diff --git a/components/gcm_driver/common/gcm_message.h b/components/gcm_driver/common/gcm_message.h
index 8172e4c..36c606e 100644
--- a/components/gcm_driver/common/gcm_message.h
+++ b/components/gcm_driver/common/gcm_message.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "base/macros.h"
 #include "components/gcm_driver/common/gcm_driver_export.h"
 
 namespace gcm {
@@ -47,6 +48,23 @@
   bool decrypted;
 };
 
+// Message to be delivered to the other party via Web Push.
+struct GCM_DRIVER_EXPORT WebPushMessage {
+  WebPushMessage();
+  WebPushMessage(WebPushMessage&& other);
+  ~WebPushMessage();
+
+  // Message ID.
+  std::string id;
+  // In seconds.
+  int time_to_live;
+  std::string payload;
+
+  static const int kMaximumTTL;
+
+  DISALLOW_COPY_AND_ASSIGN(WebPushMessage);
+};
+
 }  // namespace gcm
 
 #endif  // COMPONENTS_GCM_DRIVER_COMMON_GCM_MESSAGE_H_
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.cc b/components/gcm_driver/crypto/gcm_encryption_provider.cc
index 5f9d8d4..68cb3c1 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider.cc
+++ b/components/gcm_driver/crypto/gcm_encryption_provider.cc
@@ -10,6 +10,7 @@
 #include "base/big_endian.h"
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/strings/strcat.h"
 #include "components/gcm_driver/common/gcm_message.h"
 #include "components/gcm_driver/crypto/encryption_header_parsers.h"
 #include "components/gcm_driver/crypto/gcm_decryption_result.h"
@@ -245,13 +246,12 @@
                      version, callback));
 }
 
-void GCMEncryptionProvider::EncryptMessage(
-    const std::string& app_id,
-    const std::string& authorized_entity,
-    const std::string& p256dh,
-    const std::string& auth_secret,
-    const std::string& message,
-    const EncryptMessageCallback& callback) {
+void GCMEncryptionProvider::EncryptMessage(const std::string& app_id,
+                                           const std::string& authorized_entity,
+                                           const std::string& p256dh,
+                                           const std::string& auth_secret,
+                                           const std::string& message,
+                                           EncryptMessageCallback callback) {
   DCHECK(key_store_);
   key_store_->GetKeys(
       app_id, authorized_entity,
@@ -343,12 +343,12 @@
     const std::string& p256dh,
     const std::string& auth_secret,
     const std::string& message,
-    const EncryptMessageCallback& callback,
+    EncryptMessageCallback callback,
     std::unique_ptr<crypto::ECPrivateKey> key,
     const std::string& sender_auth_secret) {
   if (!key) {
     DLOG(ERROR) << "Unable to retrieve the keys for the outgoing message.";
-    callback.Run(GCMEncryptionResult::NO_KEYS, std::string());
+    std::move(callback).Run(GCMEncryptionResult::NO_KEYS, std::string());
     return;
   }
 
@@ -360,7 +360,8 @@
   std::string shared_secret;
   if (!ComputeSharedP256Secret(*key, p256dh, &shared_secret)) {
     DLOG(ERROR) << "Unable to calculate the shared secret.";
-    callback.Run(GCMEncryptionResult::INVALID_SHARED_SECRET, std::string());
+    std::move(callback).Run(GCMEncryptionResult::INVALID_SHARED_SECRET,
+                            std::string());
     return;
   }
 
@@ -377,33 +378,27 @@
                              auth_secret, salt, message, &record_size,
                              &ciphertext)) {
     DLOG(ERROR) << "Unable to encrypt the incoming data.";
-    callback.Run(GCMEncryptionResult::ENCRYPTION_FAILED, std::string());
+    std::move(callback).Run(GCMEncryptionResult::ENCRYPTION_FAILED,
+                            std::string());
     return;
   }
 
   // Construct encryption header.
   uint32_t rs = record_size;
-  std::vector<char> rs_buf(sizeof(rs));
-  base::WriteBigEndian(rs_buf.data(), rs);
+  char rs_buf[sizeof(rs)];
+  base::WriteBigEndian(rs_buf, rs);
+  std::string rs_str(std::begin(rs_buf), std::end(rs_buf));
 
   uint8_t key_length = sender_public_key.size();
-  std::vector<char> key_length_buf(sizeof(key_length));
-  base::WriteBigEndian(key_length_buf.data(), key_length);
+  char key_length_buf[sizeof(key_length)];
+  base::WriteBigEndian(key_length_buf, key_length);
+  std::string key_length_str(std::begin(key_length_buf),
+                             std::end(key_length_buf));
 
-  std::vector<uint8_t> payload;
-  payload.reserve(salt.size() + rs_buf.size() + key_length_buf.size() +
-                  sender_public_key.size() + ciphertext.size());
-  std::move(salt.begin(), salt.end(), std::back_inserter(payload));
-  std::move(rs_buf.begin(), rs_buf.end(), std::back_inserter(payload));
-  std::move(key_length_buf.begin(), key_length_buf.end(),
-            std::back_inserter(payload));
-  std::move(sender_public_key.begin(), sender_public_key.end(),
-            std::back_inserter(payload));
-  std::move(ciphertext.begin(), ciphertext.end(), std::back_inserter(payload));
-
-  callback.Run(GCMEncryptionResult::ENCRYPTED_DRAFT_08,
-               std::string(std::make_move_iterator(payload.begin()),
-                           std::make_move_iterator(payload.end())));
+  std::string payload = base::StrCat(
+      {salt, rs_str, key_length_str, sender_public_key, ciphertext});
+  std::move(callback).Run(GCMEncryptionResult::ENCRYPTED_DRAFT_08,
+                          std::move(payload));
 }
 
 }  // namespace gcm
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.h b/components/gcm_driver/crypto/gcm_encryption_provider.h
index dbc4e47..e9134d2 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider.h
+++ b/components/gcm_driver/crypto/gcm_encryption_provider.h
@@ -52,8 +52,7 @@
   // by the |result|. The |message| contains the dispatchable message in success
   // cases, or will be initialized to an empty, default state for failure.
   using EncryptMessageCallback =
-      base::Callback<void(GCMEncryptionResult result,
-                          const std::string& message)>;
+      base::OnceCallback<void(GCMEncryptionResult result, std::string message)>;
 
   GCMEncryptionProvider();
   ~GCMEncryptionProvider();
@@ -101,7 +100,7 @@
                       const std::string& p256dh,
                       const std::string& auth_secret,
                       const std::string& message,
-                      const EncryptMessageCallback& callback);
+                      EncryptMessageCallback callback);
 
  private:
   friend class GCMEncryptionProviderTest;
@@ -137,7 +136,7 @@
                              const std::string& p256dh,
                              const std::string& auth_secret,
                              const std::string& message,
-                             const EncryptMessageCallback& callback,
+                             EncryptMessageCallback callback,
                              std::unique_ptr<crypto::ECPrivateKey> key,
                              const std::string& sender_auth_secret);
 
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
index 1a0f07d..115109a9 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
+++ b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
@@ -115,8 +115,8 @@
                const std::string& message) {
     encryption_provider_->EncryptMessage(
         kExampleAppId, authorized_entity, p256dh, auth_secret, message,
-        base::Bind(&GCMEncryptionProviderTest::DidEncryptMessage,
-                   base::Unretained(this)));
+        base::BindOnce(&GCMEncryptionProviderTest::DidEncryptMessage,
+                       base::Unretained(this)));
 
     // The encryption keys will be read asynchronously.
     base::RunLoop().RunUntilIdle();
@@ -184,10 +184,9 @@
     decrypted_message_ = message;
   }
 
-  void DidEncryptMessage(GCMEncryptionResult result,
-                         const std::string& message) {
+  void DidEncryptMessage(GCMEncryptionResult result, std::string message) {
     encryption_result_ = result;
-    encrypted_message_ = message;
+    encrypted_message_ = std::move(message);
   }
 
   base::test::ScopedTaskEnvironment task_environment_;
@@ -581,7 +580,7 @@
       ASSERT_EQ(GCMEncryptionResult::ENCRYPTED_DRAFT_08, encryption_result());
 
       message.data["content-encoding"] = "aes128gcm";
-      message.raw_data = std::move(encrypted_message());
+      message.raw_data = encrypted_message();
       break;
     }
   }
@@ -618,7 +617,7 @@
   ASSERT_GT(public_key.size(), 0u);
 
   ASSERT_NO_FATAL_FAILURE(
-      Encrypt(authorized_entity, public_key, auth_secret, "foo"));
+      Encrypt(authorized_entity, public_key, auth_secret, kExampleMessage));
   EXPECT_EQ(GCMEncryptionResult::NO_KEYS, encryption_result());
 }
 
diff --git a/components/gcm_driver/fake_gcm_driver.cc b/components/gcm_driver/fake_gcm_driver.cc
index fb75bedd..e48e826 100644
--- a/components/gcm_driver/fake_gcm_driver.cc
+++ b/components/gcm_driver/fake_gcm_driver.cc
@@ -8,16 +8,24 @@
 #include "base/files/file_path.h"
 #include "base/sequenced_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 
 namespace gcm {
 
-FakeGCMDriver::FakeGCMDriver() : GCMDriver(base::FilePath(), nullptr) {
-}
+FakeGCMDriver::FakeGCMDriver()
+    : GCMDriver(
+          base::FilePath(),
+          nullptr,
+          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+              &test_url_loader_factory_)) {}
 
 FakeGCMDriver::FakeGCMDriver(
     const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
-    : GCMDriver(base::FilePath(), blocking_task_runner) {
-}
+    : GCMDriver(
+          base::FilePath(),
+          blocking_task_runner,
+          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+              &test_url_loader_factory_)) {}
 
 FakeGCMDriver::~FakeGCMDriver() {
 }
diff --git a/components/gcm_driver/fake_gcm_driver.h b/components/gcm_driver/fake_gcm_driver.h
index ba1664ec..8d5e681 100644
--- a/components/gcm_driver/fake_gcm_driver.h
+++ b/components/gcm_driver/fake_gcm_driver.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "components/gcm_driver/gcm_driver.h"
+#include "services/network/test/test_url_loader_factory.h"
 
 namespace base {
 class SequencedTaskRunner;
@@ -68,6 +69,8 @@
                                GCMDecryptionResult result) override;
 
  private:
+  network::TestURLLoaderFactory test_url_loader_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeGCMDriver);
 };
 
diff --git a/components/gcm_driver/gcm_desktop_utils.cc b/components/gcm_driver/gcm_desktop_utils.cc
index 7e4b42d2..750c457 100644
--- a/components/gcm_driver/gcm_desktop_utils.cc
+++ b/components/gcm_driver/gcm_desktop_utils.cc
@@ -101,7 +101,7 @@
       GetChromeBuildInfo(channel, product_category_for_subtypes),
       GetChannelStatusRequestUrl(channel),
       syncer::MakeUserAgentForSync(channel), prefs, store_path,
-      get_socket_factory_callback, url_loader_factory,
+      get_socket_factory_callback, std::move(url_loader_factory),
       network_connection_tracker, ui_task_runner, io_task_runner,
       blocking_task_runner));
 }
diff --git a/components/gcm_driver/gcm_driver.cc b/components/gcm_driver/gcm_driver.cc
index d8ed9b0..a97d6a79 100644
--- a/components/gcm_driver/gcm_driver.cc
+++ b/components/gcm_driver/gcm_driver.cc
@@ -29,9 +29,10 @@
 
 GCMDriver::GCMDriver(
     const base::FilePath& store_path,
-    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
-    : weak_ptr_factory_(this) {
-  // The |blocking_task_runner| can be NULL for tests that do not need the
+    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : web_push_sender_(std::move(url_loader_factory)), weak_ptr_factory_(this) {
+  // The |blocking_task_runner| can be nullptr for tests that do not need the
   // encryption capabilities of the GCMDriver class.
   if (blocking_task_runner)
     encryption_provider_.Init(store_path, blocking_task_runner);
@@ -332,30 +333,37 @@
                                    const std::string& auth_secret,
                                    const std::string& fcm_token,
                                    crypto::ECPrivateKey* vapid_key,
-                                   int time_to_live,
-                                   const std::string& message) {
+                                   WebPushMessage message,
+                                   SendWebPushMessageCallback callback) {
+  std::string payload_copy = message.payload;
   encryption_provider_.EncryptMessage(
-      app_id, authorized_entity, p256dh, auth_secret, message,
-      base::Bind(&GCMDriver::OnMessageEncrypted, weak_ptr_factory_.GetWeakPtr(),
-                 fcm_token, vapid_key, time_to_live));
+      app_id, authorized_entity, p256dh, auth_secret, payload_copy,
+      base::BindOnce(&GCMDriver::OnMessageEncrypted,
+                     weak_ptr_factory_.GetWeakPtr(), fcm_token, vapid_key,
+                     std::move(message), std::move(callback)));
 }
 
 void GCMDriver::OnMessageEncrypted(const std::string& fcm_token,
                                    crypto::ECPrivateKey* vapid_key,
-                                   int time_to_live,
+                                   WebPushMessage message,
+                                   SendWebPushMessageCallback callback,
                                    GCMEncryptionResult result,
-                                   const std::string& message) {
+                                   std::string payload) {
   UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.EncryptMessageResult", result,
                             GCMEncryptionResult::ENUM_SIZE);
 
   switch (result) {
-    case GCMEncryptionResult::ENCRYPTED_DRAFT_08:
-      // TODO: send the message.
+    case GCMEncryptionResult::ENCRYPTED_DRAFT_08: {
+      message.payload = std::move(payload);
+      web_push_sender_.SendMessage(fcm_token, vapid_key, message,
+                                   std::move(callback));
       return;
+    }
     case GCMEncryptionResult::NO_KEYS:
     case GCMEncryptionResult::INVALID_SHARED_SECRET:
     case GCMEncryptionResult::ENCRYPTION_FAILED: {
       LOG(ERROR) << "Webpush message encryption failed";
+      std::move(callback).Run(message.id, false);
       return;
     }
     case GCMEncryptionResult::ENUM_SIZE:
diff --git a/components/gcm_driver/gcm_driver.h b/components/gcm_driver/gcm_driver.h
index e728521..fad2ef3 100644
--- a/components/gcm_driver/gcm_driver.h
+++ b/components/gcm_driver/gcm_driver.h
@@ -17,6 +17,7 @@
 #include "components/gcm_driver/common/gcm_message.h"
 #include "components/gcm_driver/crypto/gcm_encryption_provider.h"
 #include "components/gcm_driver/gcm_client.h"
+#include "components/gcm_driver/web_push_sender.h"
 
 namespace base {
 class FilePath;
@@ -27,6 +28,10 @@
 class ECPrivateKey;
 }
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace gcm {
 
 class GCMAppHandler;
@@ -98,6 +103,8 @@
       base::Callback<void(const std::string&, const std::string&)>;
   using GetGCMStatisticsCallback =
       base::Callback<void(const GCMClient::GCMStatistics& stats)>;
+  using SendWebPushMessageCallback =
+      base::OnceCallback<void(const std::string& message_id, bool result)>;
 
   // Enumeration to be used with GetGCMStatistics() for indicating whether the
   // existing logs should be cleared or kept.
@@ -108,7 +115,8 @@
 
   GCMDriver(
       const base::FilePath& store_path,
-      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner);
+      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   virtual ~GCMDriver();
 
   // Registers |sender_ids| for an app. A registration ID will be returned by
@@ -157,19 +165,25 @@
             const OutgoingMessage& message,
             const SendCallback& callback);
 
-  // Sends a message using FCM web-push with end-to-end encryption.
-  // The |message| is encrypted using private enryption key associated with
-  // |app_id| and |authorized_entity|, against public encryption key |p256dh|
-  // with authentcaition secret |auth_secret|. Encrypted message is sent to FCM
-  // with |fcm_token|, and and authenticate with VAPID using |vapid_key|.
+  // Sends a WebPushMessage via Firebase Cloud Messaging (FCM) Web Push with
+  // end-to-end encryption.
+  // |app_id|: application ID.
+  // |authorized_entity|: authorization entity.
+  // |p256dh|: public encryption key of receiver device.
+  // |auth_secret|: authentcaition secret of receiver device.
+  // |fcm_token|: FCM registration token for receiving end.
+  // |vapid_key|: Private key to sign Voluntary Application Server
+  // Identification for Web Push header.
+  // |message|: WebPushMessage to be sent.
+  // |callback|: To be called once the asynchronous operation is done.
   void SendWebPushMessage(const std::string& app_id,
                           const std::string& authorized_entity,
                           const std::string& p256dh,
                           const std::string& auth_secret,
                           const std::string& fcm_token,
                           crypto::ECPrivateKey* vapid_key,
-                          int time_to_live,
-                          const std::string& message);
+                          WebPushMessage message,
+                          SendWebPushMessageCallback callback);
 
   // Get the public encryption key and the authentication secret associated with
   // |app_id|. If none have been associated with |app_id| yet, they will be
@@ -355,9 +369,10 @@
   // Called after webpush message is encrypted.
   void OnMessageEncrypted(const std::string& fcm_token,
                           crypto::ECPrivateKey* vapid_key,
-                          int time_to_live,
+                          WebPushMessage message,
+                          SendWebPushMessageCallback callback,
                           GCMEncryptionResult result,
-                          const std::string& payload);
+                          std::string payload);
 
   // Callback map (from app_id to callback) for Register.
   std::map<std::string, RegisterCallback> register_callbacks_;
@@ -375,6 +390,9 @@
   // App handler map (from app_id to handler pointer). The handler is not owned.
   GCMAppHandlerMap app_handlers_;
 
+  // Sender for Web Push messages.
+  WebPushSender web_push_sender_;
+
   base::WeakPtrFactory<GCMDriver> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GCMDriver);
diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc
index 93016ae..6d045ea 100644
--- a/components/gcm_driver/gcm_driver_android.cc
+++ b/components/gcm_driver/gcm_driver_android.cc
@@ -14,6 +14,7 @@
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/gcm_driver/android/jni_headers/GCMDriver_jni.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 using base::android::AppendJavaStringArrayToStringVector;
 using base::android::AttachCurrentThread;
@@ -24,11 +25,14 @@
 
 namespace gcm {
 
- GCMDriverAndroid::GCMDriverAndroid(
-     const base::FilePath& store_path,
-     const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
-     : GCMDriver(store_path, blocking_task_runner),
-       recorder_(this) {
+GCMDriverAndroid::GCMDriverAndroid(
+    const base::FilePath& store_path,
+    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : GCMDriver(store_path,
+                blocking_task_runner,
+                std::move(url_loader_factory)),
+      recorder_(this) {
   JNIEnv* env = AttachCurrentThread();
   java_ref_.Reset(Java_GCMDriver_create(env, reinterpret_cast<intptr_t>(this)));
 }
diff --git a/components/gcm_driver/gcm_driver_android.h b/components/gcm_driver/gcm_driver_android.h
index cbb137a..3e619e21 100644
--- a/components/gcm_driver/gcm_driver_android.h
+++ b/components/gcm_driver/gcm_driver_android.h
@@ -20,6 +20,10 @@
 class SequencedTaskRunner;
 }
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace gcm {
 
 // GCMDriver implementation for Android, using Android GCM APIs.
@@ -28,7 +32,8 @@
  public:
   GCMDriverAndroid(
       const base::FilePath& store_path,
-      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner);
+      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~GCMDriverAndroid() override;
 
   // Methods called from Java via JNI:
diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc
index d670b7b..5a930f3b 100644
--- a/components/gcm_driver/gcm_driver_desktop.cc
+++ b/components/gcm_driver/gcm_driver_desktop.cc
@@ -520,7 +520,7 @@
     const scoped_refptr<base::SequencedTaskRunner>& ui_thread,
     const scoped_refptr<base::SequencedTaskRunner>& io_thread,
     const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
-    : GCMDriver(store_path, blocking_task_runner),
+    : GCMDriver(store_path, blocking_task_runner, url_loader_factory_for_ui),
       gcm_channel_status_syncer_(
           new GCMChannelStatusSyncer(this,
                                      prefs,
diff --git a/components/gcm_driver/gcm_driver_unittest.cc b/components/gcm_driver/gcm_driver_unittest.cc
new file mode 100644
index 0000000..033a471
--- /dev/null
+++ b/components/gcm_driver/gcm_driver_unittest.cc
@@ -0,0 +1,332 @@
+// Copyright 2019 The Chromium 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/gcm_driver/gcm_driver_desktop.h"
+
+#include <stdint.h>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
+#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
+#include "components/gcm_driver/fake_gcm_client_factory.h"
+#include "components/gcm_driver/gcm_channel_status_request.h"
+#include "components/gcm_driver/gcm_channel_status_syncer.h"
+#include "components/gcm_driver/gcm_client_factory.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "crypto/ec_private_key.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_network_connection_tracker.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gcm {
+
+namespace {
+
+const char kTestChannelStatusRequestURL[] = "http://channel.status.request.com";
+const char kTestAppID1[] = "TestApp1";
+
+// PKCS #8 encoded P-256 private key.
+const char kPrivateKey[] =
+    "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgS8wRbDOWz0lKExvIVQiRKtPAP8"
+    "dgHUHAw5gyOd5d4jKhRANCAARZb49Va5MD/KcWtc0oiWc2e8njBDtQzj0mzcOl1fDSt16Pvu6p"
+    "fTU3MTWnImDNnkPxtXm58K7Uax8jFxA4TeXJ";
+const char kFCMToken[] = "fcm_token";
+
+void PumpCurrentLoop() {
+  base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
+}
+
+}  // namespace
+
+class GCMDriverBaseTest : public testing::Test {
+ public:
+  enum WaitToFinish { DO_NOT_WAIT, WAIT };
+
+  GCMDriverBaseTest();
+  ~GCMDriverBaseTest() override;
+
+  // testing::Test:
+  void SetUp() override;
+  void TearDown() override;
+
+  GCMDriverDesktop* driver() { return driver_.get(); }
+  const std::string& send_web_push_message_id() const {
+    return send_web_push_message_id_;
+  }
+  bool send_web_push_message_result() const {
+    return send_web_push_message_result_;
+  }
+  const std::string& send_web_push_message_payload() const {
+    return send_web_push_message_payload_;
+  }
+  const std::string& p256dh() const { return p256dh_; }
+  const std::string& auth_secret() const { return auth_secret_; }
+  network::TestURLLoaderFactory& loader() { return test_url_loader_factory_; }
+  GCMDecryptionResult decryption_result() { return decryption_result_; }
+  const IncomingMessage& decrypted_message() { return decrypted_message_; }
+
+  void PumpIOLoop();
+
+  void CreateDriver();
+  void ShutdownDriver();
+
+  void SendWebPushMessage(const std::string& app_id,
+                          WebPushMessage message,
+                          base::Optional<net::HttpStatusCode> completion_status,
+                          WaitToFinish wait_to_finish);
+  void GetEncryptionInfo(const std::string& app_id,
+                         WaitToFinish wait_to_finish);
+  void DecryptMessage(const std::string& app_id,
+                      IncomingMessage message,
+                      WaitToFinish wait_to_finish);
+
+  void SendWebPushMessageCompleted(const std::string& message_id, bool result);
+  void GetEncryptionInfoCompleted(const std::string& p256dh,
+                                  const std::string& auth_secret);
+  void DecryptMessageCompleted(GCMDecryptionResult result,
+                               const IncomingMessage& message);
+  void UnregisterCompleted(GCMClient::Result result);
+
+ private:
+  base::ScopedTempDir temp_dir_;
+  TestingPrefServiceSimple prefs_;
+  base::test::ScopedTaskEnvironment task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::UI};
+  base::Thread io_thread_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+
+  std::unique_ptr<GCMDriverDesktop> driver_;
+
+  base::Closure async_operation_completed_callback_;
+
+  std::string send_web_push_message_id_;
+  bool send_web_push_message_result_;
+  std::string send_web_push_message_payload_;
+  std::string p256dh_;
+  std::string auth_secret_;
+
+  GCMDecryptionResult decryption_result_ = GCMDecryptionResult::UNENCRYPTED;
+  IncomingMessage decrypted_message_;
+
+  DISALLOW_COPY_AND_ASSIGN(GCMDriverBaseTest);
+};
+
+GCMDriverBaseTest::GCMDriverBaseTest() : io_thread_("IOThread") {}
+
+GCMDriverBaseTest::~GCMDriverBaseTest() = default;
+
+void GCMDriverBaseTest::SetUp() {
+  GCMChannelStatusSyncer::RegisterPrefs(prefs_.registry());
+  io_thread_.Start();
+  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+  CreateDriver();
+  PumpIOLoop();
+  PumpCurrentLoop();
+}
+
+void GCMDriverBaseTest::TearDown() {
+  if (!driver_)
+    return;
+
+  ShutdownDriver();
+  driver_.reset();
+  PumpIOLoop();
+
+  io_thread_.Stop();
+}
+
+void GCMDriverBaseTest::PumpIOLoop() {
+  base::RunLoop run_loop;
+  io_thread_.task_runner()->PostTaskAndReply(
+      FROM_HERE, base::BindOnce(&PumpCurrentLoop), run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+void GCMDriverBaseTest::CreateDriver() {
+  scoped_refptr<net::URLRequestContextGetter> request_context =
+      new net::TestURLRequestContextGetter(io_thread_.task_runner());
+  GCMClient::ChromeBuildInfo chrome_build_info;
+  chrome_build_info.product_category_for_subtypes = "com.chrome.macosx";
+  driver_ = std::make_unique<GCMDriverDesktop>(
+      std::make_unique<FakeGCMClientFactory>(
+          base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner()),
+      chrome_build_info, kTestChannelStatusRequestURL, "user-agent-string",
+      &prefs_, temp_dir_.GetPath(), base::DoNothing(),
+      base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+          &test_url_loader_factory_),
+      network::TestNetworkConnectionTracker::GetInstance(),
+      base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner(),
+      task_environment_.GetMainThreadTaskRunner());
+}
+
+void GCMDriverBaseTest::ShutdownDriver() {
+  driver()->Shutdown();
+}
+
+void GCMDriverBaseTest::SendWebPushMessage(
+    const std::string& app_id,
+    WebPushMessage message,
+    base::Optional<net::HttpStatusCode> completion_status,
+    WaitToFinish wait_to_finish) {
+  std::string private_key_info;
+  ASSERT_TRUE(base::Base64Decode(kPrivateKey, &private_key_info));
+  std::unique_ptr<crypto::ECPrivateKey> private_key =
+      crypto::ECPrivateKey::CreateFromPrivateKeyInfo(std::vector<uint8_t>(
+          private_key_info.begin(), private_key_info.end()));
+  ASSERT_TRUE(private_key);
+
+  base::RunLoop run_loop;
+  async_operation_completed_callback_ = run_loop.QuitClosure();
+  driver_->SendWebPushMessage(
+      app_id, /* authorized_entity= */ "", p256dh(), auth_secret(), kFCMToken,
+      private_key.get(), std::move(message),
+      base::BindOnce(&GCMDriverBaseTest::SendWebPushMessageCompleted,
+                     base::Unretained(this)));
+
+  if (completion_status) {
+    ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+    network::TestURLLoaderFactory::PendingRequest* pendingRequest =
+        loader().GetPendingRequest(0);
+    const std::vector<network::DataElement>* body_elements =
+        pendingRequest->request.request_body->elements();
+    ASSERT_EQ(1UL, body_elements->size());
+    const network::DataElement& body = body_elements->back();
+    send_web_push_message_payload_ = std::string(body.bytes(), body.length());
+
+    test_url_loader_factory_.SimulateResponseForPendingRequest(
+        pendingRequest->request.url,
+        network::URLLoaderCompletionStatus(net::OK),
+        network::CreateResourceResponseHead(*completion_status), "");
+  }
+
+  if (wait_to_finish == WAIT)
+    run_loop.Run();
+}
+
+void GCMDriverBaseTest::GetEncryptionInfo(const std::string& app_id,
+                                          WaitToFinish wait_to_finish) {
+  base::RunLoop run_loop;
+  async_operation_completed_callback_ = run_loop.QuitClosure();
+  driver_->GetEncryptionInfo(
+      app_id, base::Bind(&GCMDriverBaseTest::GetEncryptionInfoCompleted,
+                         base::Unretained(this)));
+  if (wait_to_finish == WAIT)
+    run_loop.Run();
+}
+
+void GCMDriverBaseTest::DecryptMessage(const std::string& app_id,
+                                       IncomingMessage message,
+                                       WaitToFinish wait_to_finish) {
+  base::RunLoop run_loop;
+  async_operation_completed_callback_ = run_loop.QuitClosure();
+  driver()->GetEncryptionProviderInternal()->DecryptMessage(
+      app_id, message,
+      base::AdaptCallbackForRepeating(
+          base::BindOnce(&GCMDriverBaseTest::DecryptMessageCompleted,
+                         base::Unretained(this))));
+
+  if (wait_to_finish == WAIT)
+    run_loop.Run();
+}
+
+void GCMDriverBaseTest::SendWebPushMessageCompleted(
+    const std::string& message_id,
+    bool result) {
+  send_web_push_message_id_ = message_id;
+  send_web_push_message_result_ = result;
+  if (!async_operation_completed_callback_.is_null())
+    async_operation_completed_callback_.Run();
+}
+
+void GCMDriverBaseTest::GetEncryptionInfoCompleted(
+    const std::string& p256dh,
+    const std::string& auth_secret) {
+  p256dh_ = p256dh;
+  auth_secret_ = auth_secret;
+  if (!async_operation_completed_callback_.is_null())
+    async_operation_completed_callback_.Run();
+}
+
+void GCMDriverBaseTest::DecryptMessageCompleted(
+    GCMDecryptionResult result,
+    const IncomingMessage& message) {
+  decryption_result_ = result;
+  decrypted_message_ = message;
+  if (!async_operation_completed_callback_.is_null())
+    async_operation_completed_callback_.Run();
+}
+
+TEST_F(GCMDriverBaseTest, SendWebPushMessage) {
+  GetEncryptionInfo(kTestAppID1, GCMDriverBaseTest::WAIT);
+
+  WebPushMessage message;
+  message.id = "message_id";
+  message.time_to_live = 3600;
+  message.payload = "payload";
+  ASSERT_NO_FATAL_FAILURE(SendWebPushMessage(kTestAppID1, std::move(message),
+                                             base::make_optional(net::HTTP_OK),
+                                             GCMDriverBaseTest::WAIT));
+
+  EXPECT_EQ("message_id", send_web_push_message_id());
+  EXPECT_TRUE(send_web_push_message_result());
+
+  IncomingMessage incoming_message;
+  incoming_message.data["content-encoding"] = "aes128gcm";
+  incoming_message.raw_data = send_web_push_message_payload();
+
+  DecryptMessage(kTestAppID1, std::move(incoming_message),
+                 GCMDriverBaseTest::WAIT);
+
+  EXPECT_EQ(GCMDecryptionResult::DECRYPTED_DRAFT_08, decryption_result());
+  EXPECT_EQ("payload", decrypted_message().raw_data);
+}
+
+TEST_F(GCMDriverBaseTest, SendWebPushMessageEncryptionError) {
+  // Intentionally not creating encryption info.
+
+  WebPushMessage message;
+  message.id = "message_id";
+  message.time_to_live = 3600;
+  message.payload = "payload";
+  ASSERT_NO_FATAL_FAILURE(SendWebPushMessage(
+      kTestAppID1, std::move(message), base::nullopt, GCMDriverBaseTest::WAIT));
+
+  EXPECT_EQ("message_id", send_web_push_message_id());
+  EXPECT_FALSE(send_web_push_message_result());
+}
+
+TEST_F(GCMDriverBaseTest, SendWebPushMessageServerError) {
+  GetEncryptionInfo(kTestAppID1, GCMDriverBaseTest::WAIT);
+
+  WebPushMessage message;
+  message.id = "message_id";
+  message.time_to_live = 3600;
+  message.payload = "payload";
+  ASSERT_NO_FATAL_FAILURE(
+      SendWebPushMessage(kTestAppID1, std::move(message),
+                         base::make_optional(net::HTTP_INTERNAL_SERVER_ERROR),
+                         GCMDriverBaseTest::WAIT));
+
+  EXPECT_EQ("message_id", send_web_push_message_id());
+  EXPECT_FALSE(send_web_push_message_result());
+}
+
+}  // namespace gcm
diff --git a/components/gcm_driver/gcm_profile_service.cc b/components/gcm_driver/gcm_profile_service.cc
index 1c68291..0b853cb7c 100644
--- a/components/gcm_driver/gcm_profile_service.cc
+++ b/components/gcm_driver/gcm_profile_service.cc
@@ -14,6 +14,7 @@
 #include "components/gcm_driver/gcm_driver_constants.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if BUILDFLAG(USE_GCM_FROM_PLATFORM)
 #include "base/sequenced_task_runner.h"
@@ -29,7 +30,6 @@
 #include "components/gcm_driver/gcm_desktop_utils.h"
 #include "components/gcm_driver/gcm_driver_desktop.h"
 #include "services/identity/public/cpp/identity_manager.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
 #endif
 
 namespace gcm {
@@ -135,9 +135,11 @@
 #if BUILDFLAG(USE_GCM_FROM_PLATFORM)
 GCMProfileService::GCMProfileService(
     base::FilePath path,
-    scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) {
-  driver_.reset(new GCMDriverAndroid(path.Append(gcm_driver::kGCMStoreDirname),
-                                     blocking_task_runner));
+    scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+  driver_ = std::make_unique<GCMDriverAndroid>(
+      path.Append(gcm_driver::kGCMStoreDirname), blocking_task_runner,
+      std::move(url_loader_factory));
 }
 #else
 GCMProfileService::GCMProfileService(
@@ -157,18 +159,18 @@
     const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
     scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
     : identity_manager_(identity_manager),
-      url_loader_factory_(url_loader_factory) {
+      url_loader_factory_(std::move(url_loader_factory)) {
   driver_ = CreateGCMDriverDesktop(
       std::move(gcm_client_factory), prefs,
       path.Append(gcm_driver::kGCMStoreDirname),
       base::BindRepeating(get_socket_factory_callback,
                           weak_ptr_factory_.GetWeakPtr()),
-      url_loader_factory, network_connection_tracker, channel,
+      url_loader_factory_, network_connection_tracker, channel,
       product_category_for_subtypes, ui_task_runner, io_task_runner,
       blocking_task_runner);
 
   identity_observer_.reset(new IdentityObserver(
-      identity_manager_, url_loader_factory, driver_.get()));
+      identity_manager_, url_loader_factory_, driver_.get()));
 }
 #endif  // BUILDFLAG(USE_GCM_FROM_PLATFORM)
 
diff --git a/components/gcm_driver/gcm_profile_service.h b/components/gcm_driver/gcm_profile_service.h
index 5d41d17..8f3595f 100644
--- a/components/gcm_driver/gcm_profile_service.h
+++ b/components/gcm_driver/gcm_profile_service.h
@@ -49,7 +49,8 @@
 #if BUILDFLAG(USE_GCM_FROM_PLATFORM)
   GCMProfileService(
       base::FilePath path,
-      scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner);
+      scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 #else
   GCMProfileService(
       PrefService* prefs,
@@ -109,4 +110,3 @@
 }  // namespace gcm
 
 #endif  // COMPONENTS_GCM_DRIVER_GCM_PROFILE_SERVICE_H_
-
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.cc b/components/gcm_driver/gcm_stats_recorder_impl.cc
index 4334a53..167a0eb 100644
--- a/components/gcm_driver/gcm_stats_recorder_impl.cc
+++ b/components/gcm_driver/gcm_stats_recorder_impl.cc
@@ -166,11 +166,9 @@
 GCMStatsRecorderImpl::GCMStatsRecorderImpl()
     : is_recording_(false),
       delegate_(nullptr),
-      data_message_received_since_connected_(false),
       received_data_message_burst_size_(0) {}
 
-GCMStatsRecorderImpl::~GCMStatsRecorderImpl() {
-}
+GCMStatsRecorderImpl::~GCMStatsRecorderImpl() = default;
 
 void GCMStatsRecorderImpl::SetDelegate(Delegate* delegate) {
   delegate_ = delegate;
@@ -263,10 +261,9 @@
 
 void GCMStatsRecorderImpl::RecordConnectionInitiated(const std::string& host) {
   last_connection_initiation_time_ = base::TimeTicks::Now();
-  last_connection_success_time_ = base::TimeTicks();
-  data_message_received_since_connected_ = false;
   if (!is_recording_)
     return;
+
   RecordConnection("Connection initiated", host);
 }
 
@@ -274,6 +271,7 @@
     int64_t delay_msec) {
   if (!is_recording_)
     return;
+
   RecordConnection("Connection backoff",
                    base::StringPrintf("Delayed for %" PRId64 " msec",
                                       delay_msec));
@@ -284,7 +282,6 @@
   UMA_HISTOGRAM_MEDIUM_TIMES(
       "GCM.ConnectionLatency",
       (base::TimeTicks::Now() - last_connection_initiation_time_));
-  last_connection_success_time_ = base::TimeTicks::Now();
   last_connection_initiation_time_ = base::TimeTicks();
   if (!is_recording_)
     return;
@@ -420,7 +417,6 @@
   base::TimeTicks new_timestamp = base::TimeTicks::Now();
   if (last_received_data_message_burst_start_time_.is_null()) {
     last_received_data_message_burst_start_time_ = new_timestamp;
-    last_received_data_message_time_within_burst_ = new_timestamp;
     received_data_message_burst_size_ = 1;
   } else if ((new_timestamp - last_received_data_message_burst_start_time_) >=
              base::TimeDelta::FromSeconds(
@@ -431,21 +427,10 @@
     UMA_HISTOGRAM_COUNTS_1M("GCM.ReceivedDataMessageBurstSize",
                             received_data_message_burst_size_);
     last_received_data_message_burst_start_time_ = new_timestamp;
-    last_received_data_message_time_within_burst_ = new_timestamp;
     received_data_message_burst_size_ = 1;
   } else {
-    UMA_HISTOGRAM_TIMES(
-        "GCM.ReceivedDataMessageIntervalWithinBurst",
-        (new_timestamp - last_received_data_message_time_within_burst_));
-    last_received_data_message_time_within_burst_ = new_timestamp;
     ++received_data_message_burst_size_;
   }
-  if (!data_message_received_since_connected_) {
-    DCHECK(!last_connection_success_time_.is_null());
-    UMA_HISTOGRAM_TIMES("GCM.FirstReceivedDataMessageLatencyAfterConnection",
-                        (new_timestamp - last_connection_success_time_));
-    data_message_received_since_connected_ = true;
-  }
 
   if (!is_recording_)
     return;
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.h b/components/gcm_driver/gcm_stats_recorder_impl.h
index e5069d0..e429776 100644
--- a/components/gcm_driver/gcm_stats_recorder_impl.h
+++ b/components/gcm_driver/gcm_stats_recorder_impl.h
@@ -161,10 +161,7 @@
       decryption_failure_activities_;
 
   base::TimeTicks last_connection_initiation_time_;
-  base::TimeTicks last_connection_success_time_;
-  bool data_message_received_since_connected_;
   base::TimeTicks last_received_data_message_burst_start_time_;
-  base::TimeTicks last_received_data_message_time_within_burst_;
   int64_t received_data_message_burst_size_;
 
   DISALLOW_COPY_AND_ASSIGN(GCMStatsRecorderImpl);
diff --git a/components/gcm_driver/web_push_sender.cc b/components/gcm_driver/web_push_sender.cc
new file mode 100644
index 0000000..e5ab039
--- /dev/null
+++ b/components/gcm_driver/web_push_sender.cc
@@ -0,0 +1,189 @@
+// Copyright 2019 The Chromium 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/gcm_driver/web_push_sender.h"
+
+#include <limits.h>
+
+#include "base/base64url.h"
+#include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "components/gcm_driver/common/gcm_message.h"
+#include "components/gcm_driver/crypto/json_web_token_util.h"
+#include "components/gcm_driver/crypto/p256_key_util.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/cors/cors.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "url/gurl.h"
+
+namespace gcm {
+
+namespace {
+
+// VAPID header constants.
+const char kClaimsKeyAudience[] = "aud";
+const char kFCMServerAudience[] = "https://fcm.googleapis.com";
+
+const char kClaimsKeyExpirationTime[] = "exp";
+
+const char kAuthorizationRequestHeaderFormat[] = "vapid t=%s, k=%s";
+
+// Endpoint constants.
+const char kFCMServerUrlFormat[] = "https://fcm.googleapis.com/fcm/send/%s";
+
+// HTTP header constants.
+const char kTTL[] = "TTL";
+
+const char kContentEncodingProperty[] = "content-encoding";
+const char kContentCodingAes128Gcm[] = "aes128gcm";
+
+// Other constants.
+const char kContentEncodingOctetStream[] = "application/octet-stream";
+const int kMaximumBodySize = 4096;
+
+base::Optional<std::string> GetAuthHeader(crypto::ECPrivateKey* vapid_key,
+                                          int time_to_live) {
+  base::Value claims(base::Value::Type::DICTIONARY);
+  claims.SetKey(kClaimsKeyAudience, base::Value(kFCMServerAudience));
+
+  int64_t exp =
+      (base::Time::Now() - base::Time::UnixEpoch()).InSeconds() + time_to_live;
+  // TODO: Year 2038 problem, base::Value does not support int64_t.
+  if (exp > INT_MAX)
+    return base::nullopt;
+
+  claims.SetKey(kClaimsKeyExpirationTime,
+                base::Value(static_cast<int32_t>(exp)));
+
+  base::Optional<std::string> jwt = CreateJSONWebToken(claims, vapid_key);
+  if (!jwt)
+    return base::nullopt;
+
+  std::string public_key;
+  if (!GetRawPublicKey(*vapid_key, &public_key))
+    return base::nullopt;
+
+  std::string base64_public_key;
+  base::Base64UrlEncode(public_key, base::Base64UrlEncodePolicy::OMIT_PADDING,
+                        &base64_public_key);
+
+  return base::StringPrintf(kAuthorizationRequestHeaderFormat, jwt->c_str(),
+                            base64_public_key.c_str());
+}
+
+std::unique_ptr<network::SimpleURLLoader> BuildURLLoader(
+    const std::string& fcm_token,
+    int time_to_live,
+    const std::string& auth_header,
+    const std::string& message) {
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  std::string server_url =
+      base::StringPrintf(kFCMServerUrlFormat, fcm_token.c_str());
+  resource_request->url = GURL(server_url);
+  resource_request->load_flags =
+      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+  resource_request->method = "POST";
+  resource_request->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
+                                      auth_header);
+  resource_request->headers.SetHeader(kTTL, base::NumberToString(time_to_live));
+  resource_request->headers.SetHeader(kContentEncodingProperty,
+                                      kContentCodingAes128Gcm);
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("web_push_message", R"(
+        semantics {
+          sender: "GCMDriver WebPushSender"
+          description:
+            "Send a request via Firebase to another device that is signed in"
+            "with the same account."
+          trigger: "Users send data to another owned device."
+          data: "Web push message."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "You can enable or disable this feature in Chrome's settings under"
+            "'Sync and Google services'."
+          policy_exception_justification: "Not implemented."
+        }
+      )");
+  std::unique_ptr<network::SimpleURLLoader> loader =
+      network::SimpleURLLoader::Create(std::move(resource_request),
+                                       traffic_annotation);
+  loader->AttachStringForUpload(message, kContentEncodingOctetStream);
+  loader->SetRetryOptions(1, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+  loader->SetAllowHttpErrorResults(true);
+
+  return loader;
+}
+
+}  // namespace
+
+WebPushSender::WebPushSender(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : url_loader_factory_(std::move(url_loader_factory)),
+      weak_ptr_factory_(this) {}
+
+WebPushSender::~WebPushSender() = default;
+
+void WebPushSender::SendMessage(const std::string& fcm_token,
+                                crypto::ECPrivateKey* vapid_key,
+                                const WebPushMessage& message,
+                                SendMessageCallback callback) {
+  DCHECK(!fcm_token.empty());
+  DCHECK(vapid_key);
+  DCHECK_LE(message.time_to_live, message.kMaximumTTL);
+
+  base::Optional<std::string> auth_header =
+      GetAuthHeader(vapid_key, message.time_to_live);
+  if (!auth_header) {
+    LOG(ERROR) << "Failed to create JWT";
+    std::move(callback).Run(message.id, false);
+    return;
+  }
+
+  std::unique_ptr<network::SimpleURLLoader> url_loader = BuildURLLoader(
+      fcm_token, message.time_to_live, *auth_header, message.payload);
+  url_loader->DownloadToString(
+      url_loader_factory_.get(),
+      base::BindOnce(&WebPushSender::OnMessageSent,
+                     weak_ptr_factory_.GetWeakPtr(), message.id,
+                     std::move(url_loader), std::move(callback)),
+      kMaximumBodySize);
+}
+
+void WebPushSender::OnMessageSent(
+    const std::string& message_id,
+    std::unique_ptr<network::SimpleURLLoader> url_loader,
+    SendMessageCallback callback,
+    std::unique_ptr<std::string> response_body) {
+  int net_error = url_loader->NetError();
+  if (net_error != net::OK) {
+    LOG(ERROR) << "Network Error: " << net_error;
+    std::move(callback).Run(message_id, false);
+    return;
+  }
+
+  if (!url_loader->ResponseInfo() || !url_loader->ResponseInfo()->headers) {
+    LOG(ERROR) << "Response info not found";
+    std::move(callback).Run(message_id, false);
+    return;
+  }
+
+  int response_code = url_loader->ResponseInfo()->headers->response_code();
+  if (!network::cors::IsOkStatus(response_code)) {
+    LOG(ERROR) << "HTTP Error: " << response_code;
+    std::move(callback).Run(message_id, false);
+    return;
+  }
+
+  std::move(callback).Run(message_id, true);
+}
+
+}  // namespace gcm
diff --git a/components/gcm_driver/web_push_sender.h b/components/gcm_driver/web_push_sender.h
new file mode 100644
index 0000000..57702cd
--- /dev/null
+++ b/components/gcm_driver/web_push_sender.h
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_GCM_DRIVER_WEB_PUSH_SENDER_H_
+#define COMPONENTS_GCM_DRIVER_WEB_PUSH_SENDER_H_
+
+#include "base/macros.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace network {
+class SimpleURLLoader;
+}  // namespace network
+
+namespace crypto {
+class ECPrivateKey;
+}
+
+namespace gcm {
+
+struct WebPushMessage;
+
+// Class for sending a message via Firebase Cloud Messaging (FCM) Web Push.
+class WebPushSender {
+ public:
+  using SendMessageCallback =
+      base::OnceCallback<void(const std::string&, bool)>;
+
+  explicit WebPushSender(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+  ~WebPushSender();
+
+  // Sends a WebPushMessage via FCM Web Push. Authenticates with FCM server
+  // using Voluntary Application Server Identification for Web Push (VAPID)
+  // protocol.
+  // |fcm_token|: FCM registration token for receiving end.
+  // |vapid_key|: Private key to sign VAPID header.
+  // |message|: WebPushMessage to be sent.
+  // |callback|: To be called once the asynchronous operation is done.
+  void SendMessage(const std::string& fcm_token,
+                   crypto::ECPrivateKey* vapid_key,
+                   const WebPushMessage& message,
+                   SendMessageCallback callback);
+
+ private:
+  void OnMessageSent(const std::string& message_id,
+                     std::unique_ptr<network::SimpleURLLoader> url_loader,
+                     SendMessageCallback callback,
+                     std::unique_ptr<std::string> response_body);
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  base::WeakPtrFactory<WebPushSender> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebPushSender);
+};
+
+}  // namespace gcm
+
+#endif  // COMPONENTS_GCM_DRIVER_WEB_PUSH_SENDER_H_
diff --git a/components/gcm_driver/web_push_sender_unittest.cc b/components/gcm_driver/web_push_sender_unittest.cc
new file mode 100644
index 0000000..0f00d3a
--- /dev/null
+++ b/components/gcm_driver/web_push_sender_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/gcm_driver/web_push_sender.h"
+
+#include "base/base64.h"
+#include "components/gcm_driver/common/gcm_message.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "crypto/ec_private_key.h"
+#include "net/http/http_request_headers.h"
+#include "services/network/public/cpp/data_element.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/re2/src/re2/re2.h"
+
+namespace {
+
+// An ASN.1-encoded PrivateKeyInfo block from PKCS #8.
+const char kPrivateKey[] =
+    "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgS8wRbDOWz0lKExvIVQiRKtPAP8"
+    "dgHUHAw5gyOd5d4jKhRANCAARZb49Va5MD/KcWtc0oiWc2e8njBDtQzj0mzcOl1fDSt16Pvu6p"
+    "fTU3MTWnImDNnkPxtXm58K7Uax8jFxA4TeXJ";
+
+}  // namespace
+
+namespace gcm {
+
+WebPushMessage CreateMessage() {
+  WebPushMessage message;
+  message.id = "message_id";
+  message.time_to_live = 3600;
+  message.payload = "payload";
+  return message;
+}
+
+class WebPushSenderTest : public testing::Test {
+ public:
+  WebPushSenderTest();
+  ~WebPushSenderTest() override;
+
+  void SetUp() override;
+
+  WebPushSender* sender() { return sender_.get(); }
+  network::TestURLLoaderFactory& loader() { return test_url_loader_factory_; }
+
+  void OnMessageSent(std::string* message_id_out,
+                     bool* result_out,
+                     const std::string& message_id,
+                     bool result) {
+    *message_id_out = message_id;
+    *result_out = result;
+  }
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  std::unique_ptr<WebPushSender> sender_;
+};
+
+WebPushSenderTest::WebPushSenderTest() = default;
+WebPushSenderTest::~WebPushSenderTest() = default;
+
+void WebPushSenderTest::SetUp() {
+  sender_ = std::make_unique<WebPushSender>(
+      base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+          &test_url_loader_factory_));
+}
+
+TEST_F(WebPushSenderTest, SendMessageTest) {
+  std::string private_key_info;
+  ASSERT_TRUE(base::Base64Decode(kPrivateKey, &private_key_info));
+  std::unique_ptr<crypto::ECPrivateKey> private_key =
+      crypto::ECPrivateKey::CreateFromPrivateKeyInfo(std::vector<uint8_t>(
+          private_key_info.begin(), private_key_info.end()));
+  ASSERT_TRUE(private_key);
+
+  std::string message_id;
+  bool result;
+  sender()->SendMessage(
+      "fcm_token", private_key.get(), CreateMessage(),
+      base::BindOnce(&WebPushSenderTest::OnMessageSent, base::Unretained(this),
+                     &message_id, &result));
+
+  ASSERT_EQ(loader().NumPending(), 1);
+
+  network::TestURLLoaderFactory::PendingRequest* pendingRequest =
+      loader().GetPendingRequest(0);
+  ASSERT_EQ("https://fcm.googleapis.com/fcm/send/fcm_token",
+            pendingRequest->request.url);
+  ASSERT_EQ("POST", pendingRequest->request.method);
+
+  net::HttpRequestHeaders headers = pendingRequest->request.headers;
+  std::string auth_header, time_to_live, encoding;
+
+  ASSERT_TRUE(
+      headers.GetHeader(net::HttpRequestHeaders::kAuthorization, &auth_header));
+  const std::string expected_header =
+      "vapid "
+      "t=([a-zA-Z0-9_-])+\\.([a-zA-Z0-9_-])+\\.([a-zA-Z0-9_-])+, "
+      "k=([a-zA-Z0-9_-])+";
+  ASSERT_TRUE(re2::RE2::FullMatch(auth_header, expected_header));
+
+  ASSERT_TRUE(headers.GetHeader("TTL", &time_to_live));
+  ASSERT_EQ("3600", time_to_live);
+
+  ASSERT_TRUE(headers.GetHeader("content-encoding", &encoding));
+  ASSERT_EQ("aes128gcm", encoding);
+
+  const std::vector<network::DataElement>* body_elements =
+      pendingRequest->request.request_body->elements();
+  ASSERT_EQ(1UL, body_elements->size());
+  const network::DataElement& body = body_elements->back();
+  ASSERT_EQ("payload", std::string(body.bytes(), body.length()));
+
+  loader().SimulateResponseForPendingRequest(
+      pendingRequest->request.url, network::URLLoaderCompletionStatus(net::OK),
+      network::CreateResourceResponseHead(net::HTTP_OK), "");
+
+  ASSERT_EQ("message_id", message_id);
+  ASSERT_TRUE(result);
+}
+
+TEST_F(WebPushSenderTest, ServerErrorTest) {
+  std::string private_key_info;
+  ASSERT_TRUE(base::Base64Decode(kPrivateKey, &private_key_info));
+  std::unique_ptr<crypto::ECPrivateKey> private_key =
+      crypto::ECPrivateKey::CreateFromPrivateKeyInfo(std::vector<uint8_t>(
+          private_key_info.begin(), private_key_info.end()));
+  ASSERT_TRUE(private_key);
+
+  std::string message_id;
+  bool result;
+  sender()->SendMessage(
+      "fcm_token", private_key.get(), CreateMessage(),
+      base::BindOnce(&WebPushSenderTest::OnMessageSent, base::Unretained(this),
+                     &message_id, &result));
+
+  ASSERT_EQ(loader().NumPending(), 1);
+  loader().SimulateResponseForPendingRequest(
+      loader().GetPendingRequest(0)->request.url,
+      network::URLLoaderCompletionStatus(net::OK),
+      network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "");
+
+  ASSERT_EQ("message_id", message_id);
+  ASSERT_FALSE(result);
+}
+
+}  // namespace gcm
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index 425340f..bc15cc9 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -235,6 +235,7 @@
 void HistoryBackend::Init(
     bool force_fail,
     const HistoryDatabaseParams& history_database_params) {
+  TRACE_EVENT0("browser", "HistoryBackend::Init");
   // HistoryBackend is created on the UI thread by HistoryService, then the
   // HistoryBackend::Init() method is called on the DB thread. Create the
   // base::SupportsUserData on the DB thread since it is not thread-safe.
@@ -257,6 +258,7 @@
 void HistoryBackend::SetOnBackendDestroyTask(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     const base::Closure& task) {
+  TRACE_EVENT0("browser", "HistoryBackend::SetOnBackendDestroyTask");
   if (!backend_destroy_task_.is_null())
     DLOG(WARNING) << "Setting more than one destroy task, overriding";
   backend_destroy_task_runner_ = std::move(task_runner);
@@ -264,6 +266,7 @@
 }
 
 void HistoryBackend::Closing() {
+  TRACE_EVENT0("browser", "HistoryBackend::Closing");
   // Any scheduled commit will have a reference to us, we must make it
   // release that reference before we can be destroyed.
   CancelScheduledCommit();
@@ -271,11 +274,13 @@
 
 #if defined(OS_IOS)
 void HistoryBackend::PersistState() {
+  TRACE_EVENT0("browser", "HistoryBackend::PersistState");
   Commit();
 }
 #endif
 
 void HistoryBackend::ClearCachedDataForContextID(ContextID context_id) {
+  TRACE_EVENT0("browser", "HistoryBackend::ClearCachedDataForContextID");
   tracker_.ClearCachedDataForContextID(context_id);
 }
 
@@ -386,6 +391,7 @@
                                            int nav_entry_id,
                                            const GURL& url,
                                            Time end_ts) {
+  TRACE_EVENT0("browser", "HistoryBackend::UpdateWithPageEndTime");
   // Will be filled with the URL ID and the visit ID of the last addition.
   VisitID visit_id = tracker_.GetLastVisit(context_id, nav_entry_id, url);
   UpdateVisitDuration(visit_id, end_ts);
@@ -450,6 +456,8 @@
 }
 
 void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
+  TRACE_EVENT0("browser", "HistoryBackend::AddPage");
+
   if (!db_)
     return;
 
@@ -861,6 +869,8 @@
 
 void HistoryBackend::AddPagesWithDetails(const URLRows& urls,
                                          VisitSource visit_source) {
+  TRACE_EVENT0("browser", "HistoryBackend::AddPagesWithDetails");
+
   if (!db_)
     return;
 
@@ -920,6 +930,8 @@
 
 void HistoryBackend::SetPageTitle(const GURL& url,
                                   const base::string16& title) {
+  TRACE_EVENT0("browser", "HistoryBackend::SetPageTitle");
+
   if (!db_)
     return;
 
@@ -963,6 +975,8 @@
 
 void HistoryBackend::AddPageNoVisitForBookmark(const GURL& url,
                                                const base::string16& title) {
+  TRACE_EVENT0("browser", "HistoryBackend::AddPageNoVisitForBookmark");
+
   if (!db_)
     return;
 
@@ -1118,6 +1132,8 @@
 void HistoryBackend::SetKeywordSearchTermsForURL(const GURL& url,
                                                  KeywordID keyword_id,
                                                  const base::string16& term) {
+  TRACE_EVENT0("browser", "HistoryBackend::SetKeywordSearchTermsForURL");
+
   if (!db_)
     return;
 
@@ -1136,6 +1152,8 @@
 }
 
 void HistoryBackend::DeleteAllSearchTermsForKeyword(KeywordID keyword_id) {
+  TRACE_EVENT0("browser", "HistoryBackend::DeleteAllSearchTermsForKeyword");
+
   if (!db_)
     return;
 
@@ -1144,6 +1162,8 @@
 }
 
 void HistoryBackend::DeleteKeywordSearchTermForURL(const GURL& url) {
+  TRACE_EVENT0("browser", "HistoryBackend::DeleteKeywordSearchTermForURL");
+
   if (!db_)
     return;
 
@@ -1158,6 +1178,8 @@
 
 void HistoryBackend::DeleteMatchingURLsForKeyword(KeywordID keyword_id,
                                                   const base::string16& term) {
+  TRACE_EVENT0("browser", "HistoryBackend::DeleteMatchingURLsForKeyword");
+
   if (!db_)
     return;
 
@@ -1201,6 +1223,7 @@
 void HistoryBackend::UpdateDownload(
     const DownloadRow& data,
     bool should_commit_immediately) {
+  TRACE_EVENT0("browser", "HistoryBackend::UpdateDownload");
   if (!db_)
     return;
   db_->UpdateDownload(data);
@@ -1211,6 +1234,7 @@
 }
 
 bool HistoryBackend::CreateDownload(const DownloadRow& history_info) {
+  TRACE_EVENT0("browser", "HistoryBackend::CreateDownload");
   if (!db_)
     return false;
   bool success = db_->CreateDownload(history_info);
@@ -1227,6 +1251,7 @@
 }
 
 void HistoryBackend::RemoveDownloads(const std::set<uint32_t>& ids) {
+  TRACE_EVENT0("browser", "HistoryBackend::RemoveDownloads");
   if (!db_)
     return;
   size_t downloads_count_before = db_->CountDownloads();
@@ -1622,6 +1647,8 @@
 void HistoryBackend::DeleteFaviconMappings(
     const base::flat_set<GURL>& page_urls,
     favicon_base::IconType icon_type) {
+  TRACE_EVENT0("browser", "HistoryBackend::DeleteFaviconMappings");
+
   if (!thumbnail_db_ || !db_)
     return;
 
@@ -1643,6 +1670,8 @@
     favicon_base::IconType icon_type,
     scoped_refptr<base::RefCountedMemory> bitmap_data,
     const gfx::Size& pixel_size) {
+  TRACE_EVENT0("browser", "HistoryBackend::MergeFavicon");
+
   if (!thumbnail_db_ || !db_)
     return;
 
@@ -1799,6 +1828,7 @@
                                  favicon_base::IconType icon_type,
                                  const GURL& icon_url,
                                  const std::vector<SkBitmap>& bitmaps) {
+  TRACE_EVENT0("browser", "HistoryBackend::SetFavicons");
   SetFaviconsImpl(page_urls, icon_type, icon_url, bitmaps,
                   FaviconBitmapType::ON_VISIT);
 }
@@ -1807,6 +1837,8 @@
     const GURL& page_url_to_read,
     const favicon_base::IconTypeSet& icon_types,
     const base::flat_set<GURL>& page_urls_to_write) {
+  TRACE_EVENT0("browser", "HistoryBackend::CloneFaviconMappingsForPages");
+
   if (!db_ || !thumbnail_db_)
     return;
 
@@ -1885,6 +1917,8 @@
 }
 
 void HistoryBackend::SetFaviconsOutOfDateForPage(const GURL& page_url) {
+  TRACE_EVENT0("browser", "HistoryBackend::SetFaviconsOutOfDateForPage");
+
   std::vector<IconMapping> icon_mappings;
 
   if (!thumbnail_db_ ||
@@ -1898,6 +1932,8 @@
 }
 
 void HistoryBackend::TouchOnDemandFavicon(const GURL& icon_url) {
+  TRACE_EVENT0("browser", "HistoryBackend::TouchOnDemandFavicon");
+
   if (!thumbnail_db_)
     return;
 
@@ -1907,6 +1943,8 @@
 
 void HistoryBackend::SetImportedFavicons(
     const favicon_base::FaviconUsageDataList& favicon_usage) {
+  TRACE_EVENT0("browser", "HistoryBackend::SetImportedFavicons");
+
   if (!db_ || !thumbnail_db_)
     return;
 
@@ -2421,6 +2459,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 void HistoryBackend::DeleteURLs(const std::vector<GURL>& urls) {
+  TRACE_EVENT0("browser", "HistoryBackend::DeleteURLs");
+
   expirer_.DeleteURLs(urls, base::Time::Max());
 
   db_->GetStartDate(&first_recorded_time_);
@@ -2430,6 +2470,8 @@
 }
 
 void HistoryBackend::DeleteURL(const GURL& url) {
+  TRACE_EVENT0("browser", "HistoryBackend::DeleteURL");
+
   expirer_.DeleteURL(url, base::Time::Max());
 
   db_->GetStartDate(&first_recorded_time_);
@@ -2440,6 +2482,8 @@
 
 void HistoryBackend::DeleteURLsUntil(
     const std::vector<std::pair<GURL, base::Time>>& urls_and_timestamps) {
+  TRACE_EVENT0("browser", "HistoryBackend::DeleteURLsUntil");
+
   for (const auto& pair : urls_and_timestamps) {
     expirer_.DeleteURL(pair.first, pair.second);
   }
@@ -2551,6 +2595,8 @@
 }
 
 void HistoryBackend::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
+  TRACE_EVENT0("browser", "HistoryBackend::URLsNoLongerBookmarked");
+
   if (!db_)
     return;
 
@@ -2630,6 +2676,7 @@
     std::unique_ptr<HistoryDBTask> task,
     scoped_refptr<base::SingleThreadTaskRunner> origin_loop,
     const base::CancelableTaskTracker::IsCanceledCallback& is_canceled) {
+  TRACE_EVENT0("browser", "HistoryBackend::ProcessDBTask");
   bool scheduled = !queued_history_db_tasks_.empty();
   queued_history_db_tasks_.push_back(std::make_unique<QueuedHistoryDBTask>(
       std::move(task), origin_loop, is_canceled));
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc
index 370b14be..bb345f39 100644
--- a/components/history/core/browser/history_service.cc
+++ b/components/history/core/browser/history_service.cc
@@ -181,6 +181,8 @@
 
 #if defined(OS_IOS)
 void HistoryService::HandleBackgrounding() {
+  TRACE_EVENT0("browser", "HistoryService::HandleBackgrounding");
+
   if (!backend_task_runner_ || !history_backend_.get())
     return;
 
@@ -191,6 +193,7 @@
 #endif
 
 void HistoryService::ClearCachedDataForContextID(ContextID context_id) {
+  TRACE_EVENT0("browser", "HistoryService::ClearCachedDataForContextID");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_NORMAL,
@@ -211,6 +214,7 @@
 void HistoryService::SetKeywordSearchTermsForURL(const GURL& url,
                                                  KeywordID keyword_id,
                                                  const base::string16& term) {
+  TRACE_EVENT0("browser", "HistoryService::SetKeywordSearchTermsForURL");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_UI,
@@ -219,6 +223,7 @@
 }
 
 void HistoryService::DeleteAllSearchTermsForKeyword(KeywordID keyword_id) {
+  TRACE_EVENT0("browser", "HistoryService::DeleteAllSearchTermsForKeyword");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -231,6 +236,7 @@
 }
 
 void HistoryService::DeleteKeywordSearchTermForURL(const GURL& url) {
+  TRACE_EVENT0("browser", "HistoryService::DeleteKeywordSearchTermForURL");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_UI,
@@ -240,6 +246,7 @@
 
 void HistoryService::DeleteMatchingURLsForKeyword(KeywordID keyword_id,
                                                   const base::string16& term) {
+  TRACE_EVENT0("browser", "HistoryService::DeleteMatchingURLsForKeyword");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_UI,
@@ -248,6 +255,7 @@
 }
 
 void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
+  TRACE_EVENT0("browser", "HistoryService::URLsNoLongerBookmarked");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_NORMAL,
@@ -269,6 +277,7 @@
     const base::Location& from_here,
     std::unique_ptr<HistoryDBTask> task,
     base::CancelableTaskTracker* tracker) {
+  TRACE_EVENT0("browser", "HistoryService::ScheduleDBTask");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   base::CancelableTaskTracker::IsCanceledCallback is_canceled;
@@ -290,6 +299,7 @@
 }
 
 void HistoryService::SetOnBackendDestroyTask(const base::Closure& task) {
+  TRACE_EVENT0("browser", "HistoryService::SetOnBackendDestroyTask");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(
@@ -336,6 +346,7 @@
 }
 
 void HistoryService::AddPage(const HistoryAddPageArgs& add_page_args) {
+  TRACE_EVENT0("browser", "HistoryService::AddPage");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -363,6 +374,7 @@
 
 void HistoryService::AddPageNoVisitForBookmark(const GURL& url,
                                                const base::string16& title) {
+  TRACE_EVENT0("browser", "HistoryService::AddPageNoVisitForBookmark");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   if (history_client_ && !history_client_->CanAddURL(url))
@@ -375,6 +387,7 @@
 
 void HistoryService::SetPageTitle(const GURL& url,
                                   const base::string16& title) {
+  TRACE_EVENT0("browser", "HistoryService::SetPageTitle");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_NORMAL, base::BindOnce(&HistoryBackend::SetPageTitle,
@@ -385,6 +398,7 @@
                                            int nav_entry_id,
                                            const GURL& url,
                                            Time end_ts) {
+  TRACE_EVENT0("browser", "HistoryService::UpdateWithPageEndTime");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(
@@ -400,6 +414,7 @@
                                         Time last_visit,
                                         bool hidden,
                                         VisitSource visit_source) {
+  TRACE_EVENT0("browser", "HistoryService::AddPageWithDetails");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   // Filter out unwanted URLs.
@@ -427,6 +442,7 @@
 
 void HistoryService::AddPagesWithDetails(const URLRows& info,
                                          VisitSource visit_source) {
+  TRACE_EVENT0("browser", "HistoryService::AddPagesWithDetails");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -560,6 +576,7 @@
                                  favicon_base::IconType icon_type,
                                  const GURL& icon_url,
                                  const std::vector<SkBitmap>& bitmaps) {
+  TRACE_EVENT0("browser", "HistoryService::SetFavicons");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -582,6 +599,7 @@
     const GURL& page_url_to_read,
     const favicon_base::IconTypeSet& icon_types,
     const base::flat_set<GURL>& page_urls_to_write) {
+  TRACE_EVENT0("browser", "HistoryService::CloneFaviconMappingsForPages");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -630,6 +648,7 @@
 }
 
 void HistoryService::SetFaviconsOutOfDateForPage(const GURL& page_url) {
+  TRACE_EVENT0("browser", "HistoryService::SetFaviconsOutOfDateForPage");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_NORMAL,
@@ -638,6 +657,7 @@
 }
 
 void HistoryService::TouchOnDemandFavicon(const GURL& icon_url) {
+  TRACE_EVENT0("browser", "HistoryService::TouchOnDemandFavicon");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_NORMAL,
@@ -647,6 +667,7 @@
 
 void HistoryService::SetImportedFavicons(
     const favicon_base::FaviconUsageDataList& favicon_usage) {
+  TRACE_EVENT0("browser", "HistoryService::SetImportedFavicons");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_NORMAL,
@@ -737,6 +758,7 @@
 void HistoryService::UpdateDownload(
     const DownloadRow& data,
     bool should_commit_immediately) {
+  TRACE_EVENT0("browser", "HistoryService::UpdateDownload");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_NORMAL,
@@ -745,6 +767,7 @@
 }
 
 void HistoryService::RemoveDownloads(const std::set<uint32_t>& ids) {
+  TRACE_EVENT0("browser", "HistoryService::RemoveDownloads");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   ScheduleTask(PRIORITY_NORMAL, base::BindOnce(&HistoryBackend::RemoveDownloads,
@@ -919,6 +942,7 @@
 
 void HistoryService::ScheduleTask(SchedulePriority priority,
                                   base::OnceClosure task) {
+  TRACE_EVENT0("browser", "HistoryService::ScheduleTask");
   DCHECK(thread_checker_.CalledOnValidThread());
   CHECK(backend_task_runner_);
   // TODO(brettw): Do prioritization.
@@ -978,6 +1002,7 @@
 }
 
 void HistoryService::DeleteURL(const GURL& url) {
+  TRACE_EVENT0("browser", "HistoryService::DeleteURL");
   DCHECK(backend_task_runner_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   // We will update the visited links when we observe the delete notifications.
diff --git a/components/invalidation/impl/android/javatests/src/org/chromium/components/invalidation/InvalidationClientServiceTest.java b/components/invalidation/impl/android/javatests/src/org/chromium/components/invalidation/InvalidationClientServiceTest.java
index d8462eae..ea37ec4c 100644
--- a/components/invalidation/impl/android/javatests/src/org/chromium/components/invalidation/InvalidationClientServiceTest.java
+++ b/components/invalidation/impl/android/javatests/src/org/chromium/components/invalidation/InvalidationClientServiceTest.java
@@ -25,8 +25,6 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.CollectionUtil;
-import org.chromium.base.ContextUtils;
-import org.chromium.base.PathUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
@@ -58,7 +56,6 @@
 
     /** Id used when creating clients. */
     private static final byte[] CLIENT_ID = new byte[]{0, 4, 7};
-    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "invalidation_test";
 
     /** Intents provided to {@link #startService}. */
     private List<Intent> mStartServiceIntents;
@@ -79,8 +76,6 @@
                 return new ComponentName(this, InvalidationClientServiceTest.class);
             }
         });
-        ContextUtils.initApplicationContextForTests(getContext().getApplicationContext());
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
         LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
         setupService();
     }
diff --git a/components/invalidation/impl/fcm_network_handler_unittests.cc b/components/invalidation/impl/fcm_network_handler_unittests.cc
index 7b9024be..129472ff 100644
--- a/components/invalidation/impl/fcm_network_handler_unittests.cc
+++ b/components/invalidation/impl/fcm_network_handler_unittests.cc
@@ -21,6 +21,8 @@
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/invalidation/impl/status.h"
 #include "google_apis/gcm/engine/account_mapping.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using base::TestMockTimeTaskRunner;
@@ -103,8 +105,11 @@
 class MockGCMDriver : public gcm::GCMDriver {
  public:
   MockGCMDriver()
-      : GCMDriver(/*store_path=*/base::FilePath(),
-                  /*blocking_task_runner=*/nullptr) {}
+      : GCMDriver(
+            /*store_path=*/base::FilePath(),
+            /*blocking_task_runner=*/nullptr,
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)) {}
   ~MockGCMDriver() override = default;
 
   MOCK_METHOD4(ValidateRegistration,
@@ -158,6 +163,8 @@
                     gcm::GCMDecryptionResult result));
 
  private:
+  network::TestURLLoaderFactory test_url_loader_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(MockGCMDriver);
 };
 
diff --git a/components/language/core/browser/pref_names.cc b/components/language/core/browser/pref_names.cc
index fe340274..8c4d5005 100644
--- a/components/language/core/browser/pref_names.cc
+++ b/components/language/core/browser/pref_names.cc
@@ -11,11 +11,13 @@
 // request.
 const char kAcceptLanguages[] = "intl.accept_languages";
 
+#if defined(OS_CHROMEOS)
 // A string pref (comma-separated list) set to the preferred language IDs
 // (ex. "en-US,fr,ko").
 const char kPreferredLanguages[] = "settings.language.preferred_languages";
 const char kPreferredLanguagesSyncable[] =
     "settings.language.preferred_languages_syncable";
+#endif  // defined(OS_CHROMEOS)
 
 // The JSON representation of the user's language profile. Used as an input to
 // the user language model (i.e. for determining which languages a user
diff --git a/components/language/core/browser/pref_names.h b/components/language/core/browser/pref_names.h
index b878cfee..546f9be4 100644
--- a/components/language/core/browser/pref_names.h
+++ b/components/language/core/browser/pref_names.h
@@ -10,8 +10,10 @@
 
 extern const char kAcceptLanguages[];
 
+#if defined(OS_CHROMEOS)
 extern const char kPreferredLanguages[];
 extern const char kPreferredLanguagesSyncable[];
+#endif
 
 extern const char kUserLanguageProfile[];
 
diff --git a/components/media_message_center/media_notification_item.cc b/components/media_message_center/media_notification_item.cc
index bbec7339..7da941bb 100644
--- a/components/media_message_center/media_notification_item.cc
+++ b/components/media_message_center/media_notification_item.cc
@@ -76,13 +76,6 @@
         kMediaSessionNotificationArtworkMinSize,
         kMediaSessionNotificationArtworkDesiredSize,
         std::move(artwork_observer));
-
-    media_session::mojom::MediaControllerImageObserverPtr icon_observer;
-    icon_observer_binding_.Bind(mojo::MakeRequest(&icon_observer));
-    media_controller_ptr_->ObserveImages(
-        media_session::mojom::MediaSessionImageType::kSourceIcon,
-        message_center::kSmallImageSizeMD, message_center::kSmallImageSizeMD,
-        std::move(icon_observer));
   }
 
   MaybeHideOrShowNotification();
@@ -133,20 +126,12 @@
 void MediaNotificationItem::MediaControllerImageChanged(
     media_session::mojom::MediaSessionImageType type,
     const SkBitmap& bitmap) {
-  switch (type) {
-    case media_session::mojom::MediaSessionImageType::kArtwork:
-      session_artwork_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+  DCHECK_EQ(media_session::mojom::MediaSessionImageType::kArtwork, type);
 
-      if (view_)
-        view_->UpdateWithMediaArtwork(*session_artwork_);
-      break;
-    case media_session::mojom::MediaSessionImageType::kSourceIcon:
-      session_icon_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+  session_artwork_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
 
-      if (view_)
-        view_->UpdateWithMediaIcon(session_icon_);
-      break;
-  }
+  if (view_)
+    view_->UpdateWithMediaArtwork(*session_artwork_);
 }
 
 void MediaNotificationItem::SetView(MediaNotificationView* view) {
diff --git a/components/media_message_center/media_notification_item.h b/components/media_message_center/media_notification_item.h
index 7299f10..4618bb9 100644
--- a/components/media_message_center/media_notification_item.h
+++ b/components/media_message_center/media_notification_item.h
@@ -111,8 +111,6 @@
 
   base::Optional<gfx::ImageSkia> session_artwork_;
 
-  gfx::ImageSkia session_icon_;
-
   // True if the metadata needs to be updated on |view_|. Used to prevent
   // updating |view_|'s metadata twice on a single change.
   bool view_needs_metadata_update_ = false;
@@ -123,9 +121,6 @@
   mojo::Binding<media_session::mojom::MediaControllerImageObserver>
       artwork_observer_binding_{this};
 
-  mojo::Binding<media_session::mojom::MediaControllerImageObserver>
-      icon_observer_binding_{this};
-
   base::WeakPtrFactory<MediaNotificationItem> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(MediaNotificationItem);
diff --git a/components/media_message_center/media_notification_view.cc b/components/media_message_center/media_notification_view.cc
index b0330a3..9119b9a 100644
--- a/components/media_message_center/media_notification_view.cc
+++ b/components/media_message_center/media_notification_view.cc
@@ -351,23 +351,20 @@
   SchedulePaint();
 }
 
-void MediaNotificationView::UpdateWithMediaIcon(const gfx::ImageSkia& image) {
-  if (image.isNull()) {
-    header_row_->ClearAppIcon();
-  } else {
-    header_row_->SetAppIcon(image);
-  }
-}
-
 void MediaNotificationView::UpdateActionButtonsVisibility() {
   std::set<MediaSessionAction> visible_actions =
       CalculateVisibleActions(IsActuallyExpanded());
 
   for (auto* view : button_row_->children()) {
     views::Button* action_button = views::Button::AsButton(view);
-    action_button->SetVisible(
-        base::Contains(visible_actions,
-                       static_cast<MediaSessionAction>(action_button->tag())));
+    bool should_show = base::Contains(
+        visible_actions, static_cast<MediaSessionAction>(action_button->tag()));
+    bool should_invalidate = should_show != action_button->GetVisible();
+
+    action_button->SetVisible(should_show);
+
+    if (should_invalidate)
+      action_button->InvalidateLayout();
   }
 }
 
diff --git a/components/media_message_center/media_notification_view.h b/components/media_message_center/media_notification_view.h
index a1f3a01..00a6b53 100644
--- a/components/media_message_center/media_notification_view.h
+++ b/components/media_message_center/media_notification_view.h
@@ -82,7 +82,6 @@
   void UpdateWithMediaActions(
       const std::set<media_session::mojom::MediaSessionAction>& actions);
   void UpdateWithMediaArtwork(const gfx::ImageSkia& image);
-  void UpdateWithMediaIcon(const gfx::ImageSkia& image);
 
  private:
   friend class MediaNotificationViewTest;
diff --git a/components/media_message_center/media_notification_view_unittest.cc b/components/media_message_center/media_notification_view_unittest.cc
index e3616d4..e73b7f58 100644
--- a/components/media_message_center/media_notification_view_unittest.cc
+++ b/components/media_message_center/media_notification_view_unittest.cc
@@ -718,30 +718,6 @@
   EXPECT_EQ(accent, header_row()->accent_color_for_testing());
 }
 
-TEST_F(MediaNotificationViewTest, UpdateIconFromItem) {
-  gfx::ImageSkia original = GetAppIcon();
-  EXPECT_EQ(message_center::kSmallImageSizeMD, original.width());
-  EXPECT_EQ(message_center::kSmallImageSizeMD, original.height());
-
-  // The size for the image we provide should be different so we can compare.
-  const int alt_size = message_center::kSmallImageSizeMD + 1;
-
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(alt_size, alt_size);
-
-  GetItem()->MediaControllerImageChanged(
-      media_session::mojom::MediaSessionImageType::kSourceIcon, bitmap);
-
-  EXPECT_EQ(alt_size, GetAppIcon().width());
-  EXPECT_EQ(alt_size, GetAppIcon().height());
-
-  GetItem()->MediaControllerImageChanged(
-      media_session::mojom::MediaSessionImageType::kSourceIcon, SkBitmap());
-
-  EXPECT_EQ(message_center::kSmallImageSizeMD, GetAppIcon().width());
-  EXPECT_EQ(message_center::kSmallImageSizeMD, GetAppIcon().height());
-}
-
 TEST_F(MediaNotificationViewTest, ExpandableDefaultState) {
   EXPECT_FALSE(IsActuallyExpanded());
   EXPECT_FALSE(expand_button_enabled());
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index d2a896a..768e6829 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -23,6 +23,8 @@
     "data_use_tracker.h",
     "delegating_provider.cc",
     "delegating_provider.h",
+    "demographic_metrics_provider.cc",
+    "demographic_metrics_provider.h",
     "drive_metrics_provider.cc",
     "drive_metrics_provider.h",
     "drive_metrics_provider_android.cc",
diff --git a/components/metrics/cpu_metrics_provider.cc b/components/metrics/cpu_metrics_provider.cc
index 93af571..332e20cae 100644
--- a/components/metrics/cpu_metrics_provider.cc
+++ b/components/metrics/cpu_metrics_provider.cc
@@ -23,6 +23,7 @@
   cpu->set_vendor_name(cpu_info.vendor_name());
   cpu->set_signature(cpu_info.signature());
   cpu->set_num_cores(base::SysInfo::NumberOfProcessors());
+  cpu->set_is_hypervisor(cpu_info.is_running_in_vm());
 }
 
 }  // namespace metrics
diff --git a/components/metrics/demographic_metrics_provider.cc b/components/metrics/demographic_metrics_provider.cc
new file mode 100644
index 0000000..1565742
--- /dev/null
+++ b/components/metrics/demographic_metrics_provider.cc
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/demographic_metrics_provider.h"
+#include "base/feature_list.h"
+
+namespace metrics {
+
+// static
+const base::Feature DemographicMetricsProvider::kDemographicMetricsReporting = {
+    "DemographicMetricsReporting", base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace metrics
diff --git a/components/metrics/demographic_metrics_provider.h b/components/metrics/demographic_metrics_provider.h
new file mode 100644
index 0000000..b5d26f8
--- /dev/null
+++ b/components/metrics/demographic_metrics_provider.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_DEMOGRAPHIC_METRICS_PROVIDER_H_
+#define COMPONENTS_METRICS_DEMOGRAPHIC_METRICS_PROVIDER_H_
+
+#include "components/metrics/metrics_provider.h"
+
+namespace base {
+struct Feature;
+}
+
+namespace metrics {
+
+class DemographicMetricsProvider : public MetricsProvider {
+ public:
+  DemographicMetricsProvider() = default;
+  ~DemographicMetricsProvider() override = default;
+
+  // Kill switch to stop reporting demographic data.
+  static const base::Feature kDemographicMetricsReporting;
+};
+
+}  // namespace metrics
+
+#endif  // COMPONENTS_METRICS_DEMOGRAPHIC_METRICS_PROVIDER_H_
\ No newline at end of file
diff --git a/components/metrics/metrics_pref_names.h b/components/metrics/metrics_pref_names.h
index 3ce942bb..297e97a 100644
--- a/components/metrics/metrics_pref_names.h
+++ b/components/metrics/metrics_pref_names.h
@@ -10,8 +10,6 @@
 
 // Alphabetical list of preference names specific to the metrics
 // component. Document each in the .cc file.
-extern const char kDeprecatedMetricsInitialLogs[];
-extern const char kDeprecatedMetricsOngoingLogs[];
 extern const char kInstallDate[];
 extern const char kMetricsClientID[];
 extern const char kMetricsDefaultOptIn[];
diff --git a/components/mirroring/service/session.cc b/components/mirroring/service/session.cc
index 3f92752e..0273ebbe 100644
--- a/components/mirroring/service/session.cc
+++ b/components/mirroring/service/session.cc
@@ -241,13 +241,15 @@
 }
 
 // Checks whether receiver's build version is less than "1.|base_version|.xxxx".
-// Returns false if given version doesn't have the format of "1.xx.xxxx".
+// Returns true if given version doesn't have the format of "1.xx.xxxx", so that
+// we don't assume that the receiver has the required new capabilities.
 bool NeedsWorkaroundForOlder1DotXVersions(
     const std::string& receiver_build_version,
     int base_version) {
   if (!base::StartsWith(receiver_build_version, "1.",
-                        base::CompareCase::SENSITIVE))
-    return false;
+                        base::CompareCase::SENSITIVE)) {
+    return true;
+  }
   const size_t end_pos = receiver_build_version.find_first_of('.', 2);
   if (end_pos == std::string::npos)
     return false;
@@ -767,12 +769,11 @@
   if (answer.supports_get_status) {
     wifi_status_monitor =
         std::make_unique<WifiStatusMonitor>(&message_dispatcher_);
-    // Before 1.28 Android TV Chromecast receivers respond to GET_CAPABILITIES
-    // even though they don't support remoting.
+    // Nest Hub devices do not support remoting despite having a relatively new
+    // build version, so we cannot filter with
+    // NeedsWorkaroundForOlder1DotXVersions() here.
     if (initially_starting_session &&
-        (!NeedsWorkaroundForOlder1DotXVersions(
-             session_monitor_->GetReceiverBuildVersion(), 28) ||
-         base::StartsWith(session_params_.receiver_model_name, "Chromecast",
+        (base::StartsWith(session_params_.receiver_model_name, "Chromecast",
                           base::CompareCase::SENSITIVE) ||
          base::StartsWith(session_params_.receiver_model_name, "Eureka Dongle",
                           base::CompareCase::SENSITIVE))) {
diff --git a/components/mirroring/service/session_monitor.cc b/components/mirroring/service/session_monitor.cc
index b0fa6b6..3828f63 100644
--- a/components/mirroring/service/session_monitor.cc
+++ b/components/mirroring/service/session_monitor.cc
@@ -54,7 +54,7 @@
   bool is_connected = false;
   bool is_on_ethernet = false;
   bool has_update = false;
-  int32_t uptime_seconds = 0;
+  double uptime_seconds = 0;
 
   const bool result =
       value && value->is_dict() &&
@@ -62,7 +62,7 @@
       GetBool(*value, "connected", &is_connected) &&
       GetBool(*value, "ethernet_connected", &is_on_ethernet) &&
       GetBool(*value, "has_update", &has_update) &&
-      GetInt(*value, "uptime", &uptime_seconds) &&
+      GetDouble(*value, "uptime", &uptime_seconds) &&
       GetString(*value, "name", receiver_name);
   if (result) {
     tags->SetKey("receiverVersion", base::Value(build_version));
diff --git a/components/mirroring/service/session_monitor_unittest.cc b/components/mirroring/service/session_monitor_unittest.cc
index ead8561..29fcd700 100644
--- a/components/mirroring/service/session_monitor_unittest.cc
+++ b/components/mirroring/service/session_monitor_unittest.cc
@@ -49,12 +49,12 @@
   EXPECT_EQ(expected_value, data);
 }
 
-void VerifyIntValue(const base::Value& raw_value,
-                    const std::string& key,
-                    int32_t expected_value) {
-  int32_t data;
-  EXPECT_TRUE(GetInt(raw_value, key, &data));
-  EXPECT_EQ(expected_value, data);
+void VerifyDoubleValue(const base::Value& raw_value,
+                       const std::string& key,
+                       double expected_value) {
+  double data;
+  EXPECT_TRUE(GetDouble(raw_value, key, &data));
+  EXPECT_NEAR(expected_value, data, DBL_EPSILON * 2);
 }
 
 void VerifyWifiStatus(const base::Value& raw_value,
@@ -375,7 +375,7 @@
       "\"connected\": true,"
       "\"ethernet_connected\": false,"
       "\"has_update\": false,"
-      "\"uptime\": 132536 }";
+      "\"uptime\": 13253.6 }";
 
   SendReceiverSetupInfo(receiver_setup_info);
 
@@ -406,7 +406,7 @@
   VerifyBoolValue(*tags, "receiverConnected", true);
   VerifyBoolValue(*tags, "receiverOnEthernet", false);
   VerifyBoolValue(*tags, "receiverHasUpdatePending", false);
-  VerifyIntValue(*tags, "receiverUptimeSeconds", 132536);
+  VerifyDoubleValue(*tags, "receiverUptimeSeconds", 13253.6);
 }
 
 }  // namespace mirroring
diff --git a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/FakeModuleInstallerBackend.java b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/FakeModuleInstallerBackend.java
index dae242dc..9bc39a7 100644
--- a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/FakeModuleInstallerBackend.java
+++ b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/FakeModuleInstallerBackend.java
@@ -70,7 +70,7 @@
 
     private boolean installInternal(String moduleName) {
         Context context = ContextUtils.getApplicationContext();
-        int versionCode = BuildInfo.getInstance().versionCode;
+        long versionCode = BuildInfo.getInstance().versionCode;
 
         // Get list of all files at path where SplitCompat looks for downloaded modules.
         // May change in future releases of the Play Core SDK.
@@ -102,7 +102,7 @@
             if (srcModuleFileName.endsWith(".apk") && srcModuleFileName.startsWith(moduleName)) {
                 // Construct destination file corresponding to each source file.
                 File dstModuleFile = joinPaths(context.getFilesDir().getPath(), "splitcompat",
-                        Integer.toString(versionCode), "unverified-splits", srcModuleFileName);
+                        Long.toString(versionCode), "unverified-splits", srcModuleFileName);
 
                 // NOTE: Need to give Chrome storage permission for this to work.
                 try {
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index f2f09c6..1e34feb1 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -156,6 +156,7 @@
   FRIEND_TEST_ALL_PREFIXES(OmniboxViewViewsTest, MaintainCursorAfterFocusCycle);
   FRIEND_TEST_ALL_PREFIXES(OmniboxPopupModelTest, SetSelectedLine);
   FRIEND_TEST_ALL_PREFIXES(OmniboxPopupModelTest, TestFocusFixing);
+  FRIEND_TEST_ALL_PREFIXES(OmniboxPopupModelTest, PopupPositionChanging);
   FRIEND_TEST_ALL_PREFIXES(OmniboxPopupContentsViewTest,
                            EmitSelectedChildrenChangedAccessibilityEvent);
 
diff --git a/components/omnibox/browser/autocomplete_input.cc b/components/omnibox/browser/autocomplete_input.cc
index a16addbe..4ea8711 100644
--- a/components/omnibox/browser/autocomplete_input.cc
+++ b/components/omnibox/browser/autocomplete_input.cc
@@ -215,7 +215,7 @@
   }
 
   // Treat javascript: scheme queries followed by things that are unlikely to
-  // be code as QUERY, rather than script to execute (URL).
+  // be code as UNKNOWN, rather than script to execute (URL).
   if (RE2::FullMatch(base::UTF16ToUTF8(text), "(?i)javascript:([^;=().\"]*)")) {
     return metrics::OmniboxInputType::UNKNOWN;
   }
diff --git a/components/omnibox/browser/autocomplete_provider_unittest.cc b/components/omnibox/browser/autocomplete_provider_unittest.cc
index cdf8750..d8a7c73 100644
--- a/components/omnibox/browser/autocomplete_provider_unittest.cc
+++ b/components/omnibox/browser/autocomplete_provider_unittest.cc
@@ -584,7 +584,9 @@
 
   // Make sure the default match gets set to the highest relevance match.  The
   // highest relevance matches should come from the second provider.
-  EXPECT_EQ(kResultsPerProvider * 2, result_.size());
+  EXPECT_EQ(
+      std::min(AutocompleteResult::GetMaxMatches(), kResultsPerProvider * 2),
+      result_.size());
   ASSERT_NE(result_.end(), result_.default_match());
   EXPECT_EQ(provider2, result_.default_match()->provider);
 }
@@ -594,7 +596,9 @@
   ResetControllerWithTestProviders(false, nullptr, nullptr);
   RunTest();
 
-  ASSERT_EQ(kResultsPerProvider * 2, result_.size());
+  ASSERT_EQ(
+      std::min(AutocompleteResult::GetMaxMatches(), kResultsPerProvider * 2),
+      result_.size());
 
   // Now, check the results from the second provider, as they should not have
   // assisted query stats set.
@@ -603,7 +607,7 @@
         result_.match_at(i)->search_terms_args->assisted_query_stats.empty());
   }
   // The first provider has a test keyword, so AQS should be non-empty.
-  for (size_t i = kResultsPerProvider; i < kResultsPerProvider * 2; ++i) {
+  for (size_t i = kResultsPerProvider; i < result_.size(); ++i) {
     EXPECT_FALSE(
         result_.match_at(i)->search_terms_args->assisted_query_stats.empty());
   }
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 1d9c9bf..99d5f3c8 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -44,8 +44,14 @@
 };
 
 // static
-size_t AutocompleteResult::GetMaxMatches() {
+size_t AutocompleteResult::GetMaxMatches(bool is_zero_suggest) {
+#if (defined(OS_ANDROID))
+  constexpr size_t kDefaultMaxAutocompleteMatches = 5;
+  if (is_zero_suggest)
+    return kDefaultMaxAutocompleteMatches;
+#else
   constexpr size_t kDefaultMaxAutocompleteMatches = 6;
+#endif  // defined(OS_ANDROID)
 
   return base::GetFieldTrialParamByFeatureAsInt(
       omnibox::kUIExperimentMaxAutocompleteMatches,
@@ -188,7 +194,8 @@
 
   // In the process of trimming, drop all matches with a demoted relevance
   // score of 0.
-  const size_t max_num_matches = std::min(GetMaxMatches(), matches_.size());
+  const size_t max_num_matches =
+      std::min(GetMaxMatches(input.from_omnibox_focus()), matches_.size());
   size_t num_matches;
   for (num_matches = 0u; (num_matches < max_num_matches) &&
        (comparing_object.GetDemotedRelevance(*match_at(num_matches)) > 0);
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index 958cf22c..d5c84fd 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -31,7 +31,7 @@
   typedef ACMatches::iterator iterator;
 
   // Max number of matches we'll show from the various providers.
-  static size_t GetMaxMatches();
+  static size_t GetMaxMatches(bool is_zero_suggest = false);
 
   AutocompleteResult();
   ~AutocompleteResult();
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index f8f441a..b83c2ee 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -1112,14 +1112,14 @@
   result.SortAndCull(input, template_url_service_.get());
 
   TestData expected_data[] = {
-    { 3, 2,  800, true  },  // default match unmoved
-    { 4, 1,  900, false },  // search types
-    { 2, 1,  700, false },
-    { 6, 3, 1100, false },  // other types
-    { 5, 2, 1000, false },
-    { 1, 2,  600, false },
+      {3, 2, 800, true},                         // default match unmoved
+      {4, 1, 900, false},                        // search types
+      {2, 1, 700, false},  {6, 3, 1100, false},  // other types
+      {5, 2, 1000, false}, {1, 2, 600, false},
   };
-  AssertResultMatches(result, expected_data, base::size(expected_data));
+
+  AssertResultMatches(result, expected_data,
+                      AutocompleteResult::GetMaxMatches());
 }
 
 TEST_F(AutocompleteResultTest, SortAndCullMaxURLMatches) {
@@ -1154,7 +1154,7 @@
     result.AppendMatches(input, matches);
     result.SortAndCull(input, template_url_service_.get());
 
-    EXPECT_EQ(result.size(), 6u);
+    EXPECT_EQ(result.size(), AutocompleteResult::GetMaxMatches());
     AutocompleteMatchType::Type expected_types[] = {
         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
         AutocompleteMatchType::SEARCH_HISTORY,
@@ -1190,7 +1190,7 @@
     result.AppendMatches(input, matches);
     result.SortAndCull(input, template_url_service_.get());
 
-    EXPECT_EQ(result.size(), 6u);
+    EXPECT_EQ(result.size(), AutocompleteResult::GetMaxMatches());
     AutocompleteMatchType::Type expected_types[] = {
         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
         AutocompleteMatchType::SEARCH_HISTORY,
diff --git a/components/omnibox/browser/history_url_provider.h b/components/omnibox/browser/history_url_provider.h
index 68c96f5c..07b5c87 100644
--- a/components/omnibox/browser/history_url_provider.h
+++ b/components/omnibox/browser/history_url_provider.h
@@ -15,7 +15,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/threading/thread_checker.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/history_match.h"
@@ -134,7 +134,7 @@
   // the query runs, the query will be abandoned.  This allows us to avoid
   // running queries that are no longer needed.  Since we don't care if we run
   // the extra queries, the lack of signaling is not a problem.
-  base::CancellationFlag cancel_flag;
+  base::AtomicFlag cancel_flag;
 
   // Set by ExecuteWithDB() on the history thread when the query could not be
   // performed because the history system failed to properly init the database.
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 0251a28..66f1e01 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -156,27 +156,30 @@
       focus_source_);
 }
 
-const OmniboxEditModel::State OmniboxEditModel::GetStateForTabSwitch() {
+OmniboxEditModel::State OmniboxEditModel::GetStateForTabSwitch() const {
+  // NOTE: it's important this doesn't attempt to access any state that
+  // may come from the active WebContents. At the time this is called, the
+  // active WebContents has already changed.
+
   // Like typing, switching tabs "accepts" the temporary text as the user
   // text, because it makes little sense to have temporary text when the
   // popup is closed.
+  base::string16 user_text;
   if (user_input_in_progress_) {
-    // Weird edge case to match other browsers: if the edit is empty, revert to
-    // the permanent text (so the user can get it back easily) but select it (so
-    // on switching back, typing will "just work").
     const base::string16 display_text = view_->GetText();
-    if (MaybePrependKeyword(display_text).empty()) {
-      base::AutoReset<bool> tmp(&in_revert_, true);
-      view_->RevertAll();
-      view_->SelectAll(true);
-    } else {
-      InternalSetUserText(display_text);
-    }
+    if (!MaybePrependKeyword(display_text).empty())
+      user_text = display_text;
+    // Else case is user deleted all the text. The expectation (which matches
+    // other browsers) is when the user restores the state a revert happens as
+    // well as a select all. The revert shouldn't be done here, as at the time
+    // this is called a revert would revert to the url of the newly activated
+    // tab (because at the time this is called, the WebContents has already
+    // changed). By leaving the |user_text| empty downstream code is able to
+    // detect this and select all.
+  } else {
+    user_text = user_text_;
   }
-
-  UMA_HISTOGRAM_BOOLEAN("Omnibox.SaveStateForTabSwitch.UserInputInProgress",
-                        user_input_in_progress_);
-  return State(user_input_in_progress_, user_text_, keyword_, is_keyword_hint_,
+  return State(user_input_in_progress_, user_text, keyword_, is_keyword_hint_,
                keyword_mode_entry_method_, focus_state_, focus_source_, input_);
 }
 
@@ -213,7 +216,8 @@
   if (state->user_input_in_progress) {
     // NOTE: Be sure to set keyword-related state AFTER invoking
     // SetUserText(), as SetUserText() clears the keyword state.
-    view_->SetUserText(state->user_text, false);
+    if (!state->user_text.empty() || !state->keyword.empty())
+      view_->SetUserText(state->user_text, false);
     keyword_ = state->keyword;
     is_keyword_hint_ = state->is_keyword_hint;
     keyword_mode_entry_method_ = state->keyword_mode_entry_method;
@@ -1158,15 +1162,12 @@
 
     // 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()) {
+    // have done.
+    const size_t line_no = GetNewSelectedLine(count);
+    if (has_temporary_text_ && line_no == 0) {
       RevertTemporaryText(true);
     } else {
-      popup_model()->Move(count);
+      popup_model()->MoveTo(line_no);
     }
     return;
   }
@@ -1618,3 +1619,22 @@
 
   client_->OnFocusChanged(focus_state_, reason);
 }
+
+size_t OmniboxEditModel::GetNewSelectedLine(int count) {
+  if (!OmniboxFieldTrial::IsOmniboxWrapPopupPositionEnabled()) {
+    if (count < 0) {
+      if ((size_t)-count >= popup_model()->selected_line())
+        return 0;
+    } else if (count + popup_model()->selected_line() >=
+               popup_model()->result().size()) {
+      return popup_model()->result().size() - 1;
+    }
+    return popup_model()->selected_line() + count;
+  } else {
+    int line_no = (static_cast<int>(popup_model()->selected_line()) + count) %
+                  static_cast<int>(popup_model()->result().size());
+    if (line_no < 0)
+      line_no += popup_model()->result().size();
+    return line_no;
+  }
+}
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index b44895a9..594ccfc4 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -93,7 +93,7 @@
 
   // Returns the current state.  This assumes we are switching tabs, and changes
   // the internal state appropriately.
-  const State GetStateForTabSwitch();
+  State GetStateForTabSwitch() const;
 
   // Resets the tab state, then restores local state from |state|. |state| may
   // be nullptr if there is no saved state.
@@ -459,6 +459,11 @@
   // the view.
   void SetFocusState(OmniboxFocusState state, OmniboxFocusChangeReason reason);
 
+  // Calculates the new selected line based on |count|, how many
+  // suggestions are currently in the results, and any features
+  // that are enabled.
+  size_t GetNewSelectedLine(int count);
+
   // NOTE: |client_| must outlive |omnibox_controller_|, as the latter has a
   // reference to the former.
   std::unique_ptr<OmniboxClient> client_;
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 2538dbc..769763c 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -694,6 +694,10 @@
   return base::FeatureList::IsEnabled(omnibox::kOmniboxMaxURLMatches);
 }
 
+bool OmniboxFieldTrial::IsOmniboxWrapPopupPositionEnabled() {
+  return base::FeatureList::IsEnabled(omnibox::kOmniboxWrapPopupPosition);
+}
+
 const char OmniboxFieldTrial::kBundledExperimentFieldTrialName[] =
     "OmniboxBundledExperimentV1";
 const char OmniboxFieldTrial::kDisableProvidersRule[] = "DisableProviders";
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 947c40a..6f65c40 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -426,6 +426,10 @@
 // is enabled.
 bool IsMaxURLMatchesFeatureEnabled();
 
+// Returns whether the feature to allow the Omnibox pop-up position to wrap
+// between top and bottom is enabled.
+bool IsOmniboxWrapPopupPositionEnabled();
+
 // ---------------------------------------------------------
 // Clipboard URL suggestions:
 
diff --git a/components/omnibox/browser/omnibox_popup_model.cc b/components/omnibox/browser/omnibox_popup_model.cc
index fbaa4484..e2f80ef 100644
--- a/components/omnibox/browser/omnibox_popup_model.cc
+++ b/components/omnibox/browser/omnibox_popup_model.cc
@@ -169,15 +169,11 @@
   view_->OnDragCanceled();
 }
 
-void OmniboxPopupModel::Move(int count) {
-  const AutocompleteResult& result = this->result();
-  if (result.empty())
+void OmniboxPopupModel::MoveTo(size_t new_line) {
+  if (result().empty())
     return;
 
-  // Clamp the new line to [0, result_.count() - 1].
-  const size_t new_line = selected_line_ + count;
-  SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ? 0 : new_line,
-                  false, false);
+  SetSelectedLine(new_line, false, false);
 }
 
 void OmniboxPopupModel::SetSelectedLineState(LineState state) {
diff --git a/components/omnibox/browser/omnibox_popup_model.h b/components/omnibox/browser/omnibox_popup_model.h
index 4c1a16f..b003ee3 100644
--- a/components/omnibox/browser/omnibox_popup_model.h
+++ b/components/omnibox/browser/omnibox_popup_model.h
@@ -96,11 +96,10 @@
   void ResetToDefaultMatch();
 
   // Immediately updates and opens the popup if necessary, then moves the
-  // current selection down (|count| > 0) or up (|count| < 0), clamping to the
-  // first or last result if necessary.  If |count| == 0, the selection will be
-  // unchanged, but the popup will still redraw and modify the text in the
-  // OmniboxEditModel.
-  void Move(int count);
+  // current selection to the respective line. If the line is unchanged, the
+  // selection will be unchanged, but the popup will still redraw and modify
+  // the text in the OmniboxEditModel.
+  void MoveTo(size_t new_line);
 
   // If the selected line has both a normal match and a keyword match, this can
   // be used to choose which to select.  This allows the user to toggle between
diff --git a/components/omnibox/browser/omnibox_popup_model_unittest.cc b/components/omnibox/browser/omnibox_popup_model_unittest.cc
index a75cf2e..2a65f52c 100644
--- a/components/omnibox/browser/omnibox_popup_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_popup_model_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
 #include "components/omnibox/browser/autocomplete_match.h"
@@ -19,6 +20,7 @@
 #include "components/omnibox/browser/test_omnibox_edit_controller.h"
 #include "components/omnibox/browser/test_omnibox_view.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
+#include "components/omnibox/common/omnibox_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -35,6 +37,15 @@
   void OnDragCanceled() override {}
 };
 
+class TestOmniboxEditModel : public OmniboxEditModel {
+ public:
+  TestOmniboxEditModel(OmniboxView* view,
+                       OmniboxEditController* controller,
+                       std::unique_ptr<OmniboxClient> client)
+      : OmniboxEditModel(view, controller, std::move(client)) {}
+  bool PopupIsOpen() const override { return true; }
+};
+
 }  // namespace
 
 class OmniboxPopupModelTest : public ::testing::Test {
@@ -51,7 +62,7 @@
   base::test::ScopedTaskEnvironment task_environment_;
   TestOmniboxEditController controller_;
   TestOmniboxView view_;
-  OmniboxEditModel model_;
+  TestOmniboxEditModel model_;
   TestOmniboxPopupView popup_view_;
   OmniboxPopupModel popup_model_;
 
@@ -84,6 +95,47 @@
   EXPECT_TRUE(popup_model()->has_selected_match());
 }
 
+TEST_F(OmniboxPopupModelTest, PopupPositionChanging) {
+  ACMatches matches;
+  for (size_t i = 0; i < 3; ++i) {
+    AutocompleteMatch match(nullptr, 1000, false,
+                            AutocompleteMatchType::URL_WHAT_YOU_TYPED);
+    match.keyword = base::ASCIIToUTF16("match");
+    match.allowed_to_be_default_match = true;
+    matches.push_back(match);
+  }
+  auto* result = &model()->autocomplete_controller()->result_;
+  AutocompleteInput input(base::UTF8ToUTF16("match"),
+                          metrics::OmniboxEventProto::NTP,
+                          TestSchemeClassifier());
+  result->AppendMatches(input, matches);
+  result->SortAndCull(input, nullptr);
+  popup_model()->OnResultChanged();
+  EXPECT_EQ(0u, model()->popup_model()->selected_line());
+  model()->OnUpOrDownKeyPressed(1);
+  EXPECT_EQ(1u, model()->popup_model()->selected_line());
+  model()->OnUpOrDownKeyPressed(-1);
+  EXPECT_EQ(0u, model()->popup_model()->selected_line());
+  model()->OnUpOrDownKeyPressed(2);
+  EXPECT_EQ(2u, model()->popup_model()->selected_line());
+  // Cap at number of results.
+  model()->OnUpOrDownKeyPressed(2);
+  EXPECT_EQ(2u, model()->popup_model()->selected_line());
+  // Cap at 0 too.
+  model()->OnUpOrDownKeyPressed(-3);
+  EXPECT_EQ(0u, model()->popup_model()->selected_line());
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(omnibox::kOmniboxWrapPopupPosition);
+  // Test wrapping.
+  model()->OnUpOrDownKeyPressed(-1);
+  EXPECT_EQ(2u, model()->popup_model()->selected_line());
+  model()->OnUpOrDownKeyPressed(1);
+  EXPECT_EQ(0u, model()->popup_model()->selected_line());
+  model()->OnUpOrDownKeyPressed(1);
+  EXPECT_EQ(1u, model()->popup_model()->selected_line());
+}
+
 TEST_F(OmniboxPopupModelTest, ComputeMatchMaxWidths) {
   int contents_max_width, description_max_width;
   const int separator_width = 10;
diff --git a/components/omnibox/browser/remote_suggestions_service.cc b/components/omnibox/browser/remote_suggestions_service.cc
index 8c5ec1e..10f9ecd 100644
--- a/components/omnibox/browser/remote_suggestions_service.cc
+++ b/components/omnibox/browser/remote_suggestions_service.cc
@@ -98,20 +98,20 @@
 RemoteSuggestionsService::~RemoteSuggestionsService() {}
 
 void RemoteSuggestionsService::CreateSuggestionsRequest(
-    const std::string& current_url,
+    const TemplateURLRef::SearchTermsArgs& search_terms_args,
     const base::Time& visit_time,
-    const AutocompleteInput& input,
     const TemplateURLService* template_url_service,
     StartCallback start_callback,
     CompletionCallback completion_callback) {
-  const GURL experimental_suggest_url =
-      ExperimentalEndpointUrl(current_url, template_url_service);
+  const GURL experimental_suggest_url = ExperimentalEndpointUrl(
+      search_terms_args.current_page_url, template_url_service);
   if (experimental_suggest_url.is_valid())
-    CreateExperimentalRequest(current_url, visit_time, experimental_suggest_url,
+    CreateExperimentalRequest(search_terms_args.current_page_url, visit_time,
+                              experimental_suggest_url,
                               std::move(start_callback),
                               std::move(completion_callback));
   else
-    CreateDefaultRequest(current_url, input, template_url_service,
+    CreateDefaultRequest(search_terms_args, template_url_service,
                          std::move(start_callback),
                          std::move(completion_callback));
 }
@@ -123,8 +123,7 @@
 
 // static
 GURL RemoteSuggestionsService::EndpointUrl(
-    const std::string& current_url,
-    const AutocompleteInput& input,
+    TemplateURLRef::SearchTermsArgs search_terms_args,
     const TemplateURLService* template_url_service) {
   if (template_url_service == nullptr) {
     return GURL();
@@ -140,18 +139,12 @@
       search_engine->suggestions_url_ref();
   const SearchTermsData& search_terms_data =
       template_url_service->search_terms_data();
-  TemplateURLRef::SearchTermsArgs search_term_args;
-  if (!current_url.empty()) {
-    search_term_args.current_page_url = current_url;
-  }
-
-  search_term_args.page_classification = input.current_page_classification();
 
   // Append a specific suggest client in ChromeOS app_list launcher contexts.
   BaseSearchProvider::AppendSuggestClientToAdditionalQueryParams(
-      search_engine, search_terms_data, input.current_page_classification(),
-      &search_term_args);
-  return GURL(suggestion_url_ref.ReplaceSearchTerms(search_term_args,
+      search_engine, search_terms_data, search_terms_args.page_classification,
+      &search_terms_args);
+  return GURL(suggestion_url_ref.ReplaceSearchTerms(search_terms_args,
                                                     search_terms_data));
 }
 
@@ -194,13 +187,11 @@
 }
 
 void RemoteSuggestionsService::CreateDefaultRequest(
-    const std::string& current_url,
-    const AutocompleteInput& input,
+    const TemplateURLRef::SearchTermsArgs& search_terms_args,
     const TemplateURLService* template_url_service,
     StartCallback start_callback,
     CompletionCallback completion_callback) {
-  const GURL suggest_url =
-      EndpointUrl(current_url, input, template_url_service);
+  const GURL suggest_url = EndpointUrl(search_terms_args, template_url_service);
   DCHECK(suggest_url.is_valid());
 
   net::NetworkTrafficAnnotationTag traffic_annotation =
diff --git a/components/omnibox/browser/remote_suggestions_service.h b/components/omnibox/browser/remote_suggestions_service.h
index e669a426..4034dba 100644
--- a/components/omnibox/browser/remote_suggestions_service.h
+++ b/components/omnibox/browser/remote_suggestions_service.h
@@ -11,6 +11,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/omnibox/browser/autocomplete_input.h"
+#include "components/search_engines/template_url.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/identity/public/cpp/access_token_info.h"
 #include "url/gurl.h"
@@ -55,21 +56,19 @@
       base::OnceCallback<void(const network::SimpleURLLoader* source,
                               std::unique_ptr<std::string> response_body)>;
 
-  // Creates a loader for remote suggestions for |current_url| and passes
+  // Creates a loader for remote suggestions for |search_terms_args| and passes
   // the loader to |start_callback|. It uses a number of signals to create the
   // loader, including field trial / experimental parameters, and it may
   // pass a nullptr to |start_callback| (see below for details).
   //
-  // |current_url| may be empty, in which case the system will never use the
-  // experimental suggestions service. It's possible the non-experimental
+  // |search_terms_args| encapsulates the arguments sent to the remote service.
+  // If |search_terms_args.current_page_url| is empty, the system will never use
+  // the experimental suggestions service. It's possible the non-experimental
   // service may decide to offer general-purpose suggestions.
   //
   // |visit_time| is the time of the visit for the URL for which suggestions
   // should be fetched.
   //
-  // |input| is the current user input of the autocomplete query, whose
-  // |current_page_classification| will be used to build the suggestion url.
-  //
   // |template_url_service| may be null, but some services may be disabled.
   //
   // |start_callback| is called to transfer ownership of the created loader to
@@ -82,12 +81,12 @@
   //
   // This method sends a request for an OAuth2 token and temporarily
   // instantiates |token_fetcher_|.
-  void CreateSuggestionsRequest(const std::string& current_url,
-                                const base::Time& visit_time,
-                                const AutocompleteInput& input,
-                                const TemplateURLService* template_url_service,
-                                StartCallback start_callback,
-                                CompletionCallback completion_callback);
+  void CreateSuggestionsRequest(
+      const TemplateURLRef::SearchTermsArgs& search_terms_args,
+      const base::Time& visit_time,
+      const TemplateURLService* template_url_service,
+      StartCallback start_callback,
+      CompletionCallback completion_callback);
 
   // Advises the service to stop any process that creates a suggestion request.
   void StopCreatingSuggestionsRequest();
@@ -98,15 +97,14 @@
   // Returns an invalid URL (i.e.: GURL::is_valid() == false) in case of an
   // error.
   //
-  // |current_url| is the page the user is currently browsing and may be empty.
-  //
-  // |input| is the current user input of the autocomplete query, whose
-  // |current_page_classification| will be used to build the suggestion url.
+  // |search_terms_args| encapsulates the arguments sent to the suggest service.
+  // Various parts of it (including the current page URL and classification) are
+  // used to build the final endpoint URL. Note that the current page URL can
+  // be empty.
   //
   // Note that this method is public and is also used by ZeroSuggestProvider for
-  // suggestions that do not take |current_url| into consideration.
-  static GURL EndpointUrl(const std::string& current_url,
-                          const AutocompleteInput& input,
+  // suggestions that do not take the current page URL into consideration.
+  static GURL EndpointUrl(TemplateURLRef::SearchTermsArgs search_terms_args,
                           const TemplateURLService* template_url_service);
 
  private:
@@ -133,11 +131,11 @@
   //
   // This function is called by CreateSuggestionsRequest. See its
   // function definition for details on the parameters.
-  void CreateDefaultRequest(const std::string& current_url,
-                            const AutocompleteInput& input,
-                            const TemplateURLService* template_url_service,
-                            StartCallback start_callback,
-                            CompletionCallback completion_callback);
+  void CreateDefaultRequest(
+      const TemplateURLRef::SearchTermsArgs& search_terms_args,
+      const TemplateURLService* template_url_service,
+      StartCallback start_callback,
+      CompletionCallback completion_callback);
 
   // Upon succesful creation of an HTTP POST request for experimental remote
   // suggestions, the |callback| function is run with the HTTP POST request as a
diff --git a/components/omnibox/browser/remote_suggestions_service_unittest.cc b/components/omnibox/browser/remote_suggestions_service_unittest.cc
index ecc6e33..969a7e7 100644
--- a/components/omnibox/browser/remote_suggestions_service_unittest.cc
+++ b/components/omnibox/browser/remote_suggestions_service_unittest.cc
@@ -54,11 +54,12 @@
 
   RemoteSuggestionsService service(nullptr /* identity_manager */,
                                    GetUrlLoaderFactory());
-  AutocompleteInput input;
   base::Time visit_time;
   TemplateURLService template_url_service(nullptr, 0);
+  TemplateURLRef::SearchTermsArgs search_terms_args;
+  search_terms_args.current_page_url = "https://www.google.com/";
   service.CreateSuggestionsRequest(
-      "https://www.google.com/", visit_time, input, &template_url_service,
+      search_terms_args, visit_time, &template_url_service,
       base::BindOnce(&RemoteSuggestionsServiceTest::OnRequestStart,
                      base::Unretained(this)),
       base::BindOnce(&RemoteSuggestionsServiceTest::OnRequestComplete,
diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc
index 47a2b2d..cea23458 100644
--- a/components/omnibox/browser/search_provider.cc
+++ b/components/omnibox/browser/search_provider.cc
@@ -1110,10 +1110,18 @@
     // suggestion of some sort".
     if ((i->type != AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED) &&
         (i->type != AutocompleteMatchType::SEARCH_OTHER_ENGINE)) {
+      // IsInstantExtendedAPIEnabled is a legacy function that we no longer want
+      // to affect the number of search suggestions we provide, but we want to
+      // understand the effect of removing this check, so its impotence is
+      // controlled experimentally.
+      bool instant_check_disabled = base::FeatureList::IsEnabled(
+          omnibox::kOmniboxDisableInstantExtendedLimit);
+      bool skip_suggestion_for_instant_disabled =
+          !(instant_check_disabled || search::IsInstantExtendedAPIEnabled());
       // If we've already hit the limit on non-server-scored suggestions, and
       // this isn't a server-scored suggestion we can add, skip it.
       if ((num_suggestions >= provider_max_matches_) &&
-          (!search::IsInstantExtendedAPIEnabled() ||
+          (skip_suggestion_for_instant_disabled ||
            (i->GetAdditionalInfo(kRelevanceFromServerKey) != kTrue))) {
         continue;
       }
@@ -1164,16 +1172,12 @@
 void SearchProvider::AddRawHistoryResultsToMap(bool is_keyword,
                                                int did_not_accept_suggestion,
                                                MatchMap* map) {
-  base::TimeTicks start_time(base::TimeTicks::Now());
-
   const SearchSuggestionParser::SuggestResults* transformed_results =
       is_keyword ? &transformed_keyword_history_results_
                  : &transformed_default_history_results_;
   DCHECK(transformed_results);
   AddTransformedHistoryResultsToMap(
       *transformed_results, did_not_accept_suggestion, map);
-  UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime",
-                      base::TimeTicks::Now() - start_time);
 }
 
 void SearchProvider::AddTransformedHistoryResultsToMap(
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 6d8d4d3..23d25bfa 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -163,8 +163,12 @@
   current_page_classification_ = input.current_page_classification();
   current_url_match_ = MatchForCurrentURL();
 
+  TemplateURLRef::SearchTermsArgs search_terms_args;
+  search_terms_args.page_classification = current_page_classification_;
+  search_terms_args.omnibox_focus_type =
+      TemplateURLRef::SearchTermsArgs::OmniboxFocusType::ON_FOCUS;
   GURL suggest_url = RemoteSuggestionsService::EndpointUrl(
-      /*current_url=*/"", input, client()->GetTemplateURLService());
+      search_terms_args, client()->GetTemplateURLService());
   if (!suggest_url.is_valid())
     return;
 
@@ -191,14 +195,14 @@
     return;
   }
 
-  const std::string current_url =
+  search_terms_args.current_page_url =
       result_type_running_ == REMOTE_SEND_URL ? current_query_ : std::string();
   // Create a request for suggestions, routing completion to
   // OnRemoteSuggestionsLoaderAvailable.
   client()
       ->GetRemoteSuggestionsService(/*create_if_necessary=*/true)
       ->CreateSuggestionsRequest(
-          current_url, client()->GetCurrentVisitTimestamp(), input,
+          search_terms_args, client()->GetCurrentVisitTimestamp(),
           client()->GetTemplateURLService(),
           base::BindOnce(
               &ZeroSuggestProvider::OnRemoteSuggestionsLoaderAvailable,
@@ -278,9 +282,8 @@
       client->GetTemplateURLService();
   // Template URL service can be null in tests.
   if (template_url_service != nullptr) {
-    AutocompleteInput empty_input;
     GURL suggest_url = RemoteSuggestionsService::EndpointUrl(
-        /*current_url=*/"", /*empty input*/ empty_input, template_url_service);
+        TemplateURLRef::SearchTermsArgs(), template_url_service);
     // To check whether this is allowed, use an arbitrary insecure (http) URL
     // as the URL we'd want suggestions for.  The value of OTHER as the current
     // page classification is to correspond with that URL.
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index ffb1c1b5..d2dc995 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -116,6 +116,11 @@
 #endif
 };
 
+// Feature that enables wrapping the Omnibox position between top and
+// bottom.
+const base::Feature kOmniboxWrapPopupPosition{
+    "OmniboxWrapPopupPosition", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Feature used to reverse the sense of the tab switch button. Selecting the
 // suggestion will switch to the tab, while the button will navigate
 // locally.
@@ -264,6 +269,11 @@
 const base::Feature kOmniboxMaterialDesignWeatherIcons{
     "OmniboxMaterialDesignWeatherIcons", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Returns whether IsInstantExtendedAPIEnabled should be ignored when deciding
+// the number of Google-provided search suggestions.
+const base::Feature kOmniboxDisableInstantExtendedLimit{
+    "OmniboxDisableInstantExtendedLimit", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Feature to configure on-focus suggestions provided by ZeroSuggestProvider.
 // This feature's main job is to contain some field trial parameters such as:
 //  - "ZeroSuggestVariant" configures the per-page-classification mode of
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 31fd1031..d5323a4 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -25,6 +25,7 @@
 extern const base::Feature kOmniboxShortBookmarkSuggestions;
 extern const base::Feature kOmniboxTailSuggestions;
 extern const base::Feature kOmniboxTabSwitchSuggestions;
+extern const base::Feature kOmniboxWrapPopupPosition;
 extern const base::Feature kOmniboxReverseTabSwitchLogic;
 extern const base::Feature kExperimentalKeywordMode;
 extern const base::Feature kOmniboxPedalSuggestions;
@@ -48,6 +49,7 @@
 extern const base::Feature kDedupeGoogleDriveURLs;
 extern const base::Feature kOmniboxPopupShortcutIconsInZeroState;
 extern const base::Feature kOmniboxMaterialDesignWeatherIcons;
+extern const base::Feature kOmniboxDisableInstantExtendedLimit;
 
 // On-Focus Suggestions a.k.a. ZeroSuggest.
 extern const base::Feature kOnFocusSuggestions;
diff --git a/components/optimization_guide/optimization_guide_prefs.cc b/components/optimization_guide/optimization_guide_prefs.cc
index 4b21b5d6..5d16393 100644
--- a/components/optimization_guide/optimization_guide_prefs.cc
+++ b/components/optimization_guide/optimization_guide_prefs.cc
@@ -14,6 +14,12 @@
 // loaded, broken down by hint source.
 const char kHintLoadedCounts[] = "optimization_guide.hint_loaded_counts";
 
+// A pref that stores the last time a hints fetch was attempted. This limits the
+// frequency that hints are fetched and prevents a crash loop that continually
+// fetches hints on startup.
+const char kHintsFetcherLastFetchAttempt[] =
+    "optimization_guide.hintsfetcher.last_fetch_attempt";
+
 // A dictionary pref that stores the set of hosts that cannot have hints fetched
 // for until visited again after DataSaver was enabled. If The hash of the host
 // is in the dictionary, then it is on the blacklist and should not be used, the
@@ -29,6 +35,10 @@
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(kHintLoadedCounts, PrefRegistry::LOSSY_PREF);
+  registry->RegisterInt64Pref(
+      kHintsFetcherLastFetchAttempt,
+      base::Time().ToDeltaSinceWindowsEpoch().InMicroseconds(),
+      PrefRegistry::LOSSY_PREF);
   registry->RegisterDictionaryPref(kHintsFetcherTopHostBlacklist,
                                    PrefRegistry::LOSSY_PREF);
   registry->RegisterIntegerPref(
diff --git a/components/optimization_guide/optimization_guide_prefs.h b/components/optimization_guide/optimization_guide_prefs.h
index a1202bc..3c0d998 100644
--- a/components/optimization_guide/optimization_guide_prefs.h
+++ b/components/optimization_guide/optimization_guide_prefs.h
@@ -13,6 +13,7 @@
 namespace prefs {
 
 extern const char kHintLoadedCounts[];
+extern const char kHintsFetcherLastFetchAttempt[];
 extern const char kHintsFetcherTopHostBlacklist[];
 extern const char kHintsFetcherTopHostBlacklistState[];
 
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 4107516f..3d678d6 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -270,6 +270,8 @@
   }
 
   if (is_new_login_) {
+    metrics_util::LogNewlySavedPasswordIsGenerated(
+        pending_credentials_.type == PasswordForm::Type::kGenerated);
     SanitizePossibleUsernames(&pending_credentials_);
     pending_credentials_.date_created = base::Time::Now();
     votes_uploader_.SendVotesOnSave(observed_form_, *parsed_submitted_form_,
diff --git a/components/password_manager/core/browser/password_form_filling.cc b/components/password_manager/core/browser/password_form_filling.cc
index e5359a8..680d821 100644
--- a/components/password_manager/core/browser/password_form_filling.cc
+++ b/components/password_manager/core/browser/password_form_filling.cc
@@ -148,6 +148,11 @@
       base::FeatureList::IsEnabled(features::kFillOnAccountSelectHttp) &&
       !client.IsMainFrameSecure();
 
+  // Suppress autofilling on Android in case the Touch To Fill feature is
+  // enabled.
+  const bool enable_touch_to_fill =
+      base::FeatureList::IsEnabled(features::kTouchToFillAndroid);
+
   // Proceed to autofill.
   // Note that we provide the choices but don't actually prefill a value if:
   // (1) we are in Incognito mode, or
@@ -171,6 +176,8 @@
     wait_for_username_reason = WaitForUsernameReason::kFormNotGoodForFilling;
   } else if (enable_foas_on_http) {
     wait_for_username_reason = WaitForUsernameReason::kFoasOnHTTP;
+  } else if (enable_touch_to_fill) {
+    wait_for_username_reason = WaitForUsernameReason::kTouchToFill;
   }
 
   // Record no "FirstWaitForUsernameReason" metrics for a form that is not meant
diff --git a/components/password_manager/core/browser/password_form_filling_unittest.cc b/components/password_manager/core/browser/password_form_filling_unittest.cc
index c0c2eab6..147d3b4 100644
--- a/components/password_manager/core/browser/password_form_filling_unittest.cc
+++ b/components/password_manager/core/browser/password_form_filling_unittest.cc
@@ -238,10 +238,11 @@
   best_matches.emplace(saved_match_.username_value, &saved_match_);
 
   for (bool enable_foas_http : {false, true}) {
+    SCOPED_TRACE(testing::Message() << "Enable FOAS on HTTP: " << std::boolalpha
+                                    << enable_foas_http);
     base::test::ScopedFeatureList features;
-    enable_foas_http
-        ? features.InitAndEnableFeature(features::kFillOnAccountSelectHttp)
-        : features.InitAndDisableFeature(features::kFillOnAccountSelectHttp);
+    features.InitWithFeatureState(features::kFillOnAccountSelectHttp,
+                                  enable_foas_http);
 
     LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
         client_, &driver_, false /* is_blacklisted */, observed_form_,
@@ -253,4 +254,25 @@
   }
 }
 
+TEST_F(PasswordFormFillingTest, TouchToFill) {
+  std::map<base::string16, const autofill::PasswordForm*> best_matches;
+  best_matches.emplace(saved_match_.username_value, &saved_match_);
+
+  for (bool enable_touch_to_fill : {false, true}) {
+    SCOPED_TRACE(testing::Message() << "Enable Touch To Fill: "
+                                    << std::boolalpha << enable_touch_to_fill);
+    base::test::ScopedFeatureList features;
+    features.InitWithFeatureState(features::kTouchToFillAndroid,
+                                  enable_touch_to_fill);
+
+    LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
+        client_, &driver_, false /* is_blacklisted */, observed_form_,
+        best_matches, federated_matches_, &saved_match_,
+        metrics_recorder_.get());
+    EXPECT_EQ(enable_touch_to_fill ? LikelyFormFilling::kFillOnAccountSelect
+                                   : LikelyFormFilling::kFillOnPageLoad,
+              likely_form_filling);
+  }
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 13c83ea..9524e00 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -303,8 +303,7 @@
   }
 
   if (is_new_login_) {
-    UMA_HISTOGRAM_BOOLEAN(
-        "PasswordManager.NewlySavedPasswordIsGenerated",
+    metrics_util::LogNewlySavedPasswordIsGenerated(
         pending_credentials_.type == PasswordForm::Type::kGenerated);
     password_form_manager_helpers::SanitizePossibleUsernames(
         &pending_credentials_);
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.h b/components/password_manager/core/browser/password_form_metrics_recorder.h
index d6f5516..1bb1c46 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.h
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.h
@@ -224,7 +224,9 @@
     // User is on an HTTP site where passwords are filled on account selection
     // (FOAS).
     kFoasOnHTTP = 4,
-    kMaxValue = kFoasOnHTTP,
+    // The Touch To Fill feature is enabled.
+    kTouchToFill = 5,
+    kMaxValue = kTouchToFill,
   };
 
   // This metric records the user experience with the passwords filling. The
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.cc b/components/password_manager/core/browser/password_manager_metrics_util.cc
index 96939fd..7294c3d2 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.cc
+++ b/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -211,6 +211,11 @@
       "PasswordManager.DeleteCorruptedPasswordsResult", result);
 }
 
+void LogNewlySavedPasswordIsGenerated(bool value) {
+  base::UmaHistogramBoolean("PasswordManager.NewlySavedPasswordIsGenerated",
+                            value);
+}
+
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 void LogSyncPasswordHashChange(SyncPasswordHashChange event) {
   base::UmaHistogramEnumeration(
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index fe1984bd..f8b45c1b 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -426,6 +426,9 @@
 // present encryption key on MacOS.
 void LogDeleteCorruptedPasswordsResult(DeleteCorruptedPasswordsResult result);
 
+// Log whether a saved password was generated.
+void LogNewlySavedPasswordIsGenerated(bool value);
+
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 // Log a save sync password change event.
 void LogSyncPasswordHashChange(SyncPasswordHashChange event);
@@ -438,6 +441,7 @@
 // password hashes saved.
 void LogProtectedPasswordHashCounts(size_t gaia_hash_count,
                                     size_t enterprise_hash_count);
+
 #endif
 
 }  // namespace metrics_util
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index d53cebb..a38cd36 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -10,6 +10,8 @@
     "can_make_payment_query_factory.h",
     "payment_details_converter.cc",
     "payment_details_converter.h",
+    "payment_event_response_util.cc",
+    "payment_event_response_util.h",
     "payment_handler_host.cc",
     "payment_handler_host.h",
     "payment_request_converter.cc",
diff --git a/components/payments/content/android/java_templates/ErrorStrings.java.tmpl b/components/payments/content/android/java_templates/ErrorStrings.java.tmpl
index 8f76d23..7216c10 100644
--- a/components/payments/content/android/java_templates/ErrorStrings.java.tmpl
+++ b/components/payments/content/android/java_templates/ErrorStrings.java.tmpl
@@ -22,10 +22,32 @@
 
     public static final String INVALID_VALIDATION_ERRORS = "Invalid payment validation errors.";
 
-    public static final String TAB_OVERVIEW_MODE = "Tab overview mode dismissed UI.";
+    public static final String TAB_OVERVIEW_MODE =
+            "Tab overview mode dismissed Payment Request UI.";
+
+    public static final String TAB_SWITCH =
+            "Tab switch dismissed Payment Request UI.";
 
     public static final String ACTIVITY_NOT_FOUND = "Unable to find Chrome activity.";
 
+    public static final String PAYMENT_APP_LAUNCH_FAIL = "Unable to invoke the payment app.";
+
+    public static final String PAYMENT_APP_PRIVATE_ACTIVITY =
+            "Payment app does not have android:exported=\"true\" on the PAY activity.";
+
+    public static final String MISSING_INTENT_DATA =
+            "Payment app returned an invalid result. Missing intent data.";
+
+    public static final String MISSING_INTENT_EXTRAS =
+            "Payment app returned an invalid result. Missing intent extras.";
+
+    public static final String RESULT_CANCELED  =
+            "Payment app returned RESULT_CANCELED code. This is how payment apps "
+            + "can close their activity programmatically.";
+
+    public static final String UNRECOGNIZED_ACTIVITY_RESULT  =
+            "Payment app returned unrecognized activity result %d.";
+
     // Prevent instantiation.
     private ErrorStrings() {{}}
 }}
diff --git a/components/payments/content/content_payment_request_delegate.h b/components/payments/content/content_payment_request_delegate.h
index 6eaa019..b85ab87 100644
--- a/components/payments/content/content_payment_request_delegate.h
+++ b/components/payments/content/content_payment_request_delegate.h
@@ -52,6 +52,10 @@
   // The |web_contents| parameter should not be null. A null |web_contents|
   // parameter will return an "Invalid certificate" error message.
   virtual std::string GetInvalidSslCertificateErrorMessage() = 0;
+
+  // Returns whether the UI should be skipped for a "basic-card" scenario. This
+  // will only be true in tests.
+  virtual bool SkipUiForBasicCard() const = 0;
 };
 
 }  // namespace payments
diff --git a/components/payments/content/installable_payment_app_crawler.cc b/components/payments/content/installable_payment_app_crawler.cc
index da415de..2dc98ed 100644
--- a/components/payments/content/installable_payment_app_crawler.cc
+++ b/components/payments/content/installable_payment_app_crawler.cc
@@ -312,6 +312,17 @@
     return false;
   }
 
+  // If this is called by tests, convert service worker URLs back to test server
+  // URLs so the service worker code can be fetched and installed. This is done
+  // last so same-origin checks are performed on the "logical" URLs instead of
+  // real test URLs with ports.
+  if (ignore_port_in_origin_comparison_for_testing_) {
+    app_info->sw_js_url =
+        downloader_->FindTestServerURL(GURL(app_info->sw_js_url)).spec();
+    app_info->sw_scope =
+        downloader_->FindTestServerURL(GURL(app_info->sw_scope)).spec();
+  }
+
   installable_apps_[method_manifest_url] = std::move(app_info);
 
   return true;
diff --git a/components/payments/content/payment_event_response_util.cc b/components/payments/content/payment_event_response_util.cc
new file mode 100644
index 0000000..eff4a35
--- /dev/null
+++ b/components/payments/content/payment_event_response_util.cc
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/payments/content/payment_event_response_util.h"
+
+#include "base/strings/string_piece.h"
+#include "components/payments/core/error_strings.h"
+
+namespace payments {
+
+base::StringPiece ConvertPaymentEventResponseTypeToErrorString(
+    mojom::PaymentEventResponseType response_type) {
+  switch (response_type) {
+    case mojom::PaymentEventResponseType::PAYMENT_EVENT_SUCCESS:
+      return "";
+    case mojom::PaymentEventResponseType::PAYMENT_EVENT_REJECT:
+      return errors::kPaymentEventRejected;
+    case mojom::PaymentEventResponseType::PAYMENT_EVENT_SERVICE_WORKER_ERROR:
+      return errors::kPaymentEventServiceWorkerError;
+    case mojom::PaymentEventResponseType::PAYMENT_HANDLER_WINDOW_CLOSING:
+      return errors::kUserCancelled;
+    case mojom::PaymentEventResponseType::PAYMENT_EVENT_INTERNAL_ERROR:
+      return errors::kPaymentEventInternalError;
+    case mojom::PaymentEventResponseType::PAYMENT_EVENT_NO_RESPONSE:
+      return errors::kNoResponseToPaymentEvent;
+    case mojom::PaymentEventResponseType::PAYMENT_DETAILS_STRINGIFY_ERROR:
+      return errors::kPaymentDetailsStringifyError;
+    case mojom::PaymentEventResponseType::PAYMENT_METHOD_NAME_EMPTY:
+      return errors::kMissingMethodNameFromPaymentApp;
+    case mojom::PaymentEventResponseType::PAYMENT_DETAILS_ABSENT:
+      return errors::kMissingDetailsFromPaymentApp;
+    case mojom::PaymentEventResponseType::PAYMENT_DETAILS_NOT_OBJECT:
+      return errors::kPaymentDetailsNotObject;
+    case mojom::PaymentEventResponseType::PAYMENT_EVENT_BROWSER_ERROR:
+      return errors::kPaymentEventBrowserError;
+    case mojom::PaymentEventResponseType::PAYMENT_EVENT_TIMEOUT:
+      return errors::kPaymentEventTimeout;
+  }
+}
+
+}  // namespace payments
diff --git a/components/payments/content/payment_event_response_util.h b/components/payments/content/payment_event_response_util.h
new file mode 100644
index 0000000..afa0c53d
--- /dev/null
+++ b/components/payments/content/payment_event_response_util.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAYMENTS_CONTENT_PAYMENT_EVENT_RESPONSE_UTIL_H_
+#define COMPONENTS_PAYMENTS_CONTENT_PAYMENT_EVENT_RESPONSE_UTIL_H_
+
+#include "base/strings/string_piece_forward.h"
+#include "third_party/blink/public/mojom/payments/payment_app.mojom.h"
+
+namespace payments {
+
+// Converts the given 'paymentrequest' event |response_type| into a
+// developer-facing error string. PAYMENT_EVENT_SUCCESS is converted into an
+// empty string.
+base::StringPiece ConvertPaymentEventResponseTypeToErrorString(
+    mojom::PaymentEventResponseType response_type);
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CONTENT_PAYMENT_EVENT_RESPONSE_UTIL_H_
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index 87fc9d1f..5c46593 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -133,20 +133,20 @@
   bool allowed_origin =
       UrlUtil::IsOriginAllowedToUseWebPaymentApis(last_committed_url);
   if (!allowed_origin) {
-    prohibited_origin_or_invalid_ssl_error_message_ = errors::kProhibitedOrigin;
+    reject_show_error_message_ = errors::kProhibitedOrigin;
   }
 
   bool invalid_ssl = false;
   if (last_committed_url.SchemeIsCryptographic()) {
-    DCHECK(prohibited_origin_or_invalid_ssl_error_message_.empty());
-    prohibited_origin_or_invalid_ssl_error_message_ =
+    DCHECK(reject_show_error_message_.empty());
+    reject_show_error_message_ =
         delegate_->GetInvalidSslCertificateErrorMessage();
-    invalid_ssl = !prohibited_origin_or_invalid_ssl_error_message_.empty();
+    invalid_ssl = !reject_show_error_message_.empty();
   }
 
   if (!allowed_origin || invalid_ssl) {
     // Intentionally don't set |spec_| and |state_|, so the UI is never shown.
-    log_.Error(prohibited_origin_or_invalid_ssl_error_message_);
+    log_.Error(reject_show_error_message_);
     log_.Error(errors::kProhibitedOriginOrInvalidSslExplanation);
     return;
   }
@@ -489,8 +489,8 @@
     journey_logger_.SetNotShown(
         JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
     client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED,
-                     !prohibited_origin_or_invalid_ssl_error_message_.empty()
-                         ? prohibited_origin_or_invalid_ssl_error_message_
+                     !reject_show_error_message_.empty()
+                         ? reject_show_error_message_
                          : GetNotSupportedErrorMessage(spec_.get()));
     if (observer_for_testing_)
       observer_for_testing_->OnNotSupportedError();
@@ -511,7 +511,7 @@
   // Only allowing URL base payment apps to skip the payment sheet.
   skipped_payment_request_ui_ =
       (spec()->url_payment_method_identifiers().size() == 1 ||
-       skip_ui_for_non_url_payment_method_identifiers_for_test_) &&
+       delegate_->SkipUiForBasicCard()) &&
       base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
       base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
       is_show_user_gesture_ && state()->IsInitialized() &&
@@ -531,17 +531,12 @@
 
 void PaymentRequest::OnPaymentResponseAvailable(
     mojom::PaymentResponsePtr response) {
+  DCHECK(!response->method_name.empty());
+  DCHECK(!response->stringified_details.empty());
+
   journey_logger_.SetEventOccurred(
       JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
 
-  // Do not send invalid response to client.
-  if (response->method_name.empty() || response->stringified_details.empty()) {
-    RecordFirstAbortReason(
-        JourneyLogger::ABORT_REASON_INSTRUMENT_DETAILS_ERROR);
-    delegate_->ShowErrorMessage();
-    return;
-  }
-
   // Log the correct "selected instrument" metric according to its type and
   // the method name in response.
   DCHECK(state_->selected_instrument());
@@ -574,6 +569,17 @@
   client_->OnPaymentResponse(std::move(response));
 }
 
+void PaymentRequest::OnPaymentResponseError(const std::string& error_message) {
+  journey_logger_.SetEventOccurred(
+      JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  RecordFirstAbortReason(JourneyLogger::ABORT_REASON_INSTRUMENT_DETAILS_ERROR);
+
+  reject_show_error_message_ = error_message;
+  delegate_->ShowErrorMessage();
+  // When the user dismisses the error message, UserCancelled() will reject
+  // PaymentRequest.show() with |reject_show_error_message_|.
+}
+
 void PaymentRequest::OnShippingOptionIdSelected(
     std::string shipping_option_id) {
   client_->OnShippingOptionChange(shipping_option_id);
@@ -607,7 +613,9 @@
 
   // This sends an error to the renderer, which informs the API user.
   client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
-                   errors::kUserCancelled);
+                   !reject_show_error_message_.empty()
+                       ? reject_show_error_message_
+                       : errors::kUserCancelled);
 
   // We close all bindings and ask to be destroyed.
   client_.reset();
diff --git a/components/payments/content/payment_request.h b/components/payments/content/payment_request.h
index 6e1dc09..33eb60e1 100644
--- a/components/payments/content/payment_request.h
+++ b/components/payments/content/payment_request.h
@@ -88,6 +88,7 @@
 
   // PaymentRequestState::Delegate:
   void OnPaymentResponseAvailable(mojom::PaymentResponsePtr response) override;
+  void OnPaymentResponseError(const std::string& error_message) override;
   void OnShippingOptionIdSelected(std::string shipping_option_id) override;
   void OnShippingAddressSelected(mojom::PaymentAddressPtr address) override;
   void OnPayerInfoSelected(mojom::PayerDetailPtr payer_info) override;
@@ -115,12 +116,6 @@
 
   bool IsIncognito() const;
 
-  // Allow to skip UI into payment handlers for such payment methods as
-  // "basic-card". Used only in tests.
-  void set_skip_ui_for_non_url_payment_method_identifiers_for_test() {
-    skip_ui_for_non_url_payment_method_identifiers_for_test_ = true;
-  }
-
   content::WebContents* web_contents() { return web_contents_; }
 
   bool skipped_payment_request_ui() { return skipped_payment_request_ui_; }
@@ -218,13 +213,8 @@
   // Whether PaymentRequest.show() has been called.
   bool is_show_called_ = false;
 
-  // Whether payment instruments for such payment methods as "basic-card" can
-  // skip UI for testing of the skip-UI flow. This is always false in
-  // production.
-  bool skip_ui_for_non_url_payment_method_identifiers_for_test_ = false;
-
   // If not empty, use this error message for rejecting PaymentRequest.show().
-  std::string prohibited_origin_or_invalid_ssl_error_message_;
+  std::string reject_show_error_message_;
 
   base::WeakPtrFactory<PaymentRequest> weak_ptr_factory_;
 
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index a7557a08..1304726 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -24,6 +24,7 @@
 #include "components/payments/core/features.h"
 #include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_request_data_util.h"
+#include "components/payments/core/payments_experimental_features.h"
 #include "content/public/common/content_features.h"
 
 namespace payments {
@@ -61,12 +62,15 @@
   if (base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps)) {
     DCHECK(web_contents);
     get_all_instruments_finished_ = false;
+    bool may_crawl_for_installable_payment_apps =
+        PaymentsExperimentalFeatures::IsEnabled(
+            features::kAlwaysAllowJustInTimePaymentApp) ||
+        !spec_->supports_basic_card();
+
     ServiceWorkerPaymentAppFactory::GetInstance()->GetAllPaymentApps(
         web_contents,
         payment_request_delegate_->GetPaymentManifestWebDataService(),
-        spec_->method_data(),
-        /*may_crawl_for_installable_payment_apps=*/
-        !spec_->supports_basic_card(),
+        spec_->method_data(), may_crawl_for_installable_payment_apps,
         base::BindOnce(&PaymentRequestState::GetAllPaymentAppsCallback,
                        weak_ptr_factory_.GetWeakPtr(), web_contents,
                        top_level_origin, frame_origin),
@@ -169,6 +173,11 @@
   delegate_->OnPaymentResponseAvailable(std::move(payment_response));
 }
 
+void PaymentRequestState::OnPaymentResponseError(
+    const std::string& error_message) {
+  delegate_->OnPaymentResponseError(error_message);
+}
+
 void PaymentRequestState::OnSpecUpdated() {
   autofill::AutofillProfile* selected_shipping_profile =
       selected_shipping_profile_;
diff --git a/components/payments/content/payment_request_state.h b/components/payments/content/payment_request_state.h
index 517a6679..c0fc680 100644
--- a/components/payments/content/payment_request_state.h
+++ b/components/payments/content/payment_request_state.h
@@ -67,6 +67,9 @@
     virtual void OnPaymentResponseAvailable(
         mojom::PaymentResponsePtr response) = 0;
 
+    // Called when the invoked payment app failed.
+    virtual void OnPaymentResponseError(const std::string& error_message) = 0;
+
     // Called when the shipping option has changed to |shipping_option_id|.
     virtual void OnShippingOptionIdSelected(std::string shipping_option_id) = 0;
 
@@ -114,6 +117,7 @@
   // PaymentResponseHelper::Delegate
   void OnPaymentResponseReady(
       mojom::PaymentResponsePtr payment_response) override;
+  void OnPaymentResponseError(const std::string& error_message) override;
 
   // PaymentRequestSpec::Observer
   void OnStartUpdating(PaymentRequestSpec::UpdateReason reason) override {}
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index 9a0d109..866bb9a 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -58,6 +58,7 @@
   void OnPaymentResponseAvailable(mojom::PaymentResponsePtr response) override {
     payment_response_ = std::move(response);
   }
+  void OnPaymentResponseError(const std::string& error_message) override {}
   void OnShippingOptionIdSelected(std::string shipping_option_id) override {}
   void OnShippingAddressSelected(mojom::PaymentAddressPtr address) override {
     selected_shipping_address_ = std::move(address);
diff --git a/components/payments/content/payment_response_helper.cc b/components/payments/content/payment_response_helper.cc
index 770ba95..fd63407 100644
--- a/components/payments/content/payment_response_helper.cc
+++ b/components/payments/content/payment_response_helper.cc
@@ -68,6 +68,9 @@
 void PaymentResponseHelper::OnInstrumentDetailsReady(
     const std::string& method_name,
     const std::string& stringified_details) {
+  if (!is_waiting_for_instrument_details_)
+    return;
+
   method_name_ = method_name;
   stringified_details_ = stringified_details;
   is_waiting_for_instrument_details_ = false;
@@ -76,16 +79,27 @@
     GeneratePaymentResponse();
 }
 
+void PaymentResponseHelper::OnInstrumentDetailsError(
+    const std::string& error_message) {
+  if (!is_waiting_for_instrument_details_)
+    return;
+
+  is_waiting_for_instrument_details_ = false;
+  is_waiting_for_shipping_address_normalization_ = false;
+  delegate_->OnPaymentResponseError(error_message);
+}
+
 void PaymentResponseHelper::OnAddressNormalized(
     bool success,
     const autofill::AutofillProfile& normalized_profile) {
-  if (is_waiting_for_shipping_address_normalization_) {
-    shipping_address_ = normalized_profile;
-    is_waiting_for_shipping_address_normalization_ = false;
+  if (!is_waiting_for_shipping_address_normalization_)
+    return;
 
-    if (!is_waiting_for_instrument_details_)
-      GeneratePaymentResponse();
-  }
+  shipping_address_ = normalized_profile;
+  is_waiting_for_shipping_address_normalization_ = false;
+
+  if (!is_waiting_for_instrument_details_)
+    GeneratePaymentResponse();
 }
 
 mojom::PayerDetailPtr PaymentResponseHelper::GeneratePayerDetail(
diff --git a/components/payments/content/payment_response_helper.h b/components/payments/content/payment_response_helper.h
index f0046f1..97c828b 100644
--- a/components/payments/content/payment_response_helper.h
+++ b/components/payments/content/payment_response_helper.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_PAYMENTS_CONTENT_PAYMENT_RESPONSE_HELPER_H_
 #define COMPONENTS_PAYMENTS_CONTENT_PAYMENT_RESPONSE_HELPER_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/address_normalizer.h"
@@ -28,6 +30,8 @@
 
     virtual void OnPaymentResponseReady(
         mojom::PaymentResponsePtr payment_response) = 0;
+
+    virtual void OnPaymentResponseError(const std::string& error_message) = 0;
   };
 
   // The spec, selected_instrument and delegate cannot be null.
@@ -44,7 +48,7 @@
   void OnInstrumentDetailsReady(
       const std::string& method_name,
       const std::string& stringified_details) override;
-  void OnInstrumentDetailsError() override {}
+  void OnInstrumentDetailsError(const std::string& error_message) override;
 
   mojom::PayerDetailPtr GeneratePayerDetail(
       const autofill::AutofillProfile* selected_contact_profile) const;
diff --git a/components/payments/content/payment_response_helper_unittest.cc b/components/payments/content/payment_response_helper_unittest.cc
index 7a462a7..04530c9 100644
--- a/components/payments/content/payment_response_helper_unittest.cc
+++ b/components/payments/content/payment_response_helper_unittest.cc
@@ -47,6 +47,9 @@
     payment_response_ = std::move(response);
   }
 
+  // PaymentRequestState::Delegate:
+  void OnPaymentResponseError(const std::string& error_message) override {}
+
   // Convenience method to create a PaymentRequestSpec with specified |details|
   // and |method_data|.
   void RecreateSpecWithOptionsAndDetails(
diff --git a/components/payments/content/service_worker_payment_instrument.cc b/components/payments/content/service_worker_payment_instrument.cc
index 0658c8c..d94620d 100644
--- a/components/payments/content/service_worker_payment_instrument.cc
+++ b/components/payments/content/service_worker_payment_instrument.cc
@@ -10,6 +10,7 @@
 #include "base/bind_helpers.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/payments/content/payment_event_response_util.h"
 #include "components/payments/content/payment_request_converter.h"
 #include "components/payments/core/payment_request_delegate.h"
 #include "content/public/browser/browser_context.h"
@@ -291,11 +292,23 @@
 
 void ServiceWorkerPaymentInstrument::OnPaymentAppInvoked(
     mojom::PaymentHandlerResponsePtr response) {
-  if (delegate_ != nullptr) {
+  if (!delegate_)
+    return;
+
+  if (response->response_type ==
+      mojom::PaymentEventResponseType::PAYMENT_EVENT_SUCCESS) {
+    DCHECK(!response->method_name.empty());
+    DCHECK(!response->stringified_details.empty());
     delegate_->OnInstrumentDetailsReady(response->method_name,
                                         response->stringified_details);
-    delegate_ = nullptr;
+  } else {
+    DCHECK(response->method_name.empty());
+    DCHECK(response->stringified_details.empty());
+    delegate_->OnInstrumentDetailsError(std::string(
+        ConvertPaymentEventResponseTypeToErrorString(response->response_type)));
   }
+
+  delegate_ = nullptr;
 }
 
 bool ServiceWorkerPaymentInstrument::IsCompleteForPayment() const {
diff --git a/components/payments/content/test_content_payment_request_delegate.cc b/components/payments/content/test_content_payment_request_delegate.cc
index 88569f2..343f29e 100644
--- a/components/payments/content/test_content_payment_request_delegate.cc
+++ b/components/payments/content/test_content_payment_request_delegate.cc
@@ -49,6 +49,10 @@
   return core_delegate_.IsBrowserWindowActive();
 }
 
+bool TestContentPaymentRequestDelegate::SkipUiForBasicCard() const {
+  return false;
+}
+
 autofill::PersonalDataManager*
 TestContentPaymentRequestDelegate::GetPersonalDataManager() {
   return core_delegate_.GetPersonalDataManager();
diff --git a/components/payments/content/test_content_payment_request_delegate.h b/components/payments/content/test_content_payment_request_delegate.h
index 53f47ff..f712509 100644
--- a/components/payments/content/test_content_payment_request_delegate.h
+++ b/components/payments/content/test_content_payment_request_delegate.h
@@ -31,6 +31,7 @@
   void ShowErrorMessage() override;
   void ShowProcessingSpinner() override;
   bool IsBrowserWindowActive() const override;
+  bool SkipUiForBasicCard() const override;
   autofill::PersonalDataManager* GetPersonalDataManager() override;
   const std::string& GetApplicationLocale() const override;
   bool IsIncognito() const override;
diff --git a/components/payments/core/autofill_payment_instrument_unittest.cc b/components/payments/core/autofill_payment_instrument_unittest.cc
index bb3d97f..1c4cde0 100644
--- a/components/payments/core/autofill_payment_instrument_unittest.cc
+++ b/components/payments/core/autofill_payment_instrument_unittest.cc
@@ -42,7 +42,7 @@
     on_instrument_details_ready_called_ = true;
   }
 
-  void OnInstrumentDetailsError() override {
+  void OnInstrumentDetailsError(const std::string& error_message) override {
     on_instrument_details_error_called_ = true;
   }
 
diff --git a/components/payments/core/error_strings.cc b/components/payments/core/error_strings.cc
index db7444d..4c8831a 100644
--- a/components/payments/core/error_strings.cc
+++ b/components/payments/core/error_strings.cc
@@ -31,10 +31,20 @@
 const char kInvalidState[] = "Invalid state.";
 const char kMethodDataRequired[] = "Method data required.";
 const char kMethodNameRequired[] = "Method name required.";
+const char kMissingDetailsFromPaymentApp[] = "Payment app returned invalid response. Missing field \"details\".";
+const char kMissingMethodNameFromPaymentApp[] = "Payment app returned invalid response. Missing field \"methodName\".";
 const char kMultiplePaymentMethodsNotSupportedFormat[] = "The payment methods $ are not supported.";
+const char kNoResponseToPaymentEvent[] = "Payment handler did not respond to \"paymentrequest\" event.";
 const char kNotInASecureOrigin[] = "Not in a secure origin.";
 const char kNotInitialized[] = "Not initialized.";
 const char kNotShown[] = "Not shown.";
+const char kPaymentDetailsNotObject[] = "Payment app returned invalid response. \"details\" field is not a dictionary.";
+const char kPaymentDetailsStringifyError[] = "Payment app returned invalid response. Unable to JSON-serialize \"details\".";
+const char kPaymentEventBrowserError[] = "Browser encountered an error when firing the \"paymentrequest\" event in the payment handler.";
+const char kPaymentEventInternalError[] = "Payment handler encountered an internal error when handling the \"paymentrequest\" event.";
+const char kPaymentEventRejected[] = "Payment handler rejected the promise passed into PaymentRequestEvent.respondWith(). This is how payment handlers close their own window programmatically.";
+const char kPaymentEventServiceWorkerError[] = "Payment handler failed to provide a response because either the \"paymentrequest\" event took too long or the service worker stopped for some reason or was killed before the request finished.";
+const char kPaymentEventTimeout[] = "The \"paymentrequest\" event timed out after 5 minutes.";
 const char kProhibitedOrigin[] = "Only localhost, file://, and cryptographic scheme origins allowed.";
 const char kProhibitedOriginOrInvalidSslExplanation[] = "No UI will be shown. CanMakePayment and hasEnrolledInstrument will always return false. Show will be rejected with NotSupportedError.";
 const char kSinglePaymentMethodNotSupportedFormat[] = "The payment method $ is not supported.";
diff --git a/components/payments/core/error_strings.h b/components/payments/core/error_strings.h
index ae9ef1d..1be03dd 100644
--- a/components/payments/core/error_strings.h
+++ b/components/payments/core/error_strings.h
@@ -80,11 +80,20 @@
 // Used when non-empty "supportedMethods": "" is required, but not provided.
 extern const char kMethodNameRequired[];
 
+// The payment handler responded with an empty "details" field.
+extern const char kMissingDetailsFromPaymentApp[];
+
+// The payment handler responded with an empty "methodName" field.
+extern const char kMissingMethodNameFromPaymentApp[];
+
 // The format for the message about multiple payment methods that are not
 // supported. This format should be used with base::ReplaceChars() function,
 // where "$" is the character to replace.
 extern const char kMultiplePaymentMethodsNotSupportedFormat[];
 
+// Payment handler did not respond to the "paymentrequest" event.
+extern const char kNoResponseToPaymentEvent[];
+
 // The PaymentRequest API is available only on secure origins.
 extern const char kNotInASecureOrigin[];
 
@@ -94,10 +103,37 @@
 // Used when PaymentRequest::Show() has not been called, but should have been.
 extern const char kNotShown[];
 
+// Payment handler passed a non-object field "details" in response to the
+// "paymentrequest" event.
+extern const char kPaymentDetailsNotObject[];
+
+// Payment handler passed a non-stringifiable field "details" in response to the
+// "paymentrequest" event.
+extern const char kPaymentDetailsStringifyError[];
+
+// Used when the browser failed to fire the "paymentrequest" event without any
+// actionable corrective action from the web developer.
+extern const char kPaymentEventBrowserError[];
+
+// Service worker timed out or stopped for some reason or was killed before the
+// payment handler could respond to the "paymentrequest" event.
+extern const char kPaymentEventServiceWorkerError[];
+
+// Service worker timed out while responding to "paymentrequest" event.
+extern const char kPaymentEventTimeout[];
+
+// Payment handler encountered an internal error when handling the
+// "paymentrequest" event.
+extern const char kPaymentEventInternalError[];
+
+// Payment handler rejected the promise passed into
+// PaymentRequestEvent.respondWith() method.
+extern const char kPaymentEventRejected[];
+
 // Chrome provides payment information only to a whitelist of origin types.
 extern const char kProhibitedOrigin[];
 
-// A long form explanation of Chrome's behavior in the case of kProhibitedOrigin
+// A long form explanation of Chrome"s behavior in the case of kProhibitedOrigin
 // or kInvalidSslCertificate error.
 extern const char kProhibitedOriginOrInvalidSslExplanation[];
 
diff --git a/components/payments/core/features.cc b/components/payments/core/features.cc
index d588efb..e5c53900 100644
--- a/components/payments/core/features.cc
+++ b/components/payments/core/features.cc
@@ -34,6 +34,9 @@
 const base::Feature kWebPaymentsJustInTimePaymentApp{
     "WebPaymentsJustInTimePaymentApp", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kAlwaysAllowJustInTimePaymentApp{
+    "AlwaysAllowJustInTimePaymentApp", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kWebPaymentsPerMethodCanMakePaymentQuota{
     "WebPaymentsPerMethodCanMakePaymentQuota",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/payments/core/features.h b/components/payments/core/features.h
index f728663..f760f10 100644
--- a/components/payments/core/features.h
+++ b/components/payments/core/features.h
@@ -38,6 +38,10 @@
 // Used to control whether allow crawling just-in-time installable payment app.
 extern const base::Feature kWebPaymentsJustInTimePaymentApp;
 
+// Used to enable crawling just-in-time installable payment apps even if
+// basic-card is also requested.
+extern const base::Feature kAlwaysAllowJustInTimePaymentApp;
+
 // Used to control whether canMakePayment() quota is per-method.
 extern const base::Feature kWebPaymentsPerMethodCanMakePaymentQuota;
 
diff --git a/components/payments/core/payment_instrument.h b/components/payments/core/payment_instrument.h
index 7be1dd2..e6247662 100644
--- a/components/payments/core/payment_instrument.h
+++ b/components/payments/core/payment_instrument.h
@@ -33,7 +33,9 @@
         const std::string& method_name,
         const std::string& stringified_details) = 0;
 
-    virtual void OnInstrumentDetailsError() = 0;
+    // Should be called with a developer-facing error message to be used when
+    // rejecting PaymentRequest.show().
+    virtual void OnInstrumentDetailsError(const std::string& error_message) = 0;
   };
 
   virtual ~PaymentInstrument();
diff --git a/components/payments/core/payment_manifest_downloader.cc b/components/payments/core/payment_manifest_downloader.cc
index 5d398cba..814db83 100644
--- a/components/payments/core/payment_manifest_downloader.cc
+++ b/components/payments/core/payment_manifest_downloader.cc
@@ -158,6 +158,10 @@
                    std::move(callback));
 }
 
+GURL PaymentManifestDownloader::FindTestServerURL(const GURL& url) const {
+  return url;
+}
+
 PaymentManifestDownloader::Download::Download() {}
 
 PaymentManifestDownloader::Download::~Download() {}
diff --git a/components/payments/core/payment_manifest_downloader.h b/components/payments/core/payment_manifest_downloader.h
index 8baf5d6..de5a4605 100644
--- a/components/payments/core/payment_manifest_downloader.h
+++ b/components/payments/core/payment_manifest_downloader.h
@@ -101,6 +101,10 @@
   void DownloadWebAppManifest(const GURL& url,
                               PaymentManifestDownloadCallback callback);
 
+  // Overridden in TestDownloader to convert |url| to a test server URL. The
+  // default implementation here simply returns |url|.
+  virtual GURL FindTestServerURL(const GURL& url) const;
+
  private:
   friend class PaymentMethodManifestDownloaderTest;
   friend class TestDownloader;
diff --git a/components/payments/core/test_payment_manifest_downloader.h b/components/payments/core/test_payment_manifest_downloader.h
index 7c1a51a..220d737 100644
--- a/components/payments/core/test_payment_manifest_downloader.h
+++ b/components/payments/core/test_payment_manifest_downloader.h
@@ -87,6 +87,12 @@
   // AddTestServerURL("x");AddTestServerURL("xy"); is not.
   void AddTestServerURL(const std::string& prefix, const GURL& test_server_url);
 
+  // PaymentManifestDownloader:
+  //
+  // The reverse operation as AddTestServerURL: converts |url| back to a test
+  // server URL so it can be fetched as a normal resource outside of this class.
+  GURL FindTestServerURL(const GURL& url) const override;
+
  private:
   // PaymentManifestDownloader implementation.
   void InitiateDownload(const GURL& url,
@@ -94,8 +100,6 @@
                         int allowed_number_of_redirects,
                         PaymentManifestDownloadCallback callback) override;
 
-  GURL FindTestServerURL(const GURL& url) const;
-
   // The mapping from the URL prefix to the URL of the test server to be used.
   // Example 1:
   //
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 4fd6ac4..eda7236 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -661,10 +661,9 @@
   // See http://go/chrome-nac-server-design for more information.
   optional bytes new_public_key_verification_data_signature = 9;
 
-  // See PolicyFetchRequest.policy_type. This field is NOT signed by DMServer
-  // and is informational only. Client-side should verify and rely on the
-  // policy_type inside the signed policy_data.
-  optional string policy_type = 10;
+  // DEPRECATED! Client-side should verify and rely on the policy_type inside
+  // the signed policy_data.
+  optional string policy_type = 10 [deprecated = true];
 }
 
 // DEPRECATED: Protobuf used to generate the deprecated
@@ -1063,6 +1062,7 @@
 }
 
 // Chrome user profile level status.
+// Deprecated : Use ChromeUserProfileInfo instead.
 message ChromeUserProfileReport {
   // A string to uniquely identify this profile within the browser.
   optional string id = 1;
@@ -1112,6 +1112,14 @@
 }
 
 // Extension information.
+message ExtensionRequest {
+  // ID of the installed app/extension for a Chrome app or extension.
+  optional string id = 1;
+  // User generated justification for why the extension should be installed.
+  optional string justification = 2;
+}
+
+// Extension information.
 message Extension {
   // ID of the installed app/extension for a Chrome app or extension.
   optional string id = 1;
@@ -1286,6 +1294,9 @@
   // A list of extensions installed in the browser.
   repeated Extension extensions = 5;
 
+  // A list of extensions requested for installation.
+  repeated ExtensionRequest extension_requests = 10;
+
   // A list of plugins installed in the browser.
   repeated Plugin plugins = 6;
 
@@ -1316,14 +1327,13 @@
   optional string executable_path = 3;
 
   // Profile specific reports, one per profile.
-  // This field is replaced by ChromeUserProfileInfo and only used by old Chrome
-  // browser.
+  // Deprecated by ChromeUserProfileInfo and only used by old Chrome browser.
   repeated ChromeUserProfileReport chrome_user_profile_reports = 4;
 
   // A list of all Profiles that is created in the currently browser instance.
   // Only activated Profiles is able to upload full details while the idle ones
   // contain id and name only. Please note that some activated Profiles may not
-  // upload full details like idle one due to the limitatin of report size.
+  // upload full details like idle one due to the limitation of report size.
   // These details will be uploaded in the following reports.
   repeated ChromeUserProfileInfo profile_info_list = 6;
 }
diff --git a/components/policy/resources/policy_templates_ar.xtb b/components/policy/resources/policy_templates_ar.xtb
index 4644656..b2ec60a 100644
--- a/components/policy/resources/policy_templates_ar.xtb
+++ b/components/policy/resources/policy_templates_ar.xtb
@@ -2443,7 +2443,7 @@
       وتجدر الإشارة إلى أن هذا الأمر غير موصى به، نظرًا لأنه قد يسمح بتجاوز الإضافة nameConstraints التي تقيد أسماء المضيف والتي يمكن اعتماد شهادة مقدمة من خلالها.
 
       في حالة عدم تعيين هذه السياسة، أو في حالة تعيينها على "false"، فإن شهادات الخادم التي لا تتضمن الإضافة subjectAlternativeName، وتتضمن إما اسمًا لنظام أسماء النطاقات أو عنوان IP، لن تكون موثوقًا بها.</translation>
-<translation id="5578571772998293651">‏تسمح السياسة بتعليقات المستخدم.
+<translation id="5578571772998293651">‏تسمح السياسة بتعليقات المستخدمين.
         وفي حال ضبط السياسة على القيمة "false"، لا يمكن للمستخدمين إرسال تعليقات إلى Google.
 
         وفي حال إلغاء ضبط السياسة أو ضبطها على القيمة "true"، يمكن للمستخدمين إرسال تعليقات إلى Google من خلال "القائمة"-&gt;"المساعدة"-&gt;"الإبلاغ عن مشكلة" أو من خلال مجموعة مفاتيح.</translation>
@@ -3778,7 +3778,7 @@
       في حالة ترك هذه السياسة بدون تعيين، لن يكون هناك أي استثناءات لقائمة العناوين المحظورة من سياسة 'URLBlacklist'.</translation>
 <translation id="8176035528522326671">السماح لمستخدم المؤسسة بأن يكون مستخدمًا أساسيًا لملفات شخصية متعددة (السلوك التلقائي للمستخدمين تحت إدارة المؤسسات)</translation>
 <translation id="8183108371184777472">إيقاف تشغيل نافذة المتصفح</translation>
-<translation id="8186911565834244165">السماح بتعليقات المستخدم</translation>
+<translation id="8186911565834244165">السماح بتعليقات المستخدمين</translation>
 <translation id="8214600119442850823">توفر تهيئة مدير كلمات المرور.</translation>
 <translation id="8217516105848565518">‏تم إيقاف هذه السياسة. يُرجى استخدام RemoteAccessHostDomainList بدلاً منها.</translation>
 <translation id="8244171102276095471">‏تفعيل مجموعات تشفير RC4 في طبقة النقل الآمنة</translation>
@@ -4208,11 +4208,11 @@
 <translation id="9112897538922695510">‏يسمح بتسجيل قائمة بمعالجات البروتوكولات. وهذا ليس إلا سياسة موصى بها. يجب تعيين |protocol| التابع للبرنامج لمخطط مثل: "mailto" وتعيين |url| التابع للبرنامج لنمط عنوان URL للتطبيق الذي يعالج المخطط. يمكن أن يتضمن النمط "%s"، والذي إن وجد يتم استبداله بعنوان URL الذي تمت معالجته.
 
           يتم دمج معالجات البروتوكولات المسجلة من قبل السياسة مع المعالجات المسجلة من قبل المستخدم، وكلاهما متاح للاستخدام. يمكن للمستخدم استبعاد معالجات البروتوكولات المثبتة من قبل السياسة عن طريق تثبيت معالج تلقائي جديد، ولكن لا يمكنه إزالة معالج بروتوكول مسجل من قبل السياسة.</translation>
-<translation id="91196902572559194">‏تمنح هذه السياسة "مساعد Google" الإذن بالاستماع إلى عبارة تفعيل الصوت.
+<translation id="91196902572559194">‏تمنح هذه السياسة "مساعد Google" الإذن بالاستماع إلى عبارة التفعيل الصوتي.
 
-      في حال تفعيل السياسة، سيستمع "مساعد Google" إلى عبارة تفعيل الصوت.
-      في حال إيقاف السياسة، لن يستمع "مساعد Google" إلى عبارة تفعيل الصوت.
-      في حال عدم ضبط السياسة، يمكن للمستخدمين اختيار ما إذا كان يتم السماح "لمساعد Google" بالاستماع إلى عبارة تفعيل الصوت أو لا.
+      في حال تفعيل السياسة، سيستمع "مساعد Google" إلى عبارة التفعيل الصوتي.
+      في حال إيقاف السياسة، لن يستمع "مساعد Google" إلى عبارة التفعيل الصوتي.
+      في حال عدم ضبط السياسة، يعود للمستخدمين قرار السماح لـ "مساعد Google" بالاستماع إلى عبارة التفعيل الصوتي.
       </translation>
 <translation id="9123211093995421438">تعيّن هذه السياسة الحد الأدنى لعدد إصدارات <ph name="PRODUCT_OS_NAME" /> المسموح بالعودة إليها في أي وقت، بدءًا من الإصدار الثابت.
 
diff --git a/components/policy/resources/policy_templates_bn.xtb b/components/policy/resources/policy_templates_bn.xtb
index 9ad3526..14ee89c 100644
--- a/components/policy/resources/policy_templates_bn.xtb
+++ b/components/policy/resources/policy_templates_bn.xtb
@@ -2526,7 +2526,7 @@
 <translation id="5578571772998293651">ব্যবহারকারীকে মতামত জানাতে দেয়।
         এই নীতি ফলস হিসেবে সেট করা থাকলে, ব্যবহারকারী Google-এ মতামত পাঠাতে পারেন না।
 
-        এই নীতি ট্রু হিসেবে সেট করা থাকলে বা সেট করা না থাকলে, ব্যবহারকারী মেনু-&gt;সহায়তা-&gt;সমস্যার বিষয়ে অভিযোগ জানান বিকল্পের মাধ্যমে অথবা কীয়ের সমন্বয়ের সাহায্যে Google-এ মতামত পাঠাতে পারেন।</translation>
+        এই নীতি ট্রু হিসেবে সেট করা থাকলে বা সেট করা না থাকলে, ব্যবহারকারী মেনু-&gt;সহায়তা-&gt;সমস্যার বিষয়ে অভিযোগ জানান বিকল্পের মাধ্যমে অথবা নির্দিষ্ট কীবোর্ড শর্টকাট ব্যবহার করে Google-এ মতামত পাঠাতে পারেন।</translation>
 <translation id="5581292529942108810">Chrome রিপোর্টিং এক্সটেনশন সম্পর্কিত নীতি কনফিগার করুন।
 
       <ph name="CHROME_REPORTING_EXTENSION_NAME" /> চালু করা থাকলে এবং <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" />-এর সাথে মেশিনটি নথিভুক্ত থাকলেই এই নীতিটি কার্যকর হয়।</translation>
diff --git a/components/policy/resources/policy_templates_ca.xtb b/components/policy/resources/policy_templates_ca.xtb
index d731009..aab2981 100644
--- a/components/policy/resources/policy_templates_ca.xtb
+++ b/components/policy/resources/policy_templates_ca.xtb
@@ -3842,7 +3842,7 @@
 
       Si la política s'activa, l'Assistent de Google escoltarà la frase d'activació per veu.
       Si la política es desactiva, l'Assistent de Google no escoltarà la frase d'activació per veu.
-      Si la política no s'estableix, els usuaris podran decidir si volen permetre que l'Assistent de Google escolti la frase d'activació per veu.
+      Si la política no s'estableix, els usuaris podran decidir si permeten que l'Assistent de Google escolti la frase d'activació per veu.
       </translation>
 <translation id="9123211093995421438">Especifica el nombre mínim de fites del sistema operatiu <ph name="PRODUCT_OS_NAME" /> que es permeten per a la reversió feta a partir de la versió estable en qualsevol moment.
 
diff --git a/components/policy/resources/policy_templates_de.xtb b/components/policy/resources/policy_templates_de.xtb
index 9b231e88..1ffc502 100644
--- a/components/policy/resources/policy_templates_de.xtb
+++ b/components/policy/resources/policy_templates_de.xtb
@@ -122,7 +122,7 @@
       Der Wert für die Richtlinie sollte in Millisekunden angegeben werden.</translation>
 <translation id="1141767714195601945">Mit dieser Richtlinie werden Befehlszeilenparameter für Chrome vom Internet Explorer festgelegt.
 
-      Wenn das Add-in "Unterstützung für ältere Browser" für Internet Explorer nicht installiert ist, hat die Richtlinie keine Auswirkung.
+      Wenn das Add-in "Unterstützung älterer Browser" für Internet Explorer nicht installiert ist, hat die Richtlinie keine Auswirkung.
 
       Wenn diese Richtlinie nicht konfiguriert ist, gibt Internet Explorer die URL nur als Befehlszeilenparameter an Chrome weiter.
 
@@ -1617,7 +1617,7 @@
       Wird "false" festgelegt oder die Richtlinie nicht konfiguriert, werden keine
       Pakete gesendet.</translation>
 <translation id="3950239119790560549">Zeitbeschränkungen aktualisieren</translation>
-<translation id="3956686688560604829">Richtlinie "SiteList" von Internet Explorer zur Unterstützung für ältere Browser verwenden.</translation>
+<translation id="3956686688560604829">Richtlinie "SiteList" von Internet Explorer zur Unterstützung älterer Browser verwenden.</translation>
 <translation id="3958586912393694012">Verwendung von Smart Lock erlauben</translation>
 <translation id="3963602271515417124">Bei Festlegung auf "true" ist die Remote-Bestätigung für das Gerät erlaubt. Ein Zertifikat wird automatisch erstellt und auf den Device Management Server hochgeladen.
 
@@ -1630,7 +1630,7 @@
 <translation id="3965339130942650562">Zeitlimit bis zur Abmeldung eines inaktiven Nutzers</translation>
 <translation id="3973371701361892765">Ablage nie automatisch ausblenden</translation>
 <translation id="3984028218719007910">Legt fest, ob lokale Kontodaten nach der Abmeldung in <ph name="PRODUCT_OS_NAME" /> gespeichert werden. Bei Einstellung auf "true" werden Konten nicht dauerhaft in <ph name="PRODUCT_OS_NAME" /> gespeichert und alle Daten der Nutzersitzung werden nach der Abmeldung verworfen. Ist die Richtlinie auf "false" gesetzt oder nicht konfiguriert, können (verschlüsselte) lokale Nutzerdaten auf dem Gerät verbleiben.</translation>
-<translation id="398475542699441679">Mit dieser Richtlinie wird festgelegt, ob die Unterstützung für ältere Browser aktiviert wird.
+<translation id="398475542699441679">Mit dieser Richtlinie wird festgelegt, ob die Unterstützung älterer Browser aktiviert wird.
 
       Wenn die Richtlinie nicht konfiguriert oder auf "false" gesetzt ist, versucht Chrome nicht, bestimmte URLs in einem alternativen Browser zu starten.
 
@@ -2471,7 +2471,7 @@
 <translation id="5578571772998293651">Hiermit wird Nutzerfeedback zugelassen.
         Wenn die Richtlinie auf "false" gesetzt ist, können Nutzer kein Feedback an Google senden.
 
-        Ist sie nicht konfiguriert oder aber auf "true" gesetzt, dann können Nutzer über das Menü &gt; "Hilfe" &gt; "Problem melden" Feedback an Google senden.</translation>
+        Ist sie nicht konfiguriert oder auf "true" gesetzt, dann können Nutzer über das Menü &gt; "Hilfe" &gt; "Problem melden" oder die entsprechende Tastenkombination Feedback an Google senden.</translation>
 <translation id="5581292529942108810">Richtlinien für die Chrome Reporting Extension konfigurieren.
 
 Die Richtlinie wird nur angewendet, wenn die <ph name="CHROME_REPORTING_EXTENSION_NAME" /> aktiviert und das Gerät mit <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" /> registriert ist.</translation>
@@ -4209,7 +4209,7 @@
 <translation id="9112897538922695510">Damit können Sie eine Liste mit Protokoll-Handlern registrieren, wobei dies lediglich eine empfohlene Richtlinie sein kann. Für die Eigenschaft |protocol| sollte ein Schema wie "mailto" und für die Eigenschaft |url| das URL-Muster der Anwendung festgelegt werden, die das Schema verwaltet. Das Muster kann "%s" enthalten, was später durch die jeweilige URL ersetzt wird.
 
           Die von der Richtlinie registrierten Protokoll-Handler werden mit den Protokoll-Handlern zusammengeführt, die vom Nutzer registriert wurden, und können gemeinsam verwendet werden. Der Nutzer kann die durch die Richtlinie installierten Protokoll-Handler durch Installieren eines neuen standardmäßigen Handlers überschreiben. Ein von der Richtlinie registrierter Protokoll-Handler kann jedoch nicht entfernt werden.</translation>
-<translation id="91196902572559194">Mit dieser Richtlinie wird Google Assistant die Berechtigung erteilt, bei Gesprächen zuzuhören, um zu erkennen, ob die Wortgruppe für die Sprachaktivierung genannt wird.
+<translation id="91196902572559194">Mit dieser Richtlinie erhält Google Assistant die Berechtigung, bei Gesprächen zuzuhören und so zu prüfen, ob die Wortgruppe für die Sprachaktivierung genannt wird.
 
       Wenn die Richtlinie aktiviert ist, hört Google Assistant bei Gesprächen zu, um zu erkennen, ob die Wortgruppe für die Sprachaktivierung genannt wird.
       Wenn die Richtlinie deaktiviert ist, hört Google Assistant nicht bei Gesprächen zu, um zu erkennen, ob die Wortgruppe für die Sprachaktivierung genannt wird.
@@ -4268,7 +4268,7 @@
 <translation id="924557436754151212">Gespeicherte Passwörter bei erster Ausführung aus Standardbrowser importieren</translation>
 <translation id="926146562923985266">Mit dieser Richtlinie wird der Befehl konfiguriert, mit dem URLs in <ph name="PRODUCT_NAME" /> geöffnet werden, wenn von Internet Explorer dorthin gewechselt wird.
 
-      Wenn das Add-in "Unterstützung für ältere Browser" für Internet Explorer nicht installiert ist, hat die Richtlinie keinerlei Wirkung.
+      Wenn das Add-in "Unterstützung älterer Browser" für Internet Explorer nicht installiert ist, hat die Richtlinie keinerlei Wirkung.
 
       Wenn diese Richtlinie nicht konfiguriert ist, erkennt Internet Explorer den ausführbaren Pfad von <ph name="PRODUCT_NAME" /> automatisch, wenn <ph name="PRODUCT_NAME" /> über Internet Explorer geöffnet wird.
 
diff --git a/components/policy/resources/policy_templates_fi.xtb b/components/policy/resources/policy_templates_fi.xtb
index 51f8bab4..34e508c 100644
--- a/components/policy/resources/policy_templates_fi.xtb
+++ b/components/policy/resources/policy_templates_fi.xtb
@@ -2486,7 +2486,7 @@
 <translation id="5578571772998293651">Salli käyttäjäpalaute.
         Jos käytännön arvo on epätosi, käyttäjät eivät voi lähettää palautetta Googlelle.
 
-        Jos käytännölle ei ole määritetty arvoa tai sen arvo on tosi, käyttäjät voivat lähettää Googlelle palautetta valitsemalla Valikko-&gt;Ohje-&gt;Ilmoita ongelmasta tai näppäinyhdistelmällä.</translation>
+        Jos käytännölle ei ole määritetty arvoa tai sen arvo on tosi, käyttäjät voivat lähettää Googlelle palautetta valitsemalla Valikko &gt; Ohje &gt; Ilmoita ongelmasta tai näppäinyhdistelmällä.</translation>
 <translation id="5581292529942108810">Määritä Chromen ilmoituslaajennukseen liittyvät käytännöt.
 
       Käytäntö on voimassa vain, kun <ph name="CHROME_REPORTING_EXTENSION_NAME" /> on käytössä ja koneella on käytössä <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" />.</translation>
diff --git a/components/policy/resources/policy_templates_fr.xtb b/components/policy/resources/policy_templates_fr.xtb
index e0367acf..505d487 100644
--- a/components/policy/resources/policy_templates_fr.xtb
+++ b/components/policy/resources/policy_templates_fr.xtb
@@ -4358,9 +4358,9 @@
           Les gestionnaires de protocoles inscrits via la règle sont fusionnés avec ceux inscrits par l'utilisateur, et tous sont disponibles. L'utilisateur peut remplacer les gestionnaires de protocoles installés via la règle en installant un nouveau gestionnaire par défaut, mais il ne peut pas supprimer un gestionnaire de protocoles inscrit via la règle.</translation>
 <translation id="91196902572559194">Cette règle autorise l'Assistant Google à écouter l'expression servant à activer les commandes vocales.
 
-      Si cette règle est activée, l'Assistant Google écoute l'expression d'activation des commandes vocales.
+      Si cette règle est activée, l'Assistant Google écoute l'expression servant à activer les commandes vocales.
       Si elle est désactivée, l'Assistant Google n'écoute pas cette expression.
-      Si cette règle n'est pas définie, les utilisateurs peuvent décider d'autoriser ou non l'Assistant Google à écouter l'expression d'activation des commandes vocales.
+      Si cette règle n'est pas définie, les utilisateurs peuvent décider d'autoriser ou non l'Assistant Google à écouter l'expression servant à activer les commandes vocales.
       </translation>
 <translation id="9123211093995421438">Spécifie le nombre minimal de versions intermédiaires <ph name="PRODUCT_OS_NAME" /> autorisées pour le rollback à partir de la version stable.
 
diff --git a/components/policy/resources/policy_templates_hi.xtb b/components/policy/resources/policy_templates_hi.xtb
index 062895d..03c69e3 100644
--- a/components/policy/resources/policy_templates_hi.xtb
+++ b/components/policy/resources/policy_templates_hi.xtb
@@ -2544,10 +2544,10 @@
       ध्यान रखें कि इसका सुझाव नहीं दिया जाता, क्योंकि इससे ऐसे nameConstraints एक्सटेंशन को बायपास करने की अनुमति मिल सकती है जो ऐसे होस्टनामों पर रोक लगाता है, जिसके लिए किसी प्रमाणपत्र को मंज़ूरी दी जा सकती है.
 
       अगर इस नीति को सेट नहीं किया जाता है या गलत पर सेट किया जाता है तो, जिन सर्वर प्रमाणपत्रों में किसी डीएनएस नाम या आईपी पते वाला subjectAlternativeName एक्सटेंशन मौजूद नहीं है, उन पर भरोसा नहीं किया जाएगा.</translation>
-<translation id="5578571772998293651">उपयोगकर्ता को शिकायत भेजने की अनुमति दें.
-        अगर नीति को 'गलत' पर सेट किया जाता है, तो इस्तेमाल करने वाले लोग Google को शिकायत नहीं भेज पाएंगे.
+<translation id="5578571772998293651">उपयोगकर्ता को राय भेजने की अनुमति दें.
+        अगर नीति को 'गलत' पर सेट किया जाता है, तो इस्तेमाल करने वाले लोग Google को राय नहीं भेज पाएंगे.
 
-        अगर नीति को सेट नहीं किया जाता या 'सही' पर सेट किया जाता है, तो इस्तेमाल करने वाले लोग Google को सुझाव भेज पाएंगे. इसके लिए उन्हें मेन्यू-&gt;सहायता-&gt;'गड़बड़ी या कुंजी संयोजन की शिकायत करें' में जाना होगा.</translation>
+        अगर नीति को सेट नहीं किया जाता या 'सही' पर सेट किया जाता है, तो इस्तेमाल करने वाले लोग Google को राय भेज पाएंगे. इसके लिए उन्हें मेन्यू-&gt;सहायता-&gt;'गड़बड़ी या कुंजी संयोजन की शिकायत करें' में जाना होगा.</translation>
 <translation id="5581292529942108810">Chrome रिपोर्टिंग एक्सटेंशन से जुड़ी नीतियां कॉन्फ़िगर करें.
 
       यह नीति सिर्फ़ तब प्रभावी होती है जब <ph name="CHROME_REPORTING_EXTENSION_NAME" /> चालू हो और मशीन का नाम <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" /> के ज़रिए दर्ज कराया गया हो.</translation>
@@ -3920,7 +3920,7 @@
       अगर यह नीति सेट नहीं है, तो 'URLBlacklist' नीति से पाबंदी वाली सूची में डालने के लिए कोई अपवाद नहीं होंगे.</translation>
 <translation id="8176035528522326671">'एंटरप्राइज़ उपयोगकर्ता' को सिर्फ़ 'एक से ज़्यादा प्रोफ़ाइल' का प्राथमिक उपयोगकर्ता बनने की अनुमति दें (एंटरप्राइज़-प्रबंधित उपयोगकर्ताओं के लिए डिफ़ॉल्ट व्यवहार)</translation>
 <translation id="8183108371184777472">ब्राउज़र विंडो के लॉन्च होने की प्रक्रिया को छिपाना</translation>
-<translation id="8186911565834244165">उपयोगकर्ता को शिकायत भेजने की अनुमति दें</translation>
+<translation id="8186911565834244165">उपयोगकर्ता को राय भेजने की अनुमति दें</translation>
 <translation id="8214600119442850823">पासवर्ड प्रबंधक को कॉन्फ़िगर करती है.</translation>
 <translation id="8217516105848565518">इस नीति का समर्थन रोक दिया गया है. इसके बजाय, कृपया RemoteAccessHostDomainList का इस्तेमाल करें.</translation>
 <translation id="8244171102276095471">TLS में RC4 सिफ़र सुइट चालू करें</translation>
@@ -4344,11 +4344,11 @@
 <translation id="9112897538922695510">आपको प्रोटोकॉल प्रबंधकों की सूची रजिस्टर कराने देती है. यह सिर्फ़ एक सुझाई गई नीति हो सकती है. |protocol| गुण को केवल 'mailto' जैसी स्कीम पर और |url| गुण को स्कीन का प्रबंधन करने वाले ऐप्लिकेशन के URL प्रतिमान पर ही सेट किया जा सकता है. प्रतिमान में '%s' शामिल हो सकता है, जिसके मौजूद होने पर उसे प्रबंधित URL के ज़रिए बदल दिया जाएगा.
 
           नीति के रजिस्टर किए गए प्रोटोकॉल प्रबंधकों को उपयोगकर्ता के रजिस्टर कराए गए प्रबंधकों के साथ मिला दिया जाता है और दोनों ही इस्तेमाल के लिए उपलब्ध रहते हैं. उपयोगकर्ता, नए डिफ़ॉल्ट प्रबंधक को इंस्टॉल करके नीति के इंस्टॉल किए गए प्रोटोकॉल प्रबंधकों को ओवरराइड कर सकता है, लेकिन नीति के रजिस्टर कराए गए प्रोटोकॉल प्रबंधक को निकाल नहीं सकता.</translation>
-<translation id="91196902572559194">यह नीति Google Assistant को अनुमति देती है कि वह बोले गए पासवर्ड को सुनकर चालू हो सके.
+<translation id="91196902572559194">यह नीति Google Assistant को बोला गया पासवर्ड सुनकर चालू होने की अनुमति देती है.
 
-      अगर यह नीति लागू की जाती है, तो Google Assistant, बोला गया पासवर्ड सुनकर चालू होगी.
-      अगर यह नीति लागू नहीं की जाती, तो Google Assistant, बोला गया पासवर्ड सुनकर चालू नहीं होगी.
-      अगर नीति सेट नहीं की जाती, तो इस्तेमाल करने वाले लोग यह तय कर सकते हैं कि Google Assistant, बोला गया पासवर्ड सुनकर चालू हो या नहीं.
+      अगर यह नीति लागू होती, तो Google Assistant बोला गया पासवर्ड सुनकर चालू होगी.
+      अगर यह नीति लागू नहीं होती, तो Google Assistant बोला गया पासवर्ड सुनकर चालू नहीं होगी.
+      अगर नीति सेट नहीं की जाती, तो इस्तेमाल करने वाले लोग यह तय कर सकते हैं कि Google Assistant बोला गया पासवर्ड सुनकर चालू हो या नहीं.
       </translation>
 <translation id="9123211093995421438">यह <ph name="PRODUCT_OS_NAME" /> उपलब्धियों की ऐसी कम से कम संख्या तय करती है जिसके लिए किसी भी समय स्थिर वर्शन से रोलबैक शुरू करने की मंज़ूरी दी जानी चाहिए.
 
diff --git a/components/policy/resources/policy_templates_id.xtb b/components/policy/resources/policy_templates_id.xtb
index 7fdd114..52a7619 100644
--- a/components/policy/resources/policy_templates_id.xtb
+++ b/components/policy/resources/policy_templates_id.xtb
@@ -1556,7 +1556,7 @@
       Setiap entri cantuman berisi kamus yang harus menyertakan ID ekstensi dalam bidang 'extension-id', dan URL pembaruannya dalam bidang 'update-url'.</translation>
 <translation id="3874773863217952418">Aktifkan Tap untuk Menelusuri</translation>
 <translation id="3877517141460819966">Mode autentikasi faktor kedua yang terintegrasi</translation>
-<translation id="3879208481373875102">Konfigurasikan daftar Aplikasi Web yang diinstal otomatis</translation>
+<translation id="3879208481373875102">Konfigurasi daftar Aplikasi Web yang diinstal otomatis</translation>
 <translation id="388237772682176890">Kebijakan ini sudah tidak digunakan di M53 dan dihapus di M54, karena dukungan SPDY/3.1 telah dihapus.
 
       Menonaktifkan penggunaan protokol SPDY di <ph name="PRODUCT_NAME" />.
@@ -2456,9 +2456,9 @@
 
       Jika kebijakan ini tidak disetel, atau disetel ke false, sertifikat server yang tidak menyertakan ekstensi subjectAlternativeName yang berisi nama DNS atau alamat IP tidak akan dipercaya.</translation>
 <translation id="5578571772998293651">Izinkan masukan pengguna.
-        Kalau kebijakan disetel ke false, pengguna tidak bisa mengirim masukan ke Google.
+        Jika kebijakan disetel ke false, pengguna tidak dapat mengirim masukan ke Google.
 
-        Kalau kebijakan tidak disetel atau disetel ke true, pengguna bisa mengirim masukan ke Google melalui Menu-&gt;Bantuan-&gt;Laporkan Masalah atau kombinasi kunci.</translation>
+        Jika kebijakan tidak disetel atau disetel ke true, pengguna dapat mengirim masukan ke Google melalui Menu-&gt;Bantuan-&gt;Laporkan Masalah atau kombinasi kunci.</translation>
 <translation id="5581292529942108810">Konfigurasi kebijakan terkait Ekstensi Pelaporan Chrome.
 
       Kebijakan ini hanya berlaku jika <ph name="CHROME_REPORTING_EXTENSION_NAME" /> diaktifkan, dan mesin didaftarkan ke <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" />.</translation>
@@ -4186,9 +4186,9 @@
           Penangan protokol yang didaftarkan oleh kebijakan digabung dengan penangan protokol yang didaftarkan oleh pengguna dan keduanya tersedia untuk digunakan. Pengguna dapat mengganti penangan protokol yang dipasang oleh kebijakan dengan memasang penangan default baru, namun pengguna tidak dapat membuang penangan protokol yang didaftarkan oleh kebijakan.</translation>
 <translation id="91196902572559194">Kebijakan ini memberikan izin kepada Asisten Google untuk mendengarkan frasa aktivasi suara.
 
-      Kalau kebijakan ini diaktifkan, Asisten Google akan mendengarkan frasa aktivasi suara.
-      Kalau kebijakan ini dinonaktifkan, Asisten Google tidak akan mendengarkan frasa aktivasi suara.
-      Kalau kebijakan tidak disetel, pengguna bisa memutuskan apakah akan mengizinkan Asisten Google mendengarkan frasa aktivasi suara.
+      Jika kebijakan ini diaktifkan, Asisten Google akan mendengarkan frasa aktivasi suara.
+      Jika kebijakan ini dinonaktifkan, Asisten Google tidak akan mendengarkan frasa aktivasi suara.
+      Jika kebijakan tidak disetel, pengguna dapat memutuskan apakah akan mengizinkan Asisten Google mendengarkan frasa aktivasi suara.
       </translation>
 <translation id="9123211093995421438">Penerapan jumlah minimum rollback milestone <ph name="PRODUCT_OS_NAME" /> akan diizinkan mulai versi yang stabil kapan saja.
 
diff --git a/components/policy/resources/policy_templates_ml.xtb b/components/policy/resources/policy_templates_ml.xtb
index 9ed2e18..8a2b51d 100644
--- a/components/policy/resources/policy_templates_ml.xtb
+++ b/components/policy/resources/policy_templates_ml.xtb
@@ -2496,7 +2496,7 @@
 <translation id="5578571772998293651">ഉപയോക്തൃ ഫീഡ്‌ബാക്ക് അനുവദിക്കുക.
         നയം തെറ്റ് എന്ന് സജ്ജീകരിച്ചിട്ടുണ്ടെങ്കിൽ, ഉപയോക്താക്കൾക്ക് Google-ലേക്ക് ഫീഡ്ബാക്ക് അയയ്‌ക്കാനാവില്ല.
 
-        നയം സജ്ജീകരിച്ചത് മാറ്റുകയോ ശരി എന്ന് സജ്ജീകരിക്കുകയോ ചെയ്‌തിട്ടുണ്ടെങ്കിൽ, മെനു-&gt;സഹായം-&gt;പ്രശ്‌നം റിപ്പോർട്ട് ചെയ്യുക അല്ലെങ്കിൽ കീ കോമ്പിനേഷൻ എന്നതിലൂടെ ഉപയോക്താക്കൾക്ക് Google-ലേക്ക് ഫീഡ്ബാക്ക് അയയ്‌ക്കാം.</translation>
+        നയം സജ്ജീകരിക്കാതിരുന്നാലോ ശരി എന്ന് സജ്ജീകരിക്കുകയോ ചെയ്‌തിട്ടുണ്ടെങ്കിൽ, മെനു-&gt;സഹായം-&gt;പ്രശ്‌നം റിപ്പോർട്ട് ചെയ്യുക അല്ലെങ്കിൽ കീ കോമ്പിനേഷൻ എന്നതിലൂടെ ഉപയോക്താക്കൾക്ക് Google-ലേക്ക് ഫീഡ്ബാക്ക് അയയ്‌ക്കാം.</translation>
 <translation id="5581292529942108810">Chrome റിപ്പോർട്ട് ചെയ്യൽ വിപുലീകരണവുമായി ബന്ധപ്പെട്ട നയങ്ങൾ കോൺഫിഗർ ചെയ്യുക.
 
       <ph name="CHROME_REPORTING_EXTENSION_NAME" /> പ്രവർത്തനക്ഷമമാക്കുകയും മെഷീൻ <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" /> എന്നതിൽ എൻറോൾ ചെയ്‌തിരിക്കുമ്പോഴും മാത്രമേ ഈ നയം പ്രാബല്യത്തിൽ വരൂ.</translation>
diff --git a/components/policy/resources/policy_templates_nl.xtb b/components/policy/resources/policy_templates_nl.xtb
index 34dcf4a..e44d2f9 100644
--- a/components/policy/resources/policy_templates_nl.xtb
+++ b/components/policy/resources/policy_templates_nl.xtb
@@ -2533,7 +2533,7 @@
 <translation id="5578571772998293651">Sta gebruikersfeedback toe.
         Als het beleid is ingesteld op 'false', kunnen gebruikers geen feedback sturen naar Google.
 
-        Als het beleid is niet is ingesteld of is ingesteld op 'true', kunnen gebruikers feedback naar Google sturen via Menu -&gt; Help -&gt; Een probleem melden, of hier een toetsencombinatie voor gebruiken.</translation>
+        Als het beleid niet is ingesteld of is ingesteld op 'true', kunnen gebruikers feedback naar Google sturen via Menu -&gt; Hulp -&gt; Een probleem melden, of hier een toetsencombinatie voor gebruiken.</translation>
 <translation id="5581292529942108810">Aan Chrome Reporting Extension gerelateerde beleidsregels.
 
       Dit beleid is alleen effectief wanneer de <ph name="CHROME_REPORTING_EXTENSION_NAME" /> is ingeschakeld en de machine is ingeschreven met <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" />.</translation>
diff --git a/components/policy/resources/policy_templates_no.xtb b/components/policy/resources/policy_templates_no.xtb
index aa84e48..1d59475 100644
--- a/components/policy/resources/policy_templates_no.xtb
+++ b/components/policy/resources/policy_templates_no.xtb
@@ -2497,9 +2497,9 @@
 
       Hvis denne innstillingen ikke angis, eller hvis den angis som «false» (usann), klareres ikke tjenersertifikater som mangler en subjectAlternativeName-utvidelse og har enten et DNS-navn eller en IP-adresse.</translation>
 <translation id="5578571772998293651">Tillat brukertilbakemeldinger.
-        Hvis regelen er satt til «false» (usann), kan brukere ikke sende tilbakemeldinger til Google.
+        Hvis regelen er satt til «false» (usann), kan ikke brukerne sende tilbakemeldinger til Google.
 
-        Hvis regelen er satt til «true» (sann) eller ikke er angitt, kan brukere sende tilbakemeldinger til Google via Meny-&gt;Hjelp-&gt;Rapportér et problem eller en tastekombinasjon.</translation>
+        Hvis regelen er satt til «true» (sann) eller ikke er angitt, kan brukerne sende tilbakemeldinger til Google via Meny-&gt;Hjelp-&gt;Rapportér et problem eller en tastekombinasjon.</translation>
 <translation id="5581292529942108810">Konfigurer regler knyttet til Chrome Reporting Extension.
 
       Disse reglene gjelder bare når <ph name="CHROME_REPORTING_EXTENSION_NAME" /> er slått på og maskinen er registrert med <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" />.</translation>
diff --git a/components/policy/resources/policy_templates_pl.xtb b/components/policy/resources/policy_templates_pl.xtb
index fe6d90ec..659dc83 100644
--- a/components/policy/resources/policy_templates_pl.xtb
+++ b/components/policy/resources/policy_templates_pl.xtb
@@ -2460,9 +2460,9 @@
 
       Jeśli ta zasada nie jest skonfigurowana lub jest wyłączona, certyfikaty serwera, które nie mają rozszerzenia subjectAlternativeName zawierającego nazwę DNS lub adres IP, nie będą uważane za zaufane.</translation>
 <translation id="5578571772998293651">Zezwalaj na przesyłanie opinii użytkowników.
-        Jeśli ta zasada ma ustawienie Fałsz, użytkownicy nie mogą przesyłać opinii do Google.
+        Jeśli ta zasada ma wartość Fałsz, użytkownicy nie mogą przesyłać opinii do Google.
 
-        Jeśli ta zasada jest nieskonfigurowana lub ma ustawienie Prawda, użytkownicy mogą przesyłać opinie do Google, klikając kolejno Menu &gt;Pomoc-&gt;Zgłoś problem lub naciskając odpowiednią kombinację klawiszy.</translation>
+        Jeśli ta zasada jest nieskonfigurowana lub ma wartość Prawda, użytkownicy mogą przesyłać opinie do Google, klikając kolejno Menu &gt;Pomoc-&gt;Zgłoś problem lub naciskając odpowiednią kombinację klawiszy.</translation>
 <translation id="5581292529942108810">Konfigurowanie zasad związanych z Rozszerzeniem zgłaszającym informacje o Chrome.
 
       Ta zasada jest stosowana tylko wtedy, gdy włączone jest <ph name="CHROME_REPORTING_EXTENSION_NAME" />, a komputer jest zarejestrowany w <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" />.</translation>
diff --git a/components/policy/resources/policy_templates_sl.xtb b/components/policy/resources/policy_templates_sl.xtb
index 248dd69..5384e12 100644
--- a/components/policy/resources/policy_templates_sl.xtb
+++ b/components/policy/resources/policy_templates_sl.xtb
@@ -2550,7 +2550,7 @@
 <translation id="5578571772998293651">Omogočanje povratnih informacij uporabnikov.
         Če je ta pravilnik onemogočen, uporabniki ne morejo pošiljati povratnih informacij Googlu.
 
-        Če pravilnik ni nastavljen ali je omogočen, lahko uporabniki pošiljajo povratne informacij Googlu v »Meni -&gt; Pomoč -&gt; Prijava težave« ali s kombinacijo tipk.</translation>
+        Če pravilnik ni nastavljen ali je omogočen, lahko uporabniki pošiljajo povratne informacije Googlu v »Meni -&gt; Pomoč -&gt; Prijava težave« ali s kombinacijo tipk.</translation>
 <translation id="5581292529942108810">Konfiguriranje pravilnikov o razširitvi za poročanje za Chrome. 
 
       Ta pravilnik se uporablja samo, ko je omogočena razširitev <ph name="CHROME_REPORTING_EXTENSION_NAME" />, računalnik pa je včlanjen v pravilnik <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME" />.</translation>
diff --git a/components/policy/resources/policy_templates_ta.xtb b/components/policy/resources/policy_templates_ta.xtb
index 54651aa7..604c6a7c 100644
--- a/components/policy/resources/policy_templates_ta.xtb
+++ b/components/policy/resources/policy_templates_ta.xtb
@@ -431,10 +431,10 @@
 <translation id="1750315445671978749">எல்லாப் பதிவிறக்கங்களையும் தடு</translation>
 <translation id="1767673020408652620">தேடல் பெட்டியின் Zero Stateடில் App Recommendationsஸை இயக்கும்</translation>
 <translation id="17719159826324007">
-      இந்தக் கொள்கையை ArcSession என அமைத்தால் Android தொடங்கப்பட்டிருக்கும் பட்சத்தில் பயனர் வெளியேறும்போது மறுபடி தொடங்க சாதனத்தை வலியுறுத்தும்.
-      'எப்போதும்' என அமைத்தால் ஒவ்வொரு முறை பயனர் வெளியேறும்போதும் மறுபடி தொடங்க சாதனத்தை வலியுறுத்தும்.
-      அமைக்காமல் விட்டுவிட்டால் எந்த மாற்றத்தையும் ஏற்படுத்தாது. அத்துடன் பயனர் வெளியேறும்போது மறுபடி தொடங்க வலியுறுத்தாது. 'ஒருபோதும் வேண்டாம்' என அமைத்தாலும் இவ்வாறே செயல்படும்.
-      அதிகாரப்பூர்வமாக இணைக்கப்படாத பயனர்களிடம் மட்டுமே தாக்கத்தை ஏற்படுத்தும்.
+      இந்தக் கொள்கையை ArcSession என அமைத்தால் Android தொடங்கப்பட்டிருக்கும் பட்சத்தில் பயனர் வெளியேறும்போது சாதனத்தை மறுபடி தொடங்கும்.
+      'எப்போதும்' என அமைத்தால் ஒவ்வொரு முறை பயனர் வெளியேறும்போதும் சாதனத்தை மறுபடி தொடங்கும்.
+      அமைக்காமல் விட்டுவிட்டால் எந்த மாற்றத்தையும் ஏற்படுத்தாது. அத்துடன் பயனர் வெளியேறும்போது மறுபடியும் தொடங்காது. 'ஒருபோதும் வேண்டாம்' என அமைத்தாலும் இவ்வாறே செயல்படும்.
+      அதிகாரப்பூர்வமாக இணைக்கப்படாத பயனர்களுக்கு மட்டுமே இந்தக் கொள்கை பொருந்தும்.
       </translation>
 <translation id="1781356041596378058">Android டெவெலப்பர் விருப்பங்களுக்கான அணுகலையும் இந்தக் கொள்கை கட்டுப்படுத்தும். இந்தக் கொள்கையை “சரி” என அமைத்தால், பயனர்களால் டெவெலப்பர் விருப்பங்களை அணுக முடியாது. இந்தக் கொள்கையை “தவறு” என அமைத்தாலோ அமைக்காமல் விட்டாலோ, Android அமைப்புகள் ஆப்ஸில் இருக்கும் பதிப்பு எண்ணை ஏழு முறை தட்டுவதன் மூலம், பயனர்களால் டெவெலப்பர் விருப்பங்களை அணுக முடியும்.</translation>
 <translation id="1793346220873697538">இயல்பாக ’பின்’ அச்சிடுதலை முடக்கும்</translation>
@@ -913,7 +913,7 @@
 <translation id="267596348720209223">தேடல் வழங்குநரால் எழுத்துக் குறியாக்கங்கள் ஆதரவளிப்பதைக் குறிப்பிடுகிறது. குறியாக்கங்கள், UTF-8, GB2312 மற்றும் ISO-8859-1 போன்ற பக்கப் பெயர்களால் குறிப்பிடப்படும். அவை, வழங்கப்பட்டுள்ள வரிசையில் முயற்சிக்கின்றன. இந்தக் கொள்கை விருப்பத்தேர்வுக்குரியது. அது அமைக்கப்படவில்லை எனில் இயல்புநிலையான UTF-8 பயன்படுத்தப்படும். 'DefaultSearchProviderEnabled' இயக்கப்பட்டிருந்தால் மட்டுமே இந்தக் கொள்கை ஆதரிக்கப்படும்.</translation>
 <translation id="268577405881275241">தரவு சுருக்க ப்ராக்ஸி அம்சத்தை இயக்கு</translation>
 <translation id="2693108589792503178">கடவுச்சொல்லை மாற்றுவதற்கான URLஐ உள்ளமைக்கவும்.</translation>
-<translation id="2694143893026486692">இணைக்கப்பட்ட பெரிதாக்கி இயக்கப்பட்டுள்ளது</translation>
+<translation id="2694143893026486692">இணைக்கப்பட்ட மேக்னிஃபையர் இயக்கப்பட்டுள்ளது</translation>
 <translation id="2706708761587205154">’பின்’ மூலமாக மட்டும் அச்சிடலை அனுமதிக்கும்</translation>
 <translation id="2710534340210290498">இந்தக் கொள்கை 'தவறு' என அமைக்கப்பட்டால் பயனர்களால் திரையைப் பூட்ட முடியாது (பயனர் அமர்விலிருந்து வெளியேற மட்டுமே முடியும்). இந்தக் கொள்கை 'சரி' என அமைக்கப்பட்டாலோ அமைக்கப்படாமலேயே இருந்தாலோ கடவுச்சொல்லுடன் அங்கீகரிக்கப்படும் பயனர்களால் திரையைப் பூட்ட முடியும்.</translation>
 <translation id="2731627323327011390">ARC பயன்பாடுகளில் <ph name="PRODUCT_OS_NAME" /> சான்றிதழ்களின் உபயோகத்தை முடக்கு</translation>
@@ -4186,9 +4186,9 @@
           கொள்கை மூலம் பதிவுசெய்யப்பட்ட நெறிமுறை ஹேண்ட்லர்கள் பயனர் மூலம் பதிவுசெய்யப்பட்ட ஒன்றுடன் இணைக்கப்படும், மேலும் அவை இரண்டும் பயன்படுத்துவதற்குக் கிடைக்கும். புதிய இயல்புநிலை ஹேண்ட்லரை நிறுவுவதன் மூலம், பயனர் கொள்கையினால் நிறுவப்பட்ட நெறிமுறை ஹேண்ட்லர்களில் மேலெழுதலாம், ஆனால் கொள்கை மூலம் பதிவுசெய்யப்பட்ட நெறிமுறை ஹேண்ட்லரை அகற்ற முடியாது.</translation>
 <translation id="91196902572559194">'குரல் மூலம் செயல்படுத்தும் சொற்றொடரை' Google அசிஸ்டண்ட் கேட்பதற்கு இந்தக் கொள்கை அனுமதிக்கும்.
 
-      இந்தக் கொள்கை இயக்கப்பட்டிருந்தால் 'குரல் மூலம் செயல்படுத்தும் சொற்றொடரை' Google அசிஸ்டண்ட் கேட்க இயலும்.
-      இந்தக் கொள்கை முடக்கப்பட்டிருந்தால் 'குரல் மூலம் செயல்படுத்தும் சொற்றொடரை' Google அசிஸ்டண்ட் கேட்க இயலாது.
-      கொள்கை அமைக்கப்படாமல் இருந்தால் 'குரல் மூலம் செயல்படுத்தும் சொற்றொடரை' Google அசிஸ்டண்ட் கேட்பதற்கு அனுமதிக்கலாமா வேண்டாமா என்பதைப் பயனர்களால் தீர்மானிக்க முடியும்.
+      இந்தக் கொள்கை இயக்கப்பட்டிருந்தால் 'குரல் மூலம் செயல்படுத்தும் சொற்றொடரை' Google அசிஸ்டண்ட் கேட்கும்.
+      இந்தக் கொள்கை முடக்கப்பட்டிருந்தால் 'குரல் மூலம் செயல்படுத்தும் சொற்றொடரை' Google அசிஸ்டண்ட் கேட்காது.
+      கொள்கை அமைக்கப்படாமல் இருந்தால் 'குரல் மூலம் செயல்படுத்தும் சொற்றொடரைக்' கேட்பதற்கு Google அசிஸ்டண்ட்டை அனுமதிக்கலாமா என்பதைப் பயனர்களால் தீர்மானிக்க முடியும்.
       </translation>
 <translation id="9123211093995421438">குறிப்பிட்ட ஏதேனும் நேரத்தில், நிலையான பதிப்பில் இருந்து, எத்தனை <ph name="PRODUCT_OS_NAME" /> மைல்ஸ்டோன்களுக்கு பின்னோக்கி மீட்டமைக்க அனுமதிக்கப்படும் என்ற எண்ணிக்கையைக் குறிப்பிடும்.
 
diff --git a/components/policy/resources/policy_templates_uk.xtb b/components/policy/resources/policy_templates_uk.xtb
index 705461d2..3a1f2df6 100644
--- a/components/policy/resources/policy_templates_uk.xtb
+++ b/components/policy/resources/policy_templates_uk.xtb
@@ -4267,11 +4267,11 @@
 <translation id="9112897538922695510">Дозволяє зареєструвати список обробників протоколів. Це правило може бути лише рекомендацією. Для властивості |protocol| потрібно встановити схему, як-от "mailto", а для властивості |url| потрібно встановити шаблон URL-адреси додатка, який обробляє схему. Шаблон може містити значення "%s", яке буде замінено обробленою URL-адресою.
 
            Обробники протоколів, зареєстровані правилом, об’єднуються з обробниками протоколів, які зареєстрував користувач. Можна використовувати обидва. Користувач може замінити обробники протоколів, установлені правилом, установивши новий обробник за умовчанням, але не може видалити обробник протоколів, зареєстрований правилом.</translation>
-<translation id="91196902572559194">Це правило дозволяє Google Асистенту розпізнавати фразу для активації голосом.
+<translation id="91196902572559194">Це правило дозволяє активувати Google Асистента за допомогою голосової команди.
 
-      Якщо це правило ввімкнено, Google Асистент розпізнає фразу для активації голосом.
-      Якщо це правило вимкнено, Google Асистент не розпізнає фразу для активації голосом.
-      Якщо це правило не налаштовано, користувачі можуть вибрати, чи дозволяти Google Асистенту розпізнавати фразу для активації голосом.
+      Якщо це правило ввімкнено, Google Асистент розпізнає голосову команду для активації.
+      Якщо це правило вимкнено, Google Асистент не розпізнає голосову команду для активації.
+      Якщо це правило не налаштовано, користувачі можуть вибрати, чи дозволяти Google Асистенту розпізнавати голосову команду для активації.
       </translation>
 <translation id="9123211093995421438">Указує мінімальну кількість версій <ph name="PRODUCT_OS_NAME" />, які можна відновлювати, починаючи зі стабільної версії.
 
diff --git a/components/policy/resources/policy_templates_zh-CN.xtb b/components/policy/resources/policy_templates_zh-CN.xtb
index 0db22eac8..c50530b4 100644
--- a/components/policy/resources/policy_templates_zh-CN.xtb
+++ b/components/policy/resources/policy_templates_zh-CN.xtb
@@ -1515,7 +1515,7 @@
       每个列表条目都包含一个字典,其“extension-id”字段必须包含扩展程序 ID,而其“update-url”字段必须包含更新网址。</translation>
 <translation id="3874773863217952418">启用“点按搜索”</translation>
 <translation id="3877517141460819966">集成的双重身份验证模式</translation>
-<translation id="3879208481373875102">配置强制安装的网络应用的列表</translation>
+<translation id="3879208481373875102">配置强制安装的 Web 应用的列表</translation>
 <translation id="388237772682176890">我们在 M53 中弃用了此政策,并在 M54 中移除了此政策(因为 SPDY/3.1 已不再受支持)。
 
       禁止在 <ph name="PRODUCT_NAME" /> 中使用 SPDY 协议。
diff --git a/components/policy/test_support/BUILD.gn b/components/policy/test_support/BUILD.gn
new file mode 100644
index 0000000..376d74fd
--- /dev/null
+++ b/components/policy/test_support/BUILD.gn
@@ -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.
+
+static_library("test_support") {
+  testonly = true
+
+  data = [
+    "asn1der.py",
+    "policy_testserver.py",
+  ]
+
+  sources = [
+    "local_policy_test_server.cc",
+    "local_policy_test_server.h",
+  ]
+
+  deps = [
+    "////components/policy/core/common:common_constants",
+    "//base",
+    "//crypto",
+    "//net",
+    "//net:test_support",
+  ]
+}
diff --git a/components/policy/test_support/DEPS b/components/policy/test_support/DEPS
new file mode 100644
index 0000000..8e23c44
--- /dev/null
+++ b/components/policy/test_support/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+  # Run
+  #
+  #   buildtools/checkdeps/checkdeps.py components/policy
+  #
+  # to test.
+
+  "+crypto",
+  "+net"
+]
+
diff --git a/chrome/browser/policy/test/asn1der.py b/components/policy/test_support/asn1der.py
similarity index 100%
rename from chrome/browser/policy/test/asn1der.py
rename to components/policy/test_support/asn1der.py
diff --git a/chrome/browser/policy/test/bootstrap_deps b/components/policy/test_support/bootstrap_deps
similarity index 78%
rename from chrome/browser/policy/test/bootstrap_deps
rename to components/policy/test_support/bootstrap_deps
index cfcb914d1..c9dacaa 100644
--- a/chrome/browser/policy/test/bootstrap_deps
+++ b/components/policy/test_support/bootstrap_deps
@@ -1,10 +1,10 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2019 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 deps = {
-    "src/chrome/browser/policy/test":
-        "https://src.chromium.org/chrome/trunk/src/chrome/browser/policy/test",
+    "src/components/policy/test_support":
+        "https://src.chromium.org/chrome/trunk/src/components/policy/test_support",
     "src/net/tools/testserver":
         "https://src.chromium.org/chrome/trunk/src/net/tools/testserver",
     "src/third_party/tlslite/tlslite":
@@ -15,4 +15,4 @@
         "https://src.chromium.org/chrome/trunk/src/tools/telemetry",
     "src/third_party/catapult":
         "https://src.chromium.org/chrome/trunk/src/third_party/catapult",
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/policy/test/local_policy_test_server.cc b/components/policy/test_support/local_policy_test_server.cc
similarity index 91%
rename from chrome/browser/policy/test/local_policy_test_server.cc
rename to components/policy/test_support/local_policy_test_server.cc
index 40d6232..986ab47e 100644
--- a/chrome/browser/policy/test/local_policy_test_server.cc
+++ b/components/policy/test_support/local_policy_test_server.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/policy/test/local_policy_test_server.h"
+#include "components/policy/test_support/local_policy_test_server.h"
 
 #include <ctype.h>
 #include <stdint.h>
@@ -76,24 +76,21 @@
   client_state_file_ = server_data_dir_.GetPath().Append(kClientStateFileName);
 }
 
-LocalPolicyTestServer::LocalPolicyTestServer(const std::string& test_name)
+LocalPolicyTestServer::LocalPolicyTestServer(
+    const std::string& source_root_relative_config_file)
     : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP, base::FilePath()) {
-  // Read configuration from a file in chrome/test/data/policy.
+  // Read configuration from a file under |source_root|.
   base::ScopedAllowBlockingForTesting allow_blocking;
   base::FilePath source_root;
   CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root));
-  config_file_ = source_root
-      .AppendASCII("chrome")
-      .AppendASCII("test")
-      .AppendASCII("data")
-      .AppendASCII("policy")
-      .AppendASCII(base::StringPrintf("policy_%s.json", test_name.c_str()));
+  config_file_ = source_root.AppendASCII(source_root_relative_config_file);
 }
 
 LocalPolicyTestServer::~LocalPolicyTestServer() {}
 
 bool LocalPolicyTestServer::SetSigningKeyAndSignature(
-    const crypto::RSAPrivateKey* key, const std::string& signature) {
+    const crypto::RSAPrivateKey* key,
+    const std::string& signature) {
   base::ScopedAllowBlockingForTesting allow_blocking;
   CHECK(server_data_dir_.IsValid());
 
@@ -239,13 +236,6 @@
   ret->push_back(pyproto_dir.AppendASCII("components")
                      .AppendASCII("policy")
                      .AppendASCII("proto"));
-#if defined(OS_CHROMEOS)
-  ret->push_back(pyproto_dir.AppendASCII("chrome")
-                     .AppendASCII("browser")
-                     .AppendASCII("chromeos")
-                     .AppendASCII("policy")
-                     .AppendASCII("proto"));
-#endif
 
   return ret;
 }
@@ -258,12 +248,10 @@
     LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
     return false;
   }
-  *testserver_path = source_root
-      .AppendASCII("chrome")
-      .AppendASCII("browser")
-      .AppendASCII("policy")
-      .AppendASCII("test")
-      .AppendASCII("policy_testserver.py");
+  *testserver_path = source_root.AppendASCII("components")
+                         .AppendASCII("policy")
+                         .AppendASCII("test_support")
+                         .AppendASCII("policy_testserver.py");
   return true;
 }
 
diff --git a/chrome/browser/policy/test/local_policy_test_server.h b/components/policy/test_support/local_policy_test_server.h
similarity index 91%
rename from chrome/browser/policy/test/local_policy_test_server.h
rename to components/policy/test_support/local_policy_test_server.h
index 4c098c4..9ed66c5 100644
--- a/chrome/browser/policy/test/local_policy_test_server.h
+++ b/components/policy/test_support/local_policy_test_server.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_POLICY_TEST_LOCAL_POLICY_TEST_SERVER_H_
-#define CHROME_BROWSER_POLICY_TEST_LOCAL_POLICY_TEST_SERVER_H_
+#ifndef COMPONENTS_POLICY_TEST_SUPPORT_LOCAL_POLICY_TEST_SERVER_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_LOCAL_POLICY_TEST_SERVER_H_
 
 #include <string>
 #include <vector>
@@ -34,8 +34,9 @@
   explicit LocalPolicyTestServer(const base::FilePath& config_file);
 
   // Initializes the test server with the configuration read from
-  // chrome/test/data/policy/policy_|test_name|.json.
-  explicit LocalPolicyTestServer(const std::string& test_name);
+  // {source_root}/|source_root_relative_config_file|.
+  explicit LocalPolicyTestServer(
+      const std::string& source_root_relative_config_file);
 
   ~LocalPolicyTestServer() override;
 
@@ -111,4 +112,4 @@
 
 }  // namespace policy
 
-#endif  // CHROME_BROWSER_POLICY_TEST_LOCAL_POLICY_TEST_SERVER_H_
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_LOCAL_POLICY_TEST_SERVER_H_
diff --git a/chrome/browser/policy/test/policy_testserver.py b/components/policy/test_support/policy_testserver.py
similarity index 100%
rename from chrome/browser/policy/test/policy_testserver.py
rename to components/policy/test_support/policy_testserver.py
diff --git a/components/prefs/pref_member.cc b/components/prefs/pref_member.cc
index 6b74dde..a8fa12c 100644
--- a/components/prefs/pref_member.cc
+++ b/components/prefs/pref_member.cc
@@ -9,11 +9,11 @@
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/location.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/value_conversions.h"
 #include "components/prefs/pref_service.h"
 
-using base::SingleThreadTaskRunner;
+using base::SequencedTaskRunner;
 
 namespace subtle {
 
@@ -49,13 +49,13 @@
   }
 }
 
-void PrefMemberBase::MoveToThread(
-    scoped_refptr<SingleThreadTaskRunner> task_runner) {
+void PrefMemberBase::MoveToSequence(
+    scoped_refptr<SequencedTaskRunner> task_runner) {
   VerifyValuePrefName();
   // Load the value from preferences if it hasn't been loaded so far.
   if (!internal())
     UpdateValueFromPref(base::Closure());
-  internal()->MoveToThread(std::move(task_runner));
+  internal()->MoveToSequence(std::move(task_runner));
 }
 
 void PrefMemberBase::OnPreferenceChanged(PrefService* service,
@@ -89,14 +89,13 @@
 }
 
 PrefMemberBase::Internal::Internal()
-    : thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+    : owning_task_runner_(base::SequencedTaskRunnerHandle::Get()),
       is_managed_(false),
-      is_user_modifiable_(false) {
-}
+      is_user_modifiable_(false) {}
 PrefMemberBase::Internal::~Internal() { }
 
-bool PrefMemberBase::Internal::IsOnCorrectThread() const {
-  return thread_task_runner_->BelongsToCurrentThread();
+bool PrefMemberBase::Internal::IsOnCorrectSequence() const {
+  return owning_task_runner_->RunsTasksInCurrentSequence();
 }
 
 void PrefMemberBase::Internal::UpdateValue(base::Value* v,
@@ -105,13 +104,13 @@
                                            base::OnceClosure callback) const {
   std::unique_ptr<base::Value> value(v);
   base::ScopedClosureRunner closure_runner(std::move(callback));
-  if (IsOnCorrectThread()) {
+  if (IsOnCorrectSequence()) {
     bool rv = UpdateValueInternal(*value);
     DCHECK(rv);
     is_managed_ = is_managed;
     is_user_modifiable_ = is_user_modifiable;
   } else {
-    bool may_run = thread_task_runner_->PostTask(
+    bool may_run = owning_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&PrefMemberBase::Internal::UpdateValue, this,
                        value.release(), is_managed, is_user_modifiable,
@@ -120,10 +119,10 @@
   }
 }
 
-void PrefMemberBase::Internal::MoveToThread(
-    scoped_refptr<SingleThreadTaskRunner> task_runner) {
-  CheckOnCorrectThread();
-  thread_task_runner_ = std::move(task_runner);
+void PrefMemberBase::Internal::MoveToSequence(
+    scoped_refptr<SequencedTaskRunner> task_runner) {
+  CheckOnCorrectSequence();
+  owning_task_runner_ = std::move(task_runner);
 }
 
 bool PrefMemberVectorStringUpdate(const base::Value& value,
diff --git a/components/prefs/pref_member.h b/components/prefs/pref_member.h
index cd7a118..188f04a 100644
--- a/components/prefs/pref_member.h
+++ b/components/prefs/pref_member.h
@@ -33,6 +33,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "base/values.h"
 #include "components/prefs/pref_observer.h"
@@ -58,14 +59,14 @@
     Internal();
 
     // Update the value, either by calling |UpdateValueInternal| directly
-    // or by dispatching to the right thread.
+    // or by dispatching to the right sequence.
     // Takes ownership of |value|.
     void UpdateValue(base::Value* value,
                      bool is_managed,
                      bool is_user_modifiable,
                      base::OnceClosure callback) const;
 
-    void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+    void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner);
 
     // See PrefMember<> for description.
     bool IsManaged() const {
@@ -80,18 +81,16 @@
     friend class base::RefCountedThreadSafe<Internal>;
     virtual ~Internal();
 
-    void CheckOnCorrectThread() const {
-      DCHECK(IsOnCorrectThread());
-    }
+    void CheckOnCorrectSequence() const { DCHECK(IsOnCorrectSequence()); }
 
    private:
     // This method actually updates the value. It should only be called from
-    // the thread the PrefMember is on.
+    // the sequence the PrefMember is on.
     virtual bool UpdateValueInternal(const base::Value& value) const = 0;
 
-    bool IsOnCorrectThread() const;
+    bool IsOnCorrectSequence() const;
 
-    scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner_;
+    scoped_refptr<base::SequencedTaskRunner> owning_task_runner_;
     mutable bool is_managed_;
     mutable bool is_user_modifiable_;
 
@@ -112,7 +111,7 @@
   // See PrefMember<> for description.
   void Destroy();
 
-  void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner);
 
   // PrefObserver
   void OnPreferenceChanged(PrefService* service,
@@ -186,8 +185,8 @@
 
   // Unsubscribes the PrefMember from the PrefService. After calling this
   // function, the PrefMember may not be used any more on the UI thread.
-  // Assuming |MoveToThread| was previously called, |GetValue|, |IsManaged|,
-  // and |IsUserModifiable| can still be called from the other thread but
+  // Assuming |MoveToSequence| was previously called, |GetValue|, |IsManaged|,
+  // and |IsUserModifiable| can still be called from the other sequence but
   // the results will no longer update from the PrefService.
   // This method should only be called on the UI thread.
   void Destroy() {
@@ -195,19 +194,26 @@
   }
 
   // Moves the PrefMember to another thread, allowing read accesses from there.
-  // Changes from the PrefService will be propagated asynchronously
+  // Forwarder to MoveToSequence.
+  // TODO(crbug.com/978036): Remove after all callers use MoveToSequence
+  void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+    MoveToSequence(task_runner);
+  }
+
+  // Moves the PrefMember to another sequence, allowing read accesses from
+  // there. Changes from the PrefService will be propagated asynchronously
   // via PostTask.
   // This method should only be used from the thread the PrefMember is currently
   // on, which is the UI thread by default.
-  void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-    subtle::PrefMemberBase::MoveToThread(task_runner);
+  void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner) {
+    subtle::PrefMemberBase::MoveToSequence(task_runner);
   }
 
   // Check whether the pref is managed, i.e. controlled externally through
   // enterprise configuration management (e.g. windows group policy). Returns
   // false for unknown prefs.
   // This method should only be used from the thread the PrefMember is currently
-  // on, which is the UI thread unless changed by |MoveToThread|.
+  // on, which is the UI thread unless changed by |MoveToSequence|.
   bool IsManaged() const {
     VerifyPref();
     return internal_->IsManaged();
@@ -217,7 +223,7 @@
   // when the pref is managed by a policy or an extension, and when a command
   // line flag overrides the pref.
   // This method should only be used from the thread the PrefMember is currently
-  // on, which is the UI thread unless changed by |MoveToThread|.
+  // on, which is the UI thread unless changed by |MoveToSequence|.
   bool IsUserModifiable() const {
     VerifyPref();
     return internal_->IsUserModifiable();
@@ -225,7 +231,7 @@
 
   // Retrieve the value of the member variable.
   // This method should only be used from the thread the PrefMember is currently
-  // on, which is the UI thread unless changed by |MoveToThread|.
+  // on, which is the UI thread unless changed by |MoveToSequence|.
   ValueType GetValue() const {
     VerifyPref();
     return internal_->value();
@@ -256,7 +262,7 @@
     Internal() : value_(ValueType()) {}
 
     ValueType value() {
-      CheckOnCorrectThread();
+      CheckOnCorrectSequence();
       return value_;
     }
 
diff --git a/components/prefs/pref_member_unittest.cc b/components/prefs/pref_member_unittest.cc
index a7d4a5ea..eced7b5 100644
--- a/components/prefs/pref_member_unittest.cc
+++ b/components/prefs/pref_member_unittest.cc
@@ -8,10 +8,10 @@
 
 #include "base/bind.h"
 #include "base/location.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/task/post_task.h"
 #include "base/test/scoped_task_environment.h"
-#include "base/threading/thread.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,13 +35,12 @@
 class GetPrefValueHelper
     : public base::RefCountedThreadSafe<GetPrefValueHelper> {
  public:
-  GetPrefValueHelper() : value_(false), pref_thread_("pref thread") {
-    pref_thread_.Start();
-  }
+  GetPrefValueHelper()
+      : value_(false), task_runner_(base::CreateSequencedTaskRunner({})) {}
 
   void Init(const std::string& pref_name, PrefService* prefs) {
     pref_.Init(pref_name, prefs);
-    pref_.MoveToThread(pref_thread_.task_runner());
+    pref_.MoveToSequence(task_runner_);
   }
 
   void Destroy() {
@@ -51,18 +50,12 @@
   void FetchValue() {
     base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
                               base::WaitableEvent::InitialState::NOT_SIGNALED);
-    ASSERT_TRUE(pref_thread_.task_runner()->PostTask(
+    ASSERT_TRUE(task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&GetPrefValueHelper::GetPrefValue, this, &event)));
     event.Wait();
   }
 
-  // The thread must be stopped on the main thread. GetPrefValueHelper being
-  // ref-counted, the destructor can be called from any thread.
-  void StopThread() {
-    pref_thread_.Stop();
-  }
-
   bool value() { return value_; }
 
  private:
@@ -77,7 +70,8 @@
   BooleanPrefMember pref_;
   bool value_;
 
-  base::Thread pref_thread_;  // The thread |pref_| runs on.
+  // The sequence |pref_| runs on.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
 };
 
 class PrefMemberTestClass {
@@ -306,7 +300,7 @@
   IntegerPrefMember pref;
 }
 
-TEST_F(PrefMemberTest, MoveToThread) {
+TEST_F(PrefMemberTest, MoveToSequence) {
   TestingPrefServiceSimple prefs;
   scoped_refptr<GetPrefValueHelper> helper(new GetPrefValueHelper());
   RegisterTestPrefs(prefs.registry());
@@ -324,6 +318,4 @@
 
   helper->FetchValue();
   EXPECT_TRUE(helper->value());
-
-  helper->StopThread();
 }
diff --git a/components/previews/content/hints_fetcher.cc b/components/previews/content/hints_fetcher.cc
index 9ddd2206..92ef0ca 100644
--- a/components/previews/content/hints_fetcher.cc
+++ b/components/previews/content/hints_fetcher.cc
@@ -110,8 +110,8 @@
   url_loader_->AttachStringForUpload(serialized_request,
                                      "application/x-protobuf");
 
-  UMA_HISTOGRAM_COUNTS_100("Previews.HintsFetcher.GetHintsRequest.HostCount",
-                           hosts.size());
+  UMA_HISTOGRAM_COUNTS_100(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", hosts.size());
 
   // |url_loader_| should not retry on 5xx errors since the server may already
   // be overloaded.  |url_loader_| should retry on network changes since the
@@ -135,12 +135,14 @@
       get_hints_response =
           std::make_unique<optimization_guide::proto::GetHintsResponse>();
 
-  UMA_HISTOGRAM_ENUMERATION("Previews.HintsFetcher.GetHintsRequest.Status",
-                            static_cast<net::HttpStatusCode>(response_code),
-                            net::HTTP_VERSION_NOT_SUPPORTED);
+  UMA_HISTOGRAM_ENUMERATION(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.Status",
+      static_cast<net::HttpStatusCode>(response_code),
+      net::HTTP_VERSION_NOT_SUPPORTED);
   // Net error codes are negative but histogram enums must be positive.
-  base::UmaHistogramSparse("Previews.HintsFetcher.GetHintsRequest.NetErrorCode",
-                           -net_status);
+  base::UmaHistogramSparse(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.NetErrorCode",
+      -net_status);
 
   if (net_status == net::OK && response_code == net::HTTP_OK &&
       get_hints_response->ParseFromString(get_hints_response_data)) {
diff --git a/components/previews/content/previews_optimization_guide.cc b/components/previews/content/previews_optimization_guide.cc
index 6599f60..1b8de18d 100644
--- a/components/previews/content/previews_optimization_guide.cc
+++ b/components/previews/content/previews_optimization_guide.cc
@@ -15,6 +15,7 @@
 #include "base/time/default_clock.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/optimization_guide/hints_component_info.h"
+#include "components/optimization_guide/optimization_guide_prefs.h"
 #include "components/optimization_guide/optimization_guide_service.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/prefs/pref_service.h"
@@ -153,9 +154,6 @@
       url_loader_factory_(url_loader_factory),
       ui_weak_ptr_factory_(this) {
   DCHECK(optimization_guide_service_);
-  // TODO(mcrouse): This needs to be a pref to persist the last fetch attempt
-  // time and prevent crash loops.
-  last_fetch_attempt_ = base::Time();
   hint_cache_->Initialize(
       ShouldPurgeHintCacheStoreOnStartup(),
       base::BindOnce(&PreviewsOptimizationGuide::OnHintCacheInitialized,
@@ -426,13 +424,28 @@
       ShouldOverrideFetchHintsTimer()) {
     // Skip the fetch scheduling logic and perform a hints fetch immediately
     // after initialization.
-    last_fetch_attempt_ = time_clock_->Now();
+    SetLastHintsFetchAttemptTime(time_clock_->Now());
     FetchHints();
   } else {
     ScheduleHintsFetch();
   }
 }
 
+void PreviewsOptimizationGuide::SetLastHintsFetchAttemptTime(
+    base::Time last_attempt_time) {
+  DCHECK(pref_service_);
+  pref_service_->SetInt64(
+      optimization_guide::prefs::kHintsFetcherLastFetchAttempt,
+      last_attempt_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+}
+
+base::Time PreviewsOptimizationGuide::GetLastHintsFetchAttemptTime() const {
+  DCHECK(pref_service_);
+  return base::Time::FromDeltaSinceWindowsEpoch(
+      base::TimeDelta::FromMicroseconds(pref_service_->GetInt64(
+          optimization_guide::prefs::kHintsFetcherLastFetchAttempt)));
+}
+
 void PreviewsOptimizationGuide::ScheduleHintsFetch() {
   DCHECK(!hints_fetch_timer_.IsRunning());
   DCHECK(pref_service_);
@@ -444,13 +457,13 @@
   const base::TimeDelta time_until_update_time =
       hint_cache_->FetchedHintsUpdateTime() - time_clock_->Now();
   const base::TimeDelta time_until_retry =
-      last_fetch_attempt_ + kFetchRetryDelay - time_clock_->Now();
+      GetLastHintsFetchAttemptTime() + kFetchRetryDelay - time_clock_->Now();
   base::TimeDelta fetcher_delay;
   if (time_until_update_time <= base::TimeDelta() &&
       time_until_retry <= base::TimeDelta()) {
     // Fetched hints in the store should be updated and an attempt has not
     // been made in last |kFetchRetryDelay|.
-    last_fetch_attempt_ = time_clock_->Now();
+    SetLastHintsFetchAttemptTime(time_clock_->Now());
     hints_fetch_timer_.Start(FROM_HERE, RandomFetchDelay(), this,
                              &PreviewsOptimizationGuide::FetchHints);
   } else {
diff --git a/components/previews/content/previews_optimization_guide.h b/components/previews/content/previews_optimization_guide.h
index 53396f2..f7dc41a 100644
--- a/components/previews/content/previews_optimization_guide.h
+++ b/components/previews/content/previews_optimization_guide.h
@@ -170,6 +170,12 @@
   // engagement scores using |hints_fetcher_|.
   void FetchHints();
 
+  // Return the time when a hints fetch request was last attempted.
+  base::Time GetLastHintsFetchAttemptTime() const;
+
+  // Set the time when a hints fetch was last attempted to |last_attempt_time|.
+  void SetLastHintsFetchAttemptTime(base::Time last_attempt_time);
+
   // The OptimizationGuideService that this guide is listening to. Not owned.
   optimization_guide::OptimizationGuideService* optimization_guide_service_;
 
@@ -205,8 +211,6 @@
   // Clock used for scheduling the |hints_fetch_timer_|.
   const base::Clock* time_clock_;
 
-  base::Time last_fetch_attempt_;
-
   // A reference to the PrefService for this profile. Not owned.
   PrefService* pref_service_ = nullptr;
 
diff --git a/components/previews/content/previews_optimization_guide_unittest.cc b/components/previews/content/previews_optimization_guide_unittest.cc
index 91b78c09..7f9a957 100644
--- a/components/previews/content/previews_optimization_guide_unittest.cc
+++ b/components/previews/content/previews_optimization_guide_unittest.cc
@@ -299,6 +299,10 @@
     }
   }
 
+  const base::Clock* GetMockClock() const {
+    return scoped_task_environment_.GetMockClock();
+  }
+
   void ResetGuide() {
     guide_.reset();
     RunUntilIdle();
@@ -1924,6 +1928,43 @@
   InitializeFixedCountResourceLoadingHints();
 }
 
+TEST_F(PreviewsOptimizationGuideTest, HintsFetcherLastFetchAtttempt) {
+  base::HistogramTester histogram_tester;
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching);
+
+  // Set the last fetch attempt to 5 minutes ago, simulating a short duration
+  // since last execution (simulating browser crash/close-reopen).
+  base::TimeDelta minutes_since_attempt = base::TimeDelta::FromMinutes(5);
+  base::Time last_attempt_time = GetMockClock()->Now() - minutes_since_attempt;
+
+  pref_service()->SetInt64(
+      optimization_guide::prefs::kHintsFetcherLastFetchAttempt,
+      last_attempt_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+
+  guide()->SetHintsFetcherForTesting(
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+
+  std::vector<std::string> hosts = {"example1.com", "example2.com"};
+  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
+      .Times(1)
+      .WillRepeatedly(testing::Return(hosts));
+
+  // Load hints so that OnHintsUpdated is called. This will force FetchHints to
+  // be triggered if OptimizationHintsFetching is enabled.
+  InitializeFixedCountResourceLoadingHints();
+
+  // Move the clock forward a short duration to ensure that the hints fetch does
+  // not occur too quickly after the simulated short relaunch.
+  MoveClockForwardBy(base::TimeDelta::FromMinutes(5));
+  EXPECT_FALSE(hints_fetcher()->hints_fetched());
+
+  // Move clock forward by the maximum delay, |kTestFetchRetryDelaySeconds to
+  // trigger the hints fetch.
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
+  EXPECT_TRUE(hints_fetcher()->hints_fetched());
+}
+
 class PreviewsOptimizationGuideDataSaverOffTest
     : public PreviewsOptimizationGuideTest {
  public:
diff --git a/components/safe_browsing/db/BUILD.gn b/components/safe_browsing/db/BUILD.gn
index 1a51f76..5e5b0eea 100644
--- a/components/safe_browsing/db/BUILD.gn
+++ b/components/safe_browsing/db/BUILD.gn
@@ -438,6 +438,8 @@
   deps = [
     ":database_manager",
     "//base:base",
+    "//content/public/browser",
+    "//url:url",
   ]
 }
 
diff --git a/components/safe_browsing/db/allowlist_checker_client.cc b/components/safe_browsing/db/allowlist_checker_client.cc
index cb909af..6a8ca3f 100644
--- a/components/safe_browsing/db/allowlist_checker_client.cc
+++ b/components/safe_browsing/db/allowlist_checker_client.cc
@@ -7,33 +7,88 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "content/public/browser/browser_thread.h"
 
 namespace safe_browsing {
 
-// Static
+namespace {
+
+// Number of milliseconds to wait for the response from the Safe Browsing
+// database manager before proceeding with the timeout behavior.
+const int kLookupTimeoutMS = 5000;
+}  // namespace
+
+// static
 void AllowlistCheckerClient::StartCheckCsdWhitelist(
     scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
     const GURL& url,
     base::Callback<void(bool)> callback_for_result) {
-  // TODO(nparker): Maybe also call SafeBrowsingDatabaseManager::CanCheckUrl()
-  if (!url.is_valid()) {
-    callback_for_result.Run(true /* did_match_allowlist */);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // On timeout or if the list is unavailable, report match.
+  const bool kDefaultDoesMatchAllowlist = true;
+
+  std::unique_ptr<AllowlistCheckerClient> client = GetAllowlistCheckerClient(
+      database_manager, url, callback_for_result, kDefaultDoesMatchAllowlist);
+  if (!client) {
+    callback_for_result.Run(kDefaultDoesMatchAllowlist);
     return;
   }
 
+  AsyncMatch match = database_manager->CheckCsdWhitelistUrl(url, client.get());
+  InvokeCallbackOrRelease(match, std::move(client));
+}
+
+// static
+void AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    const GURL& url,
+    base::Callback<void(bool)> callback_for_result) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // On timeout or if the list is unavailable, report no match.
+  const bool kDefaultDoesMatchAllowlist = false;
+
+  std::unique_ptr<AllowlistCheckerClient> client = GetAllowlistCheckerClient(
+      database_manager, url, callback_for_result, kDefaultDoesMatchAllowlist);
+  if (!client) {
+    callback_for_result.Run(kDefaultDoesMatchAllowlist);
+    return;
+  }
+
+  AsyncMatch match =
+      database_manager->CheckUrlForHighConfidenceAllowlist(url, client.get());
+  InvokeCallbackOrRelease(match, std::move(client));
+}
+
+// static
+std::unique_ptr<AllowlistCheckerClient>
+AllowlistCheckerClient::GetAllowlistCheckerClient(
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    const GURL& url,
+    base::Callback<void(bool)> callback_for_result,
+    bool default_does_match_allowlist) {
+  if (!url.is_valid() || !database_manager ||
+      !database_manager->CanCheckUrl(url)) {
+    return nullptr;
+  }
+
   // Make a client for each request. The caller could have several in
   // flight at once.
-  std::unique_ptr<AllowlistCheckerClient> client =
-      std::make_unique<AllowlistCheckerClient>(callback_for_result,
-                                               database_manager);
-  AsyncMatch match = database_manager->CheckCsdWhitelistUrl(url, client.get());
+  return std::make_unique<AllowlistCheckerClient>(
+      callback_for_result, database_manager, default_does_match_allowlist);
+}
 
+// static
+void AllowlistCheckerClient::InvokeCallbackOrRelease(
+    AsyncMatch match,
+    std::unique_ptr<AllowlistCheckerClient> client) {
   switch (match) {
     case AsyncMatch::MATCH:
-      callback_for_result.Run(true /* did_match_allowlist */);
+      client->callback_for_result_.Run(true /* did_match_allowlist */);
       break;
     case AsyncMatch::NO_MATCH:
-      callback_for_result.Run(false /* did_match_allowlist */);
+      client->callback_for_result_.Run(false /* did_match_allowlist */);
       break;
     case AsyncMatch::ASYNC:
       // Client is now self-owned. When it gets called back with the result,
@@ -45,33 +100,50 @@
 
 AllowlistCheckerClient::AllowlistCheckerClient(
     base::Callback<void(bool)> callback_for_result,
-    scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    bool default_does_match_allowlist)
     : callback_for_result_(callback_for_result),
       database_manager_(database_manager),
+      default_does_match_allowlist_(default_does_match_allowlist),
       weak_factory_(this) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
   // Set a timer to fail open, i.e. call it "whitelisted", if the full
   // check takes too long.
-  auto timeout_callback =
-      base::Bind(&AllowlistCheckerClient::OnCheckWhitelistUrlTimeout,
-                 weak_factory_.GetWeakPtr());
-  timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMsec),
+  auto timeout_callback = base::Bind(&AllowlistCheckerClient::OnTimeout,
+                                     weak_factory_.GetWeakPtr());
+  timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kLookupTimeoutMS),
                timeout_callback);
 }
 
-AllowlistCheckerClient::~AllowlistCheckerClient() {}
+AllowlistCheckerClient::~AllowlistCheckerClient() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
 
 // SafeBrowsingDatabaseMananger::Client impl
 void AllowlistCheckerClient::OnCheckWhitelistUrlResult(
     bool did_match_allowlist) {
+  OnCheckUrlResult(did_match_allowlist);
+}
+
+void AllowlistCheckerClient::OnCheckUrlForHighConfidenceAllowlist(
+    bool did_match_allowlist) {
+  OnCheckUrlResult(did_match_allowlist);
+}
+
+void AllowlistCheckerClient::OnCheckUrlResult(bool did_match_allowlist) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   timer_.Stop();
   callback_for_result_.Run(did_match_allowlist);
+
   // This method is invoked only if we're already self-owned.
   delete this;
 }
 
-void AllowlistCheckerClient::OnCheckWhitelistUrlTimeout() {
+void AllowlistCheckerClient::OnTimeout() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   database_manager_->CancelCheck(this);
-  this->OnCheckWhitelistUrlResult(true /* did_match_allowlist */);
+  OnCheckUrlResult(default_does_match_allowlist_);
 }
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/db/allowlist_checker_client.h b/components/safe_browsing/db/allowlist_checker_client.h
index 957bd5c4..a7fcc29 100644
--- a/components/safe_browsing/db/allowlist_checker_client.h
+++ b/components/safe_browsing/db/allowlist_checker_client.h
@@ -5,9 +5,12 @@
 #ifndef COMPONENTS_SAFE_BROWSING_DB_ALLOWLIST_CHECKER_CLIENT_H_
 #define COMPONENTS_SAFE_BROWSING_DB_ALLOWLIST_CHECKER_CLIENT_H_
 
+#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "components/safe_browsing/db/database_manager.h"
+#include "url/gurl.h"
 
 namespace safe_browsing {
 
@@ -19,34 +22,74 @@
  public:
   using BoolCallback = base::Callback<void(bool /* is_whitelisted */)>;
 
-  // Static method to instantiate and start a check. The callback will
-  // be invoked when it's done, times out, or if database_manager gets
-  // shut down. Must be called on IO thread.
+  // Static method to lookup |url| on the CSD allowlist. |callback| will be
+  // called when the lookup result is known, or on time out, or if the
+  // |database_manager| gets shut down, whichever happens first.
+  // Must be called on IO thread.
   static void StartCheckCsdWhitelist(
       scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
       const GURL& url,
       BoolCallback callback_for_result);
 
+  // Static method to lookup |url| on the high confidence allowlist. |callback|
+  // will be called when the lookup result is known, or on time out, or if the
+  // |database_manager| gets shut down, whichever happens first.
+  // Must be called on IO thread.
+  static void StartCheckHighConfidenceAllowlist(
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      const GURL& url,
+      BoolCallback callback_for_result);
+
+  // public constructor for use with std::make_unique
   AllowlistCheckerClient(
       BoolCallback callback_for_result,
-      scoped_refptr<SafeBrowsingDatabaseManager> database_manager);
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      bool default_does_match_allowlist);
+
   ~AllowlistCheckerClient() override;
 
   // SafeBrowsingDatabaseMananger::Client impl
   void OnCheckWhitelistUrlResult(bool is_whitelisted) override;
-
- protected:
-  static const int kTimeoutMsec = 5000;
-  base::OneShotTimer timer_;
-  BoolCallback callback_for_result_;
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
-  base::WeakPtrFactory<AllowlistCheckerClient> weak_factory_;
+  void OnCheckUrlForHighConfidenceAllowlist(bool did_match_allowlist) override;
 
  private:
-  AllowlistCheckerClient();
+  // Helper method to instantiate a AllowlistCheckerClient object.
+  static std::unique_ptr<AllowlistCheckerClient> GetAllowlistCheckerClient(
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      const GURL& url,
+      base::Callback<void(bool)> callback_for_result,
+      bool default_does_match_allowlist);
+
+  // Invokes |callback_for_result_| if the allowlist lookup completed
+  // synchronously i.e if |match| is |MATCH| or |NO_MATCH|. If, however, |match|
+  // is |ASYNC|, it releases the ownership of |client| so that it can be deleted
+  // in |OnCheckUrlResult| later.
+  static void InvokeCallbackOrRelease(
+      AsyncMatch match,
+      std::unique_ptr<AllowlistCheckerClient> client);
+
+  AllowlistCheckerClient() = delete;
+
+  // Calls the |callback_for_result_| with the result of the lookup or timeout.
+  void OnCheckUrlResult(bool did_match_allowlist);
 
   // Called when the call to CheckCsdWhitelistUrl times out.
-  void OnCheckWhitelistUrlTimeout();
+  void OnTimeout();
+
+  // For setting up timeout behavior.
+  base::OneShotTimer timer_;
+
+  // The method to call when the match result is known.
+  BoolCallback callback_for_result_;
+
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+
+  // Whether to report allowlist match in any of the following cases:
+  // a) On timeout, or
+  // b) If the list is unavailable.
+  bool default_does_match_allowlist_;
+
+  base::WeakPtrFactory<AllowlistCheckerClient> weak_factory_;
 };
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/db/allowlist_checker_client_unittest.cc b/components/safe_browsing/db/allowlist_checker_client_unittest.cc
index c094cabf..5d386f7 100644
--- a/components/safe_browsing/db/allowlist_checker_client_unittest.cc
+++ b/components/safe_browsing/db/allowlist_checker_client_unittest.cc
@@ -36,6 +36,9 @@
   MOCK_METHOD2(CheckCsdWhitelistUrl,
                AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
 
+  MOCK_METHOD2(CheckUrlForHighConfidenceAllowlist,
+               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
+
  protected:
   ~MockSafeBrowsingDatabaseManager() override {}
 
@@ -49,7 +52,7 @@
   AllowlistCheckerClientTest()
       : thread_bundle_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
-        target_url_("http://foo.bar") {}
+        target_url_("https://example.test") {}
 
   void SetUp() override {
     database_manager_ = new MockSafeBrowsingDatabaseManager;
@@ -60,9 +63,7 @@
     base::RunLoop().RunUntilIdle();
 
     // Verify no callback is remaining.
-    // TODO(nparker): We should somehow EXPECT that no entry is remaining,
-    // rather than just invoking it.
-    thread_bundle_.FastForwardUntilNoTasksRemain();
+    EXPECT_TRUE(thread_bundle_.MainThreadIsIdle());
   }
 
  protected:
@@ -72,7 +73,7 @@
   scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
 };
 
-TEST_F(AllowlistCheckerClientTest, TestMatch) {
+TEST_F(AllowlistCheckerClientTest, TestCsdListMatch) {
   EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
       .WillOnce(Return(AsyncMatch::MATCH));
 
@@ -82,7 +83,7 @@
                                                  callback.Get());
 }
 
-TEST_F(AllowlistCheckerClientTest, TestNoMatch) {
+TEST_F(AllowlistCheckerClientTest, TestCsdListNoMatch) {
   EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
       .WillOnce(Return(AsyncMatch::NO_MATCH));
 
@@ -92,7 +93,7 @@
                                                  callback.Get());
 }
 
-TEST_F(AllowlistCheckerClientTest, TestAsyncNoMatch) {
+TEST_F(AllowlistCheckerClientTest, TestCsdListAsyncNoMatch) {
   SafeBrowsingDatabaseManager::Client* client;
   EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
       .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
@@ -107,7 +108,7 @@
   client->OnCheckWhitelistUrlResult(false);
 }
 
-TEST_F(AllowlistCheckerClientTest, TestAsyncTimeout) {
+TEST_F(AllowlistCheckerClientTest, TestCsdListAsyncTimeout) {
   SafeBrowsingDatabaseManager::Client* client;
   EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
       .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
@@ -123,4 +124,59 @@
   thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(5));
 }
 
+TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListMatch) {
+  EXPECT_CALL(*database_manager_,
+              CheckUrlForHighConfidenceAllowlist(target_url_, _))
+      .WillOnce(Return(AsyncMatch::MATCH));
+
+  MockBoolCallback callback;
+  EXPECT_CALL(callback, Run(true /* did_match_allowlist */));
+  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+      database_manager_, target_url_, callback.Get());
+}
+
+TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListNoMatch) {
+  EXPECT_CALL(*database_manager_,
+              CheckUrlForHighConfidenceAllowlist(target_url_, _))
+      .WillOnce(Return(AsyncMatch::NO_MATCH));
+
+  MockBoolCallback callback;
+  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
+  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+      database_manager_, target_url_, callback.Get());
+}
+
+TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListAsyncNoMatch) {
+  SafeBrowsingDatabaseManager::Client* client;
+  EXPECT_CALL(*database_manager_,
+              CheckUrlForHighConfidenceAllowlist(target_url_, _))
+      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+
+  MockBoolCallback callback;
+  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+      database_manager_, target_url_, callback.Get());
+  // Callback should not be called yet.
+
+  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
+  // The self-owned client deletes itself here.
+  client->OnCheckWhitelistUrlResult(false);
+}
+
+TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListAsyncTimeout) {
+  SafeBrowsingDatabaseManager::Client* client;
+  EXPECT_CALL(*database_manager_,
+              CheckUrlForHighConfidenceAllowlist(target_url_, _))
+      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+  EXPECT_CALL(*database_manager_, CancelCheck(_)).Times(1);
+
+  MockBoolCallback callback;
+  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+      database_manager_, target_url_, callback.Get());
+  thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  // No callback yet.
+
+  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
+  thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(5));
+}
+
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/db/database_manager.h b/components/safe_browsing/db/database_manager.h
index a211cab..061e7d6 100644
--- a/components/safe_browsing/db/database_manager.h
+++ b/components/safe_browsing/db/database_manager.h
@@ -32,7 +32,7 @@
 
 // Value returned by some functions that check an allowlist and may or may not
 // have an immediate answer.
-enum class AsyncMatch {
+enum class AsyncMatch : int {
   // If a hash prefix on the allowlist matches any of the computed hashes for
   // the URL. In this case, the callback method on the client is called back
   // later with the result.
diff --git a/components/safe_browsing/db/v4_store.cc b/components/safe_browsing/db/v4_store.cc
index d7859a7..89668bc 100644
--- a/components/safe_browsing/db/v4_store.cc
+++ b/components/safe_browsing/db/v4_store.cc
@@ -39,7 +39,6 @@
 const char kDecodeRemovals[] = ".DecodeRemovals";
 // Part 3: Represent the unit of value being measured and logged.
 const char kResult[] = ".Result";
-const char kTime[] = ".Time";
 // Part 4 (optional): Represent the name of the list for which the metric is
 // being logged. For instance, ".UrlSoceng".
 // UMA metric names for this file are generated by appending one value each,
@@ -54,32 +53,6 @@
 // The maximum store file size, as of today, is about 6MB.
 constexpr size_t kMaxStoreSizeBytes = 50 * 1000 * 1000;
 
-void RecordTimeWithAndWithoutSuffix(const std::string& metric,
-                                    base::TimeDelta time,
-                                    const base::FilePath& file_path) {
-  // The histograms below are a modified expansion of the
-  // UMA_HISTOGRAM_LONG_TIMES macro adapted to allow for a dynamically suffixed
-  // histogram name.
-  // Note: The factory creates and owns the histogram.
-  const int kBucketCount = 100;
-  base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
-      metric + kTime, base::TimeDelta::FromMilliseconds(1),
-      base::TimeDelta::FromMinutes(1), kBucketCount,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  if (histogram) {
-    histogram->AddTime(time);
-  }
-
-  std::string suffix = GetUmaSuffixForStore(file_path);
-  base::HistogramBase* histogram_suffix = base::Histogram::FactoryTimeGet(
-      metric + kTime + suffix, base::TimeDelta::FromMilliseconds(1),
-      base::TimeDelta::FromMinutes(1), kBucketCount,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  if (histogram_suffix) {
-    histogram_suffix->AddTime(time);
-  }
-}
-
 void RecordEnumWithAndWithoutSuffix(const std::string& metric,
                                     int32_t value,
                                     int32_t maximum,
@@ -130,12 +103,6 @@
                                  APPLY_UPDATE_RESULT_MAX, file_path);
 }
 
-void RecordApplyUpdateTime(const std::string& base_metric,
-                           base::TimeDelta time,
-                           const base::FilePath& file_path) {
-  RecordTimeWithAndWithoutSuffix(base_metric + kApplyUpdate, time, file_path);
-}
-
 void RecordDecodeAdditionsResult(const std::string& base_metric,
                                  V4DecodeResult result,
                                  const base::FilePath& file_path) {
@@ -143,13 +110,6 @@
                                  DECODE_RESULT_MAX, file_path);
 }
 
-void RecordDecodeAdditionsTime(const std::string& base_metric,
-                               base::TimeDelta time,
-                               const base::FilePath& file_path) {
-  RecordTimeWithAndWithoutSuffix(base_metric + kDecodeAdditions, time,
-                                 file_path);
-}
-
 void RecordDecodeRemovalsResult(const std::string& base_metric,
                                 V4DecodeResult result,
                                 const base::FilePath& file_path) {
@@ -157,13 +117,6 @@
                                  DECODE_RESULT_MAX, file_path);
 }
 
-void RecordDecodeRemovalsTime(const std::string& base_metric,
-                              base::TimeDelta time,
-                              const base::FilePath& file_path) {
-  RecordTimeWithAndWithoutSuffix(base_metric + kDecodeRemovals, time,
-                                 file_path);
-}
-
 void RecordStoreReadResult(StoreReadResult result) {
   UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreRead.Result", result,
                             STORE_READ_RESULT_MAX);
@@ -310,7 +263,6 @@
       DCHECK(removal.has_rice_indices());
 
       const RiceDeltaEncoding& rice_indices = removal.rice_indices();
-      TimeTicks before = TimeTicks::Now();
       V4DecodeResult decode_result = V4RiceDecoder::DecodeIntegers(
           rice_indices.first_value(), rice_indices.rice_parameter(),
           rice_indices.num_entries(), rice_indices.encoded_data(),
@@ -320,7 +272,6 @@
       if (decode_result != DECODE_SUCCESS) {
         return RICE_DECODING_FAILURE;
       }
-      RecordDecodeRemovalsTime(metric, TimeTicks::Now() - before, store_path_);
       raw_removals = &rice_removals;
     } else {
       NOTREACHED() << "Unexpected compression_type type: " << compression_type;
@@ -371,7 +322,6 @@
       new V4Store(task_runner_, store_path_, file_size_));
   ApplyUpdateResult apply_update_result;
   std::string metric;
-  TimeTicks before = TimeTicks::Now();
   if (response->response_type() == ListUpdateResponse::PARTIAL_UPDATE) {
     metric = kProcessPartialUpdate;
     apply_update_result = new_store->ProcessPartialUpdateAndWriteToDisk(
@@ -391,7 +341,6 @@
     new_store->last_apply_update_result_ = apply_update_result;
     new_store->last_apply_update_time_millis_ = base::Time::Now();
     new_store->checks_attempted_ = checks_attempted_;
-    RecordApplyUpdateTime(metric, TimeTicks::Now() - before, store_path_);
   } else {
     new_store.reset();
     DLOG(WARNING) << "Failure: ApplyUpdate: reason: " << apply_update_result
@@ -430,7 +379,6 @@
 
       const RiceDeltaEncoding& rice_hashes = addition.rice_hashes();
       std::vector<uint32_t> raw_hashes;
-      TimeTicks before = TimeTicks::Now();
       V4DecodeResult decode_result = V4RiceDecoder::DecodePrefixes(
           rice_hashes.first_value(), rice_hashes.rice_parameter(),
           rice_hashes.num_entries(), rice_hashes.encoded_data(), &raw_hashes);
@@ -438,8 +386,6 @@
       if (decode_result != DECODE_SUCCESS) {
         return RICE_DECODING_FAILURE;
       } else {
-        RecordDecodeAdditionsTime(metric, TimeTicks::Now() - before,
-                                  store_path_);
         char* raw_hashes_start = reinterpret_cast<char*>(raw_hashes.data());
         size_t raw_hashes_size = sizeof(uint32_t) * raw_hashes.size();
 
@@ -686,7 +632,6 @@
 
   V4StoreFileFormat file_format;
   int64_t file_size;
-  TimeTicks before = TimeTicks::Now();
   {
     // A temporary scope to make sure that |contents| get destroyed as soon as
     // we are doing using it.
@@ -730,7 +675,6 @@
     hash_prefix_map_.clear();
     return HASH_PREFIX_MAP_GENERATION_FAILURE;
   }
-  RecordApplyUpdateTime(kReadFromDisk, TimeTicks::Now() - before, store_path_);
 
   // Update |file_size_| now because we parsed the file correctly.
   file_size_ = file_size;
diff --git a/components/search/OWNERS b/components/search/OWNERS
index 4658f15a..29e15ad 100644
--- a/components/search/OWNERS
+++ b/components/search/OWNERS
@@ -3,7 +3,6 @@
 kristipark@chromium.org
 mathp@chromium.org
 ramyan@chromium.org
-treib@chromium.org
 
 # Original implementors of most of the code, but not active in Chromium anymore:
 # - jered@chromium.org
diff --git a/components/search_engines/prepopulated_engines.json b/components/search_engines/prepopulated_engines.json
index 2375b871..3dedb8c 100644
--- a/components/search_engines/prepopulated_engines.json
+++ b/components/search_engines/prepopulated_engines.json
@@ -28,7 +28,7 @@
     // Increment this if you change the data in ways that mean users with
     // existing data should get a new version. Otherwise, existing data may
     // continue to be used and updates made here will not always appear.
-    "kCurrentDataVersion": 114
+    "kCurrentDataVersion": 115
   },
 
   // The following engines are included in country lists and are added to the
@@ -111,7 +111,7 @@
       "keyword": "google.com",
       "favicon_url": "https://www.google.com/images/branding/product/ico/googleg_lodp.ico",
       "search_url": "{google:baseURL}search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:contextualSearchVersion}ie={inputEncoding}",
-      "suggest_url": "{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&gs_ri={google:suggestRid}&xssi=t&q={searchTerms}&{google:inputType}{google:cursorPosition}{google:currentPageUrl}{google:pageClassification}{google:searchVersion}{google:sessionToken}{google:prefetchQuery}sugkey={google:suggestAPIKeyParameter}",
+      "suggest_url": "{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&gs_ri={google:suggestRid}&xssi=t&q={searchTerms}&{google:inputType}{google:omniboxFocusType}{google:cursorPosition}{google:currentPageUrl}{google:pageClassification}{google:searchVersion}{google:sessionToken}{google:prefetchQuery}sugkey={google:suggestAPIKeyParameter}",
       "image_url": "{google:baseURL}searchbyimage/upload",
       "contextual_search_url": "{google:baseURL}_/contextualsearch?{google:contextualSearchVersion}{google:contextualSearchContextData}",
       "image_url_post_params": "encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight}",
diff --git a/components/search_engines/template_url.cc b/components/search_engines/template_url.cc
index 678510b..15c5ccd1f 100644
--- a/components/search_engines/template_url.cc
+++ b/components/search_engines/template_url.cc
@@ -667,6 +667,9 @@
   } else if (parameter == "google:inputType") {
     replacements->push_back(Replacement(TemplateURLRef::GOOGLE_INPUT_TYPE,
                                         start));
+  } else if (parameter == "google:omniboxFocusType") {
+    replacements->push_back(
+        Replacement(TemplateURLRef::GOOGLE_OMNIBOX_FOCUS_TYPE, start));
   } else if (parameter == "google:iOSSearchLanguage") {
     replacements->push_back(Replacement(GOOGLE_IOS_SEARCH_LANGUAGE, start));
   } else if (parameter == "google:contextualSearchVersion") {
@@ -956,6 +959,42 @@
         HandleReplacement(std::string(), input_encoding, *i, &url);
         break;
 
+      case GOOGLE_CONTEXTUAL_SEARCH_VERSION:
+        if (search_terms_args.contextual_search_params.version >= 0) {
+          HandleReplacement(
+              "ctxs",
+              base::NumberToString(
+                  search_terms_args.contextual_search_params.version),
+              *i, &url);
+        }
+        break;
+
+      case GOOGLE_CONTEXTUAL_SEARCH_CONTEXT_DATA: {
+        DCHECK(!i->is_post_param);
+
+        const SearchTermsArgs::ContextualSearchParams& params =
+            search_terms_args.contextual_search_params;
+        std::vector<std::string> args;
+
+        if (params.contextual_cards_version > 0) {
+          args.push_back("ctxsl_coca=" +
+                         base::NumberToString(params.contextual_cards_version));
+        }
+        if (!params.home_country.empty())
+          args.push_back("ctxs_hc=" + params.home_country);
+        if (params.previous_event_id != 0) {
+          args.push_back("ctxsl_pid=" +
+                         base::NumberToString(params.previous_event_id));
+        }
+        if (params.previous_event_results != 0) {
+          args.push_back("ctxsl_per=" +
+                         base::NumberToString(params.previous_event_results));
+        }
+
+        HandleReplacement(std::string(), base::JoinString(args, "&"), *i, &url);
+        break;
+      }
+
       case GOOGLE_ASSISTED_QUERY_STATS:
         DCHECK(!i->is_post_param);
         if (!search_terms_args.assisted_query_stats.empty()) {
@@ -1014,41 +1053,16 @@
                           *i, &url);
         break;
 
-      case GOOGLE_CONTEXTUAL_SEARCH_VERSION:
-        if (search_terms_args.contextual_search_params.version >= 0) {
-          HandleReplacement(
-              "ctxs",
-              base::NumberToString(
-                  search_terms_args.contextual_search_params.version),
-              *i, &url);
-        }
-        break;
-
-      case GOOGLE_CONTEXTUAL_SEARCH_CONTEXT_DATA: {
+      case GOOGLE_OMNIBOX_FOCUS_TYPE:
         DCHECK(!i->is_post_param);
-
-        const SearchTermsArgs::ContextualSearchParams& params =
-            search_terms_args.contextual_search_params;
-        std::vector<std::string> args;
-
-        if (params.contextual_cards_version > 0) {
-          args.push_back("ctxsl_coca=" +
-                         base::NumberToString(params.contextual_cards_version));
+        if (search_terms_args.omnibox_focus_type !=
+            SearchTermsArgs::OmniboxFocusType::DEFAULT) {
+          HandleReplacement("oft",
+                            base::NumberToString(static_cast<int>(
+                                search_terms_args.omnibox_focus_type)),
+                            *i, &url);
         }
-        if (!params.home_country.empty())
-          args.push_back("ctxs_hc=" + params.home_country);
-        if (params.previous_event_id != 0) {
-          args.push_back("ctxsl_pid=" +
-                         base::NumberToString(params.previous_event_id));
-        }
-        if (params.previous_event_results != 0) {
-          args.push_back("ctxsl_per=" +
-                         base::NumberToString(params.previous_event_results));
-        }
-
-        HandleReplacement(std::string(), base::JoinString(args, "&"), *i, &url);
         break;
-      }
 
       case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION:
         DCHECK(!i->is_post_param);
diff --git a/components/search_engines/template_url.h b/components/search_engines/template_url.h
index 5aa6ffc..7c604d4 100644
--- a/components/search_engines/template_url.h
+++ b/components/search_engines/template_url.h
@@ -76,6 +76,21 @@
     SearchTermsArgs(const SearchTermsArgs& other);
     ~SearchTermsArgs();
 
+    // If the search request is from the omnibox, this enum may specify details
+    // about how the user last interacted with the omnibox.
+    //
+    // These values are used as HTTP GET parameter values. Entries should not be
+    // renumbered and numeric values should never be reused.
+    enum class OmniboxFocusType {
+      // The default value. This is used for any search requests without any
+      // special interaction annotation, including: normal omnibox searches,
+      // as-you-type omnibox suggestions, as well as non-omnibox searches.
+      DEFAULT = 0,
+
+      // This search request is triggered by the user focusing the omnibox.
+      ON_FOCUS = 1,
+    };
+
     struct ContextualSearchParams {
       ContextualSearchParams();
       // Modern constructor, used when the content is sent in the HTTP header
@@ -138,6 +153,10 @@
     // The type the original input query was identified as.
     metrics::OmniboxInputType input_type = metrics::OmniboxInputType::INVALID;
 
+    // If the search request is from the omnibox, this may specify how the user
+    // last interacted with the omnibox.
+    OmniboxFocusType omnibox_focus_type = OmniboxFocusType::DEFAULT;
+
     // The optional assisted query stats, aka AQS, used for logging purposes.
     // This string contains impressions of all autocomplete matches shown
     // at the query submission time.  For privacy reasons, we require the
@@ -325,6 +344,8 @@
     GOOGLE_ASSISTED_QUERY_STATS,
     GOOGLE_BASE_URL,
     GOOGLE_BASE_SUGGEST_URL,
+    GOOGLE_CONTEXTUAL_SEARCH_VERSION,
+    GOOGLE_CONTEXTUAL_SEARCH_CONTEXT_DATA,
     GOOGLE_CURRENT_PAGE_URL,
     GOOGLE_CURSOR_POSITION,
     GOOGLE_IMAGE_ORIGINAL_HEIGHT,
@@ -336,8 +357,7 @@
     GOOGLE_INPUT_TYPE,
     GOOGLE_IOS_SEARCH_LANGUAGE,
     GOOGLE_NTP_IS_THEMED,
-    GOOGLE_CONTEXTUAL_SEARCH_VERSION,
-    GOOGLE_CONTEXTUAL_SEARCH_CONTEXT_DATA,
+    GOOGLE_OMNIBOX_FOCUS_TYPE,
     GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION,
     GOOGLE_PAGE_CLASSIFICATION,
     GOOGLE_PREFETCH_QUERY,
diff --git a/components/search_engines/template_url_unittest.cc b/components/search_engines/template_url_unittest.cc
index b4a4c96..a19a834 100644
--- a/components/search_engines/template_url_unittest.cc
+++ b/components/search_engines/template_url_unittest.cc
@@ -732,6 +732,39 @@
   }
 }
 
+// Tests replacing omnibox focus type (&oft=).
+TEST_F(TemplateURLTest, ReplaceOmniboxFocusType) {
+  struct TestData {
+    const base::string16 search_term;
+    TemplateURLRef::SearchTermsArgs::OmniboxFocusType omnibox_focus_type;
+    const std::string url;
+    const std::string expected_result;
+  } test_data[] = {
+      {ASCIIToUTF16("foo"),
+       TemplateURLRef::SearchTermsArgs::OmniboxFocusType::DEFAULT,
+       "{google:baseURL}?{searchTerms}&{google:omniboxFocusType}",
+       "http://www.google.com/?foo&"},
+      {ASCIIToUTF16("foo"),
+       TemplateURLRef::SearchTermsArgs::OmniboxFocusType::ON_FOCUS,
+       "{google:baseURL}?{searchTerms}&{google:omniboxFocusType}",
+       "http://www.google.com/?foo&oft=1&"},
+  };
+  TemplateURLData data;
+  data.input_encodings.push_back("UTF-8");
+  for (size_t i = 0; i < base::size(test_data); ++i) {
+    data.SetURL(test_data[i].url);
+    TemplateURL url(data);
+    EXPECT_TRUE(url.url_ref().IsValid(search_terms_data_));
+    ASSERT_TRUE(url.url_ref().SupportsReplacement(search_terms_data_));
+    TemplateURLRef::SearchTermsArgs search_terms_args(test_data[i].search_term);
+    search_terms_args.omnibox_focus_type = test_data[i].omnibox_focus_type;
+    GURL result(url.url_ref().ReplaceSearchTerms(search_terms_args,
+                                                 search_terms_data_));
+    ASSERT_TRUE(result.is_valid());
+    EXPECT_EQ(test_data[i].expected_result, result.spec());
+  }
+}
+
 // Tests replacing currentPageUrl.
 TEST_F(TemplateURLTest, ReplaceCurrentPageUrl) {
   struct TestData {
diff --git a/components/search_provider_logos/google_logo_api.cc b/components/search_provider_logos/google_logo_api.cc
index fc0e3e98..8edf2a9c 100644
--- a/components/search_provider_logos/google_logo_api.cc
+++ b/components/search_provider_logos/google_logo_api.cc
@@ -131,6 +131,48 @@
   return result;
 }
 
+// On success returns a pair of <mime_type, data>.
+// On error returns a pair of <string, nullptr>.
+// mime_type should be ignored if data is nullptr.
+std::pair<std::string, scoped_refptr<base::RefCountedString>>
+ParseEncodedImageData(const std::string& encoded_image_data) {
+  std::pair<std::string, scoped_refptr<base::RefCountedString>> result;
+
+  GURL encoded_image_uri(encoded_image_data);
+  if (!encoded_image_uri.is_valid() ||
+      !encoded_image_uri.SchemeIs(url::kDataScheme)) {
+    return result;
+  }
+  std::string content = encoded_image_uri.GetContent();
+  // The content should look like this: "image/png;base64,aaa..." (where
+  // "aaa..." is the base64-encoded image data).
+  size_t mime_type_end = content.find_first_of(';');
+  if (mime_type_end == std::string::npos)
+    return result;
+
+  std::string mime_type = content.substr(0, mime_type_end);
+
+  size_t base64_begin = mime_type_end + 1;
+  size_t base64_end = content.find_first_of(',', base64_begin);
+  if (base64_end == std::string::npos)
+    return result;
+  base::StringPiece base64(content.begin() + base64_begin,
+                           content.begin() + base64_end);
+  if (base64 != "base64")
+    return result;
+
+  size_t data_begin = base64_end + 1;
+  base::StringPiece data(content.begin() + data_begin, content.end());
+
+  std::string decoded_data;
+  if (!base::Base64Decode(data, &decoded_data))
+    return result;
+
+  result.first = mime_type;
+  result.second = base::RefCountedString::TakeString(&decoded_data);
+  return result;
+}
+
 }  // namespace
 
 std::unique_ptr<EncodedLogo> ParseDoodleLogoResponse(
@@ -187,6 +229,7 @@
     }
   }
 
+  const bool is_simple = (logo->metadata.type == LogoType::SIMPLE);
   const bool is_animated = (logo->metadata.type == LogoType::ANIMATED);
   bool is_interactive = (logo->metadata.type == LogoType::INTERACTIVE);
 
@@ -199,6 +242,19 @@
     logo->metadata.animated_url = ParseUrl(*image, "url", base_url);
     if (!logo->metadata.animated_url.is_valid())
       return nullptr;
+
+    const base::DictionaryValue* dark_image = nullptr;
+    if (ddljson->GetDictionary("dark_large_image", &dark_image))
+      logo->metadata.dark_animated_url = ParseUrl(*dark_image, "url", base_url);
+  }
+
+  if (is_simple || is_animated) {
+    const base::DictionaryValue* image = nullptr;
+    std::string bg_color;
+    if (ddljson->GetDictionary("dark_large_image", &image)) {
+      image->GetString("background_color", &bg_color);
+    }
+    logo->metadata.dark_background_color = bg_color;
   }
 
   const bool is_eligible_for_share_button =
@@ -227,6 +283,21 @@
                                 &logo->metadata.share_button_bg);
       }
     }
+    const base::DictionaryValue* dark_share_button = nullptr;
+    if (ddljson->GetDictionary("dark_share_button", &dark_share_button)) {
+      if (logo->metadata.short_link.is_valid()) {
+        dark_share_button->GetInteger("offset_x",
+                                      &logo->metadata.dark_share_button_x);
+        dark_share_button->GetInteger("offset_y",
+                                      &logo->metadata.dark_share_button_y);
+        dark_share_button->GetDouble("opacity",
+                                     &logo->metadata.dark_share_button_opacity);
+        dark_share_button->GetString("icon_image",
+                                     &logo->metadata.dark_share_button_icon);
+        dark_share_button->GetString("background_color",
+                                     &logo->metadata.dark_share_button_bg);
+      }
+    }
   }
 
   logo->metadata.full_page_url =
@@ -237,33 +308,25 @@
   std::string encoded_image_data;
   if (ddljson->GetString("cta_data_uri", &encoded_image_data) ||
       ddljson->GetString("data_uri", &encoded_image_data)) {
-    GURL encoded_image_uri(encoded_image_data);
-    if (!encoded_image_uri.is_valid() ||
-        !encoded_image_uri.SchemeIs(url::kDataScheme)) {
+    std::string mime_type;
+    scoped_refptr<base::RefCountedString> data;
+    std::tie(mime_type, data) = ParseEncodedImageData(encoded_image_data);
+    if (!data)
       return nullptr;
-    }
-    std::string content = encoded_image_uri.GetContent();
-    // The content should look like this: "image/png;base64,aaa..." (where
-    // "aaa..." is the base64-encoded image data).
-    size_t mime_type_end = content.find_first_of(';');
-    if (mime_type_end == std::string::npos)
-      return nullptr;
-    logo->metadata.mime_type = content.substr(0, mime_type_end);
+    logo->metadata.mime_type = mime_type;
+    logo->encoded_image = data;
+  }
 
-    size_t base64_begin = mime_type_end + 1;
-    size_t base64_end = content.find_first_of(',', base64_begin);
-    if (base64_end == std::string::npos)
-      return nullptr;
-    base::StringPiece base64(content.begin() + base64_begin,
-                             content.begin() + base64_end);
-    if (base64 != "base64")
-      return nullptr;
+  std::string dark_encoded_image_data;
+  if (ddljson->GetString("dark_cta_data_uri", &dark_encoded_image_data) ||
+      ddljson->GetString("dark_data_uri", &dark_encoded_image_data)) {
+    std::string mime_type;
+    scoped_refptr<base::RefCountedString> data;
+    std::tie(mime_type, data) = ParseEncodedImageData(dark_encoded_image_data);
 
-    size_t data_begin = base64_end + 1;
-    base::StringPiece data(content.begin() + data_begin, content.end());
-    logo->encoded_image = base::MakeRefCounted<base::RefCountedString>();
-    if (!base::Base64Decode(data, &logo->encoded_image->data()))
-      return nullptr;
+    if (data)
+      logo->metadata.dark_mime_type = mime_type;
+    logo->dark_encoded_image = data;
   }
 
   logo->metadata.on_click_url = ParseUrl(*ddljson, "target_url", base_url);
diff --git a/components/search_provider_logos/google_logo_api_unittest.cc b/components/search_provider_logos/google_logo_api_unittest.cc
index fa56f9a..ae38846 100644
--- a/components/search_provider_logos/google_logo_api_unittest.cc
+++ b/components/search_provider_logos/google_logo_api_unittest.cc
@@ -163,11 +163,13 @@
 TEST(GoogleNewLogoApiTest, ParsesStaticImage) {
   const GURL base_url("https://base.doo/");
   // Note: The base64 encoding of "abc" is "YWJj".
+  // Note: The base64 encoding of "xyz" is "eHl6".
   const std::string json = R"json()]}'
 {
   "ddljson": {
     "target_url": "/target",
-    "data_uri": "data:image/png;base64,YWJj"
+    "data_uri": "data:image/png;base64,YWJj",
+    "dark_data_uri": "data:image/png;base64,eHl6"
   }
 })json";
 
@@ -178,6 +180,7 @@
   ASSERT_FALSE(failed);
   ASSERT_TRUE(logo);
   EXPECT_EQ("abc", logo->encoded_image->data());
+  EXPECT_EQ("xyz", logo->dark_encoded_image->data());
   EXPECT_EQ(LogoType::SIMPLE, logo->metadata.type);
 }
 
@@ -196,6 +199,13 @@
       "offset_x": 111,
       "offset_y": 222,
       "opacity": 0.5
+    },
+    "dark_share_button": {
+      "background_color": "#ee22bb",
+      "icon_image": "dark_test_img",
+      "offset_x": 99,
+      "offset_y": 191,
+      "opacity": 0.7
     }
   }
 })json";
@@ -215,6 +225,11 @@
   EXPECT_EQ(111, logo->metadata.share_button_x);
   EXPECT_EQ(222, logo->metadata.share_button_y);
   EXPECT_EQ(0.5, logo->metadata.share_button_opacity);
+  EXPECT_EQ("#ee22bb", logo->metadata.dark_share_button_bg);
+  EXPECT_EQ("dark_test_img", logo->metadata.dark_share_button_icon);
+  EXPECT_EQ(99, logo->metadata.dark_share_button_x);
+  EXPECT_EQ(191, logo->metadata.dark_share_button_y);
+  EXPECT_EQ(0.7, logo->metadata.dark_share_button_opacity);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesNoShareButtonIfWrongShortLinkFormat) {
@@ -232,6 +247,13 @@
       "offset_x": 111,
       "offset_y": 222,
       "opacity": 0.5
+    },
+    "dark_share_button": {
+      "background_color": "#ee22bb",
+      "icon_image": "dark_test_img",
+      "offset_x": 99,
+      "offset_y": 191,
+      "opacity": 0.7
     }
   }
 })json";
@@ -249,6 +271,10 @@
   EXPECT_EQ(-1, logo->metadata.share_button_x);
   EXPECT_EQ(-1, logo->metadata.share_button_y);
   EXPECT_EQ(0, logo->metadata.share_button_opacity);
+  ASSERT_TRUE(logo->metadata.dark_share_button_icon.empty());
+  EXPECT_EQ(-1, logo->metadata.dark_share_button_x);
+  EXPECT_EQ(-1, logo->metadata.dark_share_button_y);
+  EXPECT_EQ(0, logo->metadata.dark_share_button_opacity);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesNoShareButtonIfShortLinkInvalid) {
@@ -266,6 +292,13 @@
       "offset_x": 111,
       "offset_y": 222,
       "opacity": 0.5
+    },
+    "dark_share_button": {
+      "background_color": "#ee22bb",
+      "icon_image": "dark_test_img",
+      "offset_x": 99,
+      "offset_y": 191,
+      "opacity": 0.7
     }
   }
 })json";
@@ -283,6 +316,10 @@
   EXPECT_EQ(-1, logo->metadata.share_button_x);
   EXPECT_EQ(-1, logo->metadata.share_button_y);
   EXPECT_EQ(0, logo->metadata.share_button_opacity);
+  ASSERT_TRUE(logo->metadata.dark_share_button_icon.empty());
+  EXPECT_EQ(-1, logo->metadata.dark_share_button_x);
+  EXPECT_EQ(-1, logo->metadata.dark_share_button_y);
+  EXPECT_EQ(0, logo->metadata.dark_share_button_opacity);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesShareButtonForAnimatedDoodle) {
@@ -305,6 +342,13 @@
       "offset_x": 111,
       "offset_y": 222,
       "opacity": 0.5
+    },
+    "dark_share_button": {
+      "background_color": "#ee22bb",
+      "icon_image": "dark_test_img",
+      "offset_x": 99,
+      "offset_y": 191,
+      "opacity": 0.7
     }
   }
 })json";
@@ -326,11 +370,17 @@
   EXPECT_EQ(111, logo->metadata.share_button_x);
   EXPECT_EQ(222, logo->metadata.share_button_y);
   EXPECT_EQ(0.5, logo->metadata.share_button_opacity);
+  EXPECT_EQ("#ee22bb", logo->metadata.dark_share_button_bg);
+  EXPECT_EQ("dark_test_img", logo->metadata.dark_share_button_icon);
+  EXPECT_EQ(99, logo->metadata.dark_share_button_x);
+  EXPECT_EQ(191, logo->metadata.dark_share_button_y);
+  EXPECT_EQ(0.7, logo->metadata.dark_share_button_opacity);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesAnimatedImage) {
   const GURL base_url("https://base.doo/");
   // Note: The base64 encoding of "abc" is "YWJj".
+  // Note: The base64 encoding of "xyz" is "eHl6".
   const std::string json = R"json()]}'
 {
   "ddljson": {
@@ -340,7 +390,12 @@
       "is_animated_gif": true,
       "url": "https://www.doodle.com/image.gif"
     },
-    "cta_data_uri": "data:image/png;base64,YWJj"
+    "dark_large_image": {
+      "is_animated_gif": true,
+      "url": "https://www.doodle.com/dark_image.gif"
+    },
+    "cta_data_uri": "data:image/png;base64,YWJj",
+    "dark_cta_data_uri": "data:image/png;base64,eHl6"
   }
 })json";
 
@@ -352,7 +407,10 @@
   ASSERT_TRUE(logo);
   EXPECT_EQ(GURL("https://www.doodle.com/image.gif"),
             logo->metadata.animated_url);
+  EXPECT_EQ(GURL("https://www.doodle.com/dark_image.gif"),
+            logo->metadata.dark_animated_url);
   EXPECT_EQ("abc", logo->encoded_image->data());
+  EXPECT_EQ("xyz", logo->dark_encoded_image->data());
   EXPECT_EQ(LogoType::ANIMATED, logo->metadata.type);
 }
 
diff --git a/components/search_provider_logos/logo_cache.cc b/components/search_provider_logos/logo_cache.cc
index c4bccf9..9f5d247 100644
--- a/components/search_provider_logos/logo_cache.cc
+++ b/components/search_provider_logos/logo_cache.cc
@@ -30,19 +30,28 @@
 const char kFullPageURLKey[] = "full_page_url";
 const char kAltTextKey[] = "alt_text";
 const char kMimeTypeKey[] = "mime_type";
+const char kDarkMimeTypeKey[] = "dark_mime_type";
 const char kNumBytesKey[] = "num_bytes";
+const char kDarkNumBytesKey[] = "dark_num_bytes";
 const char kAnimatedUrlKey[] = "animated_url";
+const char kDarkAnimatedUrlKey[] = "dark_animated_url";
 const char kLogUrlKey[] = "log_url";
 const char kCtaLogUrlKey[] = "cta_log_url";
 const char kShortLinkKey[] = "short_link";
 const char kIframeWidthPx[] = "iframe_width_px";
 const char kIframeHeightPx[] = "iframe_height_px";
+const char kDarkBackgroundColorKey[] = "dark_background_color";
 
 const char kShareButtonX[] = "share_button_x";
 const char kShareButtonY[] = "share_button_y";
 const char kShareButtonOpacity[] = "share_button_opacity";
 const char kShareButtonIcon[] = "share_button_icon";
 const char kShareButtonBg[] = "share_button_bg";
+const char kDarkShareButtonX[] = "dark_share_button_x";
+const char kDarkShareButtonY[] = "dark_share_button_y";
+const char kDarkShareButtonOpacity[] = "dark_share_button_opacity";
+const char kDarkShareButtonIcon[] = "dark_share_button_icon";
+const char kDarkShareButtonBg[] = "dark_share_button_bg";
 
 const char kSimpleType[] = "SIMPLE";
 const char kAnimatedType[] = "ANIMATED";
@@ -134,8 +143,12 @@
 
   logo_num_bytes_ =
       logo->encoded_image ? static_cast<int>(logo->encoded_image->size()) : 0;
+  dark_logo_num_bytes_ =
+      logo->dark_encoded_image
+          ? static_cast<int>(logo->dark_encoded_image->size())
+          : 0;
   UpdateMetadata(std::make_unique<LogoMetadata>(logo->metadata));
-  WriteLogo(logo->encoded_image);
+  WriteLogo(logo->encoded_image, logo->dark_encoded_image);
 }
 
 std::unique_ptr<EncodedLogo> LogoCache::GetCachedLogo() {
@@ -145,11 +158,13 @@
   if (!metadata_)
     return nullptr;
 
+  base::FilePath logo_path = GetLogoPath();
+  base::FilePath dark_logo_path = GetDarkLogoPath();
   scoped_refptr<base::RefCountedString> encoded_image;
   if (logo_num_bytes_ != 0) {
     encoded_image = new base::RefCountedString();
 
-    if (!base::ReadFileToString(GetLogoPath(), &encoded_image->data())) {
+    if (!base::ReadFileToString(logo_path, &encoded_image->data())) {
       UpdateMetadata(nullptr);
       return nullptr;
     }
@@ -162,8 +177,27 @@
     }
   }
 
+  scoped_refptr<base::RefCountedString> dark_encoded_image;
+  if (dark_logo_num_bytes_ != 0) {
+    dark_encoded_image = new base::RefCountedString();
+
+    if (!base::ReadFileToString(dark_logo_path, &dark_encoded_image->data())) {
+      UpdateMetadata(nullptr);
+      return nullptr;
+    }
+
+    if (dark_encoded_image->size() !=
+        static_cast<size_t>(dark_logo_num_bytes_)) {
+      // Delete corrupt metadata and logo.
+      DeleteLogoAndMetadata();
+      UpdateMetadata(nullptr);
+      return nullptr;
+    }
+  }
+
   std::unique_ptr<EncodedLogo> logo(new EncodedLogo());
   logo->encoded_image = encoded_image;
+  logo->dark_encoded_image = dark_encoded_image;
   logo->metadata = *metadata_;
   return logo;
 }
@@ -171,7 +205,8 @@
 // static
 std::unique_ptr<LogoMetadata> LogoCache::LogoMetadataFromString(
     const std::string& str,
-    int* logo_num_bytes) {
+    int* logo_num_bytes,
+    int* dark_logo_num_bytes) {
   std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(str);
   base::DictionaryValue* dict;
   if (!value || !value->GetAsDictionary(&dict))
@@ -183,6 +218,7 @@
   std::string on_click_url;
   std::string full_page_url;
   std::string animated_url;
+  std::string dark_animated_url;
   std::string log_url;
   std::string cta_log_url;
   std::string short_link;
@@ -193,20 +229,32 @@
       !dict->GetString(kFullPageURLKey, &full_page_url) ||
       !dict->GetString(kAltTextKey, &metadata->alt_text) ||
       !dict->GetString(kAnimatedUrlKey, &animated_url) ||
+      !dict->GetString(kDarkAnimatedUrlKey, &dark_animated_url) ||
       !dict->GetString(kLogUrlKey, &log_url) ||
       !dict->GetString(kCtaLogUrlKey, &cta_log_url) ||
       !dict->GetString(kShortLinkKey, &short_link) ||
       !dict->GetString(kMimeTypeKey, &metadata->mime_type) ||
+      !dict->GetString(kDarkMimeTypeKey, &metadata->dark_mime_type) ||
       !dict->GetBoolean(kCanShowAfterExpirationKey,
                         &metadata->can_show_after_expiration) ||
       !dict->GetInteger(kNumBytesKey, logo_num_bytes) ||
+      !dict->GetInteger(kDarkNumBytesKey, dark_logo_num_bytes) ||
       !dict->GetInteger(kShareButtonX, &metadata->share_button_x) ||
       !dict->GetInteger(kShareButtonY, &metadata->share_button_y) ||
       !dict->GetDouble(kShareButtonOpacity, &metadata->share_button_opacity) ||
       !dict->GetString(kShareButtonIcon, &metadata->share_button_icon) ||
       !dict->GetString(kShareButtonBg, &metadata->share_button_bg) ||
+      !dict->GetInteger(kDarkShareButtonX, &metadata->dark_share_button_x) ||
+      !dict->GetInteger(kDarkShareButtonY, &metadata->dark_share_button_y) ||
+      !dict->GetDouble(kDarkShareButtonOpacity,
+                       &metadata->dark_share_button_opacity) ||
+      !dict->GetString(kDarkShareButtonIcon,
+                       &metadata->dark_share_button_icon) ||
+      !dict->GetString(kDarkShareButtonBg, &metadata->dark_share_button_bg) ||
       !dict->GetInteger(kIframeWidthPx, &metadata->iframe_width_px) ||
       !dict->GetInteger(kIframeHeightPx, &metadata->iframe_height_px) ||
+      !dict->GetString(kDarkBackgroundColorKey,
+                       &metadata->dark_background_color) ||
       !GetTimeValue(*dict, kExpirationTimeKey, &metadata->expiration_time)) {
     return nullptr;
   }
@@ -215,6 +263,7 @@
   metadata->on_click_url = GURL(on_click_url);
   metadata->full_page_url = GURL(full_page_url);
   metadata->animated_url = GURL(animated_url);
+  metadata->dark_animated_url = GURL(dark_animated_url);
   metadata->log_url = GURL(log_url);
   metadata->cta_log_url = GURL(cta_log_url);
   metadata->short_link = GURL(short_link);
@@ -225,6 +274,7 @@
 // static
 void LogoCache::LogoMetadataToString(const LogoMetadata& metadata,
                                      int num_bytes,
+                                     int dark_num_bytes,
                                      std::string* str) {
   base::DictionaryValue dict;
   dict.SetString(kSourceUrlKey, metadata.source_url.spec());
@@ -234,20 +284,29 @@
   dict.SetString(kFullPageURLKey, metadata.full_page_url.spec());
   dict.SetString(kAltTextKey, metadata.alt_text);
   dict.SetString(kAnimatedUrlKey, metadata.animated_url.spec());
+  dict.SetString(kDarkAnimatedUrlKey, metadata.dark_animated_url.spec());
   dict.SetString(kLogUrlKey, metadata.log_url.spec());
   dict.SetString(kCtaLogUrlKey, metadata.cta_log_url.spec());
   dict.SetString(kShortLinkKey, metadata.short_link.spec());
   dict.SetString(kMimeTypeKey, metadata.mime_type);
+  dict.SetString(kDarkMimeTypeKey, metadata.dark_mime_type);
   dict.SetBoolean(kCanShowAfterExpirationKey,
                   metadata.can_show_after_expiration);
   dict.SetInteger(kNumBytesKey, num_bytes);
+  dict.SetInteger(kDarkNumBytesKey, dark_num_bytes);
   dict.SetInteger(kShareButtonX, metadata.share_button_x);
   dict.SetInteger(kShareButtonY, metadata.share_button_y);
   dict.SetDouble(kShareButtonOpacity, metadata.share_button_opacity);
   dict.SetString(kShareButtonIcon, metadata.share_button_icon);
   dict.SetString(kShareButtonBg, metadata.share_button_bg);
+  dict.SetInteger(kDarkShareButtonX, metadata.dark_share_button_x);
+  dict.SetInteger(kDarkShareButtonY, metadata.dark_share_button_y);
+  dict.SetDouble(kDarkShareButtonOpacity, metadata.dark_share_button_opacity);
+  dict.SetString(kDarkShareButtonIcon, metadata.dark_share_button_icon);
+  dict.SetString(kDarkShareButtonBg, metadata.dark_share_button_bg);
   dict.SetInteger(kIframeWidthPx, metadata.iframe_width_px);
   dict.SetInteger(kIframeHeightPx, metadata.iframe_height_px);
+  dict.SetString(kDarkBackgroundColorKey, metadata.dark_background_color);
   SetTimeValue(dict, kExpirationTimeKey, metadata.expiration_time);
   base::JSONWriter::Write(dict, str);
 }
@@ -256,6 +315,10 @@
   return cache_directory_.Append(FILE_PATH_LITERAL("logo"));
 }
 
+base::FilePath LogoCache::GetDarkLogoPath() {
+  return cache_directory_.Append(FILE_PATH_LITERAL("dark_logo"));
+}
+
 base::FilePath LogoCache::GetMetadataPath() {
   return cache_directory_.Append(FILE_PATH_LITERAL("metadata"));
 }
@@ -273,7 +336,8 @@
   base::FilePath metadata_path = GetMetadataPath();
   std::string str;
   if (base::ReadFileToString(metadata_path, &str)) {
-    metadata = LogoMetadataFromString(str, &logo_num_bytes_);
+    metadata =
+        LogoMetadataFromString(str, &logo_num_bytes_, &dark_logo_num_bytes_);
     if (!metadata) {
       // Delete corrupt metadata and logo.
       DeleteLogoAndMetadata();
@@ -288,11 +352,13 @@
     return;
 
   std::string str;
-  LogoMetadataToString(*metadata_, logo_num_bytes_, &str);
+  LogoMetadataToString(*metadata_, logo_num_bytes_, dark_logo_num_bytes_, &str);
   base::WriteFile(GetMetadataPath(), str.data(), static_cast<int>(str.size()));
 }
 
-void LogoCache::WriteLogo(scoped_refptr<base::RefCountedMemory> encoded_image) {
+void LogoCache::WriteLogo(
+    scoped_refptr<base::RefCountedMemory> encoded_image,
+    scoped_refptr<base::RefCountedMemory> dark_encoded_image) {
   if (!EnsureCacheDirectoryExists())
     return;
 
@@ -305,6 +371,7 @@
   // First, delete the metadata file, then update the logo file, then update the
   // metadata file.
   base::FilePath logo_path = GetLogoPath();
+  base::FilePath dark_logo_path = GetDarkLogoPath();
   base::FilePath metadata_path = GetMetadataPath();
 
   if (!base::DeleteFile(metadata_path, false))
@@ -316,12 +383,20 @@
     base::DeleteFile(logo_path, false);
     return;
   }
+  if (dark_encoded_image &&
+      base::WriteFile(dark_logo_path, dark_encoded_image->front_as<char>(),
+                      static_cast<int>(dark_encoded_image->size())) == -1) {
+    base::DeleteFile(logo_path, false);
+    base::DeleteFile(dark_logo_path, false);
+    return;
+  }
 
   WriteMetadata();
 }
 
 void LogoCache::DeleteLogoAndMetadata() {
   base::DeleteFile(GetLogoPath(), false);
+  base::DeleteFile(GetDarkLogoPath(), false);
   base::DeleteFile(GetMetadataPath(), false);
 }
 
diff --git a/components/search_provider_logos/logo_cache.h b/components/search_provider_logos/logo_cache.h
index da421c4..2310b0d 100644
--- a/components/search_provider_logos/logo_cache.h
+++ b/components/search_provider_logos/logo_cache.h
@@ -66,15 +66,18 @@
   // if |str| cannot be converted.
   static std::unique_ptr<LogoMetadata> LogoMetadataFromString(
       const std::string& str,
-      int* logo_num_bytes);
+      int* logo_num_bytes,
+      int* dark_logo_num_bytes);
 
   // Converts |metadata| to a string and stores it in |str|.
   static void LogoMetadataToString(const LogoMetadata& metadata,
                                    int logo_num_bytes,
+                                   int dark_logo_num_bytes,
                                    std::string* str);
 
   // Returns the path where the cached logo will be saved.
   base::FilePath GetLogoPath();
+  base::FilePath GetDarkLogoPath();
 
   // Returns the path where the metadata for the cached logo will be saved.
   base::FilePath GetMetadataPath();
@@ -92,7 +95,8 @@
 
   // Writes |metadata_| to the cached metadata file and |encoded_image| to the
   // cached logo file.
-  void WriteLogo(scoped_refptr<base::RefCountedMemory> encoded_image);
+  void WriteLogo(scoped_refptr<base::RefCountedMemory> encoded_image,
+                 scoped_refptr<base::RefCountedMemory> dark_encoded_image);
 
   // Deletes all the cache files.
   void DeleteLogoAndMetadata();
@@ -114,7 +118,8 @@
   // The number of bytes in the logo file, as recorded in the metadata file.
   // Valid iff |metadata_is_valid_|. This is used to verify that the logo file
   // is complete and corresponds to the current metadata file.
-  int logo_num_bytes_;
+  int logo_num_bytes_ = 0;
+  int dark_logo_num_bytes_ = 0;
 
   // Ensure LogoCache is only used sequentially.
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/components/search_provider_logos/logo_cache_unittest.cc b/components/search_provider_logos/logo_cache_unittest.cc
index 82714fd6..a1b754d8 100644
--- a/components/search_provider_logos/logo_cache_unittest.cc
+++ b/components/search_provider_logos/logo_cache_unittest.cc
@@ -31,8 +31,12 @@
   metadata.short_link = GURL("https://g.co/");
   metadata.on_click_url = GURL("https://www.google.com/search?q=chicken");
   metadata.animated_url = GURL("http://www.google.com/logos/doodle.png");
+  metadata.dark_animated_url =
+      GURL("http://www.google.com/logos/dark_doodle.png");
   metadata.alt_text = "A logo about chickens";
   metadata.mime_type = "image/jpeg";
+  metadata.dark_mime_type = "image/jpeg";
+  metadata.dark_background_color = "#ABC123";
   metadata.log_url = GURL("https://www.google.com/ddllog?a=b");
   metadata.cta_log_url = GURL("https://www.google.com/ddllog?c=d");
   metadata.share_button_x = 200;
@@ -40,6 +44,11 @@
   metadata.share_button_opacity = 0.5;
   metadata.share_button_icon = "test_img";
   metadata.share_button_bg = "#ff22ff";
+  metadata.dark_share_button_x = 150;
+  metadata.dark_share_button_y = 50;
+  metadata.dark_share_button_opacity = 0.7;
+  metadata.dark_share_button_icon = "dark_test_img";
+  metadata.dark_share_button_bg = "#22ff22";
   return metadata;
 }
 
@@ -71,6 +80,7 @@
 std::unique_ptr<EncodedLogo> GetExampleLogo() {
   auto logo = std::make_unique<EncodedLogo>();
   logo->encoded_image = CreateExampleImage(837);
+  logo->dark_encoded_image = CreateExampleImage(738);
   logo->metadata = GetExampleMetadata();
   return logo;
 }
@@ -78,6 +88,7 @@
 std::unique_ptr<EncodedLogo> GetExampleLogo2() {
   auto logo = std::make_unique<EncodedLogo>();
   logo->encoded_image = CreateExampleImage(345);
+  logo->dark_encoded_image = CreateExampleImage(543);
   logo->metadata = GetExampleMetadata2();
   return logo;
 }
@@ -85,6 +96,7 @@
 std::unique_ptr<EncodedLogo> GetExampleLogoWithoutImage() {
   auto logo = std::make_unique<EncodedLogo>();
   logo->encoded_image = nullptr;
+  logo->dark_encoded_image = nullptr;
   logo->metadata = GetExampleMetadata2();
   return logo;
 }
@@ -112,6 +124,16 @@
   EXPECT_EQ(expected_metadata.share_button_icon,
             actual_metadata.share_button_icon);
   EXPECT_EQ(expected_metadata.share_button_bg, actual_metadata.share_button_bg);
+  EXPECT_EQ(expected_metadata.dark_share_button_x,
+            actual_metadata.dark_share_button_x);
+  EXPECT_EQ(expected_metadata.dark_share_button_y,
+            actual_metadata.dark_share_button_y);
+  EXPECT_EQ(expected_metadata.dark_share_button_opacity,
+            actual_metadata.dark_share_button_opacity);
+  EXPECT_EQ(expected_metadata.dark_share_button_icon,
+            actual_metadata.dark_share_button_icon);
+  EXPECT_EQ(expected_metadata.dark_share_button_bg,
+            actual_metadata.dark_share_button_bg);
 }
 
 void ExpectLogosEqual(const EncodedLogo& expected_logo,
@@ -119,6 +141,10 @@
   ASSERT_TRUE(expected_logo.encoded_image.get());
   ASSERT_TRUE(actual_logo.encoded_image.get());
   EXPECT_TRUE(expected_logo.encoded_image->Equals(actual_logo.encoded_image));
+  ASSERT_TRUE(expected_logo.dark_encoded_image.get());
+  ASSERT_TRUE(actual_logo.dark_encoded_image.get());
+  EXPECT_TRUE(
+      expected_logo.dark_encoded_image->Equals(actual_logo.dark_encoded_image));
   ExpectMetadataEqual(expected_logo.metadata, actual_logo.metadata);
 }
 
@@ -186,25 +212,29 @@
   LogoMetadata metadata = GetExampleMetadata();
   std::string metadata_str;
   int logo_num_bytes = 33;
-  LogoCache::LogoMetadataToString(metadata, logo_num_bytes, &metadata_str);
-  std::unique_ptr<LogoMetadata> metadata2 =
-      LogoCache::LogoMetadataFromString(metadata_str, &logo_num_bytes);
+  int dark_logo_num_bytes = 44;
+  LogoCache::LogoMetadataToString(metadata, logo_num_bytes, dark_logo_num_bytes,
+                                  &metadata_str);
+  std::unique_ptr<LogoMetadata> metadata2 = LogoCache::LogoMetadataFromString(
+      metadata_str, &logo_num_bytes, &dark_logo_num_bytes);
   ASSERT_TRUE(metadata2);
   ExpectMetadataEqual(metadata, *metadata2);
 }
 
 TEST(LogoCacheSerializationTest, DeserializeCorruptMetadata) {
   int logo_num_bytes = 33;
-  std::unique_ptr<LogoMetadata> metadata =
-      LogoCache::LogoMetadataFromString("", &logo_num_bytes);
+  int dark_logo_num_bytes = 44;
+  std::unique_ptr<LogoMetadata> metadata = LogoCache::LogoMetadataFromString(
+      "", &logo_num_bytes, &dark_logo_num_bytes);
   ASSERT_FALSE(metadata);
 
   LogoMetadata example_metadata = GetExampleMetadata2();
   std::string corrupt_str;
-  LogoCache::LogoMetadataToString(
-      example_metadata, logo_num_bytes, &corrupt_str);
+  LogoCache::LogoMetadataToString(example_metadata, logo_num_bytes,
+                                  dark_logo_num_bytes, &corrupt_str);
   corrupt_str.append("@");
-  metadata = LogoCache::LogoMetadataFromString(corrupt_str, &logo_num_bytes);
+  metadata = LogoCache::LogoMetadataFromString(corrupt_str, &logo_num_bytes,
+                                               &dark_logo_num_bytes);
   ASSERT_FALSE(metadata);
 }
 
diff --git a/components/search_provider_logos/logo_common.h b/components/search_provider_logos/logo_common.h
index a26e38c9..aee03aa 100644
--- a/components/search_provider_logos/logo_common.h
+++ b/components/search_provider_logos/logo_common.h
@@ -62,11 +62,21 @@
   // ANIMATED: The mime type of the CTA image.
   std::string mime_type;
 
+  // SIMPLE: The mime type of the dark logo image. May be empty.
+  // ANIMATED: The mime type of the dark CTA image. May be empty.
+  std::string dark_mime_type;
+
+  // SIMPLE, ANIMATED: The background color to use in dark mode.
+  // INTERACTIVE: not used.
+  std::string dark_background_color;
+
   // ANIMATED: The URL for an animated image to display when the call to action
   // logo is clicked. If |animated_url| is not empty, |encoded_image| refers to
   // a call to action image.
   // SIMPLE, INTERACTIVE: not used.
   GURL animated_url;
+  GURL dark_animated_url;
+
   // The URL to ping when the CTA image is clicked. May be empty.
   GURL cta_log_url;
   // The URL to ping when the main image is clicked (i.e. the animated image if
@@ -99,18 +109,23 @@
 
   // Share button x position
   int share_button_x = -1;
+  int dark_share_button_x = -1;
 
   // Share button y position
   int share_button_y = -1;
+  int dark_share_button_y = -1;
 
   // Share button opacity
   double share_button_opacity = 0;
+  double dark_share_button_opacity = 0;
 
   // Share button icon image, uses Data URI format.
   std::string share_button_icon;
+  std::string dark_share_button_icon;
 
   // Share button background color, uses hex format.
   std::string share_button_bg;
+  std::string dark_share_button_bg;
 };
 
 enum class LogoCallbackReason {
@@ -147,6 +162,8 @@
 
   // The jpeg- or png-encoded image.
   scoped_refptr<base::RefCountedString> encoded_image;
+  // The jpeg- or png-encoded dark image. May be null.
+  scoped_refptr<base::RefCountedString> dark_encoded_image;
   // Metadata about the logo.
   LogoMetadata metadata;
 };
@@ -158,8 +175,10 @@
   Logo();
   ~Logo();
 
-  // The logo image.
+  // The light mode logo image.
   SkBitmap image;
+  // The dark mode logo image.
+  SkBitmap dark_image;
   // Metadata about the logo.
   LogoMetadata metadata;
 };
diff --git a/components/search_provider_logos/logo_service_impl.cc b/components/search_provider_logos/logo_service_impl.cc
index eb592a9..d7991f8 100644
--- a/components/search_provider_logos/logo_service_impl.cc
+++ b/components/search_provider_logos/logo_service_impl.cc
@@ -394,19 +394,19 @@
 
   if (cached_logo && cached_logo->encoded_image) {
     // Store the value of logo->encoded_image for use below. This ensures that
-    // logo->encoded_image is evaulated before base::Passed(&logo), which sets
+    // logo->encoded_image is evaluated before base::Passed(&logo), which sets
     // logo to NULL.
     scoped_refptr<base::RefCountedString> encoded_image =
         cached_logo->encoded_image;
     image_decoder_->DecodeImage(
         encoded_image->data(), gfx::Size(),  // No particular size desired.
         ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
-            &LogoServiceImpl::OnCachedLogoAvailable,
+            &LogoServiceImpl::OnLightCachedImageDecoded,
             weak_ptr_factory_.GetWeakPtr(), base::Passed(&cached_logo))));
   } else if (cached_logo) {
-    OnCachedLogoAvailable(std::move(cached_logo), SkBitmap());
+    OnCachedLogoAvailable(std::move(cached_logo), SkBitmap(), SkBitmap());
   } else {
-    OnCachedLogoAvailable({}, SkBitmap());
+    OnCachedLogoAvailable({}, SkBitmap(), SkBitmap());
   }
 }
 
@@ -423,15 +423,41 @@
                                 base::Unretained(logo_cache_.get()), metadata));
 }
 
+void LogoServiceImpl::OnLightCachedImageDecoded(
+    std::unique_ptr<EncodedLogo> cached_logo,
+    const SkBitmap& image) {
+  if (cached_logo->metadata.dark_mime_type.empty()) {
+    OnCachedLogoAvailable(std::move(cached_logo), image, SkBitmap());
+    return;
+  }
+
+  // Store the value of logo->dark_encoded_image for use below. This ensures
+  // that logo->dark_encoded_image is evaluated before base::Passed(&logo),
+  // which sets logo to NULL.
+  scoped_refptr<base::RefCountedString> dark_encoded_image =
+      cached_logo->dark_encoded_image;
+
+  image_decoder_->DecodeImage(
+      dark_encoded_image->data(), gfx::Size(),  // No particular size desired.
+      ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
+          &LogoServiceImpl::OnCachedLogoAvailable,
+          weak_ptr_factory_.GetWeakPtr(), base::Passed(&cached_logo), image)));
+}
+
 void LogoServiceImpl::OnCachedLogoAvailable(
     std::unique_ptr<EncodedLogo> encoded_logo,
-    const SkBitmap& image) {
+    const SkBitmap& image,
+    const SkBitmap& dark_image) {
   DCHECK(!is_idle_);
 
-  if (encoded_logo && encoded_logo->encoded_image && !image.isNull()) {
-    cached_logo_.reset(new Logo());
+  // A dark image is not required, but if one exists (mime type is non-empty)
+  // it must be successfully decoded.
+  if (encoded_logo && !image.isNull() &&
+      (encoded_logo->metadata.dark_mime_type.empty() || !dark_image.isNull())) {
+    cached_logo_ = std::make_unique<Logo>();
     cached_logo_->metadata = encoded_logo->metadata;
     cached_logo_->image = image;
+    cached_logo_->dark_image = dark_image;
     cached_encoded_logo_ = std::move(encoded_logo);
   }
   is_cached_logo_valid_ = true;
@@ -494,29 +520,59 @@
   if (logo)
     logo->metadata.source_url = logo_url_;
 
-  if (!logo || !logo->encoded_image) {
+  if (!logo || !logo->encoded_image ||
+      (!logo->metadata.dark_mime_type.empty() && !logo->dark_encoded_image)) {
     OnFreshLogoAvailable(std::move(logo), /*download_failed=*/false,
-                         *parsing_failed, from_http_cache, SkBitmap());
+                         *parsing_failed, from_http_cache, SkBitmap(),
+                         SkBitmap());
   } else {
     // Store the value of logo->encoded_image for use below. This ensures that
-    // logo->encoded_image is evaulated before base::Passed(&logo), which sets
+    // logo->encoded_image is evaluated before base::Passed(&logo), which sets
     // logo to NULL.
     scoped_refptr<base::RefCountedString> encoded_image = logo->encoded_image;
+
     image_decoder_->DecodeImage(
         encoded_image->data(), gfx::Size(),  // No particular size desired.
         ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
-            &LogoServiceImpl::OnFreshLogoAvailable,
+            &LogoServiceImpl::OnLightFreshImageDecoded,
             weak_ptr_factory_.GetWeakPtr(), base::Passed(&logo),
             /*download_failed=*/false, *parsing_failed, from_http_cache)));
   }
 }
 
+void LogoServiceImpl::OnLightFreshImageDecoded(
+    std::unique_ptr<EncodedLogo> logo,
+    bool download_failed,
+    bool parsing_failed,
+    bool from_http_cache,
+    const SkBitmap& image) {
+  if (logo->metadata.dark_mime_type.empty()) {
+    OnFreshLogoAvailable(std::move(logo), download_failed, parsing_failed,
+                         from_http_cache, image, SkBitmap());
+    return;
+  }
+
+  // Store the value of logo->dark_encoded_image for use below. This ensures
+  // that logo->encoded_image is evaluated before base::Passed(&logo), which
+  // sets logo to NULL.
+  scoped_refptr<base::RefCountedString> dark_encoded_image =
+      logo->dark_encoded_image;
+
+  image_decoder_->DecodeImage(
+      dark_encoded_image->data(), gfx::Size(),  // No particular size desired.
+      ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
+          &LogoServiceImpl::OnFreshLogoAvailable,
+          weak_ptr_factory_.GetWeakPtr(), base::Passed(&logo), download_failed,
+          parsing_failed, from_http_cache, image)));
+}
+
 void LogoServiceImpl::OnFreshLogoAvailable(
     std::unique_ptr<EncodedLogo> encoded_logo,
     bool download_failed,
     bool parsing_failed,
     bool from_http_cache,
-    const SkBitmap& image) {
+    const SkBitmap& image,
+    const SkBitmap& dark_image) {
   DCHECK(!is_idle_);
 
   LogoDownloadOutcome download_outcome = DOWNLOAD_OUTCOME_COUNT;
@@ -531,9 +587,13 @@
     // The cached logo was revalidated, i.e. its fingerprint was verified.
     // mime_type isn't sent when revalidating, so copy it from the cached logo.
     encoded_logo->metadata.mime_type = cached_logo_->metadata.mime_type;
+    encoded_logo->metadata.dark_mime_type =
+        cached_logo_->metadata.dark_mime_type;
     SetCachedMetadata(encoded_logo->metadata);
     download_outcome = DOWNLOAD_OUTCOME_LOGO_REVALIDATED;
-  } else if (encoded_logo && encoded_logo->encoded_image && image.isNull()) {
+  } else if ((encoded_logo && encoded_logo->encoded_image && image.isNull()) ||
+             (encoded_logo && !encoded_logo->metadata.dark_mime_type.empty() &&
+              dark_image.isNull())) {
     // Image decoding failed. Do nothing.
     download_outcome = DOWNLOAD_OUTCOME_DECODING_FAILED;
   } else if (encoded_logo && !encoded_logo->encoded_image &&
@@ -554,6 +614,7 @@
       logo.reset(new Logo());
       logo->metadata = encoded_logo->metadata;
       logo->image = image;
+      logo->dark_image = dark_image;
     }
 
     if (logo) {
@@ -638,14 +699,14 @@
   std::unique_ptr<network::SimpleURLLoader> cleanup_loader(loader_.release());
 
   if (source->NetError() != net::OK) {
-    OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
+    OnFreshLogoAvailable({}, /*download_failed=*/true, false, false, SkBitmap(),
                          SkBitmap());
     return;
   }
 
   if (!source->ResponseInfo() || !source->ResponseInfo()->headers ||
       source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
-    OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
+    OnFreshLogoAvailable({}, /*download_failed=*/true, false, false, SkBitmap(),
                          SkBitmap());
     return;
   }
diff --git a/components/search_provider_logos/logo_service_impl.h b/components/search_provider_logos/logo_service_impl.h
index b3fb983..835b384 100644
--- a/components/search_provider_logos/logo_service_impl.h
+++ b/components/search_provider_logos/logo_service_impl.h
@@ -120,10 +120,17 @@
   // will be NULL if there wasn't a valid, up-to-date logo in the cache.
   void OnCachedLogoRead(std::unique_ptr<EncodedLogo> cached_logo);
 
-  // Called when the cached logo has been decoded into an SkBitmap. |image| will
-  // be NULL if decoding failed.
+  // Called when the light cached image has been decoded into an SkBitmap.
+  // |image| will be NULL if decoding failed.
+  void OnLightCachedImageDecoded(std::unique_ptr<EncodedLogo> cached_logo,
+                                 const SkBitmap& image);
+
+  // Called when both the light and dark cached images have been decoded into an
+  // SkBitmap. |image| will be NULL if decoding failed. |dark_image| will be
+  // NULL if decoding failed or no dark image is provided.
   void OnCachedLogoAvailable(std::unique_ptr<EncodedLogo> encoded_logo,
-                             const SkBitmap& image);
+                             const SkBitmap& image,
+                             const SkBitmap& dark_image);
 
   // Stores |logo| in the cache.
   void SetCachedLogo(std::unique_ptr<EncodedLogo> logo);
@@ -140,13 +147,23 @@
                          bool from_http_cache,
                          std::unique_ptr<EncodedLogo> logo);
 
-  // Called when the fresh logo has been decoded into an SkBitmap. |image| will
+  // Called when the light image has been decoded into an SkBitmap. |image| will
   // be NULL if decoding failed.
+  void OnLightFreshImageDecoded(std::unique_ptr<EncodedLogo> logo,
+                                bool download_failed,
+                                bool parsing_failed,
+                                bool from_http_cache,
+                                const SkBitmap& image);
+
+  // Called when both the light and dark images have been decoded into an
+  // SkBitmap. |image| will be NULL if decoding failed. |dark_image| will be
+  // null if decoding failed or no dark image is provided.
   void OnFreshLogoAvailable(std::unique_ptr<EncodedLogo> logo,
                             bool download_failed,
                             bool parsing_failed,
                             bool from_http_cache,
-                            const SkBitmap& image);
+                            const SkBitmap& image,
+                            const SkBitmap& dark_image);
 
   // Invoked by |loader|.
   void OnURLLoadComplete(const network::SimpleURLLoader* source,
diff --git a/components/search_provider_logos/logo_service_impl_unittest.cc b/components/search_provider_logos/logo_service_impl_unittest.cc
index d22c695..d351963 100644
--- a/components/search_provider_logos/logo_service_impl_unittest.cc
+++ b/components/search_provider_logos/logo_service_impl_unittest.cc
@@ -95,9 +95,17 @@
   return bitmap;
 }
 
+SkBitmap MakeBitmap2(int width, int height) {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(width, height);
+  bitmap.eraseColor(SK_ColorRED);
+  return bitmap;
+}
+
 EncodedLogo EncodeLogo(const Logo& logo) {
   EncodedLogo encoded_logo;
   encoded_logo.encoded_image = EncodeBitmapAsPNG(logo.image);
+  encoded_logo.dark_encoded_image = EncodeBitmapAsPNG(logo.dark_image);
   encoded_logo.metadata = logo.metadata;
   return encoded_logo;
 }
@@ -108,6 +116,12 @@
       gfx::Image::CreateFrom1xPNGBytes(encoded_logo.encoded_image->front(),
                                        encoded_logo.encoded_image->size())
           .AsBitmap();
+  if (encoded_logo.dark_encoded_image) {
+    logo.dark_image = gfx::Image::CreateFrom1xPNGBytes(
+                          encoded_logo.dark_encoded_image->front(),
+                          encoded_logo.dark_encoded_image->size())
+                          .AsBitmap();
+  }
   logo.metadata = encoded_logo.metadata;
   return logo;
 }
@@ -115,6 +129,27 @@
 Logo GetSampleLogo(const GURL& logo_url, base::Time response_time) {
   Logo logo;
   logo.image = MakeBitmap(2, 5);
+  logo.dark_image = MakeBitmap2(20, 50);
+  logo.metadata.can_show_after_expiration = false;
+  logo.metadata.expiration_time =
+      response_time + base::TimeDelta::FromHours(19);
+  logo.metadata.fingerprint = "8bc33a80";
+  logo.metadata.source_url =
+      AppendPreliminaryParamsToDoodleURL(false, logo_url);
+  logo.metadata.on_click_url = GURL("https://www.google.com/search?q=potato");
+  logo.metadata.alt_text = "A logo about potatoes";
+  logo.metadata.animated_url = GURL("https://www.google.com/logos/doodle.png");
+  logo.metadata.dark_animated_url =
+      GURL("https://www.google.com/logos/dark_doodle.png");
+  logo.metadata.mime_type = "image/png";
+  logo.metadata.dark_mime_type = "image/png";
+  return logo;
+}
+
+Logo GetSampleLogoWithoutDarkImage(const GURL& logo_url,
+                                   base::Time response_time) {
+  Logo logo;
+  logo.image = MakeBitmap(2, 5);
   logo.metadata.can_show_after_expiration = false;
   logo.metadata.expiration_time =
       response_time + base::TimeDelta::FromHours(19);
@@ -143,10 +178,13 @@
 }
 
 std::string MakeServerResponse(const SkBitmap& image,
+                               const SkBitmap& dark_image,
                                const std::string& on_click_url,
                                const std::string& alt_text,
                                const std::string& animated_url,
+                               const std::string& dark_animated_url,
                                const std::string& mime_type,
+                               const std::string& dark_mime_type,
                                const std::string& fingerprint,
                                base::TimeDelta time_to_live) {
   base::DictionaryValue dict;
@@ -156,18 +194,28 @@
   data_uri += ";base64,";
   data_uri += EncodeBitmapAsPNGBase64(image);
 
+  std::string dark_data_uri = "data:";
+  dark_data_uri += dark_mime_type;
+  dark_data_uri += ";base64,";
+  dark_data_uri += EncodeBitmapAsPNGBase64(dark_image);
+
   dict.SetString("ddljson.target_url", on_click_url);
   dict.SetString("ddljson.alt_text", alt_text);
   if (animated_url.empty()) {
     dict.SetString("ddljson.doodle_type", "SIMPLE");
     if (!image.isNull())
       dict.SetString("ddljson.data_uri", data_uri);
+    if (!dark_image.isNull())
+      dict.SetString("ddljson.dark_data_uri", dark_data_uri);
   } else {
     dict.SetString("ddljson.doodle_type", "ANIMATED");
     dict.SetBoolean("ddljson.large_image.is_animated_gif", true);
     dict.SetString("ddljson.large_image.url", animated_url);
+    dict.SetString("ddljson.dark_large_image.url", dark_animated_url);
     if (!image.isNull())
       dict.SetString("ddljson.cta_data_uri", data_uri);
+    if (!dark_image.isNull())
+      dict.SetString("ddljson.dark_cta_data_uri", dark_data_uri);
   }
   dict.SetString("ddljson.fingerprint", fingerprint);
   if (time_to_live != base::TimeDelta())
@@ -181,9 +229,10 @@
 
 std::string MakeServerResponse(const Logo& logo, base::TimeDelta time_to_live) {
   return MakeServerResponse(
-      logo.image, logo.metadata.on_click_url.spec(), logo.metadata.alt_text,
-      logo.metadata.animated_url.spec(), logo.metadata.mime_type,
-      logo.metadata.fingerprint, time_to_live);
+      logo.image, logo.dark_image, logo.metadata.on_click_url.spec(),
+      logo.metadata.alt_text, logo.metadata.animated_url.spec(),
+      logo.metadata.dark_animated_url.spec(), logo.metadata.mime_type,
+      logo.metadata.dark_mime_type, logo.metadata.fingerprint, time_to_live);
 }
 
 template <typename Arg, typename Matcher>
@@ -520,6 +569,17 @@
   GetDecodedLogo(cached.Get(), fresh.Get());
 }
 
+TEST_F(LogoServiceImplTest, DownloadAndCacheLogoWithoutDarkImage) {
+  StrictMock<MockLogoCallback> cached;
+  StrictMock<MockLogoCallback> fresh;
+  Logo logo = GetSampleLogoWithoutDarkImage(DoodleURL(), test_clock_.Now());
+  SetServerResponse(ServerResponse(logo));
+  logo_cache_->ExpectSetCachedLogo(&logo);
+  EXPECT_CALL(cached, Run(LogoCallbackReason::DETERMINED, Eq(base::nullopt)));
+  EXPECT_CALL(fresh, Run(LogoCallbackReason::DETERMINED, Eq(logo)));
+  GetDecodedLogo(cached.Get(), fresh.Get());
+}
+
 TEST_F(LogoServiceImplTest, DownloadAndCacheEncodedLogo) {
   StrictMock<MockEncodedLogoCallback> cached;
   StrictMock<MockEncodedLogoCallback> fresh;
@@ -532,6 +592,18 @@
   GetEncodedLogo(cached.Get(), fresh.Get());
 }
 
+TEST_F(LogoServiceImplTest, DownloadAndCacheEncodedLogoWithoutDarkImage) {
+  StrictMock<MockEncodedLogoCallback> cached;
+  StrictMock<MockEncodedLogoCallback> fresh;
+  Logo logo = GetSampleLogoWithoutDarkImage(DoodleURL(), test_clock_.Now());
+  EncodedLogo encoded_logo = EncodeLogo(logo);
+  SetServerResponse(ServerResponse(logo));
+  logo_cache_->ExpectSetCachedLogo(&logo);
+  EXPECT_CALL(cached, Run(LogoCallbackReason::DETERMINED, Eq(base::nullopt)));
+  EXPECT_CALL(fresh, Run(LogoCallbackReason::DETERMINED, Eq(encoded_logo)));
+  GetEncodedLogo(cached.Get(), fresh.Get());
+}
+
 TEST_F(LogoServiceImplTest, ShouldReturnDisabledWhenDSEHasNoLogo) {
   AddSearchEngine("cr", "Chromium", "https://www.chromium.org/?q={searchTerms}",
                   GURL(/* logo disabled */), /*make_default=*/true);
@@ -633,7 +705,9 @@
   // During revalidation, the image data and mime_type are absent.
   Logo fresh_logo = cached_logo;
   fresh_logo.image.reset();
+  fresh_logo.dark_image.reset();
   fresh_logo.metadata.mime_type.clear();
+  fresh_logo.metadata.dark_mime_type.clear();
   fresh_logo.metadata.expiration_time =
       test_clock_.Now() + base::TimeDelta::FromDays(8);
   SetServerResponseWhenFingerprint(fresh_logo.metadata.fingerprint,
@@ -995,11 +1069,15 @@
 bool operator==(const Logo& a, const Logo& b) {
   return (a.image.width() == b.image.width()) &&
          (a.image.height() == b.image.height()) &&
+         (a.dark_image.width() == b.dark_image.width()) &&
+         (a.dark_image.height() == b.dark_image.height()) &&
          (a.metadata.on_click_url == b.metadata.on_click_url) &&
          (a.metadata.source_url == b.metadata.source_url) &&
          (a.metadata.animated_url == b.metadata.animated_url) &&
+         (a.metadata.dark_animated_url == b.metadata.dark_animated_url) &&
          (a.metadata.alt_text == b.metadata.alt_text) &&
          (a.metadata.mime_type == b.metadata.mime_type) &&
+         (a.metadata.dark_mime_type == b.metadata.dark_mime_type) &&
          (a.metadata.fingerprint == b.metadata.fingerprint) &&
          (a.metadata.can_show_after_expiration ==
           b.metadata.can_show_after_expiration);
diff --git a/components/security_interstitials/core/common/resources/interstitial_core.css b/components/security_interstitials/core/common/resources/interstitial_core.css
index 68a85fb..d6a0959a 100644
--- a/components/security_interstitials/core/common/resources/interstitial_core.css
+++ b/components/security_interstitials/core/common/resources/interstitial_core.css
@@ -67,6 +67,7 @@
   body.captive-portal,
   body.dark-mode-available,
   body.neterror,
+  body.supervised-user-block,
   .offline body {
     --background-color: var(--google-gray-900);
     --error-code-color: var(--google-gray-500);
diff --git a/components/security_interstitials/core/safe_browsing_loud_error_ui.cc b/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
index a795a923..a5ec63f 100644
--- a/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
+++ b/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
@@ -309,6 +309,8 @@
 
 void SafeBrowsingLoudErrorUI::PopulateBillingLoadTimeData(
     base::DictionaryValue* load_time_data) {
+  common_string_util::PopulateDarkModeDisplaySetting(load_time_data);
+
   load_time_data->SetBoolean("phishing", false);
   load_time_data->SetBoolean("overridable", true);
 
diff --git a/components/security_interstitials/core/ssl_error_ui.h b/components/security_interstitials/core/ssl_error_ui.h
index 3be2a598..23a2f67 100644
--- a/components/security_interstitials/core/ssl_error_ui.h
+++ b/components/security_interstitials/core/ssl_error_ui.h
@@ -34,9 +34,6 @@
     // Strict-Transport-Security). By default, the error assumes strict
     // enforcement was not requested.
     STRICT_ENFORCEMENT = 1 << 2,
-    // Indicates that a user decision had been previously made but the
-    // decision has expired.
-    EXPIRED_BUT_PREVIOUSLY_ALLOWED = 1 << 3,
   };
 
   SSLErrorUI(const GURL& request_url,
diff --git a/components/sessions/core/live_tab_context.h b/components/sessions/core/live_tab_context.h
index 9ba4f1c..3e1f9b2 100644
--- a/components/sessions/core/live_tab_context.h
+++ b/components/sessions/core/live_tab_context.h
@@ -8,10 +8,15 @@
 #include <string>
 #include <vector>
 
+#include "base/optional.h"
 #include "components/sessions/core/session_id.h"
 #include "components/sessions/core/sessions_export.h"
 #include "ui/base/ui_base_types.h"
 
+namespace base {
+class Token;
+}
+
 namespace gfx {
 class Rect;
 }
@@ -36,6 +41,7 @@
   virtual LiveTab* GetLiveTabAt(int index) const = 0;
   virtual LiveTab* GetActiveLiveTab() const = 0;
   virtual bool IsTabPinned(int index) const = 0;
+  virtual base::Optional<base::Token> GetTabGroupForTab(int index) const = 0;
   virtual const gfx::Rect GetRestoredBounds() const = 0;
   virtual ui::WindowShowState GetRestoredState() const = 0;
   virtual std::string GetWorkspace() const = 0;
@@ -48,6 +54,7 @@
       int tab_index,
       int selected_navigation,
       const std::string& extension_app_id,
+      base::Optional<base::Token> group,
       bool select,
       bool pin,
       bool from_last_session,
@@ -59,6 +66,7 @@
   // platform-specific data).
   virtual LiveTab* ReplaceRestoredTab(
       const std::vector<SerializedNavigationEntry>& navigations,
+      base::Optional<base::Token> group,
       int selected_navigation,
       bool from_last_session,
       const std::string& extension_app_id,
diff --git a/components/sessions/core/tab_restore_service.h b/components/sessions/core/tab_restore_service.h
index c83730c..aa3d307 100644
--- a/components/sessions/core/tab_restore_service.h
+++ b/components/sessions/core/tab_restore_service.h
@@ -10,7 +10,9 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "base/time/time.h"
+#include "base/token.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/sessions/core/serialized_navigation_entry.h"
 #include "components/sessions/core/session_id.h"
@@ -115,6 +117,9 @@
 
     // The user agent override used for the tab's navigations (if applicable).
     std::string user_agent_override;
+
+    // The group the tab belonged to, if any.
+    base::Optional<base::Token> group;
   };
 
   // Represents a previously open window.
diff --git a/components/sessions/core/tab_restore_service_helper.cc b/components/sessions/core/tab_restore_service_helper.cc
index 9ae6e54..630e9a4 100644
--- a/components/sessions/core/tab_restore_service_helper.cc
+++ b/components/sessions/core/tab_restore_service_helper.cc
@@ -306,7 +306,7 @@
           const Tab& tab = *window.tabs[tab_i];
           LiveTab* restored_tab = context->AddRestoredTab(
               tab.navigations, context->GetTabCount(),
-              tab.current_navigation_index, tab.extension_app_id,
+              tab.current_navigation_index, tab.extension_app_id, tab.group,
               static_cast<int>(tab_i) == window.selected_tab_index, tab.pinned,
               tab.from_last_session, tab.platform_data.get(),
               tab.user_agent_override);
@@ -538,6 +538,7 @@
   if (context) {
     tab->browser_id = context->GetSessionID().id();
     tab->pinned = context->IsTabPinned(tab->tabstrip_index);
+    tab->group = context->GetTabGroupForTab(tab->tabstrip_index);
   }
 }
 
@@ -549,8 +550,9 @@
   LiveTab* restored_tab;
   if (disposition == WindowOpenDisposition::CURRENT_TAB && context) {
     restored_tab = context->ReplaceRestoredTab(
-        tab.navigations, tab.current_navigation_index, tab.from_last_session,
-        tab.extension_app_id, tab.platform_data.get(), tab.user_agent_override);
+        tab.navigations, base::nullopt, tab.current_navigation_index,
+        tab.from_last_session, tab.extension_app_id, tab.platform_data.get(),
+        tab.user_agent_override);
   } else {
     // We only respect the tab's original browser if there's no disposition.
     if (disposition == WindowOpenDisposition::UNKNOWN && tab.browser_id) {
@@ -581,7 +583,7 @@
 
     restored_tab = context->AddRestoredTab(
         tab.navigations, tab_index, tab.current_navigation_index,
-        tab.extension_app_id,
+        tab.extension_app_id, base::nullopt,
         disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB, tab.pinned,
         tab.from_last_session, tab.platform_data.get(),
         tab.user_agent_override);
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index f48cf94..9527105 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -25,8 +25,6 @@
     "account_consistency_method.h",
     "account_info.cc",
     "account_info.h",
-    "account_info_util.cc",
-    "account_info_util.h",
     "avatar_icon_util.cc",
     "avatar_icon_util.h",
     "device_id_helper.cc",
@@ -76,12 +74,16 @@
     "account_fetcher_service.h",
     "account_info_fetcher.cc",
     "account_info_fetcher.h",
+    "account_info_util.cc",
+    "account_info_util.h",
     "account_tracker_service.cc",
     "account_tracker_service.h",
     "child_account_info_fetcher_android.cc",
     "child_account_info_fetcher_android.h",
     "gaia_cookie_manager_service.cc",
     "gaia_cookie_manager_service.h",
+    "mutable_profile_oauth2_token_service_delegate.cc",
+    "mutable_profile_oauth2_token_service_delegate.h",
     "oauth2_token_service_delegate_android.cc",
     "oauth2_token_service_delegate_android.h",
     "oauth_multilogin_helper.cc",
@@ -113,6 +115,8 @@
     "//components/image_fetcher/core",
     "//components/keyed_service/core",
     "//components/prefs",
+    "//components/signin/core/browser/webdata",
+    "//components/webdata/common",
     "//google_apis",
     "//net",
     "//services/network/public/cpp",
@@ -164,8 +168,6 @@
     "mirror_account_reconcilor_delegate.h",
     "multilogin_parameters.cc",
     "multilogin_parameters.h",
-    "mutable_profile_oauth2_token_service_delegate.cc",
-    "mutable_profile_oauth2_token_service_delegate.h",
     "signin_error_controller.cc",
     "signin_error_controller.h",
     "signin_header_helper.cc",
@@ -180,10 +182,6 @@
     "signin_status_metrics_provider_base.h",
     "signin_status_metrics_provider_delegate.cc",
     "signin_status_metrics_provider_delegate.h",
-    "webdata/token_service_table.cc",
-    "webdata/token_service_table.h",
-    "webdata/token_web_data.cc",
-    "webdata/token_web_data.h",
   ]
 
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
@@ -301,7 +299,6 @@
     "signin_metrics_unittest.cc",
     "signin_status_metrics_provider_unittest.cc",
     "ubertoken_fetcher_impl_unittest.cc",
-    "webdata/token_service_table_unittest.cc",
   ]
 
   deps = [
@@ -315,6 +312,7 @@
     "//components/os_crypt:test_support",
     "//components/prefs",
     "//components/prefs:test_support",
+    "//components/signin/core/browser/webdata",
     "//components/sync_preferences",
     "//components/sync_preferences:test_support",
     "//components/webdata/common",
diff --git a/components/signin/core/browser/account_tracker_service_unittest.cc b/components/signin/core/browser/account_tracker_service_unittest.cc
index b370237..02a9d2f 100644
--- a/components/signin/core/browser/account_tracker_service_unittest.cc
+++ b/components/signin/core/browser/account_tracker_service_unittest.cc
@@ -25,7 +25,6 @@
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "components/signin/core/browser/test_signin_client.h"
-#include "google_apis/gaia/fake_oauth2_token_service.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/http/http_status_code.h"
diff --git a/components/signin/core/browser/avatar_icon_util.cc b/components/signin/core/browser/avatar_icon_util.cc
index 5e613d2..2a74566 100644
--- a/components/signin/core/browser/avatar_icon_util.cc
+++ b/components/signin/core/browser/avatar_icon_util.cc
@@ -5,6 +5,7 @@
 #include "components/signin/core/browser/avatar_icon_util.h"
 
 #include <string>
+#include <vector>
 
 #include "base/stl_util.h"
 #include "base/strings/string_split.h"
@@ -16,20 +17,25 @@
 namespace {
 
 // Separator of URL path components.
-const char kURLPathSeparator[] = "/";
+constexpr char kURLPathSeparator[] = "/";
 
-// Constants describing image URL format.
+// Constants describing legacy image URL format.
 // See https://crbug.com/733306#c3 for details.
-const size_t kImageURLPathComponentsCount = 6;
-const size_t kImageURLPathComponentsCountWithOptions = 7;
-const size_t kImageURLPathOptionsComponentPosition = 5;
+constexpr size_t kLegacyURLPathComponentsCount = 6;
+constexpr size_t kLegacyURLPathComponentsCountWithOptions = 7;
+constexpr size_t kLegacyURLPathOptionsComponentPosition = 5;
+// Constants describing content image URL format.
+// See https://crbug.com/911332#c15 for details.
+constexpr size_t kContentURLPathMinComponentsCount = 2;
+constexpr size_t kContentURLPathMaxComponentsCount = 3;
+constexpr char kContentURLOptionsStartChar = '=';
 // Various options that can be embedded in image URL.
-const char kImageURLOptionSeparator[] = "-";
-const char kImageURLOptionSizePattern[] = R"(s\d+)";
-const char kImageURLOptionSizeFormat[] = "s%d";
-const char kImageURLOptionSquareCrop[] = "c";
+constexpr char kImageURLOptionSeparator[] = "-";
+constexpr char kImageURLOptionSizePattern[] = R"(s\d+)";
+constexpr char kImageURLOptionSizeFormat[] = "s%d";
+constexpr char kImageURLOptionSquareCrop[] = "c";
 // Option to disable default avatar if user doesn't have a custom one.
-const char kImageURLOptionNoSilhouette[] = "ns";
+constexpr char kImageURLOptionNoSilhouette[] = "ns";
 
 std::string BuildImageURLOptionsString(int image_size,
                                        bool no_silhouette,
@@ -53,6 +59,62 @@
   return base::JoinString(url_options, kImageURLOptionSeparator);
 }
 
+// Returns an empty vector if |url_components| couldn't be processed as a legacy
+// image URL.
+std::vector<std::string> TryProcessAsLegacyImageURL(
+    std::vector<std::string> url_components,
+    int image_size,
+    bool no_silhouette) {
+  if (url_components.back().empty())
+    return {};
+
+  if (url_components.size() == kLegacyURLPathComponentsCount) {
+    url_components.insert(
+        url_components.begin() + kLegacyURLPathOptionsComponentPosition,
+        BuildImageURLOptionsString(image_size, no_silhouette, std::string()));
+    return url_components;
+  }
+
+  if (url_components.size() == kLegacyURLPathComponentsCountWithOptions) {
+    std::string options =
+        url_components.at(kLegacyURLPathOptionsComponentPosition);
+    url_components[kLegacyURLPathOptionsComponentPosition] =
+        BuildImageURLOptionsString(image_size, no_silhouette, options);
+    return url_components;
+  }
+
+  return {};
+}
+
+// Returns an empty vector if |url_components| couldn't be processed as a
+// content image URL.
+std::vector<std::string> TryProcessAsContentImageURL(
+    std::vector<std::string> url_components,
+    int image_size,
+    bool no_silhouette) {
+  if (url_components.size() < kContentURLPathMinComponentsCount ||
+      url_components.size() > kContentURLPathMaxComponentsCount ||
+      url_components.back().empty()) {
+    return {};
+  }
+
+  std::string* options_component = &url_components.back();
+  // Extract existing options from |options_component|.
+  const size_t options_pos =
+      options_component->find(kContentURLOptionsStartChar);
+  std::string component_without_options =
+      options_component->substr(0, options_pos);
+  std::string existing_options =
+      options_pos == std::string::npos
+          ? ""
+          : options_component->substr(options_pos + 1);
+  // Update options in |options_component|.
+  *options_component =
+      component_without_options + kContentURLOptionsStartChar +
+      BuildImageURLOptionsString(image_size, no_silhouette, existing_options);
+  return url_components;
+}
+
 }  // namespace
 
 namespace signin {
@@ -66,24 +128,20 @@
       base::SplitString(old_url.path(), kURLPathSeparator,
                         base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
 
-  if (components.size() < kImageURLPathComponentsCount ||
-      components.size() > kImageURLPathComponentsCountWithOptions ||
-      components.back().empty()) {
+  auto new_components =
+      TryProcessAsContentImageURL(components, image_size, no_silhouette);
+
+  if (new_components.empty()) {
+    new_components =
+        TryProcessAsLegacyImageURL(components, image_size, no_silhouette);
+  }
+
+  if (new_components.empty()) {
+    // URL doesn't match any known patterns, so return unchanged.
     return old_url;
   }
 
-  if (components.size() == kImageURLPathComponentsCount) {
-    components.insert(
-        components.begin() + kImageURLPathOptionsComponentPosition,
-        BuildImageURLOptionsString(image_size, no_silhouette, std::string()));
-  } else {
-    DCHECK_EQ(kImageURLPathComponentsCountWithOptions, components.size());
-    std::string options = components.at(kImageURLPathOptionsComponentPosition);
-    components[kImageURLPathOptionsComponentPosition] =
-        BuildImageURLOptionsString(image_size, no_silhouette, options);
-  }
-
-  std::string new_path = base::JoinString(components, kURLPathSeparator);
+  std::string new_path = base::JoinString(new_components, kURLPathSeparator);
   GURL::Replacements replacement;
   replacement.SetPathStr(new_path);
   return old_url.ReplaceComponents(replacement);
diff --git a/components/signin/core/browser/avatar_icon_util_unittest.cc b/components/signin/core/browser/avatar_icon_util_unittest.cc
index 7f592916..f368624 100644
--- a/components/signin/core/browser/avatar_icon_util_unittest.cc
+++ b/components/signin/core/browser/avatar_icon_util_unittest.cc
@@ -9,7 +9,7 @@
 
 namespace {
 
-TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoInitialSize) {
+TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoInitialSize_LegacyURL) {
   GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/photo.jpg");
   GURL result =
       signin::GetAvatarImageURLWithOptions(in, 128, false /* no_silhouette */);
@@ -18,7 +18,8 @@
   EXPECT_EQ(result, expected);
 }
 
-TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSizeAlreadySpecified) {
+TEST(AvatarIconUtilTest,
+     GetAvatarImageURLWithOptionsSizeAlreadySpecified_LegacyURL) {
   // If there's already a size specified in the URL, it should be changed to the
   // specified size in the resulting URL.
   GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/s64-c/photo.jpg");
@@ -29,7 +30,8 @@
   EXPECT_EQ(result, expected);
 }
 
-TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsOtherSizeSpecified) {
+TEST(AvatarIconUtilTest,
+     GetAvatarImageURLWithOptionsOtherSizeSpecified_LegacyURL) {
   // If there's already a size specified in the URL, it should be changed to the
   // specified size in the resulting URL.
   GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/s128-c/photo.jpg");
@@ -40,7 +42,7 @@
   EXPECT_EQ(result, expected);
 }
 
-TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSameSize) {
+TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSameSize_LegacyURL) {
   // If there's already a size specified in the URL, and it's already the
   // requested size, true should be returned and the URL should remain
   // unchanged.
@@ -52,7 +54,8 @@
   EXPECT_EQ(result, expected);
 }
 
-TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoFileNameInPath) {
+TEST(AvatarIconUtilTest,
+     GetAvatarImageURLWithOptionsNoFileNameInPath_LegacyURL) {
   // If there is no file path component in the URL path, we should return
   // the input URL.
   GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/");
@@ -61,7 +64,7 @@
   EXPECT_EQ(result, in);
 }
 
-TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoSilhouette) {
+TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoSilhouette_LegacyURL) {
   // If there's already a size specified in the URL, it should be changed to the
   // specified size in the resulting URL.
   GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/photo.jpg");
@@ -72,7 +75,8 @@
   EXPECT_EQ(result, expected);
 }
 
-TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSizeReplaceNoSilhouette) {
+TEST(AvatarIconUtilTest,
+     GetAvatarImageURLWithOptionsSizeReplaceNoSilhouette_LegacyURL) {
   // If there's already a no_silhouette option specified in the URL, it should
   // be removed if necessary.
   GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/s64-c-ns/photo.jpg");
@@ -83,7 +87,8 @@
   EXPECT_EQ(result, expected);
 }
 
-TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsUnknownShouldBePreserved) {
+TEST(AvatarIconUtilTest,
+     GetAvatarImageURLWithOptionsUnknownShouldBePreserved_LegacyURL) {
   // If there are any unknown options encoded in URL,
   // GetAvatarImageURLWithOptions should preserve them.
   GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/s64-mo-k/photo.jpg");
@@ -94,4 +99,20 @@
   EXPECT_EQ(result, expected);
 }
 
+TEST(AvatarIconUtilTest, GetAvatarImageURLWithExistingOptions_ContentURL) {
+  GURL in("http://example.com/-A/ABcdefghijk1l2mN3=s256");
+  GURL result =
+      signin::GetAvatarImageURLWithOptions(in, 128, false /* no_silhouette */);
+  GURL expected("http://example.com/-A/ABcdefghijk1l2mN3=s128-c");
+  EXPECT_EQ(result, expected);
+}
+
+TEST(AvatarIconUtilTest, GetAvatarImageURLNoExistingOptions_ContentURL) {
+  GURL in("http://example.com/-A/ABcdefghijk1l2mN3");
+  GURL result =
+      signin::GetAvatarImageURLWithOptions(in, 128, true /* no_silhouette */);
+  GURL expected("http://example.com/-A/ABcdefghijk1l2mN3=s128-c-ns");
+  EXPECT_EQ(result, expected);
+}
+
 }  // namespace
diff --git a/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
index 53aa83b..30654f6 100644
--- a/components/signin/core/browser/fake_profile_oauth2_token_service.cc
+++ b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
@@ -123,7 +123,11 @@
 
   // Walk the requests and notify the callbacks.
   for (auto it = requests.begin(); it != requests.end(); ++it) {
-    DCHECK(it->request);
+    // Consumers can drop requests in response to callbacks on other requests
+    // (e.g., OAuthMultiloginFetcher clears all of its requests when it gets an
+    // error on any of them).
+    if (!it->request)
+      continue;
 
     bool scope_matches = all_scopes || it->scopes == scope;
     bool account_matches = account_id.empty() || account_id == it->account_id;
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index 6ef1fca0..ac5c6c8 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -434,7 +434,7 @@
 }
 
 GaiaCookieManagerService::GaiaCookieManagerService(
-    OAuth2TokenService* token_service,
+    ProfileOAuth2TokenService* token_service,
     SigninClient* signin_client)
     : token_service_(token_service),
       signin_client_(signin_client),
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index 83992005..b37e51d 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -18,11 +18,11 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
 #include "google_apis/gaia/gaia_auth_util.h"
-#include "google_apis/gaia/oauth2_token_service.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/base/backoff_entry.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
@@ -197,7 +197,7 @@
     DISALLOW_COPY_AND_ASSIGN(ExternalCcResultFetcher);
   };
 
-  GaiaCookieManagerService(OAuth2TokenService* token_service,
+  GaiaCookieManagerService(ProfileOAuth2TokenService* token_service,
                            SigninClient* signin_client);
 
   ~GaiaCookieManagerService() override;
@@ -347,7 +347,7 @@
   // Start the next request, if needed.
   void HandleNextRequest();
 
-  OAuth2TokenService* token_service_;
+  ProfileOAuth2TokenService* token_service_;
   SigninClient* signin_client_;
 
   GaiaAccountsInCookieUpdatedCallback gaia_accounts_updated_in_cookie_callback_;
diff --git a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
index 50bc5a7d6..c13406f5 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
@@ -24,9 +24,9 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "components/signin/core/browser/test_signin_client.h"
-#include "google_apis/gaia/fake_oauth2_token_service.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -96,7 +96,7 @@
 
 class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService {
  public:
-  InstrumentedGaiaCookieManagerService(OAuth2TokenService* token_service,
+  InstrumentedGaiaCookieManagerService(ProfileOAuth2TokenService* token_service,
                                        SigninClient* signin_client)
       : GaiaCookieManagerService(token_service, signin_client) {
     total++;
@@ -123,10 +123,12 @@
         error_(GoogleServiceAuthError::SERVICE_ERROR),
         canceled_(GoogleServiceAuthError::REQUEST_CANCELED) {
     AccountTrackerService::RegisterPrefs(pref_service_.registry());
-    signin_client_.reset(new TestSigninClient(&pref_service_));
+    signin_client_ = std::make_unique<TestSigninClient>(&pref_service_);
+    token_service_ =
+        std::make_unique<FakeProfileOAuth2TokenService>(&pref_service_);
   }
 
-  OAuth2TokenService* token_service() { return &token_service_; }
+  ProfileOAuth2TokenService* token_service() { return token_service_.get(); }
   TestSigninClient* signin_client() { return signin_client_.get(); }
 
   void SimulateUbertokenSuccess(GaiaCookieManagerService* gcms,
@@ -228,12 +230,12 @@
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
-  FakeOAuth2TokenService token_service_;
   GoogleServiceAuthError no_error_;
   GoogleServiceAuthError error_;
   GoogleServiceAuthError canceled_;
   TestingPrefServiceSimple pref_service_;
   std::unique_ptr<TestSigninClient> signin_client_;
+  std::unique_ptr<FakeProfileOAuth2TokenService> token_service_;
 };
 
 }  // namespace
diff --git a/components/signin/core/browser/oauth2_token_service_delegate_android.cc b/components/signin/core/browser/oauth2_token_service_delegate_android.cc
index d3321d9..b92719e8 100644
--- a/components/signin/core/browser/oauth2_token_service_delegate_android.cc
+++ b/components/signin/core/browser/oauth2_token_service_delegate_android.cc
@@ -287,7 +287,7 @@
   return std::make_unique<AndroidAccessTokenFetcher>(consumer, account_name);
 }
 
-void OAuth2TokenServiceDelegateAndroid::InvalidateAccessToken(
+void OAuth2TokenServiceDelegateAndroid::OnAccessTokenInvalidated(
     const CoreAccountId& account_id,
     const std::string& client_id,
     const OAuth2AccessTokenManager::ScopeSet& scopes,
diff --git a/components/signin/core/browser/oauth2_token_service_delegate_android.h b/components/signin/core/browser/oauth2_token_service_delegate_android.h
index 8ff48ba..77e9573 100644
--- a/components/signin/core/browser/oauth2_token_service_delegate_android.h
+++ b/components/signin/core/browser/oauth2_token_service_delegate_android.h
@@ -84,10 +84,11 @@
 
   // Overridden from OAuth2TokenServiceDelegate to intercept token fetch
   // requests and redirect them to the Account Manager.
-  void InvalidateAccessToken(const CoreAccountId& account_id,
-                             const std::string& client_id,
-                             const OAuth2AccessTokenManager::ScopeSet& scopes,
-                             const std::string& access_token) override;
+  void OnAccessTokenInvalidated(
+      const CoreAccountId& account_id,
+      const std::string& client_id,
+      const OAuth2AccessTokenManager::ScopeSet& scopes,
+      const std::string& access_token) override;
 
   // Called to notify observers when refresh tokans have been loaded.
   void FireRefreshTokensLoaded() override;
diff --git a/components/signin/core/browser/oauth_multilogin_helper.cc b/components/signin/core/browser/oauth_multilogin_helper.cc
index a779cf4..98cbb26 100644
--- a/components/signin/core/browser/oauth_multilogin_helper.cc
+++ b/components/signin/core/browser/oauth_multilogin_helper.cc
@@ -51,7 +51,7 @@
 
 OAuthMultiloginHelper::OAuthMultiloginHelper(
     SigninClient* signin_client,
-    OAuth2TokenService* token_service,
+    ProfileOAuth2TokenService* token_service,
     const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& accounts,
     const std::string& external_cc_result,
     base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback)
diff --git a/components/signin/core/browser/oauth_multilogin_helper.h b/components/signin/core/browser/oauth_multilogin_helper.h
index 176fb0ec..3b744aef 100644
--- a/components/signin/core/browser/oauth_multilogin_helper.h
+++ b/components/signin/core/browser/oauth_multilogin_helper.h
@@ -22,7 +22,7 @@
 
 class GaiaAuthFetcher;
 class GoogleServiceAuthError;
-class OAuth2TokenService;
+class ProfileOAuth2TokenService;
 class SigninClient;
 
 namespace signin {
@@ -39,7 +39,7 @@
  public:
   OAuthMultiloginHelper(
       SigninClient* signin_client,
-      OAuth2TokenService* token_service,
+      ProfileOAuth2TokenService* token_service,
       const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>&
           accounts,
       const std::string& external_cc_result,
@@ -72,7 +72,7 @@
                    net::CanonicalCookie::CookieInclusionStatus status);
 
   SigninClient* signin_client_;
-  OAuth2TokenService* token_service_;
+  ProfileOAuth2TokenService* token_service_;
 
   int fetcher_retries_ = 0;
 
diff --git a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
index 7e7642e..03b41c0 100644
--- a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
+++ b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
@@ -8,9 +8,10 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/test/scoped_task_environment.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/set_accounts_in_cookie_result.h"
 #include "components/signin/core/browser/test_signin_client.h"
-#include "google_apis/gaia/fake_oauth2_token_service.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "services/network/test/test_cookie_manager.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -151,8 +152,10 @@
                     SetCanonicalCookieCallback callback));
 };
 
-class MockTokenService : public FakeOAuth2TokenService {
+class MockTokenService : public FakeProfileOAuth2TokenService {
  public:
+  MockTokenService(PrefService* prefs) : FakeProfileOAuth2TokenService(prefs) {}
+
   MOCK_METHOD2(InvalidateTokenForMultilogin,
                void(const CoreAccountId& account_id, const std::string& token));
 };
@@ -161,7 +164,9 @@
 
 class OAuthMultiloginHelperTest : public testing::Test {
  public:
-  OAuthMultiloginHelperTest() : test_signin_client_(/*prefs=*/nullptr) {
+  OAuthMultiloginHelperTest()
+      : test_signin_client_(&pref_service_),
+        mock_token_service_(&pref_service_) {
     std::unique_ptr<MockCookieManager> cookie_manager =
         std::make_unique<MockCookieManager>();
     mock_cookie_manager_ = cookie_manager.get();
@@ -217,6 +222,7 @@
   bool callback_called_ = false;
   signin::SetAccountsInCookieResult result_;
 
+  TestingPrefServiceSimple pref_service_;
   MockCookieManager* mock_cookie_manager_;  // Owned by test_signin_client_
   TestSigninClient test_signin_client_;
   MockTokenService mock_token_service_;
@@ -224,7 +230,7 @@
 
 // Everything succeeds.
 TEST_F(OAuthMultiloginHelperTest, Success) {
-  token_service()->AddAccount(kAccountId);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelper({{kAccountId, kGaiaId}});
 
@@ -253,7 +259,7 @@
 
 // Multiple cookies in the multilogin response.
 TEST_F(OAuthMultiloginHelperTest, MultipleCookies) {
-  token_service()->AddAccount(kAccountId);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelper({{kAccountId, kGaiaId}});
 
@@ -288,7 +294,7 @@
 
 // Multiple cookies in the multilogin response.
 TEST_F(OAuthMultiloginHelperTest, SuccessWithExternalCcResult) {
-  token_service()->AddAccount(kAccountId);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelperWithExternalCcResult({{kAccountId, kGaiaId}});
 
@@ -325,7 +331,7 @@
 
 // Failure to get the access token.
 TEST_F(OAuthMultiloginHelperTest, OneAccountAccessTokenFailure) {
-  token_service()->AddAccount(kAccountId);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelper({{kAccountId, kGaiaId}});
 
@@ -338,7 +344,7 @@
 
 // Retry on transient errors in the multilogin call.
 TEST_F(OAuthMultiloginHelperTest, OneAccountTransientMultiloginError) {
-  token_service()->AddAccount(kAccountId);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelper({{kAccountId, kGaiaId}});
 
@@ -375,7 +381,7 @@
 // Stop retrying after too many transient errors in the multilogin call.
 TEST_F(OAuthMultiloginHelperTest,
        OneAccountTransientMultiloginErrorMaxRetries) {
-  token_service()->AddAccount(kAccountId);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelper({{kAccountId, kGaiaId}});
 
@@ -399,7 +405,7 @@
 
 // Persistent error in the multilogin call.
 TEST_F(OAuthMultiloginHelperTest, OneAccountPersistentMultiloginError) {
-  token_service()->AddAccount(kAccountId);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelper({{kAccountId, kGaiaId}});
 
@@ -419,8 +425,8 @@
 
 // Retry on "invalid token" in the multilogin response.
 TEST_F(OAuthMultiloginHelperTest, InvalidTokenError) {
-  token_service()->AddAccount(kAccountId);
-  token_service()->AddAccount(kAccountId2);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
+  token_service()->UpdateCredentials(kAccountId2, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelper({{kAccountId, kGaiaId}, {kAccountId2, kGaiaId2}});
 
@@ -467,8 +473,8 @@
 
 // Retry on "invalid token" in the multilogin response.
 TEST_F(OAuthMultiloginHelperTest, InvalidTokenErrorMaxRetries) {
-  token_service()->AddAccount(kAccountId);
-  token_service()->AddAccount(kAccountId2);
+  token_service()->UpdateCredentials(kAccountId, "refresh_token");
+  token_service()->UpdateCredentials(kAccountId2, "refresh_token");
   std::unique_ptr<OAuthMultiloginHelper> helper =
       CreateHelper({{kAccountId, kGaiaId}, {kAccountId2, kGaiaId2}});
 
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher.cc b/components/signin/core/browser/oauth_multilogin_token_fetcher.cc
index 3651abc..2ee0967 100644
--- a/components/signin/core/browser/oauth_multilogin_token_fetcher.cc
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher.cc
@@ -12,8 +12,8 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_client.h"
-#include "google_apis/gaia/oauth2_token_service.h"
 
 namespace {
 
@@ -28,7 +28,7 @@
 
 OAuthMultiloginTokenFetcher::OAuthMultiloginTokenFetcher(
     SigninClient* signin_client,
-    OAuth2TokenService* token_service,
+    ProfileOAuth2TokenService* token_service,
     const std::vector<CoreAccountId>& account_ids,
     SuccessCallback success_callback,
     FailureCallback failure_callback)
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher.h b/components/signin/core/browser/oauth_multilogin_token_fetcher.h
index 9120713..5da7ca6 100644
--- a/components/signin/core/browser/oauth_multilogin_token_fetcher.h
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher.h
@@ -20,7 +20,7 @@
 #include "google_apis/gaia/oauth2_access_token_manager.h"
 
 class SigninClient;
-class OAuth2TokenService;
+class ProfileOAuth2TokenService;
 
 namespace signin {
 
@@ -42,7 +42,7 @@
       base::OnceCallback<void(const GoogleServiceAuthError&)>;
 
   OAuthMultiloginTokenFetcher(SigninClient* signin_client,
-                              OAuth2TokenService* token_service,
+                              ProfileOAuth2TokenService* token_service,
                               const std::vector<CoreAccountId>& account_ids,
                               SuccessCallback success_callback,
                               FailureCallback failure_callback);
@@ -63,7 +63,7 @@
   void EraseRequest(const OAuth2AccessTokenManager::Request* request);
 
   SigninClient* signin_client_;
-  OAuth2TokenService* token_service_;
+  ProfileOAuth2TokenService* token_service_;
   const std::vector<CoreAccountId> account_ids_;
 
   SuccessCallback success_callback_;
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc b/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc
index d3bd9a3..c601ba6 100644
--- a/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc
@@ -11,8 +11,9 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/test/scoped_task_environment.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/test_signin_client.h"
-#include "google_apis/gaia/fake_oauth2_token_service.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -31,7 +32,8 @@
 
 class OAuthMultiloginTokenFetcherTest : public testing::Test {
  public:
-  OAuthMultiloginTokenFetcherTest() : test_signin_client_(/*prefs=*/nullptr) {}
+  OAuthMultiloginTokenFetcherTest()
+      : test_signin_client_(&pref_service_), token_service_(&pref_service_) {}
 
   ~OAuthMultiloginTokenFetcherTest() override = default;
 
@@ -79,12 +81,13 @@
   std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair>
       account_id_token_pairs_;
 
+  TestingPrefServiceSimple pref_service_;
   TestSigninClient test_signin_client_;
-  FakeOAuth2TokenService token_service_;
+  FakeProfileOAuth2TokenService token_service_;
 };
 
 TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountSuccess) {
-  token_service_.AddAccount(kAccountId);
+  token_service_.UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
       CreateFetcher({kAccountId});
   EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
@@ -99,7 +102,7 @@
 }
 
 TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountPersistentError) {
-  token_service_.AddAccount(kAccountId);
+  token_service_.UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
       CreateFetcher({kAccountId});
   EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
@@ -111,7 +114,7 @@
 }
 
 TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientError) {
-  token_service_.AddAccount(kAccountId);
+  token_service_.UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
       CreateFetcher({kAccountId});
   // Connection failure will be retried.
@@ -131,7 +134,7 @@
 }
 
 TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientErrorMaxRetries) {
-  token_service_.AddAccount(kAccountId);
+  token_service_.UpdateCredentials(kAccountId, "refresh_token");
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
       CreateFetcher({kAccountId});
   // Repeated connection failures.
@@ -152,9 +155,9 @@
   const CoreAccountId account_1("account_1");
   const CoreAccountId account_2("account_2");
   const CoreAccountId account_3("account_3");
-  token_service_.AddAccount(account_1);
-  token_service_.AddAccount(account_2);
-  token_service_.AddAccount(account_3);
+  token_service_.UpdateCredentials(account_1, "refresh_token");
+  token_service_.UpdateCredentials(account_2, "refresh_token");
+  token_service_.UpdateCredentials(account_3, "refresh_token");
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
       CreateFetcher({account_1, account_2, account_3});
   OAuth2AccessTokenConsumer::TokenResponse success_response;
@@ -180,9 +183,9 @@
   const CoreAccountId account_1("account_1");
   const CoreAccountId account_2("account_2");
   const CoreAccountId account_3("account_3");
-  token_service_.AddAccount(account_1);
-  token_service_.AddAccount(account_2);
-  token_service_.AddAccount(account_3);
+  token_service_.UpdateCredentials(account_1, "refresh_token");
+  token_service_.UpdateCredentials(account_2, "refresh_token");
+  token_service_.UpdateCredentials(account_3, "refresh_token");
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
       CreateFetcher({account_1, account_2, account_3});
   // Connection failures will be retried.
@@ -220,9 +223,9 @@
   const CoreAccountId account_1("account_1");
   const CoreAccountId account_2("account_2");
   const CoreAccountId account_3("account_3");
-  token_service_.AddAccount(account_1);
-  token_service_.AddAccount(account_2);
-  token_service_.AddAccount(account_3);
+  token_service_.UpdateCredentials(account_1, "refresh_token");
+  token_service_.UpdateCredentials(account_2, "refresh_token");
+  token_service_.UpdateCredentials(account_3, "refresh_token");
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
       CreateFetcher({account_1, account_2, account_3});
   EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
diff --git a/components/signin/core/browser/webdata/BUILD.gn b/components/signin/core/browser/webdata/BUILD.gn
new file mode 100644
index 0000000..a301f77
--- /dev/null
+++ b/components/signin/core/browser/webdata/BUILD.gn
@@ -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 target cannot depend on anything else from //components/signin.
+static_library("webdata") {
+  sources = [
+    "token_service_table.cc",
+    "token_service_table.h",
+    "token_web_data.cc",
+    "token_web_data.h",
+  ]
+
+  deps = [
+    "//components/os_crypt",
+    "//sql",
+  ]
+
+  public_deps = [
+    "//base",
+    "//components/webdata/common",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "token_service_table_unittest.cc",
+  ]
+
+  deps = [
+    ":webdata",
+    "//base/test:test_support",
+    "//components/os_crypt:test_support",
+    "//components/webdata/common",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/strings/components_strings_fa.xtb b/components/strings/components_strings_fa.xtb
index 145d520..83a8c61 100644
--- a/components/strings/components_strings_fa.xtb
+++ b/components/strings/components_strings_fa.xtb
@@ -151,7 +151,7 @@
 <translation id="1671391448414634642">از این به بعد، صفحه‌های <ph name="SOURCE_LANGUAGE" />، به <ph name="TARGET_LANGUAGE" /> ترجمه خواهند شد.</translation>
 <translation id="1676269943528358898">‏<ph name="SITE" /> معمولاً برای محافظت از اطلاعات شما از رمزگذاری استفاده می‌کند. اما این بار که Chrome تلاش کرد به <ph name="SITE" /> متصل شود، وب‌سایت اعتبارنامه‌ای نامعمول و نادرست را برگرداند. ممکن است مهاجمی در تلاش باشد خود را به‌جای <ph name="SITE" /> معرفی کند یا یک صفحه ورود به سیستم Wi-Fi در ارتباط اختلال ایجاد کرده باشد. اطلاعات شما همچنان ایمن است، زیرا Google Chrome قبل از هرگونه تبادل داده، اتصال را متوقف کرد.</translation>
 <translation id="168841957122794586">گواهی‌نامه سرور دارای یک کلید رمزنگاری ضعیف است.</translation>
-<translation id="1697532407822776718">آماده‌اید!</translation>
+<translation id="1697532407822776718">همه چیز مرتب است.</translation>
 <translation id="1703835215927279855">Letter</translation>
 <translation id="1706954506755087368">{1,plural, =1{این سرور نتوانست ثابت کند این <ph name="DOMAIN" /> است؛ اعتبار گواهی امنیتی آن ظاهراً فردا شروع می‌شود. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجمی اتصالتان را قطع می‌کند.}one{این سرور نتوانست ثابت کند این <ph name="DOMAIN" /> است؛ اعتبار گواهی امنیتی آن ظاهراً # روز دیگر شروع می‌شود. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجمی اتصالتان را قطع می‌کند.}other{این سرور نتوانست ثابت کند این <ph name="DOMAIN" /> است؛ اعتبار گواهی امنیتی آن ظاهراً # روز دیگر شروع می‌شود. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجمی اتصالتان را قطع می‌کند.}}</translation>
 <translation id="1710259589646384581">OS</translation>
diff --git a/components/strings/components_strings_ta.xtb b/components/strings/components_strings_ta.xtb
index 3b82629..e880594 100644
--- a/components/strings/components_strings_ta.xtb
+++ b/components/strings/components_strings_ta.xtb
@@ -1155,7 +1155,7 @@
 <translation id="7334320624316649418">&amp;மறுவரிசைப்படுத்தலை மீண்டும் செய்</translation>
 <translation id="733923710415886693">சான்றிதழ் வெளிப்படைத்தன்மை மூலம் சேவையகத்தின் சான்றிதழ் வெளியிடப்படவில்லை.</translation>
 <translation id="734600844861828519">11x15</translation>
-<translation id="7346048084945669753">அங்கீகரிக்கப்பட்டுள்ளாரா?:</translation>
+<translation id="7346048084945669753">இணைக்கப்பட்டுள்ளாரா?:</translation>
 <translation id="7349430561505560861">A4-Extra</translation>
 <translation id="7353601530677266744">கட்டளை வரி</translation>
 <translation id="7372973238305370288">தேடல் முடிவு</translation>
@@ -1188,7 +1188,7 @@
 <translation id="7451311239929941790">இந்தச் சிக்கல் குறித்து <ph name="BEGIN_LINK" />மேலும் அறிக<ph name="END_LINK" />.</translation>
 <translation id="7455133967321480974">முழுமையான இயல்புநிலையைப் பயன்படுத்து (தடு)</translation>
 <translation id="7460618730930299168">திரையிலுள்ள உள்ளடக்கமும் நீங்கள் தேர்ந்தெடுத்த உள்ளடக்கமும் மாறுபடுகின்றன. தொடரவா?</translation>
-<translation id="7465410862124366659">இந்தப் பக்கம் <ph name="ORGANIZATION" /> [<ph name="JURISDICTION" />]க்குச் சொந்தமானது எனக் கண்டறியப்பட்டுள்ளது.</translation>
+<translation id="7465410862124366659">இந்தப் பக்கம் <ph name="ORGANIZATION" /> [<ph name="JURISDICTION" />] நிறுவனத்துக்குச் சொந்தமானது எனக் கண்டறியப்பட்டுள்ளது.</translation>
 <translation id="7473891865547856676">வேண்டாம் நன்றி</translation>
 <translation id="7481312909269577407">அடுத்த பக்கம்</translation>
 <translation id="7485870689360869515">தரவு எதுவும் இல்லை.</translation>
diff --git a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
index f83991e..54c3065 100644
--- a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
+++ b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
@@ -3,40 +3,34 @@
    found in the LICENSE file. */
 
 body {
-  background-color: #f7f7f7;
-  color: #646464;
-}
-
-a {
-  color: #585858;
+  --avatar-stroke-color: var(--google-gray-50);
+  --custodian-name-color: #202124;
+  --custodian-email-color: #5f6368;
+  background-color: var(--quiet-background-color);
+  color: var(--text-color);
 }
 
 button {
-  background: #1A73E8;
+  background: var(--primary-button-fill-color);
   border: 0;
-  border-radius: 2px;
+  border-radius: 4px;
   box-sizing: border-box;
-  color: #ffffff;
+  color: var(--primary-button-text-color);
   cursor: pointer;
   font-family: 'Google Sans', Roboto, sans-serif;
   font-size: 14px;
-  font-weight: 550;
   line-height: 16pt;
   margin: 0;
-  padding: 7px 24px;
+  padding: 8px 16px;
   transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1);
   user-select: none;
 }
 
 button:active {
-  background: rgb(50, 102, 213);
+  background: var(--primary-button-fill-color-active)
   outline: 0;
 }
 
-button:hover {
-  box-shadow: 0 1px 3px rgba(0, 0, 0, .50);
-}
-
 [dir='rtl'] button {
   float: left;
 }
@@ -67,21 +61,22 @@
 }
 
 .details-button {
-  background: inherit;
-  border: 0;
+  background: var(--secondary-button-fill-color);
+  border: 1px solid var(--secondary-button-border-color);
+  color: var(--secondary-button-text-color);
   float: none;
   margin: 0;
-  padding: 10px 0;
-  text-transform: uppercase;
+  padding: 8px 16px;
 }
 
 .details-button:hover {
-  box-shadow: inherit;
-  text-decoration: underline;
+  background: var(--secondary-button-hover-fill-color);
+  border-color: var(--secondary-button-hover-border-color);
+  text-decoration: none;
 }
 
 h1 {
-  color: #333;
+  color: var(--heading-color);
   font-size: 1.6em;
   font-weight: normal;
   line-height: 1.25em;
@@ -110,7 +105,7 @@
 
 /* "Ask your Parent" title */
 .main-frame-blocked h1 {
-  color: #202124;
+  color: var(--heading-color);
   font-family: 'Google Sans', Roboto, sans-serif;
   font-size: 24px;
   font-weight: normal;
@@ -119,7 +114,7 @@
 
 /* "You need permission..." message */
 .main-frame-blocked p {
-  color: #3c4043;
+  color: var(--text-color);
   font-family: Roboto;
   font-size: 16px;
   font-weight: normal;
@@ -141,14 +136,14 @@
 }
 
 .small-link {
-  color: rgba(66, 133, 244, 100);
+  color: var(--small-link-color);
   font-family: Roboto;
   font-size: 15px;
   font-weight: 500;
 }
 
 .avatar-img {
-  border: 3px solid rgb(251, 251, 251);
+  border: 3px solid var(--avatar-stroke-color);
   border-radius: 50%;
   content: -webkit-image-set(
       url(default_100_percent/logo_avatar_circle_blue_color.png) 1x,
@@ -176,13 +171,13 @@
 }
 
 .custodian-name {
-  color: #202124;
+  color: var(--custodian-name-color);
   font-size: 16px;
   line-height: 24px;
 }
 
 .custodian-email {
-  color: #5f6368;
+  color: var(--custodian-email-color);
   font-size: 14px;
   line-height: 20px;
 }
@@ -259,9 +254,9 @@
        (min-width: 421px) and (max-width: 736px) and (min-height: 240px) and
        (max-height: 420px) and (orientation:landscape) {
   body .button-container {
-    background: #f7f7f7;
+    background: var(--background-color);
     bottom: 0;
-    box-shadow: 0 -22px 40px rgb(247, 247, 247);
+    box-shadow: 0 -22px 40px var(--background-color);
     left: 0;
     margin: 0;
     max-width: 736px;
@@ -531,3 +526,11 @@
     padding-right: 0;
   }
 }
+
+@media (prefers-color-scheme: dark) {
+  body.supervised-user-block {
+    --avatar-stroke-color: var(--google-blue-100);
+    --custodian-name-color: var(--google-gray-500);
+    --custodian-email-color: var(--google-gray-500);
+  }
+}
diff --git a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
index 7e6f621..256b944 100644
--- a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
+++ b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
@@ -6,12 +6,13 @@
     content="initial-scale=1, minimum-scale=1, width=device-width">
 <title>$i18n{blockPageTitle}</title>
 <link href="https://fonts.googleapis.com/css?family=Google+Sans" rel="stylesheet">
+<link rel="stylesheet" href="../../security_interstitials/core/common/resources/interstitial_core.css">
 <link rel="stylesheet" href="supervised_user_block_interstitial.css">
 <script src="../../../ui/webui/resources/js/cr.js"></script>
 <script src="../../../ui/webui/resources/js/util.js"></script>
 <script src="supervised_user_block_interstitial.js"></script>
 </head>
-<body>
+<body class="supervised-user-block">
 <div class="main-frame-blocked">
   <div id="information-container">
     <div class="icon" id="icon"></div>
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index e1c4640..f6a016a 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -436,7 +436,11 @@
   DVLOG(1) << "Initilalizing Control Types";
 
   // Initialize encryption.
-  sync_manager_->GetEncryptionHandler()->Init();
+  if (!sync_manager_->GetEncryptionHandler()->Init()) {
+    host_.Call(FROM_HERE,
+               &SyncEngineImpl::HandleInitializationFailureOnFrontendLoop);
+    return;
+  }
 
   // Note: experiments are currently handled via SBH::AddExperimentalTypes,
   // which is called at the end of every sync cycle.
diff --git a/components/sync/engine/sequenced_model_worker_unittest.cc b/components/sync/engine/sequenced_model_worker_unittest.cc
index 6b05f08..139f5f4 100644
--- a/components/sync/engine/sequenced_model_worker_unittest.cc
+++ b/components/sync/engine/sequenced_model_worker_unittest.cc
@@ -44,8 +44,7 @@
   // This is the work that will be scheduled to be done on the DB sequence.
   SyncerError DoWork() {
     EXPECT_TRUE(task_runner_->RunsTasksInCurrentSequence());
-    scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
-        FROM_HERE, run_loop_.QuitClosure());
+    run_loop_.Quit();
     did_do_work_ = true;
     return SyncerError(SyncerError::SYNCER_OK);
   }
@@ -55,8 +54,7 @@
   void Timeout() {
     ADD_FAILURE()
         << "Timed out waiting for work to be done on the DB sequence.";
-    scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
-        FROM_HERE, run_loop_.QuitClosure());
+    run_loop_.Quit();
   }
 
  protected:
diff --git a/components/sync/engine/sync_encryption_handler.h b/components/sync/engine/sync_encryption_handler.h
index 17635ad9..d4fd19b9 100644
--- a/components/sync/engine/sync_encryption_handler.h
+++ b/components/sync/engine/sync_encryption_handler.h
@@ -148,10 +148,10 @@
   // Reads the nigori node, updates internal state as needed, and, if an
   // empty/stale nigori node is detected, overwrites the existing
   // nigori node. Upon completion, if the cryptographer is still ready
-  // attempts to re-encrypt all sync data.
+  // attempts to re-encrypt all sync data. Returns false in case of error.
   // Note: This method is expensive (it iterates through all encrypted types),
   // so should only be used sparingly (e.g. on startup).
-  virtual void Init() = 0;
+  virtual bool Init() = 0;
 
   // Attempts to re-encrypt encrypted data types using the passphrase provided.
   // Notifies observers of the result of the operation via OnPassphraseAccepted
diff --git a/components/sync/engine_impl/apply_control_data_updates.cc b/components/sync/engine_impl/apply_control_data_updates.cc
index 7b63249..d1efb300 100644
--- a/components/sync/engine_impl/apply_control_data_updates.cc
+++ b/components/sync/engine_impl/apply_control_data_updates.cc
@@ -47,11 +47,12 @@
 
   // We apply the nigori update regardless of whether there's a conflict or
   // not in order to preserve any new encrypted types or encryption keys.
-  // TODO(zea): consider having this return a bool reflecting whether it was a
-  // valid update or not, and in the case of invalid updates not overwrite the
-  // local data.
   const sync_pb::NigoriSpecifics& nigori = entry.GetServerSpecifics().nigori();
-  trans.directory()->GetNigoriHandler()->ApplyNigoriUpdate(nigori, &trans);
+  if (!trans.directory()->GetNigoriHandler()->ApplyNigoriUpdate(nigori,
+                                                                &trans)) {
+    // If the remote update is considered invalid, do not write to local data.
+    return;
+  }
 
   // Make sure any unsynced changes are properly encrypted as necessary.
   // We only perform this if the cryptographer is ready. If not, these are
diff --git a/components/sync/engine_impl/sync_encryption_handler_impl.cc b/components/sync/engine_impl/sync_encryption_handler_impl.cc
index e3dd31d..7835156 100644
--- a/components/sync/engine_impl/sync_encryption_handler_impl.cc
+++ b/components/sync/engine_impl/sync_encryption_handler_impl.cc
@@ -21,6 +21,7 @@
 #include "components/sync/base/encryptor.h"
 #include "components/sync/base/passphrase_enums.h"
 #include "components/sync/base/sync_base_switches.h"
+#include "components/sync/base/system_encryptor.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/sync_engine_switches.h"
 #include "components/sync/engine/sync_string_conversions.h"
@@ -292,6 +293,31 @@
              sync_pb::NigoriSpecifics::UNSPECIFIED;
 }
 
+// Checks whether |specifics|'s encryption_keybag could be decrypted with any
+// of |keystore_keys|, if they won't be base64 encoded, directly or with
+// keystore_decryptor_token. Needed for UMA only.
+bool CanDecryptKeybagWithBase64DecodedKeys(
+    const std::vector<std::string>& keystore_keys,
+    const sync_pb::NigoriSpecifics& specifics) {
+  SystemEncryptor encryptor;
+  Cryptographer cryptographer(&encryptor);
+  for (const std::string& keystore_key : keystore_keys) {
+    std::string decoded_key;
+    base::Base64Decode(keystore_key, &decoded_key);
+    cryptographer.AddKey({KeyDerivationParams::CreateForPbkdf2(), decoded_key});
+  }
+
+  const sync_pb::EncryptedData& keystore_decryptor =
+      specifics.keystore_decryptor_token();
+  if (!keystore_decryptor.blob().empty() &&
+      cryptographer.CanDecrypt(keystore_decryptor)) {
+    std::string serialized_decryptor;
+    cryptographer.DecryptToString(keystore_decryptor, &serialized_decryptor);
+    cryptographer.ImportNigoriKey(serialized_decryptor);
+  }
+  return cryptographer.CanDecrypt(specifics.encryption_keybag());
+}
+
 }  // namespace
 
 SyncEncryptionHandlerImpl::Vault::Vault(Encryptor* encryptor,
@@ -340,21 +366,28 @@
   observers_.RemoveObserver(observer);
 }
 
-void SyncEncryptionHandlerImpl::Init() {
+bool SyncEncryptionHandlerImpl::Init() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   WriteTransaction trans(FROM_HERE, user_share_);
   WriteNode node(&trans);
 
-  if (node.InitTypeRoot(NIGORI) != BaseNode::INIT_OK)
-    return;
-  if (ApplyNigoriUpdateImpl(node.GetNigoriSpecifics(),
-                            trans.GetWrappedTrans())) {
-    // If we have successfully updated, we also need to replace an UNSPECIFIED
-    // key derivation method in Nigori with PBKDF2. (If the update fails,
-    // WriteEncryptionStateToNigori will do this for us.)
-    ReplaceImplicitKeyDerivationMethodInNigori(&trans);
-  } else {
-    WriteEncryptionStateToNigori(&trans, NigoriMigrationTrigger::kInit);
+  if (node.InitTypeRoot(NIGORI) != BaseNode::INIT_OK) {
+    // TODO(mastiz): This should be treated as error because it's a protocol
+    // violation if the server doesn't return the NIGORI root.
+    return true;
+  }
+
+  switch (ApplyNigoriUpdateImpl(node.GetNigoriSpecifics(),
+                                trans.GetWrappedTrans())) {
+    case ApplyNigoriUpdateResult::kSuccess:
+      // If we have successfully updated, we also need to replace an UNSPECIFIED
+      // key derivation method in Nigori with PBKDF2. (If the update fails,
+      // WriteEncryptionStateToNigori will do this for us.)
+      ReplaceImplicitKeyDerivationMethodInNigori(&trans);
+      break;
+    case ApplyNigoriUpdateResult::kRemoteMustBeCorrected:
+      WriteEncryptionStateToNigori(&trans, NigoriMigrationTrigger::kInit);
+      break;
   }
 
   PassphraseType passphrase_type = GetPassphraseType(trans.GetWrappedTrans());
@@ -396,6 +429,13 @@
                               MIGRATION_STATE_SIZE);
     UMA_HISTOGRAM_BOOLEAN("Sync.EncryptEverythingWhenCryptographerNotReady",
                           encrypt_everything_);
+    // TODO(crbug/944831): clean this up (including histogram itself and
+    // CanDecryptKeybagWithBase64DecodedKeys()) after investigation completed.
+    std::vector<std::string> all_keystore_keys = old_keystore_keys_;
+    all_keystore_keys.push_back(keystore_key_);
+    UMA_HISTOGRAM_BOOLEAN("Sync.CanDecryptNigoriKeybagWithBase64DecodedKeys",
+                          CanDecryptKeybagWithBase64DecodedKeys(
+                              all_keystore_keys, node.GetNigoriSpecifics()));
   } else if (keystore_key_.empty()) {
     // The client has no keystore key, either because it is not yet enabled or
     // the server is not sending a valid keystore key.
@@ -433,6 +473,8 @@
   // sync from happening until the the passphrase is provided.
   if (UnlockVault(trans.GetWrappedTrans()).cryptographer.is_ready())
     ReEncryptEverything(&trans);
+
+  return true;
 }
 
 void SyncEncryptionHandlerImpl::SetEncryptionPassphrase(
@@ -760,36 +802,43 @@
 
 // Note: this is called from within a syncable transaction, so we need to post
 // tasks if we want to do any work that creates a new sync_api transaction.
-void SyncEncryptionHandlerImpl::ApplyNigoriUpdate(
+bool SyncEncryptionHandlerImpl::ApplyNigoriUpdate(
     const sync_pb::NigoriSpecifics& nigori,
     syncable::BaseTransaction* const trans) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(trans);
-  if (ApplyNigoriUpdateImpl(nigori, trans)) {
-    // If we have successfully updated, we also need to replace an UNSPECIFIED
-    // key derivation method in Nigori with PBKDF2, for which we post a task.
-    // (If the update fails, RewriteNigori will do this for us.) Note that this
-    // check is redundant, but it is used to avoid the overhead of posting a
-    // task which will just do nothing.
-    if (ShouldSetExplicitCustomPassphraseKeyDerivationMethod(nigori)) {
+
+  switch (ApplyNigoriUpdateImpl(nigori, trans)) {
+    case ApplyNigoriUpdateResult::kSuccess:
+      // If we have successfully updated, we also need to replace an UNSPECIFIED
+      // key derivation method in Nigori with PBKDF2, for which we post a task.
+      // (If the update fails, RewriteNigori will do this for us.) Note that
+      // this check is redundant, but it is used to avoid the overhead of
+      // posting a task which will just do nothing.
+      if (ShouldSetExplicitCustomPassphraseKeyDerivationMethod(nigori)) {
+        base::SequencedTaskRunnerHandle::Get()->PostTask(
+            FROM_HERE,
+            base::BindOnce(
+                &SyncEncryptionHandlerImpl::
+                    ReplaceImplicitKeyDerivationMethodInNigoriWithTransaction,
+                weak_ptr_factory_.GetWeakPtr()));
+      }
+      break;
+    case ApplyNigoriUpdateResult::kRemoteMustBeCorrected:
       base::SequencedTaskRunnerHandle::Get()->PostTask(
           FROM_HERE,
-          base::BindOnce(
-              &SyncEncryptionHandlerImpl::
-                  ReplaceImplicitKeyDerivationMethodInNigoriWithTransaction,
-              weak_ptr_factory_.GetWeakPtr()));
-    }
-  } else {
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&SyncEncryptionHandlerImpl::RewriteNigori,
-                                  weak_ptr_factory_.GetWeakPtr(),
-                                  NigoriMigrationTrigger::kApplyNigoriUpdate));
+          base::BindOnce(&SyncEncryptionHandlerImpl::RewriteNigori,
+                         weak_ptr_factory_.GetWeakPtr(),
+                         NigoriMigrationTrigger::kApplyNigoriUpdate));
+      break;
   }
 
   for (auto& observer : observers_) {
     observer.OnCryptographerStateChanged(
         &UnlockVaultMutable(trans)->cryptographer);
   }
+
+  return true;
 }
 
 void SyncEncryptionHandlerImpl::UpdateNigoriFromEncryptedTypes(
@@ -1009,7 +1058,8 @@
   }
 }
 
-bool SyncEncryptionHandlerImpl::ApplyNigoriUpdateImpl(
+SyncEncryptionHandlerImpl::ApplyNigoriUpdateResult
+SyncEncryptionHandlerImpl::ApplyNigoriUpdateImpl(
     const sync_pb::NigoriSpecifics& nigori,
     syncable::BaseTransaction* const trans) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -1171,9 +1221,9 @@
       nigori.encrypt_everything() != encrypt_everything_ ||
       nigori_types_need_update || nigori_needs_new_keys) {
     DVLOG(1) << "Triggering nigori rewrite.";
-    return false;
+    return ApplyNigoriUpdateResult::kRemoteMustBeCorrected;
   }
-  return true;
+  return ApplyNigoriUpdateResult::kSuccess;
 }
 
 void SyncEncryptionHandlerImpl::RewriteNigori(
diff --git a/components/sync/engine_impl/sync_encryption_handler_impl.h b/components/sync/engine_impl/sync_encryption_handler_impl.h
index fdd4e83..551e4fb 100644
--- a/components/sync/engine_impl/sync_encryption_handler_impl.h
+++ b/components/sync/engine_impl/sync_encryption_handler_impl.h
@@ -63,7 +63,7 @@
   // SyncEncryptionHandler implementation.
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
-  void Init() override;
+  bool Init() override;
   void SetEncryptionPassphrase(const std::string& passphrase) override;
   void SetDecryptionPassphrase(const std::string& passphrase) override;
   void EnableEncryptEverything() override;
@@ -75,7 +75,7 @@
 
   // NigoriHandler implementation.
   // Note: all methods are invoked while the caller holds a transaction.
-  void ApplyNigoriUpdate(const sync_pb::NigoriSpecifics& nigori,
+  bool ApplyNigoriUpdate(const sync_pb::NigoriSpecifics& nigori,
                          syncable::BaseTransaction* const trans) override;
   void UpdateNigoriFromEncryptedTypes(
       sync_pb::NigoriSpecifics* nigori,
@@ -184,6 +184,12 @@
     kMaxValue = kInitialization
   };
 
+  // Enumeration of possible outcomes of ApplyNigoriUpdateImpl.
+  enum class ApplyNigoriUpdateResult {
+    kSuccess,
+    kRemoteMustBeCorrected,
+  };
+
   // Iterate over all encrypted types ensuring each entry is properly encrypted.
   void ReEncryptEverything(WriteTransaction* trans);
 
@@ -191,11 +197,10 @@
   //
   // Assumes |nigori| is already present in the Sync Directory.
   //
-  // Returns true on success, false if |nigori| was incompatible, and the
-  // nigori node must be corrected.
   // Note: must be called from within a transaction.
-  bool ApplyNigoriUpdateImpl(const sync_pb::NigoriSpecifics& nigori,
-                             syncable::BaseTransaction* const trans);
+  ApplyNigoriUpdateResult ApplyNigoriUpdateImpl(
+      const sync_pb::NigoriSpecifics& nigori,
+      syncable::BaseTransaction* const trans);
 
   // Wrapper around WriteEncryptionStateToNigori that creates a new write
   // transaction. Because this function can trigger a migration,
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
index cd3bedcc..b04bf1c 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -277,7 +277,7 @@
   observers_.RemoveObserver(observer);
 }
 
-void NigoriSyncBridgeImpl::Init() {
+bool NigoriSyncBridgeImpl::Init() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Init() is called after the first sync cycle, so we can have
   // |encrypt_everything_| enabled even if we don't persist the local state.
@@ -297,6 +297,7 @@
   // TODO(crbug.com/922900): notify observers about cryptographer change in
   // case UpdateLocalState() is not called in this function (i.e.
   // initialization implemented in constructor).
+  return true;
 }
 
 void NigoriSyncBridgeImpl::SetEncryptionPassphrase(
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.h b/components/sync/nigori/nigori_sync_bridge_impl.h
index d7799997..3bab2f6 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.h
+++ b/components/sync/nigori/nigori_sync_bridge_impl.h
@@ -47,7 +47,7 @@
   // SyncEncryptionHandler implementation.
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
-  void Init() override;
+  bool Init() override;
   void SetEncryptionPassphrase(const std::string& passphrase) override;
   void SetDecryptionPassphrase(const std::string& passphrase) override;
   void EnableEncryptEverything() override;
diff --git a/components/sync/syncable/nigori_handler.h b/components/sync/syncable/nigori_handler.h
index e50ecf5..bb09040 100644
--- a/components/sync/syncable/nigori_handler.h
+++ b/components/sync/syncable/nigori_handler.h
@@ -30,8 +30,9 @@
   virtual ~NigoriHandler();
 
   // Apply a nigori node update, updating the internal encryption state
-  // accordingly.
-  virtual void ApplyNigoriUpdate(const sync_pb::NigoriSpecifics& nigori,
+  // accordingly. Returns true in case of success, or false if the update has
+  // been ignored.
+  virtual bool ApplyNigoriUpdate(const sync_pb::NigoriSpecifics& nigori,
                                  syncable::BaseTransaction* const trans) = 0;
 
   // Store the current encrypt everything/encrypted types state into |nigori|.
diff --git a/components/sync/test/fake_sync_encryption_handler.cc b/components/sync/test/fake_sync_encryption_handler.cc
index e66e709..e3c2174 100644
--- a/components/sync/test/fake_sync_encryption_handler.cc
+++ b/components/sync/test/fake_sync_encryption_handler.cc
@@ -17,14 +17,15 @@
       cryptographer_(&encryptor_) {}
 FakeSyncEncryptionHandler::~FakeSyncEncryptionHandler() {}
 
-void FakeSyncEncryptionHandler::Init() {
+bool FakeSyncEncryptionHandler::Init() {
   // Set up a basic cryptographer.
   KeyParams keystore_params = {KeyDerivationParams::CreateForPbkdf2(),
                                "keystore_key"};
   cryptographer_.AddKey(keystore_params);
+  return true;
 }
 
-void FakeSyncEncryptionHandler::ApplyNigoriUpdate(
+bool FakeSyncEncryptionHandler::ApplyNigoriUpdate(
     const sync_pb::NigoriSpecifics& nigori,
     syncable::BaseTransaction* const trans) {
   if (nigori.encrypt_everything())
@@ -54,6 +55,8 @@
                                     sync_pb::EncryptedData());
     }
   }
+
+  return true;
 }
 
 void FakeSyncEncryptionHandler::UpdateNigoriFromEncryptedTypes(
diff --git a/components/sync/test/fake_sync_encryption_handler.h b/components/sync/test/fake_sync_encryption_handler.h
index 34869c1..de9a8785 100644
--- a/components/sync/test/fake_sync_encryption_handler.h
+++ b/components/sync/test/fake_sync_encryption_handler.h
@@ -35,7 +35,7 @@
   // SyncEncryptionHandler implementation.
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
-  void Init() override;
+  bool Init() override;
   void SetEncryptionPassphrase(const std::string& passphrase) override;
   void SetDecryptionPassphrase(const std::string& passphrase) override;
   void EnableEncryptEverything() override;
@@ -48,7 +48,7 @@
   syncable::NigoriHandler* GetNigoriHandler() override;
 
   // NigoriHandler implemenation.
-  void ApplyNigoriUpdate(const sync_pb::NigoriSpecifics& nigori,
+  bool ApplyNigoriUpdate(const sync_pb::NigoriSpecifics& nigori,
                          syncable::BaseTransaction* const trans) override;
   void UpdateNigoriFromEncryptedTypes(
       sync_pb::NigoriSpecifics* nigori,
diff --git a/components/test/data/autofill_assistant/autofill_assistant_target_website.html b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
index b831945da..63e90eb 100644
--- a/components/test/data/autofill_assistant/autofill_assistant_target_website.html
+++ b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
@@ -281,7 +281,7 @@
       <br>
     </div>
 
-    <div style="height:500px"/>
+    <div id="before_scroll_container"/>
     <div id="scroll_container" style="width:100px;height:100px;overflow:auto;">
       <div id="scroll_item_1" style="width:50px;height:50px">1</div>
       <div id="scroll_item_2" style="width:50px;height:50px">2</div>
@@ -291,6 +291,6 @@
       <div id="scroll_item_4" style="width:50px;height:50px">4</div>
       <div id="scroll_item_5" style="width:50px;height:50px">5</div>
     </div>
-    <div style="height:500px"/>
+    <div id="after_scroll_container"/>
   </body>
 </html>
diff --git a/components/test/data/payments/bobpay_and_cards.js b/components/test/data/payments/bobpay_and_cards.js
index 28df856..12917f2 100644
--- a/components/test/data/payments/bobpay_and_cards.js
+++ b/components/test/data/payments/bobpay_and_cards.js
@@ -7,19 +7,16 @@
 /* global PaymentRequest:false */
 
 /**
- * Launches the PaymentRequest UI with Bob Pay and credit cards as payment
- * methods.
+ * Helper function that launches the PaymentRequest UI with the specified
+ * payment methods.
+ *
+ * @param {!Array<!Object>} methods: Payment methods data for PaymentRequest
+ *     constructor.
  */
-function buy() {  // eslint-disable-line no-unused-vars
+function testPaymentMethods(methods) {
   try {
     new PaymentRequest(
-        [
-          {supportedMethods: 'https://bobpay.com'},
-          {
-            supportedMethods: 'basic-card',
-            data: {supportedNetworks: ['visa', 'mastercard']},
-          },
-        ],
+        methods,
         {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}})
         .show()
         .then(function(resp) {
@@ -40,3 +37,28 @@
     print(error.message);
   }
 }
+
+/**
+ * Launches the PaymentRequest UI with Bob Pay and credit cards as payment
+ * methods.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  testPaymentMethods([
+      {supportedMethods: 'https://bobpay.com'},
+      {
+        supportedMethods: 'basic-card',
+        data: {supportedNetworks: ['visa', 'mastercard']},
+      },
+  ]);
+}
+
+/**
+ * Launches the PaymentRequest UI with kylepay.com and basic-card as payment
+ * methods. kylepay.com hosts an installable payment app.
+ */
+function testInstallableAppAndCard() {  // eslint-disable-line no-unused-vars
+  testPaymentMethods([
+      {supportedMethods: 'https://kylepay.com/webpay'},
+      {supportedMethods: 'basic-card'},
+  ]);
+}
diff --git a/components/test/data/payments/render_tests/PaymentRequestFreeShippingTest.free_shipping.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/PaymentRequestFreeShippingTest.free_shipping.Nexus_5-19.png.sha1
index 328f07c..87347d6 100644
--- a/components/test/data/payments/render_tests/PaymentRequestFreeShippingTest.free_shipping.Nexus_5-19.png.sha1
+++ b/components/test/data/payments/render_tests/PaymentRequestFreeShippingTest.free_shipping.Nexus_5-19.png.sha1
@@ -1 +1 @@
-cc10274c55805d69434892604346c1bcdf1fc74f
\ No newline at end of file
+f3da9bd72be06e533c6f516f0af3f6b2b3a149ae
\ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_http_popup.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_http_popup.Pixel_XL-25.png.sha1
index f959db7b..d5c2bfe 100644
--- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_http_popup.Pixel_XL-25.png.sha1
+++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_http_popup.Pixel_XL-25.png.sha1
@@ -1 +1 @@
-05bdbeb07592d88f819fde004996d6dd1f9ae4c5
\ No newline at end of file
+7ad1b840085769eaf4eb15bd049193725cfb55ff
\ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_http_popup.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_http_popup.Pixel_XL-26.png.sha1
index f20eda82..abe484be 100644
--- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_http_popup.Pixel_XL-26.png.sha1
+++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_http_popup.Pixel_XL-26.png.sha1
@@ -1 +1 @@
-5beb3cf51a29d5999ef0f08a74769354c30d4e48
\ No newline at end of file
+fd110b942dbbbce815d8341c7232d639aac359e0
\ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_bad_cert_popup.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_bad_cert_popup.Pixel_XL-25.png.sha1
index 489567fe..0c15e4b 100644
--- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_bad_cert_popup.Pixel_XL-25.png.sha1
+++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_bad_cert_popup.Pixel_XL-25.png.sha1
@@ -1 +1 @@
-bab5f5c368b80d75db2f8150d17352e87d120f4e
\ No newline at end of file
+d39ceadd2039c813bc7f6dda1303dc07ebd032b8
\ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_bad_cert_popup.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_bad_cert_popup.Pixel_XL-26.png.sha1
index da749cc..70c68d85a 100644
--- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_bad_cert_popup.Pixel_XL-26.png.sha1
+++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_bad_cert_popup.Pixel_XL-26.png.sha1
@@ -1 +1 @@
-ed37b6fd37d9d95fcac3421a41092a9d4b22610c
\ No newline at end of file
+aec68c9b45f8ce029d484d85a9d0c1fd72b165c3
\ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_popup.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_popup.Pixel_XL-25.png.sha1
index a286705..805bdb3 100644
--- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_popup.Pixel_XL-25.png.sha1
+++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_popup.Pixel_XL-25.png.sha1
@@ -1 +1 @@
-e41e7e2b2e4165de37c047b02cb5b82e7586ee38
\ No newline at end of file
+346c3d8098cbc288324f58c8efbed827f4cf6fd6
\ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_popup.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_popup.Pixel_XL-26.png.sha1
index 45991134..42ccf74 100644
--- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_popup.Pixel_XL-26.png.sha1
+++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.security_token_https_popup.Pixel_XL-26.png.sha1
@@ -1 +1 @@
-306a8db476ec0e3bf17eae89bd2a99876f9a8225
\ No newline at end of file
+db86fe01fb3a0f366037f67dbaff0f31922e1c58
\ No newline at end of file
diff --git a/components/ui_devtools/views/element_utility.cc b/components/ui_devtools/views/element_utility.cc
index 042b721..ddc9a4c 100644
--- a/components/ui_devtools/views/element_utility.cc
+++ b/components/ui_devtools/views/element_utility.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "components/ui_devtools/views/element_utility.h"
+
+#include "base/strings/string_number_conversions.h"
 #include "ui/compositor/layer_owner.h"
 
 namespace ui_devtools {
@@ -13,6 +15,25 @@
   ret->emplace_back("layer-type", LayerTypeToString(layer->type()));
   ret->emplace_back("has-layer-mask",
                     layer->layer_mask_layer() ? "true" : "false");
+  ret->emplace_back("layer-opacity", base::NumberToString((layer->opacity())));
+  ret->emplace_back("layer-combined-opacity",
+                    base::NumberToString(layer->GetCombinedOpacity()));
+  ret->emplace_back("background-blur",
+                    base::NumberToString(layer->background_blur()));
+  ret->emplace_back("layer-blur", base::NumberToString(layer->layer_blur()));
+  ret->emplace_back("layer-saturation",
+                    base::NumberToString(layer->layer_saturation()));
+  ret->emplace_back("layer-brightness",
+                    base::NumberToString(layer->layer_brightness()));
+  ret->emplace_back("layer-grayscale",
+                    base::NumberToString(layer->layer_grayscale()));
+  const ui::Layer::ShapeRects* alpha_shape_bounds = layer->alpha_shape();
+  if (alpha_shape_bounds && alpha_shape_bounds->size()) {
+    gfx::Rect bounding_box;
+    for (auto& shape_bound : *alpha_shape_bounds)
+      bounding_box.Union(shape_bound);
+    ret->emplace_back("alpha-shape-bounding-box", bounding_box.ToString());
+  }
   const cc::Layer* cc_layer = layer->cc_layer_for_testing();
   if (cc_layer) {
     // Property trees must be updated in order to get valid render surface
diff --git a/components/url_formatter/tools/BUILD.gn b/components/url_formatter/tools/BUILD.gn
new file mode 100644
index 0000000..c46bcfa4
--- /dev/null
+++ b/components/url_formatter/tools/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+executable("format_url") {
+  sources = [
+    "format_url.cc",
+  ]
+  deps = [
+    "//base",
+    "//base:i18n",
+    "//components/url_formatter:url_formatter",
+  ]
+}
diff --git a/components/url_formatter/tools/DEPS b/components/url_formatter/tools/DEPS
new file mode 100644
index 0000000..0f29604
--- /dev/null
+++ b/components/url_formatter/tools/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/url_formatter",
+]
diff --git a/components/url_formatter/tools/OWNERS b/components/url_formatter/tools/OWNERS
new file mode 100644
index 0000000..ce7f431
--- /dev/null
+++ b/components/url_formatter/tools/OWNERS
@@ -0,0 +1,3 @@
+file://components/security_interstitials/OWNERS
+
+# COMPONENT: UI>Security>UrlFormatting
diff --git a/components/url_formatter/tools/README.md b/components/url_formatter/tools/README.md
new file mode 100644
index 0000000..2afc54f
--- /dev/null
+++ b/components/url_formatter/tools/README.md
@@ -0,0 +1,8 @@
+# IDN Tools
+
+This directory contains tools to help with changes related to the handling of Internationalized Domain Names (IDN) related changes.
+
+### format_url
+
+This binary takes a list of domain names, passes them through url_formatter and prints the result. This is useful when measuring the impact of spoof checks in `components/url_formatter` over a large list of domains.
+
diff --git a/components/url_formatter/tools/format_url.cc b/components/url_formatter/tools/format_url.cc
new file mode 100644
index 0000000..1be79852
--- /dev/null
+++ b/components/url_formatter/tools/format_url.cc
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This binary takes a list of domain names, tries to convert them to unicode
+// and prints out the result. The list can be passed as a text file or via
+// stdin. In both cases, the output is printed as (input_domain, output_domain)
+// pairs on separate lines.
+
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/i18n/icu_util.h"
+#include "base/logging.h"
+#include "components/url_formatter/url_formatter.h"
+
+void PrintUsage(const char* process_name) {
+  std::cout << "Usage:" << std::endl;
+  std::cout << process_name << " <file>" << std::endl;
+  std::cout << std::endl;
+  std::cout << "<file> is a text file with one hostname per line." << std::endl;
+  std::cout << "Each hostname is converted to unicode, if safe. Otherwise, "
+            << "it's printed unchanged." << std::endl;
+}
+
+void Convert(std::istream& input) {
+  base::i18n::InitializeICU();
+  for (std::string line; std::getline(input, line);) {
+    std::cout << line << ", " << url_formatter::IDNToUnicode(line) << std::endl;
+  }
+}
+
+int main(int argc, char* argv[]) {
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+
+  if (cmd->HasSwitch("help")) {
+    PrintUsage(argv[0]);
+    return 0;
+  }
+
+  if (argc > 1) {
+    const std::string filename = argv[1];
+    std::ifstream input(filename);
+    if (!input.good()) {
+      LOG(ERROR) << "Could not open file " << filename;
+      return -1;
+    }
+    Convert(input);
+  } else {
+    Convert(std::cin);
+  }
+
+  return EXIT_SUCCESS;
+}
diff --git a/components/viz/host/gpu_host_impl.cc b/components/viz/host/gpu_host_impl.cc
index 7fa4ec2..3615054c 100644
--- a/components/viz/host/gpu_host_impl.cc
+++ b/components/viz/host/gpu_host_impl.cc
@@ -17,6 +17,7 @@
 #include "build/build_config.h"
 #include "components/viz/common/features.h"
 #include "gpu/config/gpu_driver_bug_workaround_type.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "gpu/config/gpu_info.h"
@@ -447,7 +448,8 @@
     const gpu::GpuFeatureInfo& gpu_feature_info,
     const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
     const base::Optional<gpu::GpuFeatureInfo>&
-        gpu_feature_info_for_hardware_gpu) {
+        gpu_feature_info_for_hardware_gpu,
+    const gpu::GpuExtraInfo& gpu_extra_info) {
   UMA_HISTOGRAM_BOOLEAN("GPU.GPUProcessInitialized", true);
 
   // Set GPU driver bug workaround flags that are checked on the browser side.
@@ -459,7 +461,7 @@
 
   delegate_->DidInitialize(gpu_info, gpu_feature_info,
                            gpu_info_for_hardware_gpu,
-                           gpu_feature_info_for_hardware_gpu);
+                           gpu_feature_info_for_hardware_gpu, gpu_extra_info);
 
   // Remove entries so that GPU process shader caches get populated on any
   // GPU process start.
diff --git a/components/viz/host/gpu_host_impl.h b/components/viz/host/gpu_host_impl.h
index a2a7da2..06b2621c 100644
--- a/components/viz/host/gpu_host_impl.h
+++ b/components/viz/host/gpu_host_impl.h
@@ -23,6 +23,7 @@
 #include "components/viz/host/viz_host_export.h"
 #include "gpu/command_buffer/common/activity_flags.h"
 #include "gpu/config/gpu_domain_guilt.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/system/message_pipe.h"
@@ -82,7 +83,8 @@
         const gpu::GpuFeatureInfo& gpu_feature_info,
         const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
         const base::Optional<gpu::GpuFeatureInfo>&
-            gpu_feature_info_for_hardware_gpu) = 0;
+            gpu_feature_info_for_hardware_gpu,
+        const gpu::GpuExtraInfo& gpu_extra_info) = 0;
     virtual void DidFailInitialize() = 0;
     virtual void DidCreateContextSuccessfully() = 0;
     virtual void BlockDomainFrom3DAPIs(const GURL& url,
@@ -225,7 +227,8 @@
       const gpu::GpuFeatureInfo& gpu_feature_info,
       const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
       const base::Optional<gpu::GpuFeatureInfo>&
-          gpu_feature_info_for_hardware_gpu) override;
+          gpu_feature_info_for_hardware_gpu,
+      const gpu::GpuExtraInfo& gpu_extra_info) override;
   void DidFailInitialize() override;
   void DidCreateContextSuccessfully() override;
   void DidCreateOffscreenContext(const GURL& url) override;
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 38e45c1..d0f0cef 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
@@ -455,6 +455,10 @@
     return sync_token;
   }
 
+  void Flush() override {
+    // No need to flush in this implementation.
+  }
+
   // DirectContextProviderDelegate implementation.
   gpu::SharedImageManager* GetSharedImageManager() override {
     return shared_image_manager_;
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 41c64f0b..5b0adbca 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -148,6 +148,7 @@
     const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
     const base::Optional<gpu::GpuFeatureInfo>&
         gpu_feature_info_for_hardware_gpu,
+    const gpu::GpuExtraInfo& gpu_extra_info,
     gpu::VulkanImplementation* vulkan_implementation,
     base::OnceClosure exit_callback)
     : main_runner_(base::ThreadTaskRunnerHandle::Get()),
@@ -158,6 +159,7 @@
       gpu_feature_info_(gpu_feature_info),
       gpu_info_for_hardware_gpu_(gpu_info_for_hardware_gpu),
       gpu_feature_info_for_hardware_gpu_(gpu_feature_info_for_hardware_gpu),
+      gpu_extra_info_(gpu_extra_info),
 #if BUILDFLAG(ENABLE_VULKAN)
       vulkan_implementation_(vulkan_implementation),
 #endif
@@ -284,7 +286,7 @@
   DCHECK(main_runner_->BelongsToCurrentThread());
   gpu_host->DidInitialize(gpu_info_, gpu_feature_info_,
                           gpu_info_for_hardware_gpu_,
-                          gpu_feature_info_for_hardware_gpu_);
+                          gpu_feature_info_for_hardware_gpu_, gpu_extra_info_);
   gpu_host_ =
       mojom::ThreadSafeGpuHostPtr::Create(gpu_host.PassInterface(), io_runner_);
   if (!in_host_process()) {
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index 646a698..3956410 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -18,6 +18,7 @@
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/command_buffer/common/activity_flags.h"
 #include "gpu/command_buffer/service/sequence_id.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/config/gpu_preferences.h"
 #include "gpu/ipc/common/surface_handle.h"
@@ -76,6 +77,7 @@
                  const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
                  const base::Optional<gpu::GpuFeatureInfo>&
                      gpu_feature_info_for_hardware_gpu,
+                 const gpu::GpuExtraInfo& gpu_extra_info,
                  gpu::VulkanImplementation* vulkan_implementation,
                  base::OnceClosure exit_callback);
 
@@ -293,6 +295,9 @@
   base::Optional<gpu::GPUInfo> gpu_info_for_hardware_gpu_;
   base::Optional<gpu::GpuFeatureInfo> gpu_feature_info_for_hardware_gpu_;
 
+  // Information about the GPU process populated on creation.
+  gpu::GpuExtraInfo gpu_extra_info_;
+
   scoped_refptr<mojom::ThreadSafeGpuHostPtr> gpu_host_;
   std::unique_ptr<gpu::GpuChannelManager> gpu_channel_manager_;
   std::unique_ptr<media::MediaGpuChannelManager> media_gpu_channel_manager_;
diff --git a/components/viz/service/gl/gpu_service_impl_unittest.cc b/components/viz/service/gl/gpu_service_impl_unittest.cc
index e0602da3..3fe6527 100644
--- a/components/viz/service/gl/gpu_service_impl_unittest.cc
+++ b/components/viz/service/gl/gpu_service_impl_unittest.cc
@@ -53,7 +53,8 @@
     gpu_service_ = std::make_unique<GpuServiceImpl>(
         gpu::GPUInfo(), nullptr /* watchdog_thread */, io_thread_.task_runner(),
         gpu::GpuFeatureInfo(), gpu::GpuPreferences(), gpu::GPUInfo(),
-        gpu::GpuFeatureInfo(), nullptr /* vulkan_implementation */,
+        gpu::GpuFeatureInfo(), gpu::GpuExtraInfo(),
+        nullptr /* vulkan_implementation */,
         base::DoNothing() /* exit_callback */);
   }
 
diff --git a/components/viz/service/main/viz_main_impl.cc b/components/viz/service/main/viz_main_impl.cc
index dceabbf4..8b1cde48 100644
--- a/components/viz/service/main/viz_main_impl.cc
+++ b/components/viz/service/main/viz_main_impl.cc
@@ -98,7 +98,7 @@
       gpu_init_->gpu_feature_info(), gpu_init_->gpu_preferences(),
       gpu_init_->gpu_info_for_hardware_gpu(),
       gpu_init_->gpu_feature_info_for_hardware_gpu(),
-      gpu_init_->vulkan_implementation(),
+      gpu_init_->gpu_extra_info(), gpu_init_->vulkan_implementation(),
       base::BindOnce(&VizMainImpl::ExitProcess, base::Unretained(this)));
   if (dependencies_.create_display_compositor)
     gpu_service_->set_oopd_enabled();
diff --git a/components/viz/test/data/mirror_layer.png b/components/viz/test/data/mirror_layer.png
new file mode 100644
index 0000000..4dc0edc9
--- /dev/null
+++ b/components/viz/test/data/mirror_layer.png
Binary files differ
diff --git a/components/viz/test/test_context_provider.cc b/components/viz/test/test_context_provider.cc
index ec2393c..7dd29e0 100644
--- a/components/viz/test/test_context_provider.cc
+++ b/components/viz/test/test_context_provider.cc
@@ -203,6 +203,10 @@
   return most_recent_generated_token_;
 }
 
+void TestSharedImageInterface::Flush() {
+  // No need to flush in this implementation.
+}
+
 bool TestSharedImageInterface::CheckSharedImageExists(
     const gpu::Mailbox& mailbox) const {
   return shared_images_.contains(mailbox);
diff --git a/components/viz/test/test_context_provider.h b/components/viz/test/test_context_provider.h
index d2fb0392..cc2d5e26 100644
--- a/components/viz/test/test_context_provider.h
+++ b/components/viz/test/test_context_provider.h
@@ -78,6 +78,8 @@
   gpu::SyncToken GenVerifiedSyncToken() override;
   gpu::SyncToken GenUnverifiedSyncToken() override;
 
+  void Flush() override;
+
   size_t shared_image_count() const { return shared_images_.size(); }
   const gfx::Size& MostRecentSize() const { return most_recent_size_; }
   const gpu::SyncToken& MostRecentGeneratedToken() const {
diff --git a/components/viz/test/test_gpu_service_holder.cc b/components/viz/test/test_gpu_service_holder.cc
index 3245a4d..2a7dbb5 100644
--- a/components/viz/test/test_gpu_service_holder.cc
+++ b/components/viz/test/test_gpu_service_holder.cc
@@ -170,6 +170,7 @@
       gpu_feature_info, gpu_preferences,
       /*gpu_info_for_hardware_gpu=*/gpu::GPUInfo(),
       /*gpu_feature_info_for_hardware_gpu=*/gpu::GpuFeatureInfo(),
+      /*gpu_extra_info=*/gpu::GpuExtraInfo(),
 #if BUILDFLAG(ENABLE_VULKAN)
       vulkan_implementation_.get(),
 #else
diff --git a/components/webdata/common/BUILD.gn b/components/webdata/common/BUILD.gn
index 9f192d1..f8ffaa81 100644
--- a/components/webdata/common/BUILD.gn
+++ b/components/webdata/common/BUILD.gn
@@ -89,7 +89,7 @@
     "//components/autofill/core/common",
     "//components/password_manager/core/browser",
     "//components/search_engines",
-    "//components/signin/core/browser",
+    "//components/signin/core/browser/webdata",
     "//sql",
     "//testing/gtest",
   ]
diff --git a/components/webdata_services/BUILD.gn b/components/webdata_services/BUILD.gn
index 0544db7..00248e0 100644
--- a/components/webdata_services/BUILD.gn
+++ b/components/webdata_services/BUILD.gn
@@ -17,7 +17,7 @@
     "//components/keyed_service/core",
     "//components/password_manager/core/browser",
     "//components/search_engines",
-    "//components/signin/core/browser",
+    "//components/signin/core/browser/webdata",
     "//components/sync",
     "//components/webdata/common",
     "//sql",
diff --git a/content/app/strings/translations/content_strings_pt-BR.xtb b/content/app/strings/translations/content_strings_pt-BR.xtb
index abc4476..dde5e25 100644
--- a/content/app/strings/translations/content_strings_pt-BR.xtb
+++ b/content/app/strings/translations/content_strings_pt-BR.xtb
@@ -67,7 +67,7 @@
 <translation id="3078740164268491126">tabela</translation>
 <translation id="3086746722712840547">note</translation>
 <translation id="310520048233152454">Insira um URL.</translation>
-<translation id="3175736971608411871">temporizador</translation>
+<translation id="3175736971608411871">timer</translation>
 <translation id="3199563858620722075">caixa de combinação</translation>
 <translation id="3450233048674729344">O valor deve ser menor ou igual a <ph name="MAXIMUM" />.</translation>
 <translation id="3486220673238053218">definição</translation>
diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
index 8a38bd9..88c23793 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
+++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
@@ -816,17 +816,10 @@
   mouse_locked_ = locked;
   if (mouse_locked_) {
     CGAssociateMouseAndMouseCursorPosition(NO);
-    NSRect bound = [[self window] convertRectToScreen:[self bounds]];
-    gfx::Point screen_center = gfx::ScreenRectFromNSRect(bound).CenterPoint();
-    mouse_locked_screen_position_ = last_mouse_screen_position_;
-    CGDisplayMoveCursorToPoint(CGMainDisplayID(), screen_center.ToCGPoint());
     [NSCursor hide];
   } else {
     // Unlock position of mouse cursor and unhide it.
     CGAssociateMouseAndMouseCursorPosition(YES);
-    CGDisplayMoveCursorToPoint(CGMainDisplayID(),
-                               NSMakePoint(mouse_locked_screen_position_.x(),
-                                           mouse_locked_screen_position_.y()));
     [NSCursor unhide];
   }
 }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4f7203c..16f3750 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -84,6 +84,7 @@
     "//content/browser/background_fetch:background_fetch_proto",
     "//content/browser/background_sync:background_sync_proto",
     "//content/browser/cache_storage:cache_storage_proto",
+    "//content/browser/content_index:content_index_proto",
     "//content/browser/cookie_store:cookie_store_proto",
     "//content/browser/devtools:devtools_background_services_proto",
     "//content/browser/devtools:protocol_sources",
@@ -660,6 +661,10 @@
     "contacts/contacts_manager_impl.cc",
     "contacts/contacts_manager_impl.h",
     "contacts/contacts_provider.h",
+    "content_index/content_index_context.cc",
+    "content_index/content_index_context.h",
+    "content_index/content_index_database.cc",
+    "content_index/content_index_database.h",
     "content_index/content_index_service_impl.cc",
     "content_index/content_index_service_impl.h",
     "startup_data_impl.cc",
@@ -1725,6 +1730,8 @@
     "scheduler/responsiveness/native_event_observer_mac.mm",
     "scheduler/responsiveness/watcher.cc",
     "scheduler/responsiveness/watcher.h",
+    "scheduler/scoped_do_not_use_ui_default_queue_from_io.cc",
+    "scheduler/scoped_do_not_use_ui_default_queue_from_io.h",
     "scoped_active_url.cc",
     "scoped_active_url.h",
     "screen_orientation/screen_orientation_provider.cc",
@@ -1742,6 +1749,8 @@
     "service_manager/common_browser_interfaces.h",
     "service_manager/service_manager_context.cc",
     "service_manager/service_manager_context.h",
+    "service_process_host_impl.cc",
+    "service_process_host_impl.h",
     "service_worker/embedded_worker_instance.cc",
     "service_worker/embedded_worker_instance.h",
     "service_worker/embedded_worker_status.h",
@@ -1928,8 +1937,6 @@
     "web_contents/web_drag_dest_mac.mm",
     "web_contents/web_drag_utils_win.cc",
     "web_contents/web_drag_utils_win.h",
-    "web_package/http_structured_header.cc",
-    "web_package/http_structured_header.h",
     "web_package/prefetched_signed_exchange_cache.cc",
     "web_package/prefetched_signed_exchange_cache.h",
     "web_package/prefetched_signed_exchange_cache_adapter.cc",
@@ -1961,8 +1968,6 @@
     "web_package/signed_exchange_reporter.h",
     "web_package/signed_exchange_request_handler.cc",
     "web_package/signed_exchange_request_handler.h",
-    "web_package/signed_exchange_request_matcher.cc",
-    "web_package/signed_exchange_request_matcher.h",
     "web_package/signed_exchange_signature_header_field.cc",
     "web_package/signed_exchange_signature_header_field.h",
     "web_package/signed_exchange_signature_verifier.cc",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc b/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc
index 38bc1e54..4d0fc61e 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc
@@ -26,6 +26,7 @@
 #include "base/values.h"
 #include "base/win/com_init_util.h"
 #include "base/win/scoped_bstr.h"
+#include "base/win/scoped_safearray.h"
 #include "base/win/scoped_variant.h"
 #include "base/win/windows_version.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
@@ -334,10 +335,66 @@
 std::unique_ptr<base::DictionaryValue>
 AccessibilityTreeFormatterUia::BuildAccessibilityTree(
     BrowserAccessibility* start) {
-  // Find the root IUIAutomationElement for the content window.
+  // We use the UI Automation client API to produce the tree dump, but
+  // BrowserAccessibility has a pointer to a provider API implementation, and
+  // we can't directly relate the two -- the OS manages the relationship.
+  // To locate the client element we want, we'll construct a RuntimeId
+  // corresponding to our provider element, then search for that.
+
+  // Start by getting the root element for the HWND hosting the web content.
   HWND hwnd =
       start->manager()->GetRoot()->GetTargetForNativeAccessibilityEvent();
-  return BuildAccessibilityTreeForWindow(hwnd);
+  Microsoft::WRL::ComPtr<IUIAutomationElement> root;
+  uia_->ElementFromHandle(hwnd, &root);
+  CHECK(root.Get());
+
+  // The root element is provided by AXFragmentRootWin, whose RuntimeId is not
+  // in the same form as elements provided by BrowserAccessibility.
+  // Find the root element's first child, which should be provided by
+  // BrowserAccessibility. We'll use that element's RuntimeId as a template for
+  // the RuntimeId of the element we're looking for.
+  Microsoft::WRL::ComPtr<IUIAutomationCondition> true_condition;
+  uia_->CreateTrueCondition(&true_condition);
+  Microsoft::WRL::ComPtr<IUIAutomationElement> first_child;
+  root->FindFirst(TreeScope_Children, true_condition.Get(), &first_child);
+  CHECK(first_child.Get());
+
+  // Get first_child's RuntimeId and swap out the last element in its SAFEARRAY
+  // for the UniqueId of the element we want to start from.
+  base::win::ScopedSafearray runtime_id;
+  first_child->GetRuntimeId(runtime_id.Receive());
+  CHECK(runtime_id.Get());
+  LONG lower_bound = 0;
+  HRESULT hr = ::SafeArrayGetLBound(runtime_id.Get(), 1, &lower_bound);
+  CHECK(SUCCEEDED(hr));
+  LONG upper_bound = 0;
+  hr = ::SafeArrayGetUBound(runtime_id.Get(), 1, &upper_bound);
+  CHECK(SUCCEEDED(hr));
+  {
+    int32_t* runtime_id_array = nullptr;
+    ::SafeArrayAccessData(runtime_id.Get(),
+                          reinterpret_cast<void**>(&runtime_id_array));
+    CHECK(runtime_id_array);
+    CHECK((upper_bound - lower_bound) >= 0);
+    runtime_id_array[upper_bound - lower_bound] = start->GetUniqueId().Get();
+    ::SafeArrayUnaccessData(runtime_id.Get());
+  }
+
+  // Find the element with the desired RuntimeId.
+  base::win::ScopedVariant runtime_id_variant(runtime_id.Release());
+  Microsoft::WRL::ComPtr<IUIAutomationCondition> condition;
+  uia_->CreatePropertyCondition(UIA_RuntimeIdPropertyId, runtime_id_variant,
+                                &condition);
+  CHECK(condition);
+  Microsoft::WRL::ComPtr<IUIAutomationElement> start_element;
+  root->FindFirst(TreeScope_Subtree, condition.Get(), &start_element);
+  CHECK(start_element.Get());
+
+  // Build an accessibility tree starting from that element.
+  std::unique_ptr<base::DictionaryValue> tree =
+      std::make_unique<base::DictionaryValue>();
+  RecursiveBuildAccessibilityTree(start_element.Get(), tree.get());
+  return tree;
 }
 
 std::unique_ptr<base::DictionaryValue>
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 92db5030..73c0423 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -485,6 +485,10 @@
   base::Optional<int> GetSetSize() const override;
 
  protected:
+  // The UIA tree formatter needs access to GetUniqueId() to identify the
+  // starting point for tree dumps.
+  friend class AccessibilityTreeFormatterUia;
+
   using BrowserAccessibilityPositionInstance =
       BrowserAccessibilityPosition::AXPositionInstance;
   using AXPlatformRange =
diff --git a/content/browser/android/overscroll_controller_android_unittest.cc b/content/browser/android/overscroll_controller_android_unittest.cc
index 3054c84..72a9317 100644
--- a/content/browser/android/overscroll_controller_android_unittest.cc
+++ b/content/browser/android/overscroll_controller_android_unittest.cc
@@ -43,11 +43,6 @@
   MOCK_METHOD0(GetFrameSinkId, viz::FrameSinkId());
   void AddChildFrameSink(const viz::FrameSinkId& frame_sink_id) override {}
   void RemoveChildFrameSink(const viz::FrameSinkId& frame_sink_id) override {}
-  std::unique_ptr<ui::CompositorLock> GetCompositorLock(
-      ui::CompositorLockClient* client,
-      base::TimeDelta timeout) override {
-    return nullptr;
-  }
   bool IsDrawingFirstVisibleFrame() const override { return false; }
   void SetVSyncPaused(bool paused) override {}
   void OnUpdateRefreshRate(float refresh_rate) override {}
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
index 8f14f06..c21ac69 100644
--- a/content/browser/appcache/appcache_host.cc
+++ b/content/browser/appcache/appcache_host.cc
@@ -184,7 +184,7 @@
   // after cache or group loading is complete.
   // Note: Foreign entries are detected on the client side and
   // MarkAsForeignEntry is called in that case, so that detection
-  // step is skipped here. See WebApplicationCacheHostImpl.cc
+  // step is skipped here.
 
   if (cache_document_was_loaded_from != blink::mojom::kAppCacheNoCacheId) {
     LoadSelectedCache(cache_document_was_loaded_from);
@@ -217,7 +217,7 @@
     }
     // Note: The client detects if the document was not loaded using HTTP GET
     // and invokes SelectCache without a manifest url, so that detection step
-    // is also skipped here. See WebApplicationCacheHostImpl.cc
+    // is also skipped here.
     set_preferred_manifest_url(manifest_url);
     new_master_entry_url_ = document_url;
     LoadOrCreateGroup(manifest_url);
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index f97bbe40..3d70d7b7 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -549,6 +549,7 @@
 
 void AppCacheRequestHandler::MaybeCreateLoader(
     const network::ResourceRequest& tentative_resource_request,
+    BrowserContext* browser_context,
     ResourceContext* resource_context,
     LoaderCallback callback,
     FallbackCallback fallback_callback) {
@@ -635,8 +636,8 @@
 
   // Subresource loads start out just like a main resource loads, but they go
   // down different branches along the way to completion.
-  MaybeCreateLoader(resource_request, nullptr, std::move(loader_callback),
-                    std::move(fallback_callback));
+  MaybeCreateLoader(resource_request, nullptr, nullptr,
+                    std::move(loader_callback), std::move(fallback_callback));
 }
 
 void AppCacheRequestHandler::MaybeFallbackForSubresourceResponse(
diff --git a/content/browser/appcache/appcache_request_handler.h b/content/browser/appcache/appcache_request_handler.h
index 14da8144..06dce93 100644
--- a/content/browser/appcache/appcache_request_handler.h
+++ b/content/browser/appcache/appcache_request_handler.h
@@ -76,6 +76,7 @@
   // LoaderCallback is invoked.
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      BrowserContext* browser_context,
       ResourceContext* resource_context,
       LoaderCallback callback,
       FallbackCallback fallback_callback) override;
diff --git a/content/browser/appcache/appcache_storage_impl_unittest.cc b/content/browser/appcache/appcache_storage_impl_unittest.cc
index f93e60d..038244f 100644
--- a/content/browser/appcache/appcache_storage_impl_unittest.cc
+++ b/content/browser/appcache/appcache_storage_impl_unittest.cc
@@ -1741,7 +1741,7 @@
       handler_ = host2->CreateRequestHandler(
           std::make_unique<AppCacheURLLoaderRequest>(request),
           ResourceType::kMainFrame, false);
-      handler_->MaybeCreateLoader(request, nullptr, base::DoNothing(),
+      handler_->MaybeCreateLoader(request, nullptr, nullptr, base::DoNothing(),
                                   base::DoNothing());
     }
 
diff --git a/content/browser/background_fetch/background_fetch_service_unittest.cc b/content/browser/background_fetch/background_fetch_service_unittest.cc
index f457c9f..d175531 100644
--- a/content/browser/background_fetch/background_fetch_service_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_service_unittest.cc
@@ -27,6 +27,7 @@
 #include "content/browser/storage_partition_impl.h"
 #include "content/common/background_fetch/background_fetch_types.h"
 #include "content/common/service_worker/service_worker_types.h"
+#include "content/test/fake_mojo_message_dispatch_context.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
@@ -66,18 +67,6 @@
                       });
 }
 
-class FakeMojoMessageDispatchContext {
- public:
-  FakeMojoMessageDispatchContext()
-      : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) {}
-
- private:
-  mojo::Message dummy_message_;
-  mojo::internal::MessageDispatchContext context_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeMojoMessageDispatchContext);
-};
-
 std::vector<blink::mojom::FetchAPIRequestPtr> CloneRequestVector(
     const std::vector<blink::mojom::FetchAPIRequestPtr>& requests) {
   std::vector<blink::mojom::FetchAPIRequestPtr> request_cp;
diff --git a/content/browser/background_fetch/storage/cache_entry_handler_impl.cc b/content/browser/background_fetch/storage/cache_entry_handler_impl.cc
index 8b1fe1b..5c87a76 100644
--- a/content/browser/background_fetch/storage/cache_entry_handler_impl.cc
+++ b/content/browser/background_fetch/storage/cache_entry_handler_impl.cc
@@ -5,8 +5,6 @@
 #include "content/browser/background_fetch/storage/cache_entry_handler_impl.h"
 
 #include "base/guid.h"
-#include "storage/browser/blob/blob_impl.h"
-#include "storage/browser/blob/blob_storage_context.h"
 #include "third_party/blink/public/common/blob/blob_utils.h"
 
 namespace content {
@@ -42,30 +40,11 @@
       response_blob_size, std::move(request_blob), request_blob_size, trace_id);
 }
 
-void CacheEntryHandlerImpl::PopulateBody(
-    scoped_refptr<DiskCacheBlobEntry> blob_entry,
-    const blink::mojom::SerializedBlobPtr& blob,
-    CacheStorageCache::EntryIndex index) {
-  disk_cache::Entry* entry = blob_entry->disk_cache_entry().get();
-  DCHECK(entry);
-
-  blob->size = entry->GetDataSize(index);
-  blob->uuid = base::GenerateGUID();
-
-  auto blob_data = std::make_unique<storage::BlobDataBuilder>(blob->uuid);
-  auto handle = MakeDataHandle(std::move(blob_entry), index);
-  blob_data->AppendReadableDataHandle(std::move(handle));
-
-  auto blob_handle = blob_context_->AddFinishedBlob(std::move(blob_data));
-  storage::BlobImpl::Create(std::move(blob_handle), MakeRequest(&blob->blob));
-}
-
 void CacheEntryHandlerImpl::PopulateResponseBody(
     scoped_refptr<DiskCacheBlobEntry> blob_entry,
     blink::mojom::FetchAPIResponse* response) {
-  response->blob = blink::mojom::SerializedBlob::New();
-  PopulateBody(std::move(blob_entry), response->blob,
-               CacheStorageCache::INDEX_RESPONSE_BODY);
+  response->blob =
+      CreateBlob(std::move(blob_entry), CacheStorageCache::INDEX_RESPONSE_BODY);
 }
 
 void CacheEntryHandlerImpl::PopulateRequestBody(
@@ -77,9 +56,8 @@
     return;
   }
 
-  request->blob = blink::mojom::SerializedBlob::New();
-  PopulateBody(std::move(blob_entry), request->blob,
-               CacheStorageCache::INDEX_SIDE_DATA);
+  request->blob =
+      CreateBlob(std::move(blob_entry), CacheStorageCache::INDEX_SIDE_DATA);
 }
 
 base::WeakPtr<CacheStorageCacheEntryHandler>
diff --git a/content/browser/background_fetch/storage/cache_entry_handler_impl.h b/content/browser/background_fetch/storage/cache_entry_handler_impl.h
index 0646ca89..73e2c65 100644
--- a/content/browser/background_fetch/storage/cache_entry_handler_impl.h
+++ b/content/browser/background_fetch/storage/cache_entry_handler_impl.h
@@ -30,10 +30,6 @@
                            blink::mojom::FetchAPIRequest* request) override;
 
  private:
-  void PopulateBody(scoped_refptr<DiskCacheBlobEntry> blob_entry,
-                    const blink::mojom::SerializedBlobPtr& blob,
-                    CacheStorageCache::EntryIndex index);
-
   base::WeakPtr<CacheStorageCacheEntryHandler> GetWeakPtr() override;
 
   base::WeakPtrFactory<CacheEntryHandlerImpl> weak_ptr_factory_;
diff --git a/content/browser/background_sync/background_sync_registration_helper.cc b/content/browser/background_sync/background_sync_registration_helper.cc
index 825bfe7d..dd33f54 100644
--- a/content/browser/background_sync/background_sync_registration_helper.cc
+++ b/content/browser/background_sync/background_sync_registration_helper.cc
@@ -69,6 +69,14 @@
       result->options()->Clone());
 }
 
+void BackgroundSyncRegistrationHelper::NotifyInvalidOptionsProvided(
+    RegisterCallback callback) const {
+  mojo::ReportBadMessage(
+      "BackgroundSyncRegistrationHelper: Invalid options passed.");
+  std::move(callback).Run(blink::mojom::BackgroundSyncError::NOT_ALLOWED,
+                          /* options= */ nullptr);
+}
+
 void BackgroundSyncRegistrationHelper::OnGetRegistrationsResult(
     GetRegistrationsCallback callback,
     BackgroundSyncStatus status,
diff --git a/content/browser/background_sync/background_sync_registration_helper.h b/content/browser/background_sync/background_sync_registration_helper.h
index 04edbfe..34006c0 100644
--- a/content/browser/background_sync/background_sync_registration_helper.h
+++ b/content/browser/background_sync/background_sync_registration_helper.h
@@ -41,6 +41,7 @@
   void OnRegisterResult(RegisterCallback callback,
                         BackgroundSyncStatus status,
                         std::unique_ptr<BackgroundSyncRegistration> result);
+  void NotifyInvalidOptionsProvided(RegisterCallback callback) const;
   void OnGetRegistrationsResult(
       GetRegistrationsCallback callback,
       BackgroundSyncStatus status,
diff --git a/content/browser/background_sync/background_sync_service_impl_test_harness.cc b/content/browser/background_sync/background_sync_service_impl_test_harness.cc
index 2de0ca0..126aad88 100644
--- a/content/browser/background_sync/background_sync_service_impl_test_harness.cc
+++ b/content/browser/background_sync/background_sync_service_impl_test_harness.cc
@@ -19,6 +19,7 @@
 #include "content/public/test/mock_permission_manager.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/test/test_background_sync_context.h"
+#include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 
@@ -94,12 +95,21 @@
   // Don't let the tests be confused by the real-world device connectivity
   background_sync_test_util::SetIgnoreNetworkChanges(true);
 
+  mojo::core::SetDefaultProcessErrorCallback(base::AdaptCallbackForRepeating(
+      base::BindOnce(&BackgroundSyncServiceImplTestHarness::CollectMojoError,
+                     base::Unretained(this))));
+
   CreateTestHelper();
   CreateStoragePartition();
   CreateBackgroundSyncContext();
   ASSERT_NO_FATAL_FAILURE(CreateServiceWorkerRegistration());
 }
 
+void BackgroundSyncServiceImplTestHarness::CollectMojoError(
+    const std::string& message) {
+  mojo_bad_messages_.push_back(message);
+}
+
 void BackgroundSyncServiceImplTestHarness::TearDown() {
   // This must be explicitly destroyed here to ensure that destruction
   // of both the BackgroundSyncContextImpl and the BackgroundSyncManager
@@ -110,6 +120,9 @@
 
   // Restore the network observer functionality for subsequent tests
   background_sync_test_util::SetIgnoreNetworkChanges(false);
+
+  mojo::core::SetDefaultProcessErrorCallback(
+      mojo::core::ProcessErrorCallback());
 }
 
 // SetUp helper methods
diff --git a/content/browser/background_sync/background_sync_service_impl_test_harness.h b/content/browser/background_sync/background_sync_service_impl_test_harness.h
index c678da75..db158f0 100644
--- a/content/browser/background_sync/background_sync_service_impl_test_harness.h
+++ b/content/browser/background_sync/background_sync_service_impl_test_harness.h
@@ -6,12 +6,15 @@
 #define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_SERVICE_IMPL_TEST_HARNESS_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/memory/scoped_refptr.h"
 #include "content/browser/background_sync/background_sync_context_impl.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "content/test/fake_mojo_message_dispatch_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
@@ -50,12 +53,12 @@
   ~BackgroundSyncServiceImplTestHarness() override;
 
   void SetUp() override;
-
   void TearDown() override;
 
  protected:
   scoped_refptr<BackgroundSyncContextImpl> background_sync_context_;
   blink::mojom::SyncRegistrationOptionsPtr default_sync_registration_;
+  std::vector<std::string> mojo_bad_messages_;
   int64_t sw_registration_id_;
 
  private:
@@ -64,6 +67,7 @@
   void CreateStoragePartition();
   void CreateBackgroundSyncContext();
   void CreateServiceWorkerRegistration();
+  void CollectMojoError(const std::string& message);
 
   // Helpers for testing *BackgroundSyncServiceImpl methods
   void RegisterOneShotSync(
diff --git a/content/browser/background_sync/one_shot_background_sync_service_impl.cc b/content/browser/background_sync/one_shot_background_sync_service_impl.cc
index 3db6e37..20e10480 100644
--- a/content/browser/background_sync/one_shot_background_sync_service_impl.cc
+++ b/content/browser/background_sync/one_shot_background_sync_service_impl.cc
@@ -45,6 +45,12 @@
     int64_t sw_registration_id,
     RegisterCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(options);
+
+  if (options->min_interval != -1) {
+    registration_helper_->NotifyInvalidOptionsProvided(std::move(callback));
+    return;
+  }
 
   registration_helper_->Register(std::move(options), sw_registration_id,
                                  std::move(callback));
diff --git a/content/browser/background_sync/one_shot_background_sync_service_impl_unittest.cc b/content/browser/background_sync/one_shot_background_sync_service_impl_unittest.cc
index aff4b12..5298998e 100644
--- a/content/browser/background_sync/one_shot_background_sync_service_impl_unittest.cc
+++ b/content/browser/background_sync/one_shot_background_sync_service_impl_unittest.cc
@@ -74,6 +74,21 @@
   EXPECT_EQ("", reg->tag);
 }
 
+TEST_F(OneShotBackgroundSyncServiceImplTest, RegisterWithInvalidOptions) {
+  bool called = false;
+  blink::mojom::BackgroundSyncError error;
+  blink::mojom::SyncRegistrationOptionsPtr reg;
+  auto to_register = default_sync_registration_.Clone();
+  to_register->min_interval = 3600;
+
+  FakeMojoMessageDispatchContext fake_dispatch_context;
+  RegisterOneShotSync(
+      std::move(to_register),
+      base::BindOnce(&ErrorAndRegistrationCallback, &called, &error, &reg));
+  ASSERT_TRUE(called);
+  EXPECT_EQ(mojo_bad_messages_.size(), 1u);
+}
+
 TEST_F(OneShotBackgroundSyncServiceImplTest,
        GetOneShotSyncRegistrationsNoSyncRegistered) {
   bool called = false;
diff --git a/content/browser/background_sync/periodic_background_sync_service_impl.cc b/content/browser/background_sync/periodic_background_sync_service_impl.cc
index a1436ed..628b672a 100644
--- a/content/browser/background_sync/periodic_background_sync_service_impl.cc
+++ b/content/browser/background_sync/periodic_background_sync_service_impl.cc
@@ -44,6 +44,12 @@
     int64_t sw_registration_id,
     RegisterCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(options);
+
+  if (options->min_interval < 0) {
+    registration_helper_->NotifyInvalidOptionsProvided(std::move(callback));
+    return;
+  }
 
   registration_helper_->Register(std::move(options), sw_registration_id,
                                  std::move(callback));
diff --git a/content/browser/background_sync/periodic_background_sync_service_impl_unittest.cc b/content/browser/background_sync/periodic_background_sync_service_impl_unittest.cc
index e8290dd..b0a113a0 100644
--- a/content/browser/background_sync/periodic_background_sync_service_impl_unittest.cc
+++ b/content/browser/background_sync/periodic_background_sync_service_impl_unittest.cc
@@ -86,6 +86,21 @@
   EXPECT_EQ(3600, reg->min_interval);
 }
 
+TEST_F(PeriodicBackgroundSyncServiceImplTest, RegisterWithInvalidMinInterval) {
+  bool called = false;
+  blink::mojom::BackgroundSyncError error;
+  blink::mojom::SyncRegistrationOptionsPtr reg;
+  auto to_register = default_sync_registration_.Clone();
+  to_register->min_interval = -1;
+
+  FakeMojoMessageDispatchContext fake_dispatch_context;
+  RegisterPeriodicSync(
+      std::move(to_register),
+      base::BindOnce(&ErrorAndRegistrationCallback, &called, &error, &reg));
+  ASSERT_TRUE(called);
+  EXPECT_EQ(mojo_bad_messages_.size(), 1u);
+}
+
 TEST_F(PeriodicBackgroundSyncServiceImplTest,
        GetPeriodicSyncRegistrationsNoSyncRegistered) {
   bool called = false;
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index c3c10eb..1f2be47e 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -826,7 +826,7 @@
 }
 
 const SharedCorsOriginAccessList*
-BrowserContext::GetSharedCorsOriginAccessList() const {
+BrowserContext::GetSharedCorsOriginAccessList() {
   // Need to return a valid instance regardless of CORS bypass supports.
   static const base::NoDestructor<scoped_refptr<SharedCorsOriginAccessList>>
       empty_list(SharedCorsOriginAccessList::Create());
@@ -842,4 +842,8 @@
   return nullptr;
 }
 
+ContentIndexProvider* BrowserContext::GetContentIndexProvider() {
+  return nullptr;
+}
+
 }  // namespace content
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc
index 49e1530ee..b371ffc 100644
--- a/content/browser/browser_thread_unittest.cc
+++ b/content/browser/browser_thread_unittest.cc
@@ -50,7 +50,7 @@
             sequence_manager_.get(), sequence_manager_->GetRealTimeDomain());
 
     default_task_runner_ =
-        browser_ui_thread_scheduler->GetHandle().GetDefaultTaskRunner();
+        browser_ui_thread_scheduler->GetHandle()->GetDefaultTaskRunner();
 
     sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
 
@@ -307,7 +307,7 @@
           BrowserUIThreadScheduler::CreateForTesting(sequence_manager(),
                                                      GetTimeDomain());
       DeferredInitFromSubclass(
-          browser_ui_thread_scheduler->GetHandle().GetBrowserTaskRunner(
+          browser_ui_thread_scheduler->GetHandle()->GetBrowserTaskRunner(
               QueueType::kDefault));
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
index cfcf10a6..f8f9c56 100644
--- a/content/browser/browsing_instance.cc
+++ b/content/browser/browsing_instance.cc
@@ -55,6 +55,10 @@
   return site_instance != nullptr && site_instance == default_site_instance_;
 }
 
+bool BrowsingInstance::IsSiteInDefaultSiteInstance(const GURL& site_url) const {
+  return site_url_set_.find(site_url) != site_url_set_.end();
+}
+
 bool BrowsingInstance::HasSiteInstance(const GURL& url) {
   std::string site = GetSiteForURL(url).possibly_invalid_spec();
   return site_instance_map_.find(site) != site_instance_map_.end();
@@ -100,8 +104,8 @@
 scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURLHelper(
     const GURL& url,
     bool allow_default_instance) {
-  std::string site = GetSiteForURL(url).possibly_invalid_spec();
-  auto i = site_instance_map_.find(site);
+  const GURL site_url = GetSiteForURL(url);
+  auto i = site_instance_map_.find(site_url.possibly_invalid_spec());
   if (i != site_instance_map_.end())
     return i->second;
 
@@ -132,6 +136,10 @@
 
       site_instance->SetSite(SiteInstanceImpl::GetDefaultSiteURL());
     }
+
+    // Add |site_url| to the set so we can keep track of all the sites the
+    // the default SiteInstance has been returned for.
+    site_url_set_.insert(site_url);
     return site_instance;
   }
 
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h
index 775b64a8..acbf074 100644
--- a/content/browser/browsing_instance.h
+++ b/content/browser/browsing_instance.h
@@ -161,6 +161,14 @@
 
   bool IsDefaultSiteInstance(const SiteInstanceImpl* site_instance) const;
 
+  // Returns true if |site_url| has been used to get a SiteInstance from this
+  // object and the default SiteInstance was returned. This simply indicates
+  // the site may be directed to the default SiteInstance process, but it does
+  // not indicate that the site has already been committed to that process.
+  // Returns false if no request for |site_url| has resulted in this object
+  // returning the default SiteInstance.
+  bool IsSiteInDefaultSiteInstance(const GURL& site_url) const;
+
   // Helper function used by other methods in this class to ensure consistent
   // mapping between |url| and site URL.
   // Note: This should not be used by code outside this class.
@@ -207,6 +215,10 @@
   // BrowsingInstance and the SiteInstanceImpl.
   SiteInstanceImpl* default_site_instance_;
 
+  // Keeps track of the site URLs that this object mapped to the
+  // |default_site_instance_|.
+  std::set<GURL> site_url_set_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowsingInstance);
 };
 
diff --git a/content/browser/cache_storage/cache_storage_cache.h b/content/browser/cache_storage/cache_storage_cache.h
index 8350105..1489eb8d 100644
--- a/content/browser/cache_storage/cache_storage_cache.h
+++ b/content/browser/cache_storage/cache_storage_cache.h
@@ -54,7 +54,12 @@
   // The stream index for a cache Entry. This cannot be extended without changes
   // in the Entry implementation. INDEX_SIDE_DATA is used for storing any
   // additional data, such as response side blobs or request bodies.
-  enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY, INDEX_SIDE_DATA };
+  enum EntryIndex {
+    INDEX_INVALID = -1,
+    INDEX_HEADERS = 0,
+    INDEX_RESPONSE_BODY,
+    INDEX_SIDE_DATA
+  };
 
   // Create a handle that will hold the CacheStorageCache alive. Client code
   // should hold one of these handles while waiting for operation callbacks to
diff --git a/content/browser/cache_storage/cache_storage_cache_entry_handler.cc b/content/browser/cache_storage/cache_storage_cache_entry_handler.cc
index cf4cc4e..e7ad8f4 100644
--- a/content/browser/cache_storage/cache_storage_cache_entry_handler.cc
+++ b/content/browser/cache_storage/cache_storage_cache_entry_handler.cc
@@ -6,9 +6,10 @@
 
 #include "base/guid.h"
 #include "base/optional.h"
+#include "base/task/post_task.h"
 #include "content/browser/background_fetch/storage/cache_entry_handler_impl.h"
-#include "content/browser/cache_storage/cache_storage_cache.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
@@ -18,8 +19,6 @@
 
 namespace {
 
-constexpr int kInvalidSideDataIndex = -1;
-
 // A |BlobDataItem::DataHandle| implementation that wraps a
 // |DiskCacheBlobEntry|.  In addition, each |DataHandleImpl| maps the main
 // and side data to particular disk_cache indices.
@@ -31,13 +30,14 @@
   DataHandleImpl(
       scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
           blob_entry,
-      int disk_cache_index,
-      int side_data_disk_cache_index)
+      CacheStorageCache::EntryIndex disk_cache_index,
+      CacheStorageCache::EntryIndex side_data_disk_cache_index)
       : blob_entry_(std::move(blob_entry)),
         disk_cache_index_(disk_cache_index),
         side_data_disk_cache_index_(side_data_disk_cache_index) {}
 
   uint64_t GetSize() const override {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     return blob_entry_->GetSize(disk_cache_index_);
   }
 
@@ -45,19 +45,22 @@
            uint64_t src_offset,
            int bytes_to_read,
            base::OnceCallback<void(int)> callback) override {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     return blob_entry_->Read(std::move(dst_buffer), disk_cache_index_,
                              src_offset, bytes_to_read, std::move(callback));
   }
 
   uint64_t GetSideDataSize() const override {
-    if (side_data_disk_cache_index_ == kInvalidSideDataIndex)
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    if (side_data_disk_cache_index_ == CacheStorageCache::INDEX_INVALID)
       return 0;
     return blob_entry_->GetSize(side_data_disk_cache_index_);
   }
 
   int ReadSideData(scoped_refptr<net::IOBuffer> dst_buffer,
                    base::OnceCallback<void(int)> callback) override {
-    if (side_data_disk_cache_index_ == kInvalidSideDataIndex)
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    if (side_data_disk_cache_index_ == CacheStorageCache::INDEX_INVALID)
       return net::ERR_FAILED;
     return blob_entry_->Read(std::move(dst_buffer), side_data_disk_cache_index_,
                              /* offset= */ 0, GetSideDataSize(),
@@ -65,11 +68,13 @@
   }
 
   void PrintTo(::std::ostream* os) const override {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     blob_entry_->PrintTo(os);
     *os << ",disk_cache_index:" << disk_cache_index_;
   }
 
   const char* BytesReadHistogramLabel() const override {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     return "DiskCache.CacheStorage";
   }
 
@@ -78,12 +83,35 @@
 
   const scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
       blob_entry_;
-  const int disk_cache_index_;
-  const int side_data_disk_cache_index_;
+  const CacheStorageCache::EntryIndex disk_cache_index_;
+  const CacheStorageCache::EntryIndex side_data_disk_cache_index_;
 
   DISALLOW_COPY_AND_ASSIGN(DataHandleImpl);
 };
 
+void FinalizeBlobOnIOThread(
+    base::WeakPtr<storage::BlobStorageContext> blob_context,
+    scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry> blob_entry,
+    CacheStorageCache::EntryIndex disk_cache_index,
+    CacheStorageCache::EntryIndex side_data_disk_cache_index,
+    std::string uuid,
+    blink::mojom::BlobRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Just allow the blob mojo message pipe to automatically close if we're
+  // shutting down.
+  if (!blob_context)
+    return;
+
+  auto inner_handle = base::MakeRefCounted<DataHandleImpl>(
+      std::move(blob_entry), disk_cache_index, side_data_disk_cache_index);
+  auto blob_data = std::make_unique<storage::BlobDataBuilder>(uuid);
+  blob_data->AppendReadableDataHandle(std::move(inner_handle));
+  auto blob_handle = blob_context->AddFinishedBlob(std::move(blob_data));
+
+  storage::BlobImpl::Create(std::move(blob_handle), std::move(request));
+}
+
 }  // namespace
 
 CacheStorageCacheEntryHandler::DiskCacheBlobEntry::DiskCacheBlobEntry(
@@ -91,45 +119,138 @@
     base::WeakPtr<CacheStorageCacheEntryHandler> entry_handler,
     CacheStorageCacheHandle cache_handle,
     disk_cache::ScopedEntryPtr disk_cache_entry)
-    : entry_handler_(std::move(entry_handler)),
+    : base::RefCountedDeleteOnSequence<DiskCacheBlobEntry>(
+          base::SequencedTaskRunnerHandle::Get()),
+      task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      entry_handler_(std::move(entry_handler)),
       cache_handle_(std::move(cache_handle)),
-      disk_cache_entry_(std::move(disk_cache_entry)) {}
+      disk_cache_entry_(std::move(disk_cache_entry)),
+      valid_(true),
+      key_(disk_cache_entry_->GetKey()),
+      index_headers_size_(
+          disk_cache_entry_->GetDataSize(CacheStorageCache::INDEX_HEADERS)),
+      index_response_body_size_(disk_cache_entry_->GetDataSize(
+          CacheStorageCache::INDEX_RESPONSE_BODY)),
+      index_side_data_size_(
+          disk_cache_entry_->GetDataSize(CacheStorageCache::INDEX_SIDE_DATA)) {}
 
 int CacheStorageCacheEntryHandler::DiskCacheBlobEntry::Read(
     scoped_refptr<net::IOBuffer> dst_buffer,
-    int disk_cache_index,
+    CacheStorageCache::EntryIndex disk_cache_index,
     uint64_t offset,
     int bytes_to_read,
     base::OnceCallback<void(int)> callback) {
-  if (!disk_cache_entry_)
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!valid_)
     return net::ERR_CACHE_READ_FAILURE;
 
-  return disk_cache_entry_->ReadData(disk_cache_index, offset, dst_buffer.get(),
-                                     bytes_to_read, std::move(callback));
+  if (task_runner_->RunsTasksInCurrentSequence()) {
+    return ReadOnSequenceInternal(std::move(dst_buffer), disk_cache_index,
+                                  offset, bytes_to_read, std::move(callback));
+  }
+
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&DiskCacheBlobEntry::ReadOnSequence, this,
+                                std::move(dst_buffer), disk_cache_index, offset,
+                                bytes_to_read, std::move(callback)));
+  return net::ERR_IO_PENDING;
 }
 
 int CacheStorageCacheEntryHandler::DiskCacheBlobEntry::GetSize(
-    int disk_cache_index) const {
-  if (!disk_cache_entry_)
+    CacheStorageCache::EntryIndex disk_cache_index) const {
+  // Callable on any thread.
+  if (!valid_)
     return 0;
-  return disk_cache_entry_->GetDataSize(disk_cache_index);
+  switch (disk_cache_index) {
+    case CacheStorageCache::INDEX_INVALID:
+      return 0;
+    case CacheStorageCache::INDEX_HEADERS:
+      return index_headers_size_;
+    case CacheStorageCache::INDEX_RESPONSE_BODY:
+      return index_response_body_size_;
+    case CacheStorageCache::INDEX_SIDE_DATA:
+      return index_side_data_size_;
+  }
+  NOTREACHED();
 }
 
 void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::PrintTo(
     ::std::ostream* os) const {
-  if (disk_cache_entry_)
-    *os << "disk_cache_key:" << disk_cache_entry_->GetKey();
+  // Callable on any thread.
+  if (valid_)
+    *os << "disk_cache_key:" << key_;
   else
     *os << "<invalidated>";
 }
 
 void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::Invalidate() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  valid_ = false;
   cache_handle_ = base::nullopt;
   entry_handler_ = nullptr;
   disk_cache_entry_ = nullptr;
 }
 
+disk_cache::ScopedEntryPtr&
+CacheStorageCacheEntryHandler::DiskCacheBlobEntry::disk_cache_entry() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return disk_cache_entry_;
+}
+
+void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::ReadOnSequence(
+    scoped_refptr<net::IOBuffer> dst_buffer,
+    int disk_cache_index,
+    uint64_t offset,
+    int bytes_to_read,
+    base::OnceCallback<void(int)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // The disk_cache behavior of sometimes returning the result synchronously
+  // and sometimes invoking the callback requires us to adapt our callback
+  // to repeating here.  Ideally disk_cache should be refactored to always
+  // report the result through the callback.
+  auto adapted_callback = base::AdaptCallbackForRepeating(std::move(callback));
+
+  int result = ReadOnSequenceInternal(std::move(dst_buffer), disk_cache_index,
+                                      offset, bytes_to_read, adapted_callback);
+
+  if (result == net::ERR_IO_PENDING)
+    return;
+
+  DidReadOnSequence(std::move(adapted_callback), result);
+}
+
+int CacheStorageCacheEntryHandler::DiskCacheBlobEntry::ReadOnSequenceInternal(
+    scoped_refptr<net::IOBuffer> dst_buffer,
+    int disk_cache_index,
+    uint64_t offset,
+    int bytes_to_read,
+    base::OnceCallback<void(int)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!disk_cache_entry_) {
+    return net::ERR_CACHE_READ_FAILURE;
+  }
+
+  return disk_cache_entry_->ReadData(
+      disk_cache_index, offset, dst_buffer.get(), bytes_to_read,
+      base::BindOnce(&DiskCacheBlobEntry::DidReadOnSequence, this,
+                     std::move(callback)));
+}
+
+void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::DidReadOnSequence(
+    base::OnceCallback<void(int)> callback,
+    int result) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    std::move(callback).Run(result);
+  } else {
+    base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
+                             base::BindOnce(std::move(callback), result));
+  }
+}
+
 CacheStorageCacheEntryHandler::DiskCacheBlobEntry::~DiskCacheBlobEntry() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (entry_handler_)
     entry_handler_->EraseDiskCacheBlobEntry(this);
 }
@@ -164,6 +285,7 @@
       blink::mojom::FetchAPIRequestPtr request,
       blink::mojom::FetchAPIResponsePtr response,
       int64_t trace_id) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     blink::mojom::BlobPtr blob;
     uint64_t blob_size = blink::BlobUtils::kUnknownSize;
     blink::mojom::BlobPtr side_data_blob;
@@ -185,25 +307,10 @@
 
   void PopulateResponseBody(scoped_refptr<DiskCacheBlobEntry> blob_entry,
                             blink::mojom::FetchAPIResponse* response) override {
-    disk_cache::Entry* entry = blob_entry->disk_cache_entry().get();
-    DCHECK(entry);
-
-    // Create a blob with the response body data.
-    response->blob = blink::mojom::SerializedBlob::New();
-    response->blob->size =
-        entry->GetDataSize(CacheStorageCache::INDEX_RESPONSE_BODY);
-    response->blob->uuid = base::GenerateGUID();
-    auto blob_data =
-        std::make_unique<storage::BlobDataBuilder>(response->blob->uuid);
-
-    auto inner_handle = MakeDataHandleWithSideData(
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    response->blob = CreateBlobWithSideData(
         std::move(blob_entry), CacheStorageCache::INDEX_RESPONSE_BODY,
         CacheStorageCache::INDEX_SIDE_DATA);
-    blob_data->AppendReadableDataHandle(std::move(inner_handle));
-    auto blob_handle = blob_context_->AddFinishedBlob(std::move(blob_data));
-
-    storage::BlobImpl::Create(std::move(blob_handle),
-                              MakeRequest(&response->blob->blob));
   }
 
   void PopulateRequestBody(scoped_refptr<DiskCacheBlobEntry> blob_entry,
@@ -225,6 +332,7 @@
 CacheStorageCacheEntryHandler::CreateDiskCacheBlobEntry(
     CacheStorageCacheHandle cache_handle,
     disk_cache::ScopedEntryPtr disk_cache_entry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto blob_entry =
       base::MakeRefCounted<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>(
           util::PassKey<CacheStorageCacheEntryHandler>(), GetWeakPtr(),
@@ -237,6 +345,7 @@
 CacheStorageCacheEntryHandler::~CacheStorageCacheEntryHandler() = default;
 
 void CacheStorageCacheEntryHandler::InvalidateDiskCacheBlobEntrys() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Calling Invalidate() can cause the CacheStorageCacheEntryHandler to be
   // destroyed. Be careful not to touch |this| after calling Invalidate().
   std::set<DiskCacheBlobEntry*> entries = std::move(blob_entries_);
@@ -246,6 +355,7 @@
 
 void CacheStorageCacheEntryHandler::EraseDiskCacheBlobEntry(
     DiskCacheBlobEntry* blob_entry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_NE(blob_entries_.count(blob_entry), 0u);
   blob_entries_.erase(blob_entry);
 }
@@ -266,23 +376,38 @@
   NOTREACHED();
 }
 
-// static
-scoped_refptr<storage::BlobDataItem::DataHandle>
-CacheStorageCacheEntryHandler::MakeDataHandle(
+blink::mojom::SerializedBlobPtr CacheStorageCacheEntryHandler::CreateBlob(
     scoped_refptr<DiskCacheBlobEntry> blob_entry,
-    int disk_cache_index) {
-  return MakeDataHandleWithSideData(std::move(blob_entry), disk_cache_index,
-                                    kInvalidSideDataIndex);
+    CacheStorageCache::EntryIndex disk_cache_index) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return CreateBlobWithSideData(std::move(blob_entry), disk_cache_index,
+                                CacheStorageCache::INDEX_INVALID);
 }
 
-// static
-scoped_refptr<storage::BlobDataItem::DataHandle>
-CacheStorageCacheEntryHandler::MakeDataHandleWithSideData(
+blink::mojom::SerializedBlobPtr
+CacheStorageCacheEntryHandler::CreateBlobWithSideData(
     scoped_refptr<DiskCacheBlobEntry> blob_entry,
-    int disk_cache_index,
-    int side_data_disk_cache_index) {
-  return base::MakeRefCounted<DataHandleImpl>(
-      std::move(blob_entry), disk_cache_index, side_data_disk_cache_index);
+    CacheStorageCache::EntryIndex disk_cache_index,
+    CacheStorageCache::EntryIndex side_data_disk_cache_index) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto blob = blink::mojom::SerializedBlob::New();
+  blob->size = blob_entry->GetSize(disk_cache_index);
+  blob->uuid = base::GenerateGUID();
+
+  if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    FinalizeBlobOnIOThread(blob_context_, std::move(blob_entry),
+                           disk_cache_index, side_data_disk_cache_index,
+                           blob->uuid, MakeRequest(&blob->blob));
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&FinalizeBlobOnIOThread, blob_context_,
+                       std::move(blob_entry), disk_cache_index,
+                       side_data_disk_cache_index, blob->uuid,
+                       MakeRequest(&blob->blob)));
+  }
+
+  return blob;
 }
 
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_cache_entry_handler.h b/content/browser/cache_storage/cache_storage_cache_entry_handler.h
index 4a3ff54..17fa43cb4 100644
--- a/content/browser/cache_storage/cache_storage_cache_entry_handler.h
+++ b/content/browser/cache_storage/cache_storage_cache_entry_handler.h
@@ -9,9 +9,11 @@
 #include <set>
 
 #include "base/macros.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/util/type_safety/pass_key.h"
+#include "content/browser/cache_storage/cache_storage_cache.h"
 #include "content/browser/cache_storage/cache_storage_cache_handle.h"
 #include "content/browser/cache_storage/scoped_writable_entry.h"
 #include "content/common/content_export.h"
@@ -61,15 +63,18 @@
 
 class CONTENT_EXPORT CacheStorageCacheEntryHandler {
  public:
-  // The |DiskCacheBlobEntry| is a ref-counted object containing both
+  // The DiskCacheBlobEntry is a ref-counted object containing both
   // a disk_cache Entry and a Handle to the cache in which it lives.  This
-  // blob entry can then be used to create a specialized
-  // |BlobDataItem::DataHandle| by calling CacheStorageCacheEntryHandle's
-  // MakeDataHandle().  This ensure both the cache and the disk_cache entry live
-  // as long as the blob.
-  // TODO(crbug/960012): Support |DiskCacheBlobEntry| on a separate sequence
-  // from the |BlobDataItem::DataHandle|.
-  class DiskCacheBlobEntry : public base::RefCounted<DiskCacheBlobEntry> {
+  // blob entry can then be used to create a BlobDataItem::DataHandle by
+  // calling CacheStorageCacheEntryHandle's MakeDataHandle().  This ensures
+  // both the cache and the disk_cache entry live as long as the blob.
+  //
+  // The blob DataHandle always lives on the IO thread.  The DiskCacheBlobEntry
+  // is held cross-sequence, but is always created and destroyed on the
+  // cache_storage scheduler sequence. This ensure both the cache and the
+  // disk_cache entry live as long as the blob.
+  class DiskCacheBlobEntry
+      : public base::RefCountedDeleteOnSequence<DiskCacheBlobEntry> {
    public:
     // Use |CacheStorageCacheEntryHandler::CreateDiskCacheBlobEntry|.
     DiskCacheBlobEntry(
@@ -78,28 +83,70 @@
         CacheStorageCacheHandle cache_handle,
         disk_cache::ScopedEntryPtr disk_cache_entry);
 
+    // Only callable on IO thread.
     int Read(scoped_refptr<net::IOBuffer> dst_buffer,
-             int disk_cache_index,
+             CacheStorageCache::EntryIndex disk_cache_index,
              uint64_t offset,
              int bytes_to_read,
              base::OnceCallback<void(int)> callback);
 
-    int GetSize(int disk_cache_index) const;
+    // Callable on any thread.
+    int GetSize(CacheStorageCache::EntryIndex disk_cache_index) const;
 
+    // Callable on any thread.
     void PrintTo(::std::ostream* os) const;
 
+    // Only callable on the creation sequence.
     void Invalidate();
 
-    disk_cache::ScopedEntryPtr& disk_cache_entry() { return disk_cache_entry_; }
+    // Only callable on the creation sequence.
+    disk_cache::ScopedEntryPtr& disk_cache_entry();
 
    private:
-    friend class base::RefCounted<DiskCacheBlobEntry>;
+    friend class base::DeleteHelper<DiskCacheBlobEntry>;
+    friend class base::RefCountedDeleteOnSequence<DiskCacheBlobEntry>;
     ~DiskCacheBlobEntry();
 
+    // Only callable on the creation sequence.
+    void ReadOnSequence(scoped_refptr<net::IOBuffer> dst_buffer,
+                        int disk_cache_index,
+                        uint64_t offset,
+                        int bytes_to_read,
+                        base::OnceCallback<void(int)> callback);
+
+    // Only callable on the creation sequence.
+    int ReadOnSequenceInternal(scoped_refptr<net::IOBuffer> dst_buffer,
+                               int disk_cache_index,
+                               uint64_t offset,
+                               int bytes_to_read,
+                               base::OnceCallback<void(int)> callback);
+
+    void DidReadOnSequence(base::OnceCallback<void(int)> callback, int result);
+
+    // Accessed on any thread
+    const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+    // Only accessed on the creation sequence.
     base::WeakPtr<CacheStorageCacheEntryHandler> entry_handler_;
     base::Optional<CacheStorageCacheHandle> cache_handle_;
     disk_cache::ScopedEntryPtr disk_cache_entry_;
 
+    // Accessed on any thread.
+    std::atomic<bool> valid_;
+
+    // Cached on the creation sequence so that they can be synchronously
+    // read on the IO thread.
+    const std::string key_;
+    const int index_headers_size_;
+    const int index_response_body_size_;
+    const int index_side_data_size_;
+
+    // For methods that should only be executed on the creation sequence.
+    SEQUENCE_CHECKER(sequence_checker_);
+
+    // Do not add a WeakPtrFactory since this class uses a cross-sequence
+    // delete helper.
+
     DISALLOW_COPY_AND_ASSIGN(DiskCacheBlobEntry);
   };
 
@@ -130,19 +177,17 @@
   CacheStorageCacheEntryHandler(
       base::WeakPtr<storage::BlobStorageContext> blob_context);
 
-  // Create a |BlobDataItem::DataHandle| for a |DiskCacheBlobEntry|
-  // where the data is stored in the given disk_cache index.
-  static scoped_refptr<storage::BlobDataItem::DataHandle> MakeDataHandle(
+  // Create a serialized blob from the given entry and disk_cache index.  This
+  // blob will not have any side data.
+  blink::mojom::SerializedBlobPtr CreateBlob(
       scoped_refptr<DiskCacheBlobEntry> blob_entry,
-      int disk_cache_index);
+      CacheStorageCache::EntryIndex disk_cache_index);
 
-  // Create a |BlobDataItem::DataHandle| for a |DiskCacheBlobEntry|
-  // where the main data and side data are stored in the given disk_cache
-  // indices.
-  static scoped_refptr<storage::BlobDataItem::DataHandle>
-  MakeDataHandleWithSideData(scoped_refptr<DiskCacheBlobEntry> blob_entry,
-                             int disk_cache_index,
-                             int side_data_disk_cache_index);
+  // Create a serialized blob from the given entry and disk_cache indices.
+  blink::mojom::SerializedBlobPtr CreateBlobWithSideData(
+      scoped_refptr<DiskCacheBlobEntry> blob_entry,
+      CacheStorageCache::EntryIndex disk_cache_index,
+      CacheStorageCache::EntryIndex side_data_disk_cache_index);
 
   base::WeakPtr<storage::BlobStorageContext> blob_context_;
 
@@ -155,6 +200,7 @@
   // data in it.
   std::set<DiskCacheBlobEntry*> blob_entries_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
   DISALLOW_COPY_AND_ASSIGN(CacheStorageCacheEntryHandler);
 };
 
diff --git a/content/browser/cache_storage/cache_storage_context_impl.cc b/content/browser/cache_storage/cache_storage_context_impl.cc
index e8eedcc..52ef3da 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.cc
+++ b/content/browser/cache_storage/cache_storage_context_impl.cc
@@ -34,6 +34,7 @@
 
 CacheStorageContextImpl::~CacheStorageContextImpl() {
   // Can be destroyed on any thread.
+  task_runner_->ReleaseSoon(FROM_HERE, std::move(cache_manager_));
 }
 
 void CacheStorageContextImpl::Init(
@@ -92,24 +93,33 @@
 }
 
 scoped_refptr<CacheStorageManager> CacheStorageContextImpl::CacheManager() {
-  // If we're shutdown or already on the target sequence, then just return the
-  // real manager.
-  if (!cache_manager_ || task_runner_->RunsTasksInCurrentSequence())
+  // If we're already on the target sequence, then just return the real manager.
+  //
+  // Note, we can't check for nullptr cache_manager_ here because it is not
+  // threadsafe.  In addition we may be creating a cross-sequence manager
+  // wrapper while the task to set cache_manager_ is waiting to run.  This
+  // should be fine since the cross-sequence wrapper will initialize after the
+  // manager is set.  See the comment in Init().
+  if (task_runner_->RunsTasksInCurrentSequence())
     return cache_manager_;
   // Otherwise we have to create a cross-sequence wrapper to provide safe
   // access.
   return base::MakeRefCounted<CrossSequenceCacheStorageManager>(task_runner_,
-                                                                cache_manager_);
+                                                                this);
 }
 
 void CacheStorageContextImpl::SetBlobParametersForCache(
     ChromeBlobStorageContext* blob_storage_context) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  task_runner_->PostTask(
-      FROM_HERE,
+  if (!blob_storage_context)
+    return;
+  // We can only get a WeakPtr to the BlobStorageContext on the IO thread.
+  // Bounce there first before setting the context on the manager.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
       base::BindOnce(
-          &CacheStorageContextImpl::SetBlobParametersForCacheOnTaskRunner, this,
-          base::RetainedRef(blob_storage_context)));
+          &CacheStorageContextImpl::GetBlobStorageContextWeakPtrOnIOThread,
+          this, base::RetainedRef(blob_storage_context)));
 }
 
 void CacheStorageContextImpl::GetAllOriginsInfo(
@@ -223,14 +233,22 @@
   cache_manager_ = nullptr;
 }
 
-void CacheStorageContextImpl::SetBlobParametersForCacheOnTaskRunner(
+void CacheStorageContextImpl::GetBlobStorageContextWeakPtrOnIOThread(
     ChromeBlobStorageContext* blob_storage_context) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(blob_storage_context);
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &CacheStorageContextImpl::SetBlobParametersForCacheOnTaskRunner, this,
+          blob_storage_context->context()->AsWeakPtr()));
+}
 
-  if (cache_manager_ && blob_storage_context) {
-    cache_manager_->SetBlobParametersForCache(
-        blob_storage_context->context()->AsWeakPtr());
-  }
+void CacheStorageContextImpl::SetBlobParametersForCacheOnTaskRunner(
+    base::WeakPtr<storage::BlobStorageContext> blob_storage_context) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  if (cache_manager_)
+    cache_manager_->SetBlobParametersForCache(blob_storage_context);
 }
 
 void CacheStorageContextImpl::CreateQuotaClientsOnIOThread(
diff --git a/content/browser/cache_storage/cache_storage_context_impl.h b/content/browser/cache_storage/cache_storage_context_impl.h
index 4d0b6b7..779ce5f 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.h
+++ b/content/browser/cache_storage/cache_storage_context_impl.h
@@ -23,6 +23,7 @@
 }
 
 namespace storage {
+class BlobStorageContext;
 class QuotaManagerProxy;
 }
 
@@ -37,11 +38,26 @@
 class CacheStorageDispatcherHost;
 class CacheStorageManager;
 
+// An intermediate abstract interface that exposes the CacheManager() method.
+// This is mainly used in some places instead of the full
+// CacheStorageContextImpl to make it easier to write tests where we want to
+// provide a specific manager instance.
+class CONTENT_EXPORT CacheStorageContextWithManager
+    : public CacheStorageContext {
+ public:
+  // Callable on any sequence.  May return nullptr during shutdown.
+  virtual scoped_refptr<CacheStorageManager> CacheManager() = 0;
+
+ protected:
+  ~CacheStorageContextWithManager() override = default;
+};
+
 // One instance of this exists per StoragePartition, and services multiple
 // child processes/origins. Most logic is delegated to the owned
 // CacheStorageManager instance, which is only accessed on the target
 // sequence.
-class CONTENT_EXPORT CacheStorageContextImpl : public CacheStorageContext {
+class CONTENT_EXPORT CacheStorageContextImpl
+    : public CacheStorageContextWithManager {
  public:
   explicit CacheStorageContextImpl(BrowserContext* browser_context);
 
@@ -68,11 +84,10 @@
   void AddBinding(blink::mojom::CacheStorageRequest request,
                   const url::Origin& origin);
 
-  // Callable on any sequence.  If called on the cache_storage target sequence
-  // the real manager will be returned directly.  If called on any other
-  // sequence then a cross-sequence wrapper object will be created and returned
-  // instead.
-  scoped_refptr<CacheStorageManager> CacheManager();
+  // If called on the cache_storage target sequence the real manager will be
+  // returned directly.  If called on any other sequence then a cross-sequence
+  // wrapper object will be created and returned instead.
+  scoped_refptr<CacheStorageManager> CacheManager() override;
 
   bool is_incognito() const { return is_incognito_; }
 
@@ -102,9 +117,12 @@
 
   void ShutdownOnTaskRunner();
 
-  void SetBlobParametersForCacheOnTaskRunner(
+  void GetBlobStorageContextWeakPtrOnIOThread(
       ChromeBlobStorageContext* blob_storage_context);
 
+  void SetBlobParametersForCacheOnTaskRunner(
+      base::WeakPtr<storage::BlobStorageContext> blob_storage_context);
+
   void CreateQuotaClientsOnIOThread(
       scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy);
 
@@ -118,7 +136,9 @@
   // Initialized in Init().
   scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_;
 
-  // Only accessed on the target sequence.
+  // Created and accessed on the target sequence.  Released on the target
+  // sequence in SHutdownOnTaskRunner() or the destructor via
+  // SequencedTaskRunner::ReleaseSoon().
   scoped_refptr<CacheStorageManager> cache_manager_;
 
   // Initialized from the UI thread and bound to |task_runner_|.
diff --git a/content/browser/cache_storage/cache_storage_manager.cc b/content/browser/cache_storage/cache_storage_manager.cc
index f84fd68..5d8127fc 100644
--- a/content/browser/cache_storage/cache_storage_manager.cc
+++ b/content/browser/cache_storage/cache_storage_manager.cc
@@ -15,8 +15,4 @@
   return !origin.opaque();
 }
 
-CacheStorageManager::CacheStorageManager()
-    : base::RefCountedDeleteOnSequence<CacheStorageManager>(
-          base::SequencedTaskRunnerHandle::Get()) {}
-
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_manager.h b/content/browser/cache_storage/cache_storage_manager.h
index db8904d..cc14a43 100644
--- a/content/browser/cache_storage/cache_storage_manager.h
+++ b/content/browser/cache_storage/cache_storage_manager.h
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/memory/ref_counted.h"
 #include "content/browser/cache_storage/cache_storage_handle.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
@@ -43,7 +43,7 @@
 // TODO(jkarlin): Remove CacheStorage from memory once they're no
 // longer in active use.
 class CONTENT_EXPORT CacheStorageManager
-    : public base::RefCountedDeleteOnSequence<CacheStorageManager> {
+    : public base::RefCounted<CacheStorageManager> {
  public:
   // Open the CacheStorage for the given origin and owner.  A reference counting
   // handle is returned which can be stored and used similar to a weak pointer.
@@ -79,10 +79,9 @@
   static bool IsValidQuotaOrigin(const url::Origin& origin);
 
  protected:
-  friend class base::DeleteHelper<CacheStorageManager>;
-  friend class base::RefCountedDeleteOnSequence<CacheStorageManager>;
+  friend class base::RefCounted<CacheStorageManager>;
 
-  CacheStorageManager();
+  CacheStorageManager() = default;
   virtual ~CacheStorageManager() = default;
 };
 
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index 64c2c0d..43b89f3b2 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -239,6 +239,26 @@
   std::unique_ptr<base::RunLoop> loop_;
 };
 
+class TestCacheStorageContext : public CacheStorageContextWithManager {
+ public:
+  explicit TestCacheStorageContext(scoped_refptr<CacheStorageManager> manager)
+      : manager_(std::move(manager)) {}
+
+  scoped_refptr<CacheStorageManager> CacheManager() override {
+    return manager_;
+  }
+
+  void GetAllOriginsInfo(GetUsageInfoCallback callback) override {
+    NOTREACHED();
+  }
+
+  void DeleteForOrigin(const GURL& origin_url) override { NOTREACHED(); }
+
+ private:
+  ~TestCacheStorageContext() override = default;
+  scoped_refptr<CacheStorageManager> manager_;
+};
+
 class CacheStorageManagerTest : public testing::Test {
  public:
   CacheStorageManagerTest()
@@ -368,8 +388,10 @@
         cache_manager_ = std::move(legacy_manager);
         break;
       case TestManager::kCrossSequence:
+        auto context = base::MakeRefCounted<TestCacheStorageContext>(
+            std::move(legacy_manager));
         cache_manager_ = base::MakeRefCounted<CrossSequenceCacheStorageManager>(
-            base::ThreadTaskRunnerHandle::Get(), std::move(legacy_manager));
+            base::ThreadTaskRunnerHandle::Get(), std::move(context));
         break;
     }
   }
diff --git a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.cc b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.cc
index 3fc7dff..666521dd 100644
--- a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.cc
+++ b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.h"
 
+#include "content/browser/cache_storage/cache_storage_context_impl.h"
 #include "content/browser/cache_storage/cache_storage_histogram_utils.h"
 #include "content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_cache.h"
 #include "content/browser/cache_storage/cross_sequence/cross_sequence_utils.h"
@@ -23,8 +24,8 @@
 
   Inner(const url::Origin& origin,
         CacheStorageOwner owner,
-        scoped_refptr<CacheStorageManager> target_manager)
-      : handle_(target_manager->OpenCacheStorage(origin, owner)) {}
+        scoped_refptr<CacheStorageContextWithManager> context)
+      : handle_(context->CacheManager()->OpenCacheStorage(origin, owner)) {}
 
   void OpenCache(scoped_refptr<CrossSequenceCacheStorageCache> cache_wrapper,
                  const std::string& cache_name,
@@ -148,13 +149,10 @@
     const url::Origin& origin,
     CacheStorageOwner owner,
     scoped_refptr<base::SequencedTaskRunner> target_task_runner,
-    scoped_refptr<CacheStorageManager> target_manager)
+    scoped_refptr<CacheStorageContextWithManager> context)
     : CacheStorage(origin),
       target_task_runner_(std::move(target_task_runner)),
-      inner_(target_task_runner_,
-             origin,
-             std::move(owner),
-             std::move(target_manager)),
+      inner_(target_task_runner_, origin, std::move(owner), std::move(context)),
       weak_factory_(this) {}
 
 CacheStorageHandle CrossSequenceCacheStorage::CreateHandle() {
diff --git a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.h b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.h
index a75301e..1f7e7de 100644
--- a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.h
+++ b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.h
@@ -13,6 +13,8 @@
 
 namespace content {
 
+class CacheStorageContextWithManager;
+
 // A CacheStorage implementation that can be used from one sequence to access
 // a real CacheStorage executing on a different sequence.  The
 // CrossSequenceCacheStorageManager constructs instances of this class in
@@ -29,7 +31,7 @@
       const url::Origin& origin,
       CacheStorageOwner owner,
       scoped_refptr<base::SequencedTaskRunner> target_task_runner,
-      scoped_refptr<CacheStorageManager> target_manager);
+      scoped_refptr<CacheStorageContextWithManager> context);
 
   // CacheStorage
   CacheStorageHandle CreateHandle() override;
diff --git a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.cc b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.cc
index 6eb46ac..0b240df 100644
--- a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.cc
+++ b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.cc
@@ -5,6 +5,7 @@
 #include "content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h"
 
 #include "content/browser/cache_storage/cache_storage.h"
+#include "content/browser/cache_storage/cache_storage_context_impl.h"
 #include "content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage.h"
 #include "content/browser/cache_storage/cross_sequence/cross_sequence_utils.h"
 
@@ -17,8 +18,8 @@
 // order to post on the outer's original sequence.
 class CrossSequenceCacheStorageManager::Inner {
  public:
-  explicit Inner(scoped_refptr<CacheStorageManager> target_manager)
-      : target_manager_(std::move(target_manager)) {}
+  explicit Inner(scoped_refptr<CacheStorageContextWithManager> context)
+      : target_manager_(context->CacheManager()) {}
 
   void GetAllOriginsUsage(CacheStorageOwner owner,
                           CacheStorageContext::GetUsageInfoCallback callback) {
@@ -60,10 +61,10 @@
 
 CrossSequenceCacheStorageManager::CrossSequenceCacheStorageManager(
     scoped_refptr<base::SequencedTaskRunner> target_task_runner,
-    scoped_refptr<CacheStorageManager> target_manager)
+    scoped_refptr<CacheStorageContextWithManager> context)
     : target_task_runner_(std::move(target_task_runner)),
-      target_manager_(target_manager),
-      inner_(target_task_runner_, std::move(target_manager_)) {}
+      context_(context),
+      inner_(target_task_runner_, std::move(context)) {}
 
 CacheStorageHandle CrossSequenceCacheStorageManager::OpenCacheStorage(
     const url::Origin& origin,
@@ -74,7 +75,7 @@
   // The CrossSequenceCacheStorage object will asynchronously open the real
   // CacheStorage on the correct sequence.
   auto storage = base::MakeRefCounted<CrossSequenceCacheStorage>(
-      origin, owner, target_task_runner_, target_manager_);
+      origin, owner, target_task_runner_, context_);
   return storage->CreateHandle();
 }
 
diff --git a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h
index 2852fda4..976cb35 100644
--- a/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h
+++ b/content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h
@@ -11,6 +11,8 @@
 
 namespace content {
 
+class CacheStorageContextWithManager;
+
 // A CacheStorageManager implementation that can be used from one sequence to
 // access the real CacheStorageManager executing on a different sequence.  The
 // CacheStorageContextImpl will create one of these whenever code calls the
@@ -23,7 +25,7 @@
  public:
   CrossSequenceCacheStorageManager(
       scoped_refptr<base::SequencedTaskRunner> target_task_runner,
-      scoped_refptr<CacheStorageManager> target_manager);
+      scoped_refptr<CacheStorageContextWithManager> context);
 
   // CacheStorageManager
   CacheStorageHandle OpenCacheStorage(const url::Origin& origin,
@@ -53,7 +55,7 @@
   ~CrossSequenceCacheStorageManager() override;
 
   const scoped_refptr<base::SequencedTaskRunner> target_task_runner_;
-  const scoped_refptr<CacheStorageManager> target_manager_;
+  const scoped_refptr<CacheStorageContextWithManager> context_;
 
   // The |inner_| object is SequenceBound<> to the target sequence used by the
   // real manager.
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
index 0e51b9e..28ed4f3 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
@@ -464,7 +464,7 @@
   LegacyCacheStorageCache* cache = new LegacyCacheStorageCache(
       origin, owner, cache_name, base::FilePath(), cache_storage,
       std::move(scheduler_task_runner), std::move(quota_manager_proxy),
-      blob_context, 0 /* cache_size */, 0 /* cache_padding */,
+      std::move(blob_context), 0 /* cache_size */, 0 /* cache_padding */,
       std::move(cache_padding_key));
   cache->SetObserver(cache_storage);
   cache->InitBackend();
@@ -488,7 +488,8 @@
   LegacyCacheStorageCache* cache = new LegacyCacheStorageCache(
       origin, owner, cache_name, path, cache_storage,
       std::move(scheduler_task_runner), std::move(quota_manager_proxy),
-      blob_context, cache_size, cache_padding, std::move(cache_padding_key));
+      std::move(blob_context), cache_size, cache_padding,
+      std::move(cache_padding_key));
   cache->SetObserver(cache_storage);
   cache->InitBackend();
   return base::WrapUnique(cache);
@@ -925,7 +926,6 @@
       cache_storage_(cache_storage),
       scheduler_task_runner_(std::move(scheduler_task_runner)),
       quota_manager_proxy_(std::move(quota_manager_proxy)),
-      blob_storage_context_(blob_context),
       scheduler_(new CacheStorageScheduler(CacheStorageSchedulerClient::kCache,
                                            scheduler_task_runner_)),
       cache_size_(cache_size),
@@ -934,8 +934,9 @@
       max_query_size_bytes_(kMaxQueryCacheResultBytes),
       cache_observer_(nullptr),
       cache_entry_handler_(
-          CacheStorageCacheEntryHandler::CreateCacheEntryHandler(owner,
-                                                                 blob_context)),
+          CacheStorageCacheEntryHandler::CreateCacheEntryHandler(
+              owner,
+              std::move(blob_context))),
       memory_only_(path.empty()),
       weak_ptr_factory_(this) {
   DCHECK(!origin_.opaque());
@@ -1192,14 +1193,6 @@
       return;
     }
 
-    if (!blob_storage_context_) {
-      std::move(query_cache_context->callback)
-          .Run(MakeErrorStorage(
-                   ErrorStorageType::kQueryCacheDidReadMetadataNullBlobContext),
-               nullptr);
-      return;
-    }
-
     cache_entry_handler_->PopulateResponseBody(blob_entry,
                                                match->response.get());
   } else if (!(query_cache_context->query_types &
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
index 429bb9d..d4175914 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
@@ -515,7 +515,6 @@
 
   const scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner_;
   scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
-  base::WeakPtr<storage::BlobStorageContext> blob_storage_context_;
   BackendState backend_state_ = BACKEND_UNINITIALIZED;
   std::unique_ptr<CacheStorageScheduler> scheduler_;
   bool initializing_ = false;
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
index 86c25df3..591fa28 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
@@ -13,7 +13,6 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/memory_pressure_listener.h"
-#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "content/browser/cache_storage/cache_storage_context_impl.h"
@@ -160,9 +159,6 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  // Do not add a WeakPtrFactory since this class is destroyed via a
-  // cross-thread delete helper.
-
   DISALLOW_COPY_AND_ASSIGN(LegacyCacheStorageManager);
 };
 
diff --git a/content/browser/content_index/BUILD.gn b/content/browser/content_index/BUILD.gn
new file mode 100644
index 0000000..5a53c66
--- /dev/null
+++ b/content/browser/content_index/BUILD.gn
@@ -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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("content_index_proto") {
+  sources = [
+    "content_index.proto",
+  ]
+}
diff --git a/content/browser/content_index/content_index.proto b/content/browser/content_index/content_index.proto
new file mode 100644
index 0000000..e439f6e
--- /dev/null
+++ b/content/browser/content_index/content_index.proto
@@ -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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package content.proto;
+
+// Stores all fields of the developer provided content description.
+message ContentDescription {
+  optional string id = 1;
+  optional string title = 2;
+  optional string description = 3;
+  optional int32 category = 4;
+  optional string icon_url = 5;
+  optional string launch_url = 6;
+}
+
+// Contains the developer-provided description as well as other metadata that
+// will be useful for ranking content.
+message ContentEntry {
+  optional ContentDescription description = 1;
+
+  // Time from Windows epoch in microseconds.
+  optional int64 timestamp = 2;
+}
diff --git a/content/browser/content_index/content_index_context.cc b/content/browser/content_index/content_index_context.cc
new file mode 100644
index 0000000..283c703
--- /dev/null
+++ b/content/browser/content_index/content_index_context.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 "content/browser/content_index/content_index_context.h"
+
+namespace content {
+
+ContentIndexContext::ContentIndexContext(
+    BrowserContext* browser_context,
+    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
+    : content_index_database_(browser_context,
+                              std::move(service_worker_context)) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+void ContentIndexContext::InitializeOnIOThread() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
+
+ContentIndexDatabase& ContentIndexContext::database() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  return content_index_database_;
+}
+
+ContentIndexContext::~ContentIndexContext() = default;
+
+}  // namespace content
diff --git a/content/browser/content_index/content_index_context.h b/content/browser/content_index/content_index_context.h
new file mode 100644
index 0000000..3bee4980
--- /dev/null
+++ b/content/browser/content_index/content_index_context.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium 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_CONTENT_INDEX_CONTENT_INDEX_CONTEXT_H_
+#define CONTENT_BROWSER_CONTENT_INDEX_CONTENT_INDEX_CONTEXT_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/browser/content_index/content_index_database.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+class BrowserContext;
+class ServiceWorkerContextWrapper;
+
+// Owned by the Storage Partition. Components that want to query or modify the
+// Content Index database should hold a reference to this.
+class CONTENT_EXPORT ContentIndexContext
+    : public base::RefCountedThreadSafe<ContentIndexContext,
+                                        BrowserThread::DeleteOnIOThread> {
+ public:
+  ContentIndexContext(
+      BrowserContext* browser_context,
+      scoped_refptr<ServiceWorkerContextWrapper> service_worker_context);
+
+  void InitializeOnIOThread();
+
+  ContentIndexDatabase& database();
+
+ private:
+  friend class base::DeleteHelper<ContentIndexContext>;
+  friend class base::RefCountedThreadSafe<ContentIndexContext,
+                                          BrowserThread::DeleteOnIOThread>;
+  friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
+
+  ~ContentIndexContext();
+
+  ContentIndexDatabase content_index_database_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentIndexContext);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_CONTENT_INDEX_CONTENT_INDEX_CONTEXT_H_
diff --git a/content/browser/content_index/content_index_database.cc b/content/browser/content_index/content_index_database.cc
new file mode 100644
index 0000000..8bf79b8
--- /dev/null
+++ b/content/browser/content_index/content_index_database.cc
@@ -0,0 +1,200 @@
+// Copyright 2019 The Chromium 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/content_index/content_index_database.h"
+
+#include <string>
+
+#include "base/time/time.h"
+#include "content/browser/content_index/content_index.pb.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "url/origin.h"
+
+namespace content {
+
+namespace {
+
+constexpr char kEntryPrefix[] = "content_index:entry_";
+
+std::string EntryKey(const std::string& id) {
+  return kEntryPrefix + id;
+}
+
+std::string CreateSerializedContentEntry(
+    const blink::mojom::ContentDescription& description,
+    base::Time entry_time) {
+  // Convert description.
+  proto::ContentDescription description_proto;
+  description_proto.set_id(description.id);
+  description_proto.set_title(description.title);
+  description_proto.set_description(description.description);
+  description_proto.set_category(static_cast<int>(description.category));
+  description_proto.set_icon_url(description.icon_url);
+  description_proto.set_launch_url(description.launch_url);
+
+  // Create entry.
+  proto::ContentEntry entry;
+  *entry.mutable_description() = std::move(description_proto);
+  entry.set_timestamp(entry_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+
+  return entry.SerializeAsString();
+}
+
+blink::mojom::ContentDescriptionPtr DescriptionFromProto(
+    const proto::ContentDescription& description) {
+  // Validate.
+  if (description.category() <
+          static_cast<int>(blink::mojom::ContentCategory::kMinValue) ||
+      description.category() >
+          static_cast<int>(blink::mojom::ContentCategory::kMaxValue)) {
+    return nullptr;
+  }
+
+  // Convert.
+  auto result = blink::mojom::ContentDescription::New();
+  result->id = description.id();
+  result->title = description.title();
+  result->description = description.description();
+  result->category =
+      static_cast<blink::mojom::ContentCategory>(description.category());
+  result->icon_url = description.icon_url();
+  result->launch_url = description.launch_url();
+  return result;
+}
+
+}  // namespace
+
+ContentIndexDatabase::ContentIndexDatabase(
+    BrowserContext* browser_context,
+    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
+    : browser_context_(browser_context),
+      service_worker_context_(std::move(service_worker_context)),
+      weak_ptr_factory_(this) {}
+
+ContentIndexDatabase::~ContentIndexDatabase() = default;
+
+void ContentIndexDatabase::AddEntry(
+    int64_t service_worker_registration_id,
+    const url::Origin& origin,
+    blink::mojom::ContentDescriptionPtr description,
+    const SkBitmap& icon,
+    blink::mojom::ContentIndexService::AddCallback callback) {
+  base::Time entry_time = base::Time::Now();
+  std::string key = EntryKey(description->id);
+  std::string value = CreateSerializedContentEntry(*description, entry_time);
+  DCHECK(!value.empty());
+
+  // Entry to pass over to the provider.
+  ContentIndexEntry entry(service_worker_registration_id,
+                          std::move(description), entry_time);
+
+  // TODO(crbug.com/973844): Serialize and store icon.
+  service_worker_context_->StoreRegistrationUserData(
+      service_worker_registration_id, origin.GetURL(),
+      {{std::move(key), std::move(value)}},
+      base::BindOnce(&ContentIndexDatabase::DidAddEntry,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     std::move(entry)));
+}
+
+void ContentIndexDatabase::DidAddEntry(
+    blink::mojom::ContentIndexService::AddCallback callback,
+    ContentIndexEntry entry,
+    blink::ServiceWorkerStatusCode status) {
+  if (status != blink::ServiceWorkerStatusCode::kOk) {
+    std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR);
+    return;
+  }
+
+  std::move(callback).Run(blink::mojom::ContentIndexError::NONE);
+
+  if (auto* provider = browser_context_->GetContentIndexProvider())
+    provider->OnContentAdded(std::move(entry), weak_ptr_factory_.GetWeakPtr());
+}
+
+void ContentIndexDatabase::DeleteEntry(
+    int64_t service_worker_registration_id,
+    const std::string& entry_id,
+    blink::mojom::ContentIndexService::DeleteCallback callback) {
+  service_worker_context_->ClearRegistrationUserData(
+      service_worker_registration_id, {EntryKey(entry_id)},
+      base::BindOnce(
+          &ContentIndexDatabase::DidDeleteEntry, weak_ptr_factory_.GetWeakPtr(),
+          service_worker_registration_id, entry_id, std::move(callback)));
+}
+
+void ContentIndexDatabase::DidDeleteEntry(
+    int64_t service_worker_registration_id,
+    const std::string& entry_id,
+    blink::mojom::ContentIndexService::DeleteCallback callback,
+    blink::ServiceWorkerStatusCode status) {
+  if (status != blink::ServiceWorkerStatusCode::kOk) {
+    std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR);
+    return;
+  }
+
+  std::move(callback).Run(blink::mojom::ContentIndexError::NONE);
+  if (auto* provider = browser_context_->GetContentIndexProvider())
+    provider->OnContentDeleted(service_worker_registration_id, entry_id);
+}
+
+void ContentIndexDatabase::GetDescriptions(
+    int64_t service_worker_registration_id,
+    blink::mojom::ContentIndexService::GetDescriptionsCallback callback) {
+  service_worker_context_->GetRegistrationUserDataByKeyPrefix(
+      service_worker_registration_id, kEntryPrefix,
+      base::BindOnce(&ContentIndexDatabase::DidGetDescriptions,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ContentIndexDatabase::DidGetDescriptions(
+    blink::mojom::ContentIndexService::GetDescriptionsCallback callback,
+    const std::vector<std::string>& data,
+    blink::ServiceWorkerStatusCode status) {
+  if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
+    std::move(callback).Run(blink::mojom::ContentIndexError::NONE,
+                            /* descriptions= */ {});
+    return;
+  } else if (status != blink::ServiceWorkerStatusCode::kOk) {
+    std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR,
+                            /* descriptions= */ {});
+    return;
+  }
+
+  std::vector<blink::mojom::ContentDescriptionPtr> descriptions;
+  descriptions.reserve(data.size());
+
+  // TODO(crbug.com/973844): Clear the storage if there is data corruption.
+  for (const auto& serialized_entry : data) {
+    proto::ContentEntry entry;
+    if (!entry.ParseFromString(serialized_entry)) {
+      std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR,
+                              /* descriptions= */ {});
+      return;
+    }
+
+    auto description = DescriptionFromProto(entry.description());
+    if (!description) {
+      std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR,
+                              /* descriptions= */ {});
+      return;
+    }
+
+    descriptions.push_back(std::move(description));
+  }
+
+  std::move(callback).Run(blink::mojom::ContentIndexError::NONE,
+                          std::move(descriptions));
+}
+
+void ContentIndexDatabase::GetIcon(
+    int64_t service_worker_registration_id,
+    const std::string& description_id,
+    base::OnceCallback<void(SkBitmap)> icon_callback) {
+  // TODO(crbug.com/973844): Implement this after icon is fetched & persisted.
+  std::move(icon_callback).Run(SkBitmap());
+}
+
+}  // namespace content
diff --git a/content/browser/content_index/content_index_database.h b/content/browser/content_index/content_index_database.h
new file mode 100644
index 0000000..c3b5d1f
--- /dev/null
+++ b/content/browser/content_index/content_index_database.h
@@ -0,0 +1,75 @@
+// Copyright 2019 The Chromium 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_CONTENT_INDEX_CONTENT_INDEX_DATABASE_H_
+#define CONTENT_BROWSER_CONTENT_INDEX_CONTENT_INDEX_DATABASE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/content_index_provider.h"
+#include "third_party/blink/public/mojom/content_index/content_index.mojom.h"
+
+namespace url {
+class Origin;
+}  // namespace url
+
+namespace content {
+
+class BrowserContext;
+
+// Handles interacting with the Service Worker Database for Content Index
+// entries. This is owned by the ContentIndexContext.
+class CONTENT_EXPORT ContentIndexDatabase
+    : public ContentIndexProvider::Client {
+ public:
+  ContentIndexDatabase(
+      BrowserContext* browser_context,
+      scoped_refptr<ServiceWorkerContextWrapper> service_worker_context);
+  ~ContentIndexDatabase() override;
+
+  void AddEntry(int64_t service_worker_registration_id,
+                const url::Origin& origin,
+                blink::mojom::ContentDescriptionPtr description,
+                const SkBitmap& icon,
+                blink::mojom::ContentIndexService::AddCallback callback);
+
+  void DeleteEntry(int64_t service_worker_registration_id,
+                   const std::string& entry_id,
+                   blink::mojom::ContentIndexService::DeleteCallback callback);
+
+  void GetDescriptions(
+      int64_t service_worker_registration_id,
+      blink::mojom::ContentIndexService::GetDescriptionsCallback callback);
+
+  // ContentIndexProvider::Client implementation.
+  void GetIcon(int64_t service_worker_registration_id,
+               const std::string& description_id,
+               base::OnceCallback<void(SkBitmap)> icon_callback) override;
+
+ private:
+  void DidAddEntry(blink::mojom::ContentIndexService::AddCallback callback,
+                   ContentIndexEntry entry,
+                   blink::ServiceWorkerStatusCode status);
+  void DidDeleteEntry(
+      int64_t service_worker_registration_id,
+      const std::string& entry_id,
+      blink::mojom::ContentIndexService::DeleteCallback callback,
+      blink::ServiceWorkerStatusCode status);
+  void DidGetDescriptions(
+      blink::mojom::ContentIndexService::GetDescriptionsCallback callback,
+      const std::vector<std::string>& data,
+      blink::ServiceWorkerStatusCode status);
+
+  BrowserContext* browser_context_;
+  scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
+  base::WeakPtrFactory<ContentIndexDatabase> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentIndexDatabase);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_CONTENT_INDEX_CONTENT_INDEX_DATABASE_H_
diff --git a/content/browser/content_index/content_index_database_unittest.cc b/content/browser/content_index/content_index_database_unittest.cc
new file mode 100644
index 0000000..eb647345
--- /dev/null
+++ b/content/browser/content_index/content_index_database_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright 2019 The Chromium 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/content_index/content_index_database.h"
+
+#include <string>
+
+#include "base/run_loop.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/public/browser/content_index_provider.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"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
+
+namespace content {
+namespace {
+
+using ::testing::_;
+
+class MockContentIndexProvider : public ContentIndexProvider {
+ public:
+  MOCK_METHOD2(OnContentAdded,
+               void(ContentIndexEntry entry,
+                    base::WeakPtr<ContentIndexProvider::Client>));
+  MOCK_METHOD2(OnContentDeleted,
+               void(int64_t service_Worker_registration_id,
+                    const std::string& description_id));
+};
+
+class ContentIndexTestBrowserContext : public TestBrowserContext {
+ public:
+  ContentIndexTestBrowserContext()
+      : delegate_(std::make_unique<MockContentIndexProvider>()) {}
+  ~ContentIndexTestBrowserContext() override = default;
+
+  MockContentIndexProvider* GetContentIndexProvider() override {
+    return delegate_.get();
+  }
+
+ private:
+  std::unique_ptr<MockContentIndexProvider> delegate_;
+};
+
+void DidRegisterServiceWorker(int64_t* out_service_worker_registration_id,
+                              base::OnceClosure quit_closure,
+                              blink::ServiceWorkerStatusCode status,
+                              const std::string& status_message,
+                              int64_t service_worker_registration_id) {
+  DCHECK(out_service_worker_registration_id);
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status) << status_message;
+
+  *out_service_worker_registration_id = service_worker_registration_id;
+
+  std::move(quit_closure).Run();
+}
+
+void DidFindServiceWorkerRegistration(
+    scoped_refptr<ServiceWorkerRegistration>* out_service_worker_registration,
+    base::OnceClosure quit_closure,
+    blink::ServiceWorkerStatusCode status,
+    scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
+  DCHECK(out_service_worker_registration);
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
+      << blink::ServiceWorkerStatusToString(status);
+
+  *out_service_worker_registration = service_worker_registration;
+
+  std::move(quit_closure).Run();
+}
+
+void DatabaseErrorCallback(base::OnceClosure quit_closure,
+                           blink::mojom::ContentIndexError* out_error,
+                           blink::mojom::ContentIndexError error) {
+  *out_error = error;
+  std::move(quit_closure).Run();
+}
+
+void GetEntriesCallback(
+    base::OnceClosure quit_closure,
+    blink::mojom::ContentIndexError* out_error,
+    std::vector<blink::mojom::ContentDescriptionPtr>* out_descriptions,
+    blink::mojom::ContentIndexError error,
+    std::vector<blink::mojom::ContentDescriptionPtr> descriptions) {
+  if (out_error)
+    *out_error = error;
+  DCHECK(out_descriptions);
+  *out_descriptions = std::move(descriptions);
+  std::move(quit_closure).Run();
+}
+
+}  // namespace
+
+class ContentIndexDatabaseTest : public ::testing::Test {
+ public:
+  ContentIndexDatabaseTest()
+      : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+        embedded_worker_test_helper_(base::FilePath() /* in memory */) {}
+
+  ~ContentIndexDatabaseTest() override = default;
+
+  void SetUp() override {
+    // Register Service Worker.
+    service_worker_registration_id_ = RegisterServiceWorker();
+    ASSERT_NE(service_worker_registration_id_,
+              blink::mojom::kInvalidServiceWorkerRegistrationId);
+    database_ = std::make_unique<ContentIndexDatabase>(
+        &browser_context_, embedded_worker_test_helper_.context_wrapper());
+  }
+
+  blink::mojom::ContentDescriptionPtr CreateDescription(const std::string& id) {
+    return blink::mojom::ContentDescription::New(
+        id, "title", "description", blink::mojom::ContentCategory::HOME_PAGE,
+        "https://example.com", "https://example.com");
+  }
+
+  blink::mojom::ContentIndexError AddEntry(
+      blink::mojom::ContentDescriptionPtr description) {
+    base::RunLoop run_loop;
+    blink::mojom::ContentIndexError error;
+    database_->AddEntry(
+        service_worker_registration_id_, origin_, std::move(description),
+        SkBitmap(),
+        base::BindOnce(&DatabaseErrorCallback, run_loop.QuitClosure(), &error));
+    run_loop.Run();
+
+    return error;
+  }
+
+  blink::mojom::ContentIndexError DeleteEntry(const std::string& id) {
+    base::RunLoop run_loop;
+    blink::mojom::ContentIndexError error;
+    database_->DeleteEntry(
+        service_worker_registration_id_, id,
+        base::BindOnce(&DatabaseErrorCallback, run_loop.QuitClosure(), &error));
+    run_loop.Run();
+
+    return error;
+  }
+
+  std::vector<blink::mojom::ContentDescriptionPtr> GetDescriptions(
+      blink::mojom::ContentIndexError* out_error = nullptr) {
+    base::RunLoop run_loop;
+    std::vector<blink::mojom::ContentDescriptionPtr> descriptions;
+    database_->GetDescriptions(
+        service_worker_registration_id_,
+        base::BindOnce(&GetEntriesCallback, run_loop.QuitClosure(), out_error,
+                       &descriptions));
+    run_loop.Run();
+    return descriptions;
+  }
+
+  MockContentIndexProvider* provider() {
+    return browser_context_.GetContentIndexProvider();
+  }
+
+  int64_t service_worker_registration_id() {
+    return service_worker_registration_id_;
+  }
+
+  ContentIndexDatabase* database() { return database_.get(); }
+
+ private:
+  int64_t RegisterServiceWorker() {
+    GURL script_url(origin_.GetURL().spec() + "sw.js");
+    int64_t service_worker_registration_id =
+        blink::mojom::kInvalidServiceWorkerRegistrationId;
+
+    {
+      blink::mojom::ServiceWorkerRegistrationOptions options;
+      options.scope = origin_.GetURL();
+      base::RunLoop run_loop;
+      embedded_worker_test_helper_.context()->RegisterServiceWorker(
+          script_url, options,
+          base::BindOnce(&DidRegisterServiceWorker,
+                         &service_worker_registration_id,
+                         run_loop.QuitClosure()));
+
+      run_loop.Run();
+    }
+
+    if (service_worker_registration_id ==
+        blink::mojom::kInvalidServiceWorkerRegistrationId) {
+      ADD_FAILURE() << "Could not obtain a valid Service Worker registration";
+      return blink::mojom::kInvalidServiceWorkerRegistrationId;
+    }
+
+    {
+      base::RunLoop run_loop;
+      embedded_worker_test_helper_.context()->storage()->FindRegistrationForId(
+          service_worker_registration_id, origin_.GetURL(),
+          base::BindOnce(&DidFindServiceWorkerRegistration,
+                         &service_worker_registration_,
+                         run_loop.QuitClosure()));
+      run_loop.Run();
+    }
+
+    // Wait for the worker to be activated.
+    base::RunLoop().RunUntilIdle();
+
+    if (!service_worker_registration_) {
+      ADD_FAILURE() << "Could not find the new Service Worker registration.";
+      return blink::mojom::kInvalidServiceWorkerRegistrationId;
+    }
+
+    return service_worker_registration_id;
+  }
+
+  TestBrowserThreadBundle thread_bundle_;  // Must be first member.
+  ContentIndexTestBrowserContext browser_context_;
+  url::Origin origin_ = url::Origin::Create(GURL("https://example.com"));
+  int64_t service_worker_registration_id_ =
+      blink::mojom::kInvalidServiceWorkerRegistrationId;
+  EmbeddedWorkerTestHelper embedded_worker_test_helper_;
+  scoped_refptr<ServiceWorkerRegistration> service_worker_registration_;
+  std::unique_ptr<ContentIndexDatabase> database_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentIndexDatabaseTest);
+};
+
+TEST_F(ContentIndexDatabaseTest, DatabaseOperations) {
+  // Initially database will be empty.
+  {
+    blink::mojom::ContentIndexError error;
+    auto descriptions = GetDescriptions(&error);
+    EXPECT_TRUE(descriptions.empty());
+    EXPECT_EQ(error, blink::mojom::ContentIndexError::NONE);
+  }
+
+  // Insert entries and expect to find them.
+  EXPECT_EQ(AddEntry(CreateDescription("id1")),
+            blink::mojom::ContentIndexError::NONE);
+  EXPECT_EQ(AddEntry(CreateDescription("id2")),
+            blink::mojom::ContentIndexError::NONE);
+  EXPECT_EQ(GetDescriptions().size(), 2u);
+
+  // Remove an entry.
+  EXPECT_EQ(DeleteEntry("id2"), blink::mojom::ContentIndexError::NONE);
+
+  // Inspect the last remaining element.
+  auto descriptions = GetDescriptions();
+  ASSERT_EQ(descriptions.size(), 1u);
+  auto expected_description = CreateDescription("id1");
+  EXPECT_TRUE(descriptions[0]->Equals(*expected_description));
+}
+
+TEST_F(ContentIndexDatabaseTest, AddDuplicateIdWillOverwrite) {
+  auto description1 = CreateDescription("id");
+  description1->title = "title1";
+  auto description2 = CreateDescription("id");
+  description2->title = "title2";
+
+  EXPECT_EQ(AddEntry(std::move(description1)),
+            blink::mojom::ContentIndexError::NONE);
+  EXPECT_EQ(AddEntry(std::move(description2)),
+            blink::mojom::ContentIndexError::NONE);
+
+  auto descriptions = GetDescriptions();
+  ASSERT_EQ(descriptions.size(), 1u);
+  EXPECT_EQ(descriptions[0]->id, "id");
+  EXPECT_EQ(descriptions[0]->title, "title2");
+}
+
+TEST_F(ContentIndexDatabaseTest, DeleteNonExistentEntry) {
+  auto descriptions = GetDescriptions();
+  EXPECT_TRUE(descriptions.empty());
+
+  EXPECT_EQ(DeleteEntry("id"), blink::mojom::ContentIndexError::NONE);
+}
+
+TEST_F(ContentIndexDatabaseTest, ProviderUpdated) {
+  {
+    std::unique_ptr<ContentIndexEntry> out_entry;
+    ContentIndexProvider::Client* client_ptr;
+    EXPECT_CALL(*provider(), OnContentAdded(_, _))
+        .WillOnce(testing::Invoke([&](auto entry, auto client) {
+          out_entry = std::make_unique<ContentIndexEntry>(std::move(entry));
+          client_ptr = client.get();
+        }));
+    EXPECT_EQ(AddEntry(CreateDescription("id")),
+              blink::mojom::ContentIndexError::NONE);
+    ASSERT_TRUE(out_entry);
+    ASSERT_TRUE(out_entry->description);
+    EXPECT_EQ(out_entry->service_worker_registration_id,
+              service_worker_registration_id());
+    EXPECT_EQ(out_entry->description->id, "id");
+    EXPECT_FALSE(out_entry->registration_time.is_null());
+    EXPECT_EQ(client_ptr, database());
+  }
+
+  {
+    EXPECT_CALL(*provider(),
+                OnContentDeleted(service_worker_registration_id(), "id"));
+    EXPECT_EQ(DeleteEntry("id"), blink::mojom::ContentIndexError::NONE);
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/content_index/content_index_service_impl.cc b/content/browser/content_index/content_index_service_impl.cc
index c0b04aee..f9945ce8 100644
--- a/content/browser/content_index/content_index_service_impl.cc
+++ b/content/browser/content_index/content_index_service_impl.cc
@@ -8,19 +8,24 @@
 
 #include "base/bind.h"
 #include "base/task/post_task.h"
+#include "content/browser/content_index/content_index_database.h"
+#include "content/browser/storage_partition_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "url/origin.h"
 
 namespace content {
 
 namespace {
 
-void CreateOnIO(blink::mojom::ContentIndexServiceRequest request) {
+void CreateOnIO(blink::mojom::ContentIndexServiceRequest request,
+                const url::Origin& origin,
+                scoped_refptr<ContentIndexContext> content_index_context) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  mojo::MakeStrongBinding(std::make_unique<ContentIndexServiceImpl>(),
+  mojo::MakeStrongBinding(std::make_unique<ContentIndexServiceImpl>(
+                              origin, std::move(content_index_context)),
                           std::move(request));
 }
 
@@ -33,11 +38,21 @@
     const url::Origin& origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
-                           base::BindOnce(&CreateOnIO, std::move(request)));
+  auto* storage_partition = static_cast<StoragePartitionImpl*>(
+      render_process_host->GetStoragePartition());
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(
+          &CreateOnIO, std::move(request), origin,
+          base::WrapRefCounted(storage_partition->GetContentIndexContext())));
 }
 
-ContentIndexServiceImpl::ContentIndexServiceImpl() = default;
+ContentIndexServiceImpl::ContentIndexServiceImpl(
+    const url::Origin& origin,
+    scoped_refptr<ContentIndexContext> content_index_context)
+    : origin_(origin),
+      content_index_context_(std::move(content_index_context)) {}
 
 ContentIndexServiceImpl::~ContentIndexServiceImpl() = default;
 
@@ -48,8 +63,9 @@
     AddCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  // TODO(crbug.com/973844): Implement this.
-  std::move(callback).Run(blink::mojom::ContentIndexError::NONE);
+  content_index_context_->database().AddEntry(service_worker_registration_id,
+                                              origin_, std::move(description),
+                                              icon, std::move(callback));
 }
 
 void ContentIndexServiceImpl::Delete(int64_t service_worker_registration_id,
@@ -57,8 +73,8 @@
                                      DeleteCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  // TODO(crbug.com/973844): Implement this.
-  std::move(callback).Run(blink::mojom::ContentIndexError::NONE);
+  content_index_context_->database().DeleteEntry(
+      service_worker_registration_id, content_id, std::move(callback));
 }
 
 void ContentIndexServiceImpl::GetDescriptions(
@@ -66,9 +82,8 @@
     GetDescriptionsCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  // TODO(crbug.com/973844): Implement this.
-  std::move(callback).Run(blink::mojom::ContentIndexError::NONE,
-                          /* descriptions= */ {});
+  content_index_context_->database().GetDescriptions(
+      service_worker_registration_id, std::move(callback));
 }
 
 }  // namespace content
diff --git a/content/browser/content_index/content_index_service_impl.h b/content/browser/content_index/content_index_service_impl.h
index c90559f..d3a5409 100644
--- a/content/browser/content_index/content_index_service_impl.h
+++ b/content/browser/content_index/content_index_service_impl.h
@@ -5,8 +5,11 @@
 #ifndef CONTENT_BROWSER_CONTENT_INDEX_CONTENT_INDEX_SERVICE_IMPL_H_
 #define CONTENT_BROWSER_CONTENT_INDEX_CONTENT_INDEX_SERVICE_IMPL_H_
 
+#include "base/memory/scoped_refptr.h"
+#include "content/browser/content_index/content_index_context.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/mojom/content_index/content_index.mojom.h"
+#include "url/origin.h"
 
 namespace url {
 class Origin;
@@ -16,6 +19,8 @@
 
 class RenderProcessHost;
 
+// Lazily constructed by the corresponding renderer when the Content Index API
+// is triggered.
 class CONTENT_EXPORT ContentIndexServiceImpl
     : public blink::mojom::ContentIndexService {
  public:
@@ -23,7 +28,9 @@
                      RenderProcessHost* render_process_host,
                      const url::Origin& origin);
 
-  ContentIndexServiceImpl();
+  ContentIndexServiceImpl(
+      const url::Origin& origin,
+      scoped_refptr<ContentIndexContext> content_index_context);
   ~ContentIndexServiceImpl() override;
 
   // blink::mojom::ContentIndexService implementation.
@@ -38,6 +45,9 @@
                        GetDescriptionsCallback callback) override;
 
  private:
+  url::Origin origin_;
+  scoped_refptr<ContentIndexContext> content_index_context_;
+
   DISALLOW_COPY_AND_ASSIGN(ContentIndexServiceImpl);
 };
 
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index 80447202..8f0d291d8 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -18,6 +18,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_package/signed_exchange_envelope.h"
 #include "content/common/navigation_params.mojom.h"
+#include "content/public/browser/file_select_listener.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
@@ -279,6 +280,22 @@
   return had_interceptors;
 }
 
+bool InterceptFileChooser(
+    RenderFrameHostImpl* rfh,
+    std::unique_ptr<content::FileSelectListener>* listener,
+    const blink::mojom::FileChooserParams& params) {
+  DevToolsAgentHostImpl* agent_host = RenderFrameDevToolsAgentHost::GetFor(rfh);
+  if (!agent_host)
+    return false;
+  std::vector<protocol::PageHandler*> page_handlers =
+      protocol::PageHandler::ForAgentHost(agent_host);
+  for (auto* handler : page_handlers) {
+    if (handler->InterceptFileChooser(rfh, listener, params))
+      return true;
+  }
+  return false;
+}
+
 bool WillCreateURLLoaderFactoryForServiceWorker(
     RenderProcessHost* rph,
     int routing_id,
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index 0ef3544..16e9152 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -15,6 +15,7 @@
 #include "content/common/navigation_params.mojom.h"
 #include "content/public/browser/certificate_request_result_type.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 
 class GURL;
 
@@ -34,6 +35,7 @@
 namespace content {
 class SignedExchangeEnvelope;
 class FrameTreeNode;
+class FileSelectListener;
 class NavigationHandleImpl;
 class NavigationRequest;
 class NavigationThrottle;
@@ -55,6 +57,11 @@
     bool is_download,
     network::mojom::URLLoaderFactoryRequest* loader_factory_request);
 
+bool InterceptFileChooser(
+    RenderFrameHostImpl* rfh,
+    std::unique_ptr<content::FileSelectListener>* listener,
+    const blink::mojom::FileChooserParams& params);
+
 bool WillCreateURLLoaderFactoryForServiceWorker(
     RenderProcessHost* rph,
     int routing_id,
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 97d8387f..f000418 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -22,6 +22,7 @@
 #include "base/task/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/devtools/devtools_agent_host_impl.h"
 #include "content/browser/devtools/protocol/devtools_download_manager_delegate.h"
 #include "content/browser/devtools/protocol/devtools_download_manager_helper.h"
@@ -38,6 +39,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_manager.h"
+#include "content/public/browser/file_select_listener.h"
 #include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -183,7 +185,9 @@
 }  // namespace
 
 PageHandler::PageHandler(EmulationHandler* emulation_handler,
-                         bool allow_set_download_behavior)
+                         void** active_file_chooser_interceptor,
+                         bool allow_set_download_behavior,
+                         bool allow_file_access)
     : DevToolsDomainHandler(Page::Metainfo::domainName),
       enabled_(false),
       screencast_enabled_(false),
@@ -199,7 +203,9 @@
       last_surface_size_(gfx::Size()),
       host_(nullptr),
       emulation_handler_(emulation_handler),
+      active_file_chooser_interceptor_(active_file_chooser_interceptor),
       allow_set_download_behavior_(allow_set_download_behavior),
+      allow_file_access_(allow_file_access),
       observer_(this),
       weak_factory_(this) {
   bool create_video_consumer = true;
@@ -351,6 +357,8 @@
   enabled_ = false;
   screencast_enabled_ = false;
 
+  SetInterceptFileChooserDialog(false);
+
   if (video_consumer_)
     video_consumer_->StopCapture();
 
@@ -611,6 +619,114 @@
   return Response::OK();
 }
 
+Response PageHandler::SetInterceptFileChooserDialog(bool enabled) {
+  if (!allow_file_access_)
+    return Response::Error("Not Allowed");
+  if (*active_file_chooser_interceptor_ == this && enabled)
+    return Response::OK();
+  if (*active_file_chooser_interceptor_ &&
+      *active_file_chooser_interceptor_ != this) {
+    return enabled
+               ? Response::Error(
+                     "Cannot enable file chooser interception because other "
+                     "protocol client already intercepts it")
+               : Response::Error("File chooser interception was not enabled");
+  }
+  *active_file_chooser_interceptor_ = enabled ? this : nullptr;
+  if (!enabled && file_chooser_listener_)
+    FallbackOrCancelFileChooser();
+  return Response::OK();
+}
+
+Response PageHandler::HandleFileChooser(
+    const std::string& action,
+    Maybe<protocol::Array<std::string>> optional_files) {
+  if (!host_)
+    return Response::Error("Cannot resolve file paths");
+  if (!file_chooser_listener_)
+    return Response::Error("No pending file chooser");
+
+  if (action == Page::HandleFileChooser::ActionEnum::Fallback) {
+    if (optional_files.isJust()) {
+      return Response::InvalidParams(
+          "Either 'ignore' or 'files' parameter should be specified; received "
+          "both");
+    }
+    FallbackOrCancelFileChooser();
+    return Response::OK();
+  }
+
+  if (action == Page::HandleFileChooser::ActionEnum::Accept) {
+    if (!optional_files.isJust())
+      return Response::InvalidParams("Files must be specified");
+    std::unique_ptr<protocol::Array<std::string>> files =
+        optional_files.takeJust();
+    if (file_chooser_params_->mode ==
+            blink::mojom::FileChooserParams::Mode::kOpen &&
+        files->size() > 1) {
+      return Response::Error("Expected to accept a single file");
+    }
+    std::vector<blink::mojom::FileChooserFileInfoPtr> chooser_files;
+    for (const std::string& file : *files) {
+      base::FilePath file_path = base::FilePath::FromUTF8Unsafe(file);
+      ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
+          host_->GetProcess()->GetID(), file_path);
+      chooser_files.push_back(blink::mojom::FileChooserFileInfo::NewNativeFile(
+          blink::mojom::NativeFileInfo::New(file_path, base::string16())));
+    }
+    file_chooser_listener_->FileSelected(
+        std::move(chooser_files), base::FilePath(), file_chooser_params_->mode);
+    file_chooser_listener_.reset();
+    file_chooser_params_.reset();
+    file_chooser_rfh_id_.reset();
+    return Response::OK();
+  }
+
+  if (action == Page::HandleFileChooser::ActionEnum::Cancel) {
+    file_chooser_listener_->FileSelectionCanceled();
+    file_chooser_listener_.reset();
+    file_chooser_params_.reset();
+    file_chooser_rfh_id_.reset();
+    return Response::OK();
+  }
+
+  return Response::InvalidParams("Unknown action '" + action + "'");
+}
+
+void PageHandler::FallbackOrCancelFileChooser() {
+  RenderFrameHost* rfh = RenderFrameHost::FromID(file_chooser_rfh_id_->first,
+                                                 file_chooser_rfh_id_->second);
+  WebContents* web_contents = GetWebContents();
+  if (rfh && web_contents && web_contents->GetDelegate()) {
+    web_contents->GetDelegate()->RunFileChooser(
+        rfh, std::move(file_chooser_listener_), *file_chooser_params_);
+  } else {
+    file_chooser_listener_->FileSelectionCanceled();
+  }
+  file_chooser_listener_.reset();
+  file_chooser_params_.reset();
+  file_chooser_rfh_id_.reset();
+}
+
+bool PageHandler::InterceptFileChooser(
+    RenderFrameHostImpl* rfh,
+    std::unique_ptr<FileSelectListener>* listener,
+    const blink::mojom::FileChooserParams& params) {
+  if (*active_file_chooser_interceptor_ != this)
+    return false;
+  file_chooser_rfh_id_ =
+      std::make_pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
+  DCHECK(!file_chooser_listener_);
+  file_chooser_listener_ = std::move(*listener);
+  file_chooser_params_ =
+      std::make_unique<blink::mojom::FileChooserParams>(params);
+  frontend_->FileChooserOpened(
+      params.mode == blink::mojom::FileChooserParams::Mode::kOpen
+          ? Page::FileChooserOpened::ModeEnum::SelectSingle
+          : Page::FileChooserOpened::ModeEnum::SelectMultiple);
+  return true;
+}
+
 void PageHandler::CaptureSnapshot(
     Maybe<std::string> format,
     std::unique_ptr<CaptureSnapshotCallback> callback) {
diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h
index 677f479e..e86d824 100644
--- a/content/browser/devtools/protocol/page_handler.h
+++ b/content/browser/devtools/protocol/page_handler.h
@@ -26,6 +26,7 @@
 #include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/render_widget_host_observer.h"
 #include "content/public/common/javascript_dialog_type.h"
+#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 #include "third_party/blink/public/mojom/manifest/manifest_manager.mojom.h"
 #include "url/gurl.h"
 
@@ -46,6 +47,7 @@
 namespace content {
 
 class DevToolsAgentHostImpl;
+class FileSelectListener;
 class FrameTreeNode;
 class NavigationRequest;
 class RenderFrameHostImpl;
@@ -59,7 +61,10 @@
                     public Page::Backend,
                     public RenderWidgetHostObserver {
  public:
-  PageHandler(EmulationHandler* handler, bool allow_set_download_behavior);
+  PageHandler(EmulationHandler* handler,
+              void** active_file_chooser_interceptor,
+              bool allow_set_download_behavior,
+              bool allow_file_access);
   ~PageHandler() override;
 
   static std::vector<PageHandler*> EnabledForWebContents(
@@ -113,6 +118,10 @@
       std::unique_ptr<NavigationEntries>* entries) override;
   Response NavigateToHistoryEntry(int entry_id) override;
   Response ResetNavigationHistory() override;
+  Response SetInterceptFileChooserDialog(bool enabled) override;
+  Response HandleFileChooser(
+      const std::string& action,
+      Maybe<protocol::Array<std::string>> files) override;
 
   void CaptureScreenshot(
       Maybe<std::string> format,
@@ -163,6 +172,10 @@
   void GetInstallabilityErrors(
       std::unique_ptr<GetInstallabilityErrorsCallback> callback) override;
 
+  bool InterceptFileChooser(RenderFrameHostImpl* rfh,
+                            std::unique_ptr<FileSelectListener>* listener,
+                            const blink::mojom::FileChooserParams& params);
+
  private:
   enum EncodingFormat { PNG, JPEG };
 
@@ -175,6 +188,7 @@
   void ScreencastFrameEncoded(
       std::unique_ptr<Page::ScreencastFrameMetadata> metadata,
       const protocol::Binary& data);
+  void FallbackOrCancelFileChooser();
 
   void ScreenshotCaptured(
       std::unique_ptr<CaptureScreenshotCallback> callback,
@@ -219,13 +233,19 @@
 
   RenderFrameHostImpl* host_;
   EmulationHandler* emulation_handler_;
+  void** active_file_chooser_interceptor_;
   bool allow_set_download_behavior_;
+  const bool allow_file_access_;
+
   std::unique_ptr<Page::Frontend> frontend_;
   ScopedObserver<RenderWidgetHost, RenderWidgetHostObserver> observer_;
   JavaScriptDialogCallback pending_dialog_;
   scoped_refptr<DevToolsDownloadManagerDelegate> download_manager_delegate_;
   base::flat_map<base::UnguessableToken, std::unique_ptr<NavigateCallback>>
       navigate_callbacks_;
+  std::unique_ptr<FileSelectListener> file_chooser_listener_;
+  std::unique_ptr<blink::mojom::FileChooserParams> file_chooser_params_;
+  base::Optional<std::pair<int, int>> file_chooser_rfh_id_;
 
   base::WeakPtrFactory<PageHandler> weak_factory_;
 
diff --git a/content/browser/devtools/protocol/system_info_handler.cc b/content/browser/devtools/protocol/system_info_handler.cc
index 9ca271f..db89e209 100644
--- a/content/browser/devtools/protocol/system_info_handler.cc
+++ b/content/browser/devtools/protocol/system_info_handler.cc
@@ -114,10 +114,6 @@
     in_aux_attributes_ = false;
   }
 
-  void BeginANGLEFeature() override {}
-
-  void EndANGLEFeature() override {}
-
  private:
   protocol::DictionaryValue* dictionary_;
   bool in_aux_attributes_;
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index 4f57e10..54721bf5 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -57,8 +57,8 @@
                 "domain": "Page",
                 "include": ["enable", "disable", "reload", "navigate", "stopLoading", "getNavigationHistory", "navigateToHistoryEntry", "resetNavigationHistory", "captureScreenshot",
                     "startScreencast", "stopScreencast", "screencastFrameAck", "handleJavaScriptDialog", "setColorPickerEnabled",
-                    "printToPDF", "bringToFront", "setDownloadBehavior", "getAppManifest", "crash", "close", "setWebLifecycleState", "captureSnapshot", "getInstallabilityErrors"],
-                "include_events": ["colorPicked", "interstitialShown", "interstitialHidden", "javascriptDialogOpening", "javascriptDialogClosed", "downloadWillBegin", "screencastVisibilityChanged", "screencastFrame"],
+                    "printToPDF", "bringToFront", "setDownloadBehavior", "getAppManifest", "crash", "close", "setWebLifecycleState", "captureSnapshot", "getInstallabilityErrors", "setInterceptFileChooserDialog", "handleFileChooser"],
+                "include_events": ["colorPicked", "interstitialShown", "interstitialHidden", "javascriptDialogOpening", "javascriptDialogClosed", "downloadWillBegin", "screencastVisibilityChanged", "screencastFrame", "fileChooserOpened"],
                 "async": ["captureScreenshot", "printToPDF", "navigate", "getAppManifest", "reload", "captureSnapshot", "getInstallabilityErrors"]
             },
             {
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 1a385b5d..4a289e1 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -327,7 +327,9 @@
           : protocol::TargetHandler::AccessMode::kAutoAttachOnly,
       GetId(), GetRendererChannel(), session->GetRootSession()));
   session->AddHandler(std::make_unique<protocol::PageHandler>(
-      emulation_handler_ptr, session->client()->MayWriteLocalFiles()));
+      emulation_handler_ptr, &active_file_chooser_interceptor_,
+      session->client()->MayWriteLocalFiles(),
+      session->client()->MayReadLocalFiles()));
   session->AddHandler(std::make_unique<protocol::SecurityHandler>());
   if (!frame_tree_node_ || !frame_tree_node_->parent()) {
     session->AddHandler(std::make_unique<protocol::TracingHandler>(
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index 000892d..574e98f 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -144,6 +144,7 @@
   RenderFrameHostImpl* frame_host_ = nullptr;
   base::flat_set<NavigationHandleImpl*> navigation_handles_;
   bool render_frame_alive_ = false;
+  void* active_file_chooser_interceptor_ = nullptr;
 
   // The FrameTreeNode associated with this agent.
   FrameTreeNode* frame_tree_node_;
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 3b9d0cf..b6681857 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -567,10 +567,10 @@
     }
   }
 
-  if (delegate_ &&
-      delegate_->InterceptDownloadIfApplicable(
-          info.url(), user_agent, info.content_disposition, info.mime_type,
-          info.request_origin, info.total_bytes, web_contents)) {
+  if (delegate_ && delegate_->InterceptDownloadIfApplicable(
+                       info.url(), user_agent, info.content_disposition,
+                       info.mime_type, info.request_origin, info.total_bytes,
+                       info.transient, web_contents)) {
     if (info.request_handle)
       info.request_handle->CancelRequest(false);
     return true;
diff --git a/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc b/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc
index ac69f740..c8ea0b4 100644
--- a/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc
+++ b/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc
@@ -203,13 +203,13 @@
     // The filesystem must be opened on the IO sequence.
     base::RunLoop loop;
     io_task_runner_->PostTask(
-        FROM_HERE, BindLambdaForTesting([&]() {
-          file_system_context_->OpenFileSystem(
-              GURL("http://remote/"), storage::kFileSystemTypeTemporary,
-              storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
-              base::BindOnce(&FileSystemURLLoaderFactoryTest::OnOpenFileSystem,
-                             loop.QuitClosure()));
-        }));
+        FROM_HERE,
+        base::BindOnce(
+            &FileSystemContext::OpenFileSystem, file_system_context_,
+            GURL("http://remote/"), storage::kFileSystemTypeTemporary,
+            storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+            base::BindOnce(&FileSystemURLLoaderFactoryTest::OnOpenFileSystem,
+                           loop.QuitClosure())));
     loop.Run();
   }
 
diff --git a/content/browser/font_unique_name_lookup/font_unique_name_lookup.cc b/content/browser/font_unique_name_lookup/font_unique_name_lookup.cc
index 6bfbf36f..3dff08e9 100644
--- a/content/browser/font_unique_name_lookup/font_unique_name_lookup.cc
+++ b/content/browser/font_unique_name_lookup/font_unique_name_lookup.cc
@@ -16,6 +16,7 @@
 #include "base/task/post_task.h"
 #include "base/time/time.h"
 #include "third_party/blink/public/common/font_unique_name_lookup/font_table_matcher.h"
+#include "third_party/blink/public/common/font_unique_name_lookup/font_table_persistence.h"
 #include "third_party/blink/public/common/font_unique_name_lookup/font_unique_name_table.pb.h"
 #include "third_party/blink/public/common/font_unique_name_lookup/icu_fold_case_util.h"
 
@@ -67,11 +68,25 @@
                              duration);
 }
 
-bool SfntNameIsEnglish(const FT_SfntName& sfnt_name) {
+bool IsRelevantNameRecord(const FT_SfntName& sfnt_name) {
+  if (sfnt_name.name_id != TT_NAME_ID_FULL_NAME &&
+      sfnt_name.name_id != TT_NAME_ID_PS_NAME)
+    return false;
+
+  // From the CSS Fonts spec chapter 4.3. Font reference: the src descriptor
+  // "For OpenType fonts with multiple localizations of the full font name,
+  // the US English version is used (language ID = 0x409 for Windows and
+  // language ID = 0 for Macintosh) or the first localization when a US
+  // English full font name is not available (the OpenType specification
+  // recommends that all fonts minimally include US English names)."
+  // Since we can assume Android system fonts contain an English name,
+  // continue here.
   if (sfnt_name.platform_id == TT_PLATFORM_MICROSOFT)
     return sfnt_name.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES;
+
   if (sfnt_name.platform_id == TT_PLATFORM_MACINTOSH)
     return sfnt_name.language_id == TT_MAC_LANGID_ENGLISH;
+
   return false;
 }
 
@@ -154,15 +169,7 @@
       return;
     }
 
-    // From the CSS Fonts spec chapter 4.3. Font reference: the src descriptor
-    // "For OpenType fonts with multiple localizations of the full font name,
-    // the US English version is used (language ID = 0x409 for Windows and
-    // language ID = 0 for Macintosh) or the first localization when a US
-    // English full font name is not available (the OpenType specification
-    // recommends that all fonts minimally include US English names)."
-    // Since we can assume Android system fonts contain an English name,
-    // continue here.
-    if (!SfntNameIsEnglish(sfnt_name))
+    if (!IsRelevantNameRecord(sfnt_name))
       continue;
 
     std::string sfnt_name_string = "";
@@ -310,62 +317,17 @@
 }
 
 bool FontUniqueNameLookup::LoadFromFile() {
-  // Reset to empty to ensure IsValid() is false if reading fails.
-  proto_storage_ = base::MappedReadOnlyRegion();
-  base::File table_cache_file(
-      TableCacheFilePath(),
-      base::File::FLAG_OPEN | base::File::Flags::FLAG_READ);
-  if (!table_cache_file.IsValid()) {
-    LogUMALoadFromFileSuccess(false);
-    return false;
-  }
-  proto_storage_ =
-      base::ReadOnlySharedMemoryRegion::Create(table_cache_file.GetLength());
-  if (!proto_storage_.IsValid() || !proto_storage_.mapping.size()) {
-    LogUMALoadFromFileSuccess(false);
-    return false;
-  }
-  int read_result = table_cache_file.Read(
-      0, static_cast<char*>(proto_storage_.mapping.memory()),
-      table_cache_file.GetLength());
-  // If no bytes were read or Read() returned -1 we are not able to reconstruct
-  // a font table from the cached file.
-  if (read_result <= 0) {
-    proto_storage_ = base::MappedReadOnlyRegion();
-    LogUMALoadFromFileSuccess(false);
-    return false;
-  }
-
-  blink::FontUniqueNameTable font_table;
-  if (!font_table.ParseFromArray(proto_storage_.mapping.memory(),
-                                 proto_storage_.mapping.size())) {
-    proto_storage_ = base::MappedReadOnlyRegion();
-    LogUMALoadFromFileSuccess(false);
-    return false;
-  }
-  LogUMALoadFromFileSuccess(true);
-  return true;
+  bool load_success = blink::font_table_persistence::LoadFromFile(
+      TableCacheFilePath(), &proto_storage_);
+  LogUMALoadFromFileSuccess(load_success);
+  return load_success;
 }
 
 bool FontUniqueNameLookup::PersistToFile() {
-  DCHECK(proto_storage_.IsValid() && proto_storage_.mapping.size());
-  base::File table_cache_file(
-      TableCacheFilePath(),
-      base::File::FLAG_CREATE_ALWAYS | base::File::Flags::FLAG_WRITE);
-  if (!table_cache_file.IsValid()) {
-    LogUMAPersistSuccess(false);
-    return false;
-  }
-  if (table_cache_file.Write(
-          0, static_cast<char*>(proto_storage_.mapping.memory()),
-          proto_storage_.mapping.size()) == -1) {
-    table_cache_file.SetLength(0);
-    proto_storage_ = base::MappedReadOnlyRegion();
-    LogUMAPersistSuccess(false);
-    return false;
-  }
-  LogUMAPersistSuccess(true);
-  return true;
+  bool persist_success = blink::font_table_persistence::PersistToFile(
+      proto_storage_, TableCacheFilePath());
+  LogUMAPersistSuccess(persist_success);
+  return persist_success;
 }
 
 void FontUniqueNameLookup::ScheduleLoadOrUpdateTable() {
diff --git a/content/browser/font_unique_name_lookup/font_unique_name_lookup_unittest.cc b/content/browser/font_unique_name_lookup/font_unique_name_lookup_unittest.cc
index 3945b0d0..ce5150cf 100644
--- a/content/browser/font_unique_name_lookup/font_unique_name_lookup_unittest.cc
+++ b/content/browser/font_unique_name_lookup/font_unique_name_lookup_unittest.cc
@@ -57,16 +57,13 @@
   return std::vector<std::string>(start_copy, end_copy);
 }
 
-enum class TruncateLength { TruncateToZero, TruncateHalf };
-
-void TruncateFile(const base::FilePath& file_path,
-                  TruncateLength truncate_length) {
+void TruncateFileToLength(const base::FilePath& file_path,
+                          int64_t truncated_length) {
   base::File file_to_truncate(
       file_path, base::File::FLAG_OPEN | base::File::Flags::FLAG_WRITE);
-  size_t truncate_to = truncate_length == TruncateLength::TruncateHalf
-                           ? file_to_truncate.GetLength() / 2
-                           : 0;
-  file_to_truncate.SetLength(truncate_to);
+
+  ASSERT_TRUE(file_to_truncate.IsValid());
+  ASSERT_TRUE(file_to_truncate.SetLength(truncated_length));
 }
 
 }  // namespace
@@ -98,29 +95,38 @@
   ASSERT_GT(matcher_after_load.AvailableFonts(), 0u);
 }
 
-// http://crbug.com/928818
-#if defined(ADDRESS_SANITIZER)
-#define MAYBE_TestHandleFiledRead DISABLED_TestHandleFailedRead
-#else
-#define MAYBE_TestHandleFiledRead TestHandleFailedRead
-#endif
-TEST_F(FontUniqueNameLookupTest, MAYBE_TestHandleFiledRead) {
-  base::DeleteFile(font_unique_name_lookup_->TableCacheFilePathForTesting(),
-                   false);
+TEST_F(FontUniqueNameLookupTest, TestHandleFailedRead) {
+  ASSERT_FALSE(base::PathExists(
+      font_unique_name_lookup_->TableCacheFilePathForTesting()));
   ASSERT_FALSE(font_unique_name_lookup_->LoadFromFile());
   ASSERT_TRUE(font_unique_name_lookup_->UpdateTable());
   base::ReadOnlySharedMemoryMapping mapping =
       font_unique_name_lookup_->DuplicateMemoryRegion().Map();
   blink::FontTableMatcher matcher(mapping);
-  ASSERT_GT(matcher.AvailableFonts(), 0u);
+
+  // AOSP Android Kitkat has 81 fonts, the Kitkat bot seems to have 74,
+  // Marshmallow has 149, Oreo 247, let's expect at least 50.
+  ASSERT_GT(matcher.AvailableFonts(), 50u);
   ASSERT_TRUE(font_unique_name_lookup_->PersistToFile());
+  ASSERT_TRUE(base::PathExists(
+      font_unique_name_lookup_->TableCacheFilePathForTesting()));
+  int64_t file_size;
+  ASSERT_TRUE(base::GetFileSize(
+      font_unique_name_lookup_->TableCacheFilePathForTesting(), &file_size));
+  // For 81 fonts minimumm, very conservatively assume we have at least 1k of
+  // data, it's rather around 30k in practice.
+  ASSERT_GT(file_size, 1024);
   ASSERT_TRUE(font_unique_name_lookup_->LoadFromFile());
-  TruncateFile(font_unique_name_lookup_->TableCacheFilePathForTesting(),
-               TruncateLength::TruncateHalf);
-  ASSERT_FALSE(font_unique_name_lookup_->LoadFromFile());
-  TruncateFile(font_unique_name_lookup_->TableCacheFilePathForTesting(),
-               TruncateLength::TruncateToZero);
-  ASSERT_FALSE(font_unique_name_lookup_->LoadFromFile());
+
+  // For each truncated size, reading must fail, otherwise we successfully read
+  // a truncated protobuf.
+  for (int64_t truncated_size = file_size - 1; truncated_size >= 0;
+       truncated_size -= file_size) {
+    TruncateFileToLength(
+        font_unique_name_lookup_->TableCacheFilePathForTesting(),
+        truncated_size);
+    ASSERT_FALSE(font_unique_name_lookup_->LoadFromFile());
+  }
 }
 
 TEST_F(FontUniqueNameLookupTest, TestMatchPostScriptName) {
@@ -181,6 +187,19 @@
   ASSERT_EQ(match_result->ttc_index, 0u);
 }
 
+TEST_F(FontUniqueNameLookupTest, DontMatchOtherNames) {
+  ASSERT_TRUE(font_unique_name_lookup_->UpdateTable());
+  blink::FontTableMatcher matcher(
+      font_unique_name_lookup_->DuplicateMemoryRegion().Map());
+  // Name id 9 is the designer field, which we must not match against.
+  auto match_result = matcher.MatchName("Christian Robertson");
+  ASSERT_FALSE(match_result);
+  // Name id 13 contains the license, which we also must not match.
+  match_result =
+      matcher.MatchName("Licensed under the Apache License, Version 2.0");
+  ASSERT_FALSE(match_result);
+}
+
 namespace {
 size_t GetNumTables(base::File& font_file) {
   font_file.Seek(base::File::FROM_BEGIN, 5);
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index d1c1ce1..005a93e 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -9728,7 +9728,7 @@
 // the url by canceling a main frame navigation.
 // See https://crbug.com/966914.
 // Failing on Linux CFI. http://crbug.com/974319
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_MACOSX)
 #define MAYBE_CrossProcessIframeToInvalidURLCancelsRedirectSpoof \
   DISABLED_CrossProcessIframeToInvalidURLCancelsRedirectSpoof
 #else
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index bcc66e4..55cc07fc 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -883,6 +883,16 @@
 
   CreateNavigationHandle(false);
 
+  if (CheckAboutSrcDoc() == AboutSrcDocCheckResult::BLOCK_REQUEST) {
+    OnRequestFailedInternal(
+        network::URLLoaderCompletionStatus(net::ERR_INVALID_URL),
+        true /* skip_throttles */, base::nullopt /* error_page_content*/,
+        false /* collapse_frame */);
+    // DO NOT ADD CODE after this. The previous call to OnRequestFailedInternal
+    // has destroyed the NavigationRequest.
+    return;
+  }
+
   if (!NeedsUrlLoader()) {
     // There is no need to make a network request for this navigation, so commit
     // it immediately.
@@ -2327,6 +2337,31 @@
   return LegacyProtocolInSubresourceCheckResult::BLOCK_REQUEST;
 }
 
+NavigationRequest::AboutSrcDocCheckResult NavigationRequest::CheckAboutSrcDoc()
+    const {
+  if (!common_params_.url.IsAboutSrcdoc())
+    return AboutSrcDocCheckResult::ALLOW_REQUEST;
+
+  // Loading about:srcdoc in the main frame can't have any reasonable meaning.
+  // There might be a malicious website trying to exploit a bug from this. As a
+  // defensive measure, do not proceed. They would have failed anyway later.
+  if (frame_tree_node_->IsMainFrame())
+    return AboutSrcDocCheckResult::BLOCK_REQUEST;
+
+  // Navigations to about:srcdoc?foo or about:srcdoc#foo are never caused by
+  // using the iframe srcdoc attribute. Only about:srcdoc is expected.
+  if (common_params_.url != GURL(url::kAboutSrcdocURL))
+    return AboutSrcDocCheckResult::BLOCK_REQUEST;
+
+  // TODO(arthursonzogni): Disallow navigations to about:srcdoc initiated from a
+  // different frame or from a different window.
+
+  // TODO(arthursonzogni): Disallow browser initiated navigations to
+  // about:srcdoc, except session history navigations.
+
+  return AboutSrcDocCheckResult::ALLOW_REQUEST;
+}
+
 void NavigationRequest::UpdateCommitNavigationParamsHistory() {
   NavigationController* navigation_controller =
       frame_tree_node_->navigator()->GetController();
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index e222663..b2140084 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -565,6 +565,14 @@
   LegacyProtocolInSubresourceCheckResult CheckLegacyProtocolInSubresource()
       const;
 
+  // Block about:srcdoc navigation that aren't expected to happen. For instance,
+  // main frame navigations or about:srcdoc#foo.
+  enum class AboutSrcDocCheckResult {
+    ALLOW_REQUEST,
+    BLOCK_REQUEST,
+  };
+  AboutSrcDocCheckResult CheckAboutSrcDoc() const;
+
   // Called before a commit. Updates the history index and length held in
   // CommitNavigationParams. This is used to update this shared state with the
   // renderer process.
diff --git a/content/browser/frame_host/navigation_request_info.cc b/content/browser/frame_host/navigation_request_info.cc
index b0ab6cd..e1ebf72 100644
--- a/content/browser/frame_host/navigation_request_info.cc
+++ b/content/browser/frame_host/navigation_request_info.cc
@@ -40,21 +40,6 @@
       devtools_navigation_token(devtools_navigation_token),
       devtools_frame_token(devtools_frame_token) {}
 
-NavigationRequestInfo::NavigationRequestInfo(const NavigationRequestInfo& other)
-    : common_params(other.common_params),
-      begin_params(other.begin_params.Clone()),
-      site_for_cookies(other.site_for_cookies),
-      top_frame_origin(other.top_frame_origin),
-      is_main_frame(other.is_main_frame),
-      parent_is_main_frame(other.parent_is_main_frame),
-      are_ancestors_secure(other.are_ancestors_secure),
-      frame_tree_node_id(other.frame_tree_node_id),
-      is_for_guests_only(other.is_for_guests_only),
-      report_raw_headers(other.report_raw_headers),
-      is_prerendering(other.is_prerendering),
-      upgrade_if_insecure(other.upgrade_if_insecure),
-      devtools_frame_token(other.devtools_frame_token) {}
-
 NavigationRequestInfo::~NavigationRequestInfo() {}
 
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_request_info.h b/content/browser/frame_host/navigation_request_info.h
index e0d5915..42fef93 100644
--- a/content/browser/frame_host/navigation_request_info.h
+++ b/content/browser/frame_host/navigation_request_info.h
@@ -39,7 +39,7 @@
                             blob_url_loader_factory,
                         const base::UnguessableToken& devtools_navigation_token,
                         const base::UnguessableToken& devtools_frame_token);
-  NavigationRequestInfo(const NavigationRequestInfo& other);
+  NavigationRequestInfo(const NavigationRequestInfo& other) = delete;
   ~NavigationRequestInfo();
 
   const CommonNavigationParams common_params;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 3f37130f..b4cc2ad 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -4778,6 +4778,18 @@
     }
   }
 
+  // Main frame navigation to about:srcdoc and navigation to about:srcdoc?foo or
+  // about:srcdoc#foo are expected to be blocked.
+  //
+  // TODO(arthursonzogni): Replace DumpWithoutCrashing by a CHECK on M79 if it
+  // is never reached.
+  if (common_params.url.IsAboutSrcdoc()) {
+    if (frame_tree_node_->IsMainFrame() ||
+        common_params.url != GURL(url::kAboutSrcdocURL)) {
+      base::debug::DumpWithoutCrashing();
+    }
+  }
+
   // If this is an attempt to commit a URL in an incompatible process, capture a
   // crash dump to diagnose why it is occurring.
   // TODO(creis): Remove this check after we've gathered enough information to
@@ -5358,7 +5370,7 @@
   web_ui_.reset();
 }
 
-const content::mojom::ImageDownloaderPtr&
+const blink::mojom::ImageDownloaderPtr&
 RenderFrameHostImpl::GetMojoImageDownloader() {
   if (!mojo_image_downloader_.get() && GetRemoteInterfaces())
     GetRemoteInterfaces()->GetInterface(&mojo_image_downloader_);
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 8c861f9..66b5840d 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -45,7 +45,6 @@
 #include "content/common/frame_delete_intention.h"
 #include "content/common/frame_message_enums.h"
 #include "content/common/frame_replication_state.h"
-#include "content/common/image_downloader/image_downloader.mojom.h"
 #include "content/common/input/input_handler.mojom.h"
 #include "content/common/navigation_params.mojom.h"
 #include "content/public/browser/browser_thread.h"
@@ -78,6 +77,7 @@
 #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h"
 #include "third_party/blink/public/mojom/frame/navigation_initiator.mojom.h"
 #include "third_party/blink/public/mojom/idle/idle_manager.mojom.h"
+#include "third_party/blink/public/mojom/image_downloader/image_downloader.mojom.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 #include "third_party/blink/public/mojom/sms/sms_receiver.mojom-forward.h"
@@ -726,7 +726,7 @@
   void ClearAllWebUI();
 
   // Returns the Mojo ImageDownloader service.
-  const content::mojom::ImageDownloaderPtr& GetMojoImageDownloader();
+  const blink::mojom::ImageDownloaderPtr& GetMojoImageDownloader();
 
   // Returns pointer to renderer side FindInPage associated with this frame.
   const blink::mojom::FindInPageAssociatedPtr& GetFindInPage();
@@ -1866,7 +1866,7 @@
   std::unique_ptr<PermissionServiceContext> permission_service_context_;
 
   // Holder of Mojo connection with ImageDownloader service in RenderFrame.
-  content::mojom::ImageDownloaderPtr mojo_image_downloader_;
+  blink::mojom::ImageDownloaderPtr mojo_image_downloader_;
 
   // Holder of Mojo connection with FindInPage service in Blink.
   blink::mojom::FindInPageAssociatedPtr find_in_page_;
diff --git a/content/browser/gpu/gpu_data_manager_impl.cc b/content/browser/gpu/gpu_data_manager_impl.cc
index da6ef878..4db3318 100644
--- a/content/browser/gpu/gpu_data_manager_impl.cc
+++ b/content/browser/gpu/gpu_data_manager_impl.cc
@@ -130,6 +130,12 @@
                                  gpu_feature_info_for_hardware_gpu);
 }
 
+void GpuDataManagerImpl::UpdateGpuExtraInfo(
+    const gpu::GpuExtraInfo& gpu_extra_info) {
+  base::AutoLock auto_lock(lock_);
+  private_->UpdateGpuExtraInfo(gpu_extra_info);
+}
+
 gpu::GpuFeatureInfo GpuDataManagerImpl::GetGpuFeatureInfo() const {
   base::AutoLock auto_lock(lock_);
   return private_->GetGpuFeatureInfo();
@@ -146,6 +152,11 @@
   return private_->GetGpuFeatureInfoForHardwareGpu();
 }
 
+gpu::GpuExtraInfo GpuDataManagerImpl::GetGpuExtraInfo() const {
+  base::AutoLock auto_lock(lock_);
+  return private_->GetGpuExtraInfo();
+}
+
 void GpuDataManagerImpl::UpdateGpuPreferences(
     gpu::GpuPreferences* gpu_preferences,
     GpuProcessKind kind) const {
diff --git a/content/browser/gpu/gpu_data_manager_impl.h b/content/browser/gpu/gpu_data_manager_impl.h
index dfbc831..64afd8d 100644
--- a/content/browser/gpu/gpu_data_manager_impl.h
+++ b/content/browser/gpu/gpu_data_manager_impl.h
@@ -25,6 +25,7 @@
 #include "content/public/common/three_d_api_types.h"
 #include "gpu/config/gpu_control_list.h"
 #include "gpu/config/gpu_domain_guilt.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/config/gpu_mode.h"
@@ -83,12 +84,15 @@
   void UpdateGpuFeatureInfo(const gpu::GpuFeatureInfo& gpu_feature_info,
                             const base::Optional<gpu::GpuFeatureInfo>&
                                 gpu_feature_info_for_hardware_gpu);
+  void UpdateGpuExtraInfo(const gpu::GpuExtraInfo& gpu_extra_info);
 
   gpu::GpuFeatureInfo GetGpuFeatureInfo() const;
 
   gpu::GPUInfo GetGPUInfoForHardwareGpu() const;
   gpu::GpuFeatureInfo GetGpuFeatureInfoForHardwareGpu() const;
 
+  gpu::GpuExtraInfo GetGpuExtraInfo() const;
+
   // Update GpuPreferences based on blacklisting decisions.
   void UpdateGpuPreferences(gpu::GpuPreferences* gpu_preferences,
                             GpuProcessKind kind) const;
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index 77f86114..575f189 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -550,6 +550,11 @@
   }
 }
 
+void GpuDataManagerImplPrivate::UpdateGpuExtraInfo(
+    const gpu::GpuExtraInfo& gpu_extra_info) {
+  gpu_extra_info_ = gpu_extra_info;
+}
+
 gpu::GpuFeatureInfo GpuDataManagerImplPrivate::GetGpuFeatureInfo() const {
   return gpu_feature_info_;
 }
@@ -559,6 +564,10 @@
   return gpu_feature_info_for_hardware_gpu_;
 }
 
+gpu::GpuExtraInfo GpuDataManagerImplPrivate::GetGpuExtraInfo() const {
+  return gpu_extra_info_;
+}
+
 void GpuDataManagerImplPrivate::AppendGpuCommandLine(
     base::CommandLine* command_line,
     GpuProcessKind kind) const {
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.h b/content/browser/gpu/gpu_data_manager_impl_private.h
index b19b5a2..9098349 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.h
+++ b/content/browser/gpu/gpu_data_manager_impl_private.h
@@ -69,8 +69,11 @@
   void UpdateGpuFeatureInfo(const gpu::GpuFeatureInfo& gpu_feature_info,
                             const base::Optional<gpu::GpuFeatureInfo>&
                                 gpu_feature_info_for_hardware_gpu);
+  void UpdateGpuExtraInfo(const gpu::GpuExtraInfo& process_info);
+
   gpu::GpuFeatureInfo GetGpuFeatureInfo() const;
   gpu::GpuFeatureInfo GetGpuFeatureInfoForHardwareGpu() const;
+  gpu::GpuExtraInfo GetGpuExtraInfo() const;
 
   void AppendGpuCommandLine(base::CommandLine* command_line,
                             GpuProcessKind kind) const;
@@ -189,6 +192,8 @@
   gpu::GpuFeatureInfo gpu_feature_info_for_hardware_gpu_;
   gpu::GPUInfo gpu_info_for_hardware_gpu_;
 
+  gpu::GpuExtraInfo gpu_extra_info_;
+
   const scoped_refptr<GpuDataManagerObserverList> observer_list_;
 
   // Contains the 1000 most recent log messages.
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index eb2ccb2..57d8ebfa 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -37,6 +37,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "gpu/config/gpu_feature_type.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/config/gpu_lists_version.h"
@@ -503,9 +504,10 @@
 }
 
 std::unique_ptr<base::ListValue> GetANGLEFeatures() {
-  gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
+  gpu::GpuExtraInfo gpu_extra_info =
+      GpuDataManagerImpl::GetInstance()->GetGpuExtraInfo();
   auto angle_features_list = std::make_unique<base::ListValue>();
-  for (const auto& feature : gpu_info.angle_features) {
+  for (const auto& feature : gpu_extra_info.angle_features) {
     auto angle_feature = std::make_unique<base::DictionaryValue>();
     angle_feature->SetString("name", feature.name);
     angle_feature->SetString("category", feature.category);
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index b1b5971a..4165ba00 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -973,7 +973,8 @@
     const gpu::GpuFeatureInfo& gpu_feature_info,
     const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
     const base::Optional<gpu::GpuFeatureInfo>&
-        gpu_feature_info_for_hardware_gpu) {
+        gpu_feature_info_for_hardware_gpu,
+    const gpu::GpuExtraInfo& gpu_extra_info) {
   if (GetGpuCrashCount() > 0) {
     LOG(WARNING) << "Reinitialized the GPU process after a crash. The reported "
                     "initialization time was "
@@ -986,6 +987,7 @@
     gpu_data_manager->UpdateGpuFeatureInfo(gpu_feature_info,
                                            gpu_feature_info_for_hardware_gpu);
     gpu_data_manager->UpdateGpuInfo(gpu_info, gpu_info_for_hardware_gpu);
+    gpu_data_manager->UpdateGpuExtraInfo(gpu_extra_info);
   }
 
 #if defined(OS_ANDROID)
diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h
index 327f186..ba6822bc 100644
--- a/content/browser/gpu/gpu_process_host.h
+++ b/content/browser/gpu/gpu_process_host.h
@@ -26,6 +26,7 @@
 #include "content/public/browser/gpu_data_manager.h"
 #include "gpu/command_buffer/common/activity_flags.h"
 #include "gpu/command_buffer/common/constants.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/config/gpu_mode.h"
@@ -138,7 +139,8 @@
       const gpu::GpuFeatureInfo& gpu_feature_info,
       const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
       const base::Optional<gpu::GpuFeatureInfo>&
-          gpu_feature_info_for_hardware_gpu) override;
+          gpu_feature_info_for_hardware_gpu,
+      const gpu::GpuExtraInfo& gpu_extra_info) override;
   void DidFailInitialize() override;
   void DidCreateContextSuccessfully() override;
   void BlockDomainFrom3DAPIs(const GURL& url, gpu::DomainGuilt guilt) override;
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
index 337a095..4bfc6fe 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
@@ -76,6 +76,10 @@
   closure.Run();
 }
 
+ACTION_P(QuitLoop, run_loop) {
+  run_loop->Quit();
+}
+
 MATCHER_P(IsAssociatedInterfacePtrInfoValid,
           tf,
           std::string(negation ? "isn't" : "is") + " " +
@@ -252,7 +256,7 @@
                                         std::string(""), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                      testing::SaveArg<4>(&metadata),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         connection->Open(idb_mojo_factory_.get());
       }));
@@ -295,7 +299,7 @@
                                         std::string(""), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                      testing::SaveArg<4>(&metadata),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection->Open(idb_mojo_factory_.get());
@@ -307,19 +311,19 @@
   EXPECT_EQ(connection->db_name, metadata.name);
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(2, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(2, loop2.QuitClosure());
         EXPECT_CALL(*connection->connection_callbacks, Complete(kTransactionId))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(
             *connection->open_callbacks,
             MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(false), _))
             .Times(1)
-            .WillOnce(RunClosure(std::move(quit_closure)));
+            .WillOnce(RunClosure(std::move(quit_closure2)));
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
@@ -364,7 +368,7 @@
                                         std::string(""), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info1),
                                      testing::SaveArg<4>(&metadata1),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection1->Open(idb_mojo_factory_.get());
@@ -376,6 +380,8 @@
   IndexedDBDatabaseMetadata metadata2;
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(3, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         connection2 = std::make_unique<TestDatabaseConnection>(
@@ -385,23 +391,21 @@
         // Check that we're called in order and the second connection gets it's
         // database after the first connection completes.
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(3, loop2.QuitClosure());
         EXPECT_CALL(*connection1->connection_callbacks,
                     Complete(kTransactionId))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(
             *connection1->open_callbacks,
             MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(false), _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(
             *connection2->open_callbacks,
             MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(true), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info2),
                                      testing::SaveArg<1>(&metadata2),
-                                     RunClosure(std::move(quit_closure))));
+                                     RunClosure(std::move(quit_closure2))));
 
         connection1->database.Bind(std::move(database_info1));
         ASSERT_TRUE(connection1->database.is_bound());
@@ -456,7 +460,7 @@
                                         std::string(""), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                      testing::SaveArg<4>(&metadata),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection->Open(idb_mojo_factory_.get());
@@ -470,11 +474,11 @@
   std::unique_ptr<StrictMock<MockMojoIndexedDBCallbacks>> put_callbacks;
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(3, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(3, loop2.QuitClosure());
 
         put_callbacks =
             std::make_unique<StrictMock<MockMojoIndexedDBCallbacks>>();
@@ -482,18 +486,18 @@
         EXPECT_CALL(*put_callbacks,
                     Error(blink::kWebIDBDatabaseExceptionUnknownError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
 
         EXPECT_CALL(*connection->connection_callbacks,
                     Abort(kTransactionId,
                           blink::kWebIDBDatabaseExceptionUnknownError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
 
         EXPECT_CALL(*connection->open_callbacks,
                     Error(blink::kWebIDBDatabaseExceptionAbortError, _))
             .Times(1)
-            .WillOnce(RunClosure(std::move(quit_closure)));
+            .WillOnce(RunClosure(std::move(quit_closure2)));
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
@@ -560,7 +564,7 @@
                                         std::string(), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                      testing::SaveArg<4>(&metadata),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection->Open(idb_mojo_factory_.get());
@@ -574,20 +578,20 @@
   blink::mojom::IDBStatus callback_result = blink::mojom::IDBStatus::IOError;
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(3, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(3, loop2.QuitClosure());
 
         EXPECT_CALL(*connection->connection_callbacks, Complete(kTransactionId))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(
             *connection->open_callbacks,
             MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(false), _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
@@ -595,7 +599,7 @@
 
         connection->version_change_transaction->Commit(0);
         idb_mojo_factory_->AbortTransactionsAndCompactDatabase(base::BindOnce(
-            &StatusCallback, std::move(quit_closure), &callback_result));
+            &StatusCallback, std::move(quit_closure2), &callback_result));
       }));
   loop2.Run();
   EXPECT_EQ(blink::mojom::IDBStatus::OK, callback_result);
@@ -632,7 +636,7 @@
                                         std::string(), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                      testing::SaveArg<4>(&metadata),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection->Open(idb_mojo_factory_.get());
@@ -646,24 +650,24 @@
   blink::mojom::IDBStatus callback_result = blink::mojom::IDBStatus::IOError;
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(4, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(4, loop2.QuitClosure());
 
         EXPECT_CALL(*connection->connection_callbacks,
                     Abort(kTransactionId,
                           blink::kWebIDBDatabaseExceptionUnknownError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->open_callbacks,
                     Error(blink::kWebIDBDatabaseExceptionAbortError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
@@ -672,7 +676,7 @@
             kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
             blink::IndexedDBKeyPath(), false);
         idb_mojo_factory_->AbortTransactionsAndCompactDatabase(base::BindOnce(
-            &StatusCallback, std::move(quit_closure), &callback_result));
+            &StatusCallback, std::move(quit_closure2), &callback_result));
       }));
   loop2.Run();
 
@@ -708,7 +712,7 @@
                                         std::string(), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                      testing::SaveArg<4>(&metadata),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection->Open(idb_mojo_factory_.get());
@@ -722,30 +726,30 @@
   blink::mojom::IDBStatus callback_result = blink::mojom::IDBStatus::IOError;
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(4, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(4, loop2.QuitClosure());
 
         EXPECT_CALL(*connection->connection_callbacks,
                     Abort(kTransactionId,
                           blink::kWebIDBDatabaseExceptionUnknownError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->open_callbacks,
                     Error(blink::kWebIDBDatabaseExceptionAbortError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
         ASSERT_TRUE(connection->version_change_transaction.is_bound());
         idb_mojo_factory_->AbortTransactionsAndCompactDatabase(base::BindOnce(
-            &StatusCallback, std::move(quit_closure), &callback_result));
+            &StatusCallback, std::move(quit_closure2), &callback_result));
       }));
   loop2.Run();
 
@@ -783,7 +787,7 @@
                           blink::mojom::IDBDataLoss::None, std::string(), _))
               .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                        testing::SaveArg<4>(&metadata),
-                                       RunClosure(loop.QuitClosure())));
+                                       QuitLoop(&loop)));
 
           // Queue open request message.
           connection->Open(idb_mojo_factory_.get());
@@ -798,29 +802,29 @@
   blink::mojom::IDBStatus callback_result = blink::mojom::IDBStatus::IOError;
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(4, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(4, loop2.QuitClosure());
         EXPECT_CALL(*connection->connection_callbacks, Complete(kTransactionId))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(
             *connection->open_callbacks,
             MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(false), _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
         ASSERT_TRUE(connection->version_change_transaction.is_bound());
         connection->version_change_transaction->Commit(0);
         idb_mojo_factory_->AbortTransactionsForDatabase(base::BindOnce(
-            &StatusCallback, std::move(quit_closure), &callback_result));
+            &StatusCallback, std::move(quit_closure2), &callback_result));
       }));
   loop2.Run();
 
@@ -859,7 +863,7 @@
                                         std::string(), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                      testing::SaveArg<4>(&metadata),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection->Open(idb_mojo_factory_.get());
@@ -873,24 +877,24 @@
   blink::mojom::IDBStatus callback_result = blink::mojom::IDBStatus::IOError;
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(4, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(4, loop2.QuitClosure());
 
         EXPECT_CALL(*connection->connection_callbacks,
                     Abort(kTransactionId,
                           blink::kWebIDBDatabaseExceptionUnknownError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->open_callbacks,
                     Error(blink::kWebIDBDatabaseExceptionAbortError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
@@ -899,7 +903,7 @@
             kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
             blink::IndexedDBKeyPath(), false);
         idb_mojo_factory_->AbortTransactionsForDatabase(base::BindOnce(
-            &StatusCallback, std::move(quit_closure), &callback_result));
+            &StatusCallback, std::move(quit_closure2), &callback_result));
       }));
   loop2.Run();
 
@@ -936,7 +940,7 @@
                                         std::string(), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info),
                                      testing::SaveArg<4>(&metadata),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection->Open(idb_mojo_factory_.get());
@@ -950,30 +954,30 @@
   blink::mojom::IDBStatus callback_result = blink::mojom::IDBStatus::IOError;
 
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(4, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(4, loop2.QuitClosure());
 
         EXPECT_CALL(*connection->connection_callbacks,
                     Abort(kTransactionId,
                           blink::kWebIDBDatabaseExceptionUnknownError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->open_callbacks,
                     Error(blink::kWebIDBDatabaseExceptionAbortError, _))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection->connection_callbacks, ForcedClose())
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
         ASSERT_TRUE(connection->version_change_transaction.is_bound());
         idb_mojo_factory_->AbortTransactionsForDatabase(base::BindOnce(
-            &StatusCallback, std::move(quit_closure), &callback_result));
+            &StatusCallback, std::move(quit_closure2), &callback_result));
       }));
   loop2.Run();
 
@@ -1020,7 +1024,7 @@
                             blink::mojom::IDBDataLoss::None, std::string(), _))
         .WillOnce(testing::DoAll(MoveArg<0>(&database_info1),
                                  testing::SaveArg<4>(&metadata1),
-                                 RunClosure(loop.QuitClosure())));
+                                 QuitLoop(&loop)));
 
     // Queue open request message.
     connection1.Open(idb_mojo_factory_.get());
@@ -1076,7 +1080,7 @@
                     blink::mojom::IDBDataLoss::None, std::string(), _))
         .WillOnce(testing::DoAll(MoveArg<0>(&database_info2),
                                  testing::SaveArg<4>(&metadata2),
-                                 RunClosure(loop.QuitClosure())));
+                                 QuitLoop(&loop)));
 
     // Queue open request message.
     connection2.Open(idb_mojo_factory_.get());
@@ -1128,7 +1132,7 @@
                     blink::mojom::IDBDataLoss::None, std::string(), _))
         .WillOnce(testing::DoAll(MoveArg<0>(&database_info3),
                                  testing::SaveArg<4>(&metadata3),
-                                 RunClosure(loop.QuitClosure())));
+                                 QuitLoop(&loop)));
 
     // Queue open request message.
     connection3.Open(idb_mojo_factory_.get());
@@ -1199,7 +1203,7 @@
                                         std::string(), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info1),
                                      testing::SaveArg<4>(&metadata1),
-                                     RunClosure(loop.QuitClosure())));
+                                     QuitLoop(&loop)));
 
         // Queue open request message.
         connection1->Open(idb_mojo_factory_.get());
@@ -1214,27 +1218,27 @@
 
   // Add object store entry.
   base::RunLoop loop2;
+  base::RepeatingClosure quit_closure2 =
+      base::BarrierClosure(3, loop2.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(3, loop2.QuitClosure());
 
         put_callbacks =
             std::make_unique<StrictMock<MockMojoIndexedDBCallbacks>>();
 
         EXPECT_CALL(*put_callbacks, SuccessKey(_))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(*connection1->connection_callbacks,
                     Complete(kTransactionId1))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure2));
         EXPECT_CALL(
             *connection1->open_callbacks,
             MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(false), _))
             .Times(1)
-            .WillOnce(RunClosure(std::move(quit_closure)));
+            .WillOnce(RunClosure(std::move(quit_closure2)));
 
         connection1->database.Bind(std::move(database_info1));
         ASSERT_TRUE(connection1->database.is_bound());
@@ -1296,7 +1300,7 @@
                         blink::mojom::IDBDataLoss::None, std::string(), _))
             .WillOnce(testing::DoAll(MoveArg<0>(&database_info2),
                                      testing::SaveArg<4>(&metadata2),
-                                     RunClosure(loop4.QuitClosure())));
+                                     QuitLoop(&loop4)));
 
         // Queue open request message.
         connection2->Open(idb_mojo_factory_.get());
@@ -1311,27 +1315,27 @@
 
   // Clear object store.
   base::RunLoop loop5;
+  base::RepeatingClosure quit_closure5 =
+      base::BarrierClosure(3, loop5.QuitClosure());
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         ::testing::InSequence dummy;
-        base::RepeatingClosure quit_closure =
-            base::BarrierClosure(3, loop5.QuitClosure());
 
         clear_callbacks =
             std::make_unique<StrictMock<MockMojoIndexedDBCallbacks>>();
 
         EXPECT_CALL(*clear_callbacks, Success())
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure5));
         EXPECT_CALL(*connection2->connection_callbacks,
                     Complete(kTransactionId2))
             .Times(1)
-            .WillOnce(RunClosure(quit_closure));
+            .WillOnce(RunClosure(quit_closure5));
         EXPECT_CALL(
             *connection2->open_callbacks,
             MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(false), _))
             .Times(1)
-            .WillOnce(RunClosure(std::move(quit_closure)));
+            .WillOnce(RunClosure(std::move(quit_closure5)));
 
         connection2->database.Bind(std::move(database_info2));
         ASSERT_TRUE(connection2->database.is_bound());
diff --git a/content/browser/indexed_db/scopes/README.md b/content/browser/indexed_db/scopes/README.md
index 03c0d369..e18b6a1 100644
--- a/content/browser/indexed_db/scopes/README.md
+++ b/content/browser/indexed_db/scopes/README.md
@@ -70,7 +70,7 @@
 
 * Allow the 'user' of the scopes system to choose the key prefix (`prefix`).
 * Scope # is a varint
-* Sequence # is a Fixed64
+* Sequence # is a big-endian Fixed64 (to support bytewise sorting)
 
 See [`leveldb_scopes_coding.h`](leveldb_scopes_coding.h) for the key encoding implementation.
 
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_coding.cc b/content/browser/indexed_db/scopes/leveldb_scopes_coding.cc
index 4cf36d1..b3b7032 100644
--- a/content/browser/indexed_db/scopes/leveldb_scopes_coding.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_coding.cc
@@ -6,9 +6,21 @@
 
 #include <utility>
 
+#include "base/big_endian.h"
 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
 
 namespace content {
+namespace {
+
+void EncodeBigEndianFixed64(uint64_t number, std::string* output) {
+  DCHECK(output);
+  size_t start_index = output->size();
+  output->resize(output->size() + sizeof(uint64_t));
+  base::WriteBigEndian(&(*output)[start_index], number);
+}
+
+}  // namespace
+
 namespace leveldb_scopes {
 
 std::tuple<bool, int64_t> ParseScopeMetadataId(
@@ -114,7 +126,7 @@
     int64_t scope_number,
     int64_t undo_sequence_number) {
   UndoTaskKeyPrefix(scopes_prefix, scope_number);
-  EncodeInt(undo_sequence_number, &key_buffer_);
+  EncodeBigEndianFixed64(undo_sequence_number, &key_buffer_);
   return leveldb::Slice(key_buffer_);
 }
 
@@ -123,20 +135,8 @@
     int64_t scope_number,
     int64_t cleanup_sequence_number) {
   CleanupTaskKeyPrefix(scopes_prefix, scope_number);
-  EncodeInt(cleanup_sequence_number, &key_buffer_);
+  EncodeBigEndianFixed64(cleanup_sequence_number, &key_buffer_);
   return leveldb::Slice(key_buffer_);
 }
 
-std::tuple<bool, int64_t> ParseScopeMetadata(leveldb::Slice key,
-                                             int prefix_bytes_to_skip) {
-  int64_t scope_id = 0;
-  DCHECK_GE(prefix_bytes_to_skip, 0);
-  if (key.size() < static_cast<size_t>(prefix_bytes_to_skip + 1))
-    return std::make_tuple(false, 0);
-  base::StringPiece part(key.data() + prefix_bytes_to_skip,
-                         key.size() - prefix_bytes_to_skip);
-  bool success = DecodeVarInt(&part, &scope_id);
-  return std::make_tuple(success, scope_id);
-}
-
 }  // namespace content
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_coding.h b/content/browser/indexed_db/scopes/leveldb_scopes_coding.h
index 8c10aad..73fd3ec 100644
--- a/content/browser/indexed_db/scopes/leveldb_scopes_coding.h
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_coding.h
@@ -107,9 +107,6 @@
   std::string key_buffer_;
 };
 
-std::tuple<bool, int64_t> ParseScopeMetadata(leveldb::Slice key,
-                                             int prefix_bytes_to_skip);
-
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_INDEXED_DB_SCOPES_LEVELDB_SCOPES_CODING_H_
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_coding_unittest.cc b/content/browser/indexed_db/scopes/leveldb_scopes_coding_unittest.cc
index 060275c..4bbda13 100644
--- a/content/browser/indexed_db/scopes/leveldb_scopes_coding_unittest.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_coding_unittest.cc
@@ -4,8 +4,21 @@
 
 #include "content/browser/indexed_db/scopes/leveldb_scopes_coding.h"
 
+#include <ostream>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace leveldb {
+std::ostream& operator<<(std::ostream& os, const leveldb::Slice& slice) {
+  os << "0x";
+  for (size_t i = 0; i < slice.size(); ++i) {
+    const char c = slice[i];
+    os << std::hex << std::setfill('0') << std::setw(2) << (int)c;
+  }
+  return os;
+}
+}  // namespace leveldb
+
 namespace content {
 namespace {
 
@@ -83,8 +96,12 @@
   ScopesEncoder encoder;
 
   std::string expected = "AB\x02\x81\x08";
+  // Add the 'undo' type.
   expected.push_back(0x00);
-  expected += "\x01\x04";
+  // Encode the big endian fixed length nunber.
+  for (size_t i = 0; i < 6; ++i)
+    expected.push_back(0x00);
+  expected += "\x04\x01";
   EXPECT_EQ(leveldb::Slice(expected),
             encoder.UndoTaskKey(scopes_prefix, kScopeNumber, kSequenceNumber));
 }
@@ -95,7 +112,11 @@
   std::vector<uint8_t> scopes_prefix = {'A', 'B'};
   ScopesEncoder encoder;
 
-  std::string expected = "AB\x02\x81\x08\x01\x01\x04";
+  std::string expected = "AB\x02\x81\x08\x01";
+  // Encode the big endian fixed length nunber.
+  for (size_t i = 0; i < 6; ++i)
+    expected.push_back(0x00);
+  expected += "\x04\x01";
   EXPECT_EQ(
       leveldb::Slice(expected),
       encoder.CleanupTaskKey(scopes_prefix, kScopeNumber, kSequenceNumber));
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index 7403e84c..86026502 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -192,7 +192,7 @@
   }
 
   ~RequestInterceptor() {
-    WaitForCleanUpOnIOThread(
+    WaitForCleanUpOnInterceptorThread(
         network::ResourceResponseHead(), "",
         network::URLLoaderCompletionStatus(net::ERR_NOT_IMPLEMENTED));
   }
@@ -211,8 +211,8 @@
     }
 
     // Wait until IO cleanup completes.
-    WaitForCleanUpOnIOThread(test_client_.response_head(), body_,
-                             test_client_.completion_status());
+    WaitForCleanUpOnInterceptorThread(test_client_.response_head(), body_,
+                                      test_client_.completion_status());
 
     // Mark the request as completed (for DCHECK purposes).
     request_completed_ = true;
@@ -307,7 +307,6 @@
   }
 
   bool InterceptorCallback(URLLoaderInterceptor::RequestParams* params) {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     DCHECK(params);
 
     if (url_to_intercept_ != params->url_request.url)
@@ -317,6 +316,7 @@
     if (request_intercepted_)
       return false;
     request_intercepted_ = true;
+    interceptor_task_runner_ = base::ThreadTaskRunnerHandle::Get();
 
     // Modify |params| if requested.
     if (request_initiator_to_inject_.has_value())
@@ -336,30 +336,32 @@
     return false;
   }
 
-  void WaitForCleanUpOnIOThread(network::ResourceResponseHead response_head,
-                                std::string response_body,
-                                network::URLLoaderCompletionStatus status) {
+  void WaitForCleanUpOnInterceptorThread(
+      network::ResourceResponseHead response_head,
+      std::string response_body,
+      network::URLLoaderCompletionStatus status) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-    if (io_cleanup_done_)
+    if (cleanup_done_)
       return;
 
-    base::RunLoop run_loop;
-    base::PostTaskWithTraitsAndReply(
-        FROM_HERE, {BrowserThread::IO},
-        base::BindOnce(&RequestInterceptor::CleanUpOnIOThread,
-                       base::Unretained(this), response_head, response_body,
-                       status),
-        run_loop.QuitClosure());
-    run_loop.Run();
+    if (interceptor_task_runner_) {
+      base::RunLoop run_loop;
+      interceptor_task_runner_->PostTaskAndReply(
+          FROM_HERE,
+          base::BindOnce(&RequestInterceptor::CleanUpOnInterceptorThread,
+                         base::Unretained(this), response_head, response_body,
+                         status),
+          run_loop.QuitClosure());
+      run_loop.Run();
+    }
 
-    io_cleanup_done_ = true;
+    cleanup_done_ = true;
   }
 
-  void CleanUpOnIOThread(network::ResourceResponseHead response_head,
-                         std::string response_body,
-                         network::URLLoaderCompletionStatus status) {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  void CleanUpOnInterceptorThread(network::ResourceResponseHead response_head,
+                                  std::string response_body,
+                                  network::URLLoaderCompletionStatus status) {
     if (!request_intercepted_)
       return;
 
@@ -399,11 +401,12 @@
   network::TestURLLoaderClient test_client_;
   std::string body_;
   bool request_completed_ = false;
-  bool io_cleanup_done_ = false;
+  bool cleanup_done_ = false;
 
-  // IO thread state:
+  // Interceptor thread state:
   network::mojom::URLLoaderClientPtr original_client_;
   bool request_intercepted_ = false;
+  scoped_refptr<base::SingleThreadTaskRunner> interceptor_task_runner_;
   network::mojom::URLLoaderClientPtr test_client_ptr_;
   std::unique_ptr<mojo::Binding<network::mojom::URLLoaderClient>>
       test_client_binding_;
diff --git a/content/browser/loader/cross_site_document_resource_handler.cc b/content/browser/loader/cross_site_document_resource_handler.cc
index c432d2d..b9bd89a 100644
--- a/content/browser/loader/cross_site_document_resource_handler.cc
+++ b/content/browser/loader/cross_site_document_resource_handler.cc
@@ -384,13 +384,11 @@
 
   // At this point the block-vs-allow decision was made, but might be still
   // suppressed because of |is_initiator_scheme_excluded_|.  We perform the
-  // suppression at such a late point, because we want to ensure we only call
-  // LogInitiatorSchemeBypassingDocumentBlocking for cases that actuall matter
-  // in practice.
-  if (confirmed_blockable && is_initiator_scheme_excluded_) {
-    initiator_scheme_prevented_blocking_ = true;
+  // suppression at such a late point, because we want*ed* to ensure we only
+  // call LogInitiatorSchemeBypassingDocumentBlocking for cases that actually
+  // matter in practice.
+  if (confirmed_blockable && is_initiator_scheme_excluded_)
     confirmed_blockable = false;
-  }
 
   // At this point we have already made a block-vs-allow decision and we know
   // that we can wake the |next_handler_| and let it catch-up with our
@@ -526,20 +524,8 @@
   } else {
     // Only report CORB status for successful (i.e. non-aborted,
     // non-errored-out) requests.
-    if (status.is_success()) {
+    if (status.is_success())
       analyzer_->LogAllowedResponse();
-      if (initiator_scheme_prevented_blocking_ &&
-          analyzer_->ShouldReportBlockedResponse() && GetRequestInfo()) {
-        base::PostTaskWithTraits(
-            FROM_HERE, {BrowserThread::UI},
-            base::BindOnce(&ContentBrowserClient::
-                               LogInitiatorSchemeBypassingDocumentBlocking,
-                           base::Unretained(GetContentClient()->browser()),
-                           request()->initiator().value(),
-                           GetRequestInfo()->GetChildID(),
-                           GetRequestInfo()->GetResourceType()));
-      }
-    }
 
     next_handler_->OnResponseCompleted(status, std::move(controller));
   }
diff --git a/content/browser/loader/cross_site_document_resource_handler.h b/content/browser/loader/cross_site_document_resource_handler.h
index dcb9722..b2177bf1 100644
--- a/content/browser/loader/cross_site_document_resource_handler.h
+++ b/content/browser/loader/cross_site_document_resource_handler.h
@@ -158,10 +158,6 @@
   // ContentBrowserClient::GetInitatorSchemeBypassingDocumentBlocking
   bool is_initiator_scheme_excluded_ = false;
 
-  // Whether |is_initiator_scheme_excluded_| actually prevented blocking from
-  // happening.
-  bool initiator_scheme_prevented_blocking_ = false;
-
   // Whether the response is being blocked because of the presence of
   // Cross-Origin-Resource-Policy header in 'no-cors' mode (in this case the
   // response should fail with net::ERR_BLOCKED_BY_RESPONSE error code).
diff --git a/content/browser/loader/cross_site_document_resource_handler_unittest.cc b/content/browser/loader/cross_site_document_resource_handler_unittest.cc
index c7f9ea5..52bf4c2 100644
--- a/content/browser/loader/cross_site_document_resource_handler_unittest.cc
+++ b/content/browser/loader/cross_site_document_resource_handler_unittest.cc
@@ -1317,7 +1317,7 @@
         "Sensitive, Blocked: html with CORS heuristic and no sniff",
         __LINE__,
         "http://www.a.com/resource.html",  // target_url
-        ResourceType::kScript,             // resource_type
+        ResourceType::kXhr,                // resource_type
         "http://www.a.com/",               // initiator_origin
         OriginHeader::kOmit,               // cors_request
         "HTTP/1.1 200 OK\n"
@@ -1337,7 +1337,7 @@
         "Sensitive, Needs Sniffing: html with CORS heuristic",
         __LINE__,
         "http://www.a.com/resource.html",  // target_url
-        ResourceType::kScript,             // resource_type
+        ResourceType::kXhr,                // resource_type
         "http://www.a.com/",               // initiator_origin
         OriginHeader::kOmit,               // cors_request
         "HTTP/1.1 200 OK\n"
@@ -1400,7 +1400,7 @@
         "Sensitive, Blocked: html with cache heuristic and no sniff",
         __LINE__,
         "http://www.a.com/resource.html",  // target_url
-        ResourceType::kScript,             // resource_type
+        ResourceType::kXhr,                // resource_type
         "http://www.a.com/",               // initiator_origin
         OriginHeader::kOmit,               // cors_request
         "HTTP/1.1 200 OK\n"
@@ -1420,7 +1420,7 @@
         "Sensitive, Needs Sniffing: html with cache heuristic",
         __LINE__,
         "http://www.a.com/resource.html",  // target_url
-        ResourceType::kScript,             // resource_type
+        ResourceType::kXhr,                // resource_type
         "http://www.a.com/",               // initiator_origin
         OriginHeader::kOmit,               // cors_request
         "HTTP/1.1 200 OK\n"
@@ -1449,15 +1449,75 @@
         "Vary: Origin\n"
         "Content-Range: bytes 200-1000/67589\n"
         "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
-        "unknown/mime_type",                        // response_content_type
-        MimeType::kOthers,                          // canonical_mime_type
-        MimeTypeBucket::kOther,                     // mime_type_bucket
+        "unknown/mime_type",                    // response_content_type
+        MimeType::kOthers,                      // canonical_mime_type
+        MimeTypeBucket::kOther,                 // mime_type_bucket
+        {"var x=3;"},                           // packets
+        true,                                   // resource_is_sensitive
+        CrossOriginProtectionDecision::kAllow,  // protection_decision
+        Verdict::kAllow,                        // verdict
+        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
+    },
+
+    // Responses with the accept-ranges header.
+    {
+        "Sensitive response with an accept-ranges header. ",
+        __LINE__,
+        "http://www.a.com/resource.html",  // target_url
+        ResourceType::kXhr,                // resource_type
+        "http://www.a.com/",               // initiator_origin
+        OriginHeader::kOmit,               // cors_request
+        "HTTP/1.1 200 OK\n"
+        "Accept-Ranges: bytes\n"
+        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
+        "text/html",                                // response_content_type
+        MimeType::kHtml,                            // canonical_mime_type
+        MimeTypeBucket::kProtected,                 // mime_type_bucket
         {"<html><head>this should sniff as HTML"},  // packets
         true,                                       // resource_is_sensitive
-        CrossOriginProtectionDecision::kAllow,      // protection_decision
-        Verdict::kAllow,                            // verdict
-        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
+        CrossOriginProtectionDecision::kNeedToSniffMore,  // protection_decision
+        Verdict::kAllow,                                  // verdict
+        kVerdictPacketForHeadersBasedVerdict,             // verdict_packet
     },
+    {
+        "Sensitive response with an accept-ranges header but value |none|.",
+        __LINE__,
+        "http://www.a.com/resource.html",  // target_url
+        ResourceType::kXhr,                // resource_type
+        "http://www.a.com/",               // initiator_origin
+        OriginHeader::kOmit,               // cors_request
+        "HTTP/1.1 200 OK\n"
+        "Accept-Ranges: none\n"
+        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
+        "text/html",                                // response_content_type
+        MimeType::kHtml,                            // canonical_mime_type
+        MimeTypeBucket::kProtected,                 // mime_type_bucket
+        {"<html><head>this should sniff as HTML"},  // packets
+        true,                                       // resource_is_sensitive
+        CrossOriginProtectionDecision::kNeedToSniffMore,  // protection_decision
+        Verdict::kAllow,                                  // verdict
+        kVerdictPacketForHeadersBasedVerdict,             // verdict_packet
+    },
+    {
+        "Non-sensitive response with an accept-ranges header.",
+        __LINE__,
+        "http://www.a.com/resource.html",  // target_url
+        ResourceType::kXhr,                // resource_type
+        "http://www.a.com/",               // initiator_origin
+        OriginHeader::kOmit,               // cors_request
+        "HTTP/1.1 200 OK\n"
+        "Accept-Ranges: bytes",                     // response_headers
+        "text/html",                                // response_content_type
+        MimeType::kHtml,                            // canonical_mime_type
+        MimeTypeBucket::kProtected,                 // mime_type_bucket
+        {"<html><head>this should sniff as HTML"},  // packets
+        false,                                      // resource_is_sensitive
+        CrossOriginProtectionDecision::kNeedToSniffMore,  // protection_decision
+        Verdict::kAllow,                                  // verdict
+        kVerdictPacketForHeadersBasedVerdict,             // verdict_packet
+    },
+    // TODO(krstnmnlsn): Add a scenario with nosniff to cover the "sensitive and
+    // kBlock" case when the additional accept-ranges UMA statistics are added.
 };
 
 // TestResourceDispatcherHost is a ResourceDispatcherHostImpl that the test
@@ -1676,7 +1736,7 @@
   // Verify MIME type was classified correctly.
   ASSERT_TRUE(document_blocker_->has_response_started_);
   EXPECT_EQ(scenario.canonical_mime_type,
-            document_blocker_->analyzer_->canonical_mime_type());
+            document_blocker_->analyzer_->canonical_mime_type_for_testing());
 
   // Verify that we will sniff content into a different buffer if sniffing is
   // needed.  Note that the different buffer is used even for blocking cases
@@ -2106,7 +2166,7 @@
   // CrossSiteDocumentResourceHandler's behavior.
   ASSERT_TRUE(document_blocker_->has_response_started_);
   EXPECT_EQ(scenario.canonical_mime_type,
-            document_blocker_->analyzer_->canonical_mime_type());
+            document_blocker_->analyzer_->canonical_mime_type_for_testing());
   EXPECT_EQ(expected_to_sniff, document_blocker_->analyzer_->needs_sniffing());
   ASSERT_EQ(expected_to_block_based_on_headers,
             document_blocker_->should_block_based_on_headers_);
@@ -2190,6 +2250,15 @@
   base::HistogramTester::CountsMap expected_counts;
   expected_counts["SiteIsolation.CORBProtection.SensitiveResource"] = 1;
   if (scenario.resource_is_sensitive) {
+    // Check that we reported correctly if the server supports range requests.
+    bool supports_range_requests = network::CrossOriginReadBlocking::
+        ResponseAnalyzer::SupportsRangeRequests(response->head);
+    EXPECT_THAT(histograms.GetAllSamples(
+                    "SiteIsolation.CORBProtection.SensitiveWithRangeSupport"),
+                testing::ElementsAre(base::Bucket(supports_range_requests, 1)));
+    expected_counts["SiteIsolation.CORBProtection.SensitiveWithRangeSupport"] =
+        1;
+
     std::string mime_type_bucket_string;
     switch (scenario.mime_type_bucket) {
       case MimeTypeBucket::kProtected:
diff --git a/content/browser/loader/navigation_loader_interceptor.h b/content/browser/loader/navigation_loader_interceptor.h
index 87d3302..88b0879 100644
--- a/content/browser/loader/navigation_loader_interceptor.h
+++ b/content/browser/loader/navigation_loader_interceptor.h
@@ -18,6 +18,7 @@
 
 namespace content {
 
+class BrowserContext;
 class ResourceContext;
 struct ResourceRequest;
 struct SubresourceLoaderParams;
@@ -63,8 +64,16 @@
   // |reset_subresource_loader_params| parameter to |fallback_callback|
   // indicates whether to discard the subresource loader params previously
   // returned by MaybeCreateSubresourceLoaderParams().
+  //
+  // |browser_context| will only be non-null when this interceptor is running on
+  // the UI thread, and |resource_context| will only be non-null when running on
+  // the IO thread.
+  //
+  // TODO(http://crbug.com/824840): Once all interceptors support
+  // running on UI, remove |resource_context|.
   virtual void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      BrowserContext* browser_context,
       ResourceContext* resource_context,
       LoaderCallback callback,
       FallbackCallback fallback_callback) = 0;
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index d4b734f..25d8df22 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -118,11 +118,13 @@
 
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      BrowserContext* browser_context,
       ResourceContext* resource_context,
       LoaderCallback callback,
       FallbackCallback fallback_callback) override {
-    browser_interceptor_->MaybeCreateLoader(
-        tentative_resource_request, resource_context, std::move(callback));
+    browser_interceptor_->MaybeCreateLoader(tentative_resource_request,
+                                            browser_context, resource_context,
+                                            std::move(callback));
   }
 
  private:
@@ -643,8 +645,9 @@
       return;
     }
 
-    if (!IsNavigationLoaderOnUIEnabled()) {
-      // TODO(http://crbug.com/824840): Support interceptors on UI thread.
+    if (IsNavigationLoaderOnUIEnabled()) {
+      CreateInterceptorsForUI(request_info);
+    } else {
       CreateInterceptorsForIO(
           request_info, service_worker_navigation_handle_core,
           appcache_handle_core, prefetched_signed_exchange_cache,
@@ -672,6 +675,24 @@
     Restart();
   }
 
+  void CreateInterceptorsForUI(NavigationRequestInfo* request_info) {
+    // See if embedders want to add interceptors.
+    std::vector<std::unique_ptr<URLLoaderRequestInterceptor>>
+        browser_interceptors =
+            GetContentClient()
+                ->browser()
+                ->WillCreateURLLoaderRequestInterceptors(
+                    navigation_ui_data_.get(), request_info->frame_tree_node_id,
+                    network_loader_factory_);
+    if (!browser_interceptors.empty()) {
+      for (auto& browser_interceptor : browser_interceptors) {
+        interceptors_.push_back(
+            std::make_unique<NavigationLoaderInterceptorBrowserContainer>(
+                std::move(browser_interceptor)));
+      }
+    }
+  }
+
   void CreateInterceptorsForIO(
       NavigationRequestInfo* request_info,
       ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core,
@@ -839,8 +860,16 @@
     // See if the next interceptor wants to handle the request.
     if (interceptor_index_ < interceptors_.size()) {
       auto* next_interceptor = interceptors_[interceptor_index_++].get();
+      // Set either BrowserContext or ResourceContext depending on what thread
+      // the interceptors are running on.
+      BrowserContext* browser_context_to_use = nullptr;
+      ResourceContext* resource_context_to_use = nullptr;
+      if (IsNavigationLoaderOnUIEnabled())
+        browser_context_to_use = browser_context_;
+      else
+        resource_context_to_use = resource_context_;
       next_interceptor->MaybeCreateLoader(
-          *resource_request_, resource_context_,
+          *resource_request_, browser_context_to_use, resource_context_to_use,
           base::BindOnce(&URLLoaderRequestController::MaybeStartLoader,
                          base::Unretained(this), next_interceptor),
           base::BindOnce(
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index aa31b443..e5ae433 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -73,6 +73,7 @@
   }
 
   void MaybeCreateLoader(const network::ResourceRequest& resource_request,
+                         BrowserContext* browser_context,
                          ResourceContext* resource_context,
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override {
diff --git a/content/browser/media/media_canplaytype_browsertest.cc b/content/browser/media/media_canplaytype_browsertest.cc
index 5973d51..c9c4ab4 100644
--- a/content/browser/media/media_canplaytype_browsertest.cc
+++ b/content/browser/media/media_canplaytype_browsertest.cc
@@ -14,6 +14,8 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "media/base/media_switches.h"
+#include "media/base/supported_types.h"
+#include "media/base/video_codecs.h"
 #include "media/media_buildflags.h"
 #include "ui/display/display_switches.h"
 
@@ -1537,12 +1539,15 @@
 IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_NewVp9Variants) {
   const std::string kSupportedMimeTypes[] = {"video/webm", "video/mp4"};
   for (const auto& mime_type : kSupportedMimeTypes) {
-// Profile 2 and 3 support is currently disabled on ARM and MIPS.
-#if defined(ARCH_CPU_ARM_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
-    const char* kVP9Profile2And3Probably = kNot;
-#else
-    const char* kVP9Profile2And3Probably = kProbably;
-#endif
+    // Profile 2 and 3 support isn't always available.
+    const char* kVP9Profile2 =
+        media::IsVp9ProfileSupportedForTesting(media::VP9PROFILE_PROFILE2)
+            ? kProbably
+            : kNot;
+    const char* kVP9Profile3 =
+        media::IsVp9ProfileSupportedForTesting(media::VP9PROFILE_PROFILE3)
+            ? kProbably
+            : kNot;
 
     // E.g. "'video/webm; "
     std::string prefix = "'" + mime_type + "; ";
@@ -1560,10 +1565,8 @@
     // Profiles 0 and 1 are always supported supported. Profiles 2 and 3 are
     // only supported on certain architectures.
     EXPECT_EQ(kProbably, CanPlay(prefix + "codecs=\"vp09.01.10.08\"'"));
-    EXPECT_EQ(kVP9Profile2And3Probably,
-              CanPlay(prefix + "codecs=\"vp09.02.10.08\"'"));
-    EXPECT_EQ(kVP9Profile2And3Probably,
-              CanPlay(prefix + "codecs=\"vp09.03.10.08\"'"));
+    EXPECT_EQ(kVP9Profile2, CanPlay(prefix + "codecs=\"vp09.02.10.08\"'"));
+    EXPECT_EQ(kVP9Profile3, CanPlay(prefix + "codecs=\"vp09.03.10.08\"'"));
   }
 }
 
diff --git a/content/browser/media/midi_browsertest.cc b/content/browser/media/midi_browsertest.cc
index ed69c0e..569243e 100644
--- a/content/browser/media/midi_browsertest.cc
+++ b/content/browser/media/midi_browsertest.cc
@@ -2,46 +2,72 @@
 // 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 "build/build_config.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 
 namespace content {
 
+namespace {
+
 class MidiBrowserTest : public ContentBrowserTest {
  public:
-  MidiBrowserTest() {}
+  MidiBrowserTest()
+      : https_test_server_(std::make_unique<net::EmbeddedTestServer>(
+            net::EmbeddedTestServer::TYPE_HTTPS)) {}
   ~MidiBrowserTest() override {}
+
+  void NavigateAndCheckResult(const std::string& path) {
+    const base::string16 expected = base::ASCIIToUTF16("pass");
+    content::TitleWatcher watcher(shell()->web_contents(), expected);
+    const base::string16 failed = base::ASCIIToUTF16("fail");
+    watcher.AlsoWaitForTitle(failed);
+
+    NavigateToURL(shell(), https_test_server_->GetURL(path));
+
+    const base::string16 result = watcher.WaitAndGetTitle();
+#if defined(OS_LINUX)
+    // Try does not allow accessing /dev/snd/seq, and it results in a platform
+    // specific initialization error. See http://crbug.com/371230.
+    // Also, Chromecast does not support the feature and results in
+    // NotSupportedError.
+    EXPECT_TRUE(result == failed || result == expected);
+    if (result == failed) {
+      std::string error_message;
+      ASSERT_TRUE(ExecuteScriptAndExtractString(
+          shell(), "domAutomationController.send(error_message)",
+          &error_message));
+      EXPECT_TRUE("Platform dependent initialization failed." ==
+                      error_message ||
+                  "The implementation did not support the requested type of "
+                  "object or operation." == error_message);
+    }
+#else
+    EXPECT_EQ(expected, result);
+#endif
+  }
+
+ private:
+  void SetUpOnMainThread() override {
+    https_test_server_->ServeFilesFromSourceDirectory(GetTestDataFilePath());
+    ASSERT_TRUE(https_test_server_->Start());
+  }
+
+  std::unique_ptr<net::EmbeddedTestServer> https_test_server_;
 };
 
 IN_PROC_BROWSER_TEST_F(MidiBrowserTest, RequestMIDIAccess) {
-  bool result;
-  NavigateToURL(shell(), GURL("about:blank"));
-  ASSERT_TRUE(ExecuteScriptAndExtractBool(
-      shell(),
-      "navigator.requestMIDIAccess()"
-      "  .then("
-      "    _ => domAutomationController.send(true),"
-      "    _ => domAutomationController.send(false));",
-      &result));
-  // We cannot check result since it relies on the availabity of system
-  // level MIDI on the test runner.
+  NavigateAndCheckResult("/midi/request_midi_access.html");
 }
 
 IN_PROC_BROWSER_TEST_F(MidiBrowserTest, SubscribeAll) {
-  bool result;
-  NavigateToURL(shell(), GURL("about:blank"));
-  ASSERT_TRUE(ExecuteScriptAndExtractBool(
-      shell(),
-      "navigator.requestMIDIAccess()"
-      "  .then("
-      "    e => { e.inputs.forEach(i => i.onmidimessage = console.log);"
-      "             domAutomationController.send(true) },"
-      "    _ => domAutomationController.send(false));",
-      &result));
-  // We cannot check result since it relies on the availabity of system
-  // level MIDI on the test runner.
+  NavigateAndCheckResult("/midi/subscribe_all.html");
 }
 
+}  // namespace
+
 }  // namespace content
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 5f94e9ce..035e503b 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -237,7 +237,6 @@
       EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
 
     base::RunLoop run_loop;
-    base::OnceClosure quit_closure = run_loop.QuitClosure();
     base::Lock lock;
 
     // Intercept network requests and record them.
@@ -248,7 +247,7 @@
               *params->url_request.top_frame_origin;
 
           if (params->url_request.url == final_resource)
-            std::move(quit_closure).Run();
+            run_loop.Quit();
           return false;
         }));
 
@@ -850,7 +849,7 @@
                   params->url_request.request_initiator;
 
               if (params->url_request.url == final_url)
-                std::move(run_loop_.QuitClosure()).Run();
+                run_loop_.Quit();
               return false;
             }));
   }
@@ -1929,4 +1928,127 @@
   EXPECT_EQ(url, data.new_web_contents->GetMainFrame()->GetLastCommittedURL());
 }
 
+// Test NavigationRequest::CheckAboutSrcDoc()
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, BlockedSrcDocBrowserInitiated) {
+  // 1. Main frame navigations to about:srcdoc and its variations are blocked.
+  for (const char* url :
+       {"about:srcdoc", "about:srcdoc?foo", "about:srcdoc#foo"}) {
+    NavigationHandleObserver handle_observer(shell()->web_contents(),
+                                             GURL(url));
+    EXPECT_FALSE(NavigateToURL(shell(), GURL(url)));
+    EXPECT_TRUE(handle_observer.has_committed());
+    EXPECT_TRUE(handle_observer.is_error());
+    EXPECT_EQ(net::ERR_INVALID_URL, handle_observer.net_error_code());
+  }
+
+  // 2. Subframe navigations to variations of about:srcdoc are blocked.
+  for (const char* url : {"about:srcdoc?foo", "about:srcdoc#foo"}) {
+    GURL main_url =
+        embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html");
+    EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+    NavigationHandleObserver handle_observer(shell()->web_contents(),
+                                             GURL(url));
+    shell()->LoadURLForFrame(GURL(url), "child-name-0",
+                             ui::PAGE_TRANSITION_FORWARD_BACK);
+    WaitForLoadStop(shell()->web_contents());
+    EXPECT_TRUE(handle_observer.has_committed());
+    EXPECT_TRUE(handle_observer.is_error());
+    EXPECT_EQ(net::ERR_INVALID_URL, handle_observer.net_error_code());
+  }
+
+  // 3. Subframe navigation to about:srcdoc are not blocked.
+  {
+    GURL main_url =
+        embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html");
+    EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+    NavigationHandleObserver handle_observer(shell()->web_contents(),
+                                             GURL("about::srcdoc"));
+    shell()->LoadURLForFrame(GURL("about::srcdoc"), "child-name-0",
+                             ui::PAGE_TRANSITION_FORWARD_BACK);
+    WaitForLoadStop(shell()->web_contents());
+    EXPECT_TRUE(handle_observer.has_committed());
+    EXPECT_FALSE(handle_observer.is_error());
+    EXPECT_EQ(net::OK, handle_observer.net_error_code());
+  }
+}
+
+// Test NavigationRequest::CheckAboutSrcDoc().
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, BlockedSrcDocRendererInitiated) {
+  EXPECT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  FrameTreeNode* main_frame =
+      static_cast<WebContentsImpl*>(shell()->web_contents())
+          ->GetFrameTree()
+          ->root();
+
+  // 1. Main frame navigations to about:srcdoc and its variations are blocked.
+  for (const char* url :
+       {"about:srcdoc", "about:srcdoc?foo", "about:srcdoc#foo"}) {
+    DidStartNavigationObserver start_observer(shell()->web_contents());
+    NavigationHandleObserver handle_observer(shell()->web_contents(),
+                                             GURL(url));
+    // TODO(arthursonzogni): It shouldn't be possible to navigate to
+    // about:srcdoc by executing location.href="about:srcdoc". Other web
+    // browsers like Firefox aren't allowing this.
+    EXPECT_TRUE(ExecJs(main_frame, JsReplace("location.href = $1", url)));
+
+    start_observer.Wait();
+    WaitForLoadStop(shell()->web_contents());
+
+    // TODO(arthursonzogni): The navigation should be blocked.
+    EXPECT_TRUE(handle_observer.has_committed());
+    EXPECT_FALSE(handle_observer.is_error());
+    EXPECT_EQ(net::OK, handle_observer.net_error_code());
+  }
+
+  // 2. Subframe navigations to variations of about:srcdoc are blocked.
+  for (const char* url : {"about:srcdoc?foo", "about:srcdoc#foo"}) {
+    GURL main_url =
+        embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html");
+    EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+    DidStartNavigationObserver start_observer(shell()->web_contents());
+    NavigationHandleObserver handle_observer(shell()->web_contents(),
+                                             GURL(url));
+    FrameTreeNode* subframe = main_frame->child_at(0);
+    // TODO(arthursonzogni): It shouldn't be possible to navigate to
+    // about:srcdoc by executing location.href="about:srcdoc". Other web
+    // browsers like Firefox aren't allowing this.
+    EXPECT_TRUE(ExecJs(subframe, JsReplace("location.href = $1", url)));
+
+    start_observer.Wait();
+    WaitForLoadStop(shell()->web_contents());
+
+    // TODO(arthursonzogni): The navigation should be blocked.
+    EXPECT_TRUE(handle_observer.has_committed());
+    EXPECT_FALSE(handle_observer.is_error());
+    EXPECT_EQ(net::OK, handle_observer.net_error_code());
+  }
+
+  // 3. Subframe navigation to about:srcdoc are not blocked.
+  {
+    GURL main_url =
+        embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html");
+    EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+    DidStartNavigationObserver start_observer(shell()->web_contents());
+    NavigationHandleObserver handle_observer(shell()->web_contents(),
+                                             GURL("about:srcdoc"));
+    FrameTreeNode* subframe = main_frame->child_at(0);
+    // TODO(arthursonzogni): It shouldn't be possible to navigate to
+    // about:srcdoc by executing location.href="about:srcdoc". Other web
+    // browsers like Firefox aren't allowing this.
+    EXPECT_TRUE(ExecJs(subframe, JsReplace("location.href = 'about:srcdoc'")));
+
+    start_observer.Wait();
+    WaitForLoadStop(shell()->web_contents());
+
+    EXPECT_TRUE(handle_observer.has_committed());
+    EXPECT_FALSE(handle_observer.is_error());
+    EXPECT_EQ(net::OK, handle_observer.net_error_code());
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index 6396a449..3eac522f 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -1042,11 +1042,17 @@
   // Need to use FlushAsyncForTesting instead of FlushForTesting because the IO
   // thread doesn't support nested message loops.
   base::RunLoop run_loop;
-  base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
-                           base::BindLambdaForTesting([&]() {
-                             filter->GetCookieManager()->FlushAsyncForTesting(
-                                 run_loop.QuitClosure());
-                           }));
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindLambdaForTesting(
+          [filter, quit_closure = run_loop.QuitClosure()]() {
+            filter->GetCookieManager()->FlushAsyncForTesting(
+                std::move(quit_closure));
+          }));
+  // TODO(flaken): This Run() was omitted in a previous iteration of this test,
+  // yet the test passed unflakily; figure out whether the above
+  // FlushAsyncForTesting() is needed.
+  run_loop.Run();
 
   // content_shell uses in-memory cookie database, so the value saved earlier
   // won't persist across crashes. What matters is that new access works.
diff --git a/content/browser/payments/payment_app_provider_impl.cc b/content/browser/payments/payment_app_provider_impl.cc
index 88643bb..9941db8 100644
--- a/content/browser/payments/payment_app_provider_impl.cc
+++ b/content/browser/payments/payment_app_provider_impl.cc
@@ -32,6 +32,8 @@
 namespace content {
 namespace {
 
+using payments::mojom::PaymentEventResponseType;
+
 using ServiceWorkerStartCallback =
     base::OnceCallback<void(scoped_refptr<ServiceWorkerVersion>,
                             blink::ServiceWorkerStatusCode)>;
@@ -161,15 +163,15 @@
     delete this;
   }
 
-  void OnErrorStatus(blink::ServiceWorkerStatusCode service_worker_status) {
+  void RespondWithErrorAndDeleteSelf(PaymentEventResponseType response_type) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    DCHECK(service_worker_status != blink::ServiceWorkerStatusCode::kOk);
 
     if (event_type_ == ServiceWorkerMetrics::EventType::PAYMENT_REQUEST) {
       base::PostTaskWithTraits(
           FROM_HERE, {BrowserThread::UI},
           base::BindOnce(std::move(invoke_payment_app_callback_),
-                         payments::mojom::PaymentHandlerResponse::New()));
+                         payments::mojom::PaymentHandlerResponse::New(
+                             "", "", response_type)));
     } else if (event_type_ ==
                    ServiceWorkerMetrics::EventType::CAN_MAKE_PAYMENT ||
                event_type_ == ServiceWorkerMetrics::EventType::ABORT_PAYMENT) {
@@ -185,13 +187,31 @@
     delete this;
   }
 
+  void OnErrorStatus(blink::ServiceWorkerStatusCode service_worker_status) {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    DCHECK(service_worker_status != blink::ServiceWorkerStatusCode::kOk);
+
+    PaymentEventResponseType response_type =
+        PaymentEventResponseType::PAYMENT_EVENT_BROWSER_ERROR;
+    if (service_worker_status ==
+        blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected) {
+      response_type = PaymentEventResponseType::PAYMENT_EVENT_REJECT;
+    } else if (service_worker_status ==
+               blink::ServiceWorkerStatusCode::kErrorTimeout) {
+      response_type = PaymentEventResponseType::PAYMENT_EVENT_TIMEOUT;
+    }
+
+    RespondWithErrorAndDeleteSelf(response_type);
+  }
+
   int request_id() { return request_id_; }
 
   void AbortPaymentSinceOpennedWindowClosing() {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
     service_worker_version_->FinishRequest(request_id_, false);
-    OnErrorStatus(blink::ServiceWorkerStatusCode::kErrorAbort);
+    RespondWithErrorAndDeleteSelf(
+        PaymentEventResponseType::PAYMENT_HANDLER_WINDOW_CLOSING);
   }
 
  private:
@@ -311,8 +331,11 @@
   if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(std::move(callback),
-                       payments::mojom::PaymentHandlerResponse::New()));
+        base::BindOnce(
+            std::move(callback),
+            payments::mojom::PaymentHandlerResponse::New(
+                "", "",
+                PaymentEventResponseType::PAYMENT_EVENT_BROWSER_ERROR)));
     return;
   }
 
@@ -389,7 +412,8 @@
         browser_context, registration_id, std::move(event_data),
         std::move(callback));
   } else {
-    std::move(callback).Run(payments::mojom::PaymentHandlerResponse::New());
+    std::move(callback).Run(payments::mojom::PaymentHandlerResponse::New(
+        "", "", PaymentEventResponseType::PAYMENT_EVENT_BROWSER_ERROR));
   }
 }
 
@@ -488,8 +512,11 @@
   if (!url.is_valid() || !scope.is_valid() || method.empty()) {
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(std::move(callback),
-                       payments::mojom::PaymentHandlerResponse::New()));
+        base::BindOnce(
+            std::move(callback),
+            payments::mojom::PaymentHandlerResponse::New(
+                "", "",
+                PaymentEventResponseType::PAYMENT_EVENT_BROWSER_ERROR)));
     return;
   }
 
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index e4c126d..0bb45ae 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -230,50 +230,6 @@
   Portal* portal_ = nullptr;
 };
 
-// The PortalAdoptionObserver observes calls to AdoptPortal on
-// RenderFrameHostImpl. This observer should be used to wait for a single
-// call to AdoptPortal.
-class PortalAdoptionObserver : public mojom::FrameHostInterceptorForTesting {
- public:
-  explicit PortalAdoptionObserver(RenderFrameHostImpl* render_frame_host_impl)
-      : render_frame_host_impl_(render_frame_host_impl) {
-    render_frame_host_impl_->frame_host_binding_for_testing()
-        .SwapImplForTesting(this);
-  }
-
-  ~PortalAdoptionObserver() override {}
-
-  FrameHost* GetForwardingInterface() override {
-    return render_frame_host_impl_;
-  }
-
-  void AdoptPortal(const base::UnguessableToken& portal_token,
-                   AdoptPortalCallback callback) override {
-    Portal* portal = Portal::FromToken(portal_token);
-    DCHECK(portal);
-    RenderFrameProxyHost* proxy_host = portal->CreateProxyAndAttachPortal();
-    std::move(callback).Run(proxy_host->GetRoutingID(),
-                            portal->GetDevToolsFrameToken());
-    portal_adopted_ = true;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
-  void WaitUntilPortalAdopted() {
-    if (portal_adopted_)
-      return;
-    base::RunLoop run_loop;
-    run_loop_ = &run_loop;
-    run_loop.Run();
-    run_loop_ = nullptr;
-  }
-
- private:
-  RenderFrameHostImpl* render_frame_host_impl_;
-  base::RunLoop* run_loop_ = nullptr;
-  bool portal_adopted_ = false;
-};
-
 class PortalBrowserTest : public ContentBrowserTest {
  protected:
   PortalBrowserTest() {}
@@ -451,13 +407,13 @@
                      "});"));
 
   {
-    PortalAdoptionObserver adoption_observer(portal_frame);
+    PortalCreatedObserver adoption_observer(portal_frame);
     EXPECT_TRUE(ExecJs(main_frame,
                        "portal.activate().then(() => { "
                        "  document.body.removeChild(portal); "
                        "});"));
     portal_interceptor->WaitForActivate();
-    adoption_observer.WaitUntilPortalAdopted();
+    adoption_observer.WaitUntilPortalCreated();
   }
   // After activation, the shell's WebContents should be the previous portal's
   // WebContents.
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 9115c15..af1e42958 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -415,7 +415,6 @@
       needs_animate_(false),
       pending_frames_(0U),
       layer_tree_frame_sink_request_pending_(false),
-      lock_manager_(base::ThreadTaskRunnerHandle::Get()),
       enable_surface_synchronization_(
           features::IsSurfaceSynchronizationEnabled()),
       enable_viz_(features::IsVizDisplayCompositorEnabled()),
@@ -556,6 +555,13 @@
   settings.single_thread_proxy_scheduler = true;
   settings.use_painted_device_scale_factor = true;
 
+  if (features::IsSurfaceSynchronizationEnabled()) {
+    // TODO(crbug.com/933846): LatencyRecovery is causing jank on Android.
+    // Disable in viz mode for now, with plan to disable more widely once
+    // viz launches.
+    settings.enable_latency_recovery = false;
+  }
+
   animation_host_ = cc::AnimationHost::CreateMainInstance();
 
   cc::LayerTreeHost::InitParams params;
@@ -981,16 +987,6 @@
   return !readback_layer_tree_->children().empty();
 }
 
-std::unique_ptr<ui::CompositorLock> CompositorImpl::GetCompositorLock(
-    ui::CompositorLockClient* client,
-    base::TimeDelta timeout) {
-  std::unique_ptr<cc::ScopedDeferMainFrameUpdate>
-      scoped_defer_main_frame_update =
-          host_ ? host_->DeferMainFrameUpdate() : nullptr;
-  return lock_manager_.GetCompositorLock(
-      client, timeout, std::move(scoped_defer_main_frame_update));
-}
-
 bool CompositorImpl::IsDrawingFirstVisibleFrame() const {
   return !has_submitted_frame_since_became_visible_;
 }
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index bd9e16e..995a380 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -85,7 +85,6 @@
   bool SupportsETC1NonPowerOfTwo() const override;
 
   // Test functions:
-  bool IsLockedForTesting() const { return lock_manager_.IsLocked(); }
   void SetVisibleForTesting(bool visible) { SetVisible(visible); }
   void SetSwapCompletedWithSizeCallbackForTesting(
       base::RepeatingCallback<void(const gfx::Size&)> cb) {
@@ -153,9 +152,6 @@
   viz::FrameSinkId GetFrameSinkId() override;
   void AddChildFrameSink(const viz::FrameSinkId& frame_sink_id) override;
   void RemoveChildFrameSink(const viz::FrameSinkId& frame_sink_id) override;
-  std::unique_ptr<ui::CompositorLock> GetCompositorLock(
-      ui::CompositorLockClient* client,
-      base::TimeDelta timeout) override;
   bool IsDrawingFirstVisibleFrame() const override;
   void SetVSyncPaused(bool paused) override;
   void OnUpdateRefreshRate(float refresh_rate) override;
@@ -256,7 +252,6 @@
   bool has_layer_tree_frame_sink_ = false;
   std::unordered_set<viz::FrameSinkId, viz::FrameSinkIdHash>
       pending_child_frame_sink_ids_;
-  ui::CompositorLockManager lock_manager_;
   bool has_submitted_frame_since_became_visible_ = false;
 
   // If true, we are using surface synchronization.
diff --git a/content/browser/renderer_host/compositor_impl_android_browsertest.cc b/content/browser/renderer_host/compositor_impl_android_browsertest.cc
index 918b80d..d6b41a4 100644
--- a/content/browser/renderer_host/compositor_impl_android_browsertest.cc
+++ b/content/browser/renderer_host/compositor_impl_android_browsertest.cc
@@ -154,78 +154,6 @@
   DISALLOW_COPY_AND_ASSIGN(ContextLostRunLoop);
 };
 
-// RunLoop implementation that runs until it observes a compositor frame.
-class CompositorFrameRunLoop : public ui::WindowAndroidObserver {
- public:
-  CompositorFrameRunLoop(ui::WindowAndroid* window) : window_(window) {
-    window_->AddObserver(this);
-  }
-  ~CompositorFrameRunLoop() override { window_->RemoveObserver(this); }
-
-  void RunUntilFrame() { run_loop_.Run(); }
-
- private:
-  // ui::WindowAndroidObserver:
-  void OnCompositingDidCommit() override { run_loop_.Quit(); }
-  void OnRootWindowVisibilityChanged(bool visible) override {}
-  void OnAttachCompositor() override {}
-  void OnDetachCompositor() override {}
-  void OnActivityStopped() override {}
-  void OnActivityStarted() override {}
-
-  ui::WindowAndroid* const window_;
-  base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(CompositorFrameRunLoop);
-};
-
-IN_PROC_BROWSER_TEST_P(CompositorImplLowEndBrowserTest,
-                       CompositorImplDropsResourcesOnBackground) {
-  // This test makes invalid assumptions when surface synchronization is
-  // enabled. The compositor lock is obsolete, and inspecting frames
-  // from the CompositorImpl does not guarantee renderer CompositorFrames
-  // are ready.
-  if (features::IsSurfaceSynchronizationEnabled())
-    return;
-
-  auto* rwhva = render_widget_host_view_android();
-  auto* compositor = compositor_impl();
-  auto context = GpuBrowsertestCreateContext(
-      GpuBrowsertestEstablishGpuChannelSyncRunLoop());
-  context->BindToCurrentThread();
-
-  CompositorFrameRunLoop(window()).RunUntilFrame();
-  EXPECT_TRUE(rwhva->HasValidFrame());
-
-  ContextLostRunLoop run_loop(context.get());
-  compositor->SetVisibleForTesting(false);
-  base::android::ApplicationStatusListener::NotifyApplicationStateChange(
-      base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES);
-  rwhva->OnRootWindowVisibilityChanged(false);
-  rwhva->Hide();
-
-  // Ensure that context is eventually dropped and at that point we do not have
-  // a valid frame.
-  run_loop.RunUntilContextLost();
-  EXPECT_FALSE(rwhva->HasValidFrame());
-
-  // Become visible again:
-  compositor->SetVisibleForTesting(true);
-  base::android::ApplicationStatusListener::NotifyApplicationStateChange(
-      base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
-  rwhva->Show();
-  rwhva->OnRootWindowVisibilityChanged(true);
-
-  // We should have taken the compositor lock on resume.
-  EXPECT_TRUE(compositor->IsLockedForTesting());
-  EXPECT_FALSE(rwhva->HasValidFrame());
-
-  // The compositor should eventually be unlocked and produce a frame.
-  CompositorFrameRunLoop(window()).RunUntilFrame();
-  EXPECT_FALSE(compositor->IsLockedForTesting());
-  EXPECT_TRUE(rwhva->HasValidFrame());
-}
-
 // RunLoop implementation that runs until it observes a swap with size.
 class CompositorSwapRunLoop {
  public:
@@ -251,6 +179,42 @@
   DISALLOW_COPY_AND_ASSIGN(CompositorSwapRunLoop);
 };
 
+IN_PROC_BROWSER_TEST_P(CompositorImplLowEndBrowserTest,
+                       CompositorImplDropsResourcesOnBackground) {
+  auto* rwhva = render_widget_host_view_android();
+  auto* compositor = compositor_impl();
+  auto context = GpuBrowsertestCreateContext(
+      GpuBrowsertestEstablishGpuChannelSyncRunLoop());
+  context->BindToCurrentThread();
+
+  // Run until we've swapped once. At this point we should have a valid frame.
+  CompositorSwapRunLoop(compositor_impl()).RunUntilSwap();
+  EXPECT_TRUE(rwhva->HasValidFrame());
+
+  ContextLostRunLoop run_loop(context.get());
+  compositor->SetVisibleForTesting(false);
+  base::android::ApplicationStatusListener::NotifyApplicationStateChange(
+      base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES);
+  rwhva->OnRootWindowVisibilityChanged(false);
+  rwhva->Hide();
+
+  // Ensure that context is eventually dropped and at that point we do not have
+  // a valid frame.
+  run_loop.RunUntilContextLost();
+  EXPECT_FALSE(rwhva->HasValidFrame());
+
+  // Become visible again:
+  compositor->SetVisibleForTesting(true);
+  base::android::ApplicationStatusListener::NotifyApplicationStateChange(
+      base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
+  rwhva->Show();
+  rwhva->OnRootWindowVisibilityChanged(true);
+
+  // Wait for a swap after becoming visible.
+  CompositorSwapRunLoop(compositor_impl()).RunUntilSwap();
+  EXPECT_TRUE(rwhva->HasValidFrame());
+}
+
 IN_PROC_BROWSER_TEST_P(CompositorImplBrowserTest,
                        CompositorImplReceivesSwapCallbacks) {
   // OOP-R is required for this test to succeed with SkDDL, but is disabled on
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
index 8bfb6b9..7248dce 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
@@ -34,6 +34,7 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_features.h"
 #include "third_party/blink/public/common/font_unique_name_lookup/font_table_matcher.h"
+#include "third_party/blink/public/common/font_unique_name_lookup/font_table_persistence.h"
 #include "third_party/blink/public/common/font_unique_name_lookup/icu_fold_case_util.h"
 #include "ui/gfx/win/direct_write.h"
 
@@ -252,64 +253,17 @@
 
   if (!IsFontUniqueNameTableValid())
     return false;
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  base::FilePath table_cache_file_path = TableCacheFilePath();
-  if (table_cache_file_path.empty())
-    return false;
-  base::File table_cache_file(
-      table_cache_file_path,
-      base::File::FLAG_CREATE_ALWAYS | base::File::Flags::FLAG_WRITE);
-  if (!table_cache_file.IsValid())
-    return false;
-  if (table_cache_file.Write(
-          0, static_cast<char*>(font_table_memory_.mapping.memory()),
-          font_table_memory_.mapping.size()) == -1) {
-    table_cache_file.SetLength(0);
-    return false;
-  }
-  return true;
+
+  return blink::font_table_persistence::PersistToFile(font_table_memory_,
+                                                      TableCacheFilePath());
 }
 
 bool DWriteFontLookupTableBuilder::LoadFromFile() {
   DCHECK(caching_enabled_);
   DCHECK(!IsFontUniqueNameTableValid());
-  base::File table_cache_file;
-  {
-    base::ScopedBlockingCall scoped_blocking_call(
-        FROM_HERE, base::BlockingType::MAY_BLOCK);
-    base::FilePath table_cache_file_path = TableCacheFilePath();
-    if (table_cache_file_path.empty())
-      return false;
-    table_cache_file =
-        base::File(table_cache_file_path,
-                   base::File::FLAG_OPEN | base::File::Flags::FLAG_READ);
-    if (!table_cache_file.IsValid())
-      return false;
-  }
-  font_table_memory_ =
-      base::ReadOnlySharedMemoryRegion::Create(table_cache_file.GetLength());
-  if (!IsFontUniqueNameTableValid())
-    return false;
-  int read_result = table_cache_file.Read(
-      0, static_cast<char*>(font_table_memory_.mapping.memory()),
-      table_cache_file.GetLength());
-  // If no bytes were read or Read() returned -1 we are not able to reconstruct
-  // a font table from the cached file.
-  if (read_result <= 0) {
-    font_table_memory_ = base::MappedReadOnlyRegion();
-    return false;
-  }
 
-  blink::FontUniqueNameTable font_table;
-  if (!font_table.ParseFromArray(font_table_memory_.mapping.memory(),
-                                 font_table_memory_.mapping.size())) {
-    // TODO(https://crbug.com/941434): Track failure to parse cache in UMA data.
-    font_table_memory_ = base::MappedReadOnlyRegion();
-    return false;
-  }
-
-  return true;
+  return blink::font_table_persistence::LoadFromFile(TableCacheFilePath(),
+                                                     &font_table_memory_);
 }
 
 DWriteFontLookupTableBuilder::CallbackOnTaskRunner::CallbackOnTaskRunner(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index e43d9c4..47fe620 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -207,6 +207,7 @@
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/service_manager/sandbox/switches.h"
 #include "services/service_manager/zygote/common/zygote_buildflags.h"
+#include "storage/browser/database/database_tracker.h"
 #include "storage/browser/fileapi/sandbox_file_system_backend.h"
 #include "third_party/blink/public/common/page/launching_process_state.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
@@ -1012,6 +1013,10 @@
   }
 
  private:
+  using ProcessID = int;
+  using SiteProcessIDPair = std::pair<GURL, ProcessID>;
+  using SiteProcessIDPairSet = std::set<SiteProcessIDPair>;
+
   void RegisterProcessForSite(RenderProcessHost* host,
                               SiteInstanceImpl* site_instance) {
     if (!HasProcess(host))
@@ -1022,7 +1027,15 @@
 
   RenderProcessHost* TakeFreshestProcessForSite(
       SiteInstanceImpl* site_instance) {
-    RenderProcessHost* host = FindFreshestProcessForSite(site_instance);
+    SiteProcessIDPair site_process_pair =
+        FindFreshestProcessForSite(site_instance);
+
+    if (site_process_pair.first.is_empty())
+      return nullptr;
+
+    RenderProcessHost* host =
+        RenderProcessHost::FromID(site_process_pair.second);
+
     if (!host)
       return nullptr;
 
@@ -1037,20 +1050,31 @@
                                      site_url, site_instance->lock_url()))
       return nullptr;
 
-    site_process_set_.erase(SiteProcessIDPair(site_url, host->GetID()));
+    site_process_set_.erase(site_process_pair);
     if (!HasProcess(host))
       host->RemoveObserver(this);
     return host;
   }
 
-  RenderProcessHost* FindFreshestProcessForSite(
+  SiteProcessIDPair FindFreshestProcessForSite(
       SiteInstanceImpl* site_instance) const {
-    GURL site_url(site_instance->GetSiteURL());
-    for (const auto& site_process_pair : base::Reversed(site_process_set_)) {
-      if (site_process_pair.first == site_url)
-        return RenderProcessHost::FromID(site_process_pair.second);
+    const auto reversed_site_process_set = base::Reversed(site_process_set_);
+    if (site_instance->IsDefaultSiteInstance()) {
+      // See if we can find an entry that maps to a site associated with the
+      // default SiteInstance. This allows the default SiteInstance to reuse a
+      // service worker process for any site that has been associated with it.
+      for (const auto& site_process_pair : reversed_site_process_set) {
+        if (site_instance->IsSiteInDefaultSiteInstance(site_process_pair.first))
+          return site_process_pair;
+      }
+    } else {
+      const GURL site_url(site_instance->GetSiteURL());
+      for (const auto& site_process_pair : reversed_site_process_set) {
+        if (site_process_pair.first == site_url)
+          return site_process_pair;
+      }
     }
-    return nullptr;
+    return SiteProcessIDPair();
   }
 
   // Returns true if this tracker contains the process ID |host->GetID()|.
@@ -1063,10 +1087,6 @@
     return false;
   }
 
-  using ProcessID = int;
-  using SiteProcessIDPair = std::pair<GURL, ProcessID>;
-  using SiteProcessIDPairSet = std::set<SiteProcessIDPair>;
-
   // Use std::set because duplicates don't need to be tracked separately (eg.,
   // service workers for the same site in the same process). It is sorted in the
   // order of insertion.
@@ -2086,7 +2106,8 @@
 
   registry->AddInterface(
       base::BindRepeating(
-          &WebDatabaseHostImpl::Create, GetID(),
+          &RenderProcessHostImpl::BindWebDatabaseHostImpl,
+          base::Unretained(this),
           base::WrapRefCounted(storage_partition_impl_->GetDatabaseTracker())),
       storage_partition_impl_->GetDatabaseTracker()->task_runner());
 
@@ -2287,6 +2308,13 @@
   video_decoder_proxy_->Add(std::move(request));
 }
 
+void RenderProcessHostImpl::BindWebDatabaseHostImpl(
+    scoped_refptr<storage::DatabaseTracker> db_tracker,
+    blink::mojom::WebDatabaseHostRequest request) {
+  WebDatabaseHostImpl::Create(GetID(), std::move(db_tracker),
+                              std::move(request));
+}
+
 void RenderProcessHostImpl::CreateRendererHost(
     mojom::RendererHostAssociatedRequest request) {
   renderer_host_binding_.Bind(std::move(request));
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 88b217b..f66459736 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -66,6 +66,7 @@
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-shared.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
+#include "third_party/blink/public/mojom/webdatabase/web_database.mojom.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
 #if defined(OS_ANDROID)
@@ -77,6 +78,10 @@
 class PersistentMemoryAllocator;
 }
 
+namespace storage {
+class DatabaseTracker;
+}
+
 namespace viz {
 class GpuClient;
 }
@@ -567,6 +572,9 @@
       blink::mojom::BroadcastChannelProviderRequest request);
   void CreateRendererHost(mojom::RendererHostAssociatedRequest request);
   void BindVideoDecoderService(media::mojom::InterfaceFactoryRequest request);
+  void BindWebDatabaseHostImpl(
+      scoped_refptr<storage::DatabaseTracker> db_tracker,
+      blink::mojom::WebDatabaseHostRequest request);
 
   // Control message handlers.
   void OnUserMetricsRecordAction(const std::string& action);
diff --git a/content/browser/renderer_host/render_process_host_unittest.cc b/content/browser/renderer_host/render_process_host_unittest.cc
index 560fa2a..8c428a55 100644
--- a/content/browser/renderer_host/render_process_host_unittest.cc
+++ b/content/browser/renderer_host/render_process_host_unittest.cc
@@ -197,14 +197,7 @@
   // the newest unmatched service worker's process (i.e., sw_host2).
   scoped_refptr<SiteInstanceImpl> site_instance1 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  if (AreDefaultSiteInstancesEnabled()) {
-    EXPECT_TRUE(site_instance1->IsDefaultSiteInstance());
-    // TODO(acolwell): Remove once CreateForServiceWorker() can return a
-    // default SiteInstance.
-    EXPECT_NE(sw_host2, site_instance1->GetProcess());
-  } else {
-    EXPECT_EQ(sw_host2, site_instance1->GetProcess());
-  }
+  EXPECT_EQ(sw_host2, site_instance1->GetProcess());
 
   // Getting a RenderProcessHost for a navigation to the same site must reuse
   // the newest unmatched service worker's process (i.e., sw_host1). sw_host2
@@ -212,25 +205,14 @@
   // with a corresponding unmatched service worker.
   scoped_refptr<SiteInstanceImpl> site_instance2 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  if (AreDefaultSiteInstancesEnabled()) {
-    EXPECT_TRUE(site_instance2->IsDefaultSiteInstance());
-    // TODO(acolwell): Remove once CreateForServiceWorker() can return a
-    // default SiteInstance.
-    EXPECT_NE(sw_host1, site_instance2->GetProcess());
-  } else {
-    EXPECT_EQ(sw_host1, site_instance2->GetProcess());
-  }
+  EXPECT_EQ(sw_host1, site_instance2->GetProcess());
 
   // Getting a RenderProcessHost for a navigation should return a new process
   // because there is no unmatched service worker's process.
   scoped_refptr<SiteInstanceImpl> site_instance3 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  if (AreDefaultSiteInstancesEnabled()) {
-    EXPECT_TRUE(site_instance3->IsDefaultSiteInstance());
-  } else {
-    EXPECT_NE(sw_host1, site_instance3->GetProcess());
-    EXPECT_NE(sw_host2, site_instance3->GetProcess());
-  }
+  EXPECT_NE(sw_host1, site_instance3->GetProcess());
+  EXPECT_NE(sw_host2, site_instance3->GetProcess());
 }
 
 class UnsuitableHostContentBrowserClient : public ContentBrowserClient {
@@ -322,14 +304,7 @@
   // the newest unmatched service worker's process (i.e., sw_host2).
   scoped_refptr<SiteInstanceImpl> site_instance1 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  if (AreDefaultSiteInstancesEnabled()) {
-    EXPECT_TRUE(site_instance1->IsDefaultSiteInstance());
-    // TODO(acolwell): Remove once CreateForServiceWorker() can return
-    // a default SiteInstance.
-    EXPECT_NE(sw_host2, site_instance1->GetProcess());
-  } else {
-    EXPECT_EQ(sw_host2, site_instance1->GetProcess());
-  }
+  EXPECT_EQ(sw_host2, site_instance1->GetProcess());
 
   // Getting a RenderProcessHost for a navigation to the same site must reuse
   // the newest unmatched service worker's process (i.e., sw_host1). sw_host2
@@ -337,14 +312,7 @@
   // with a corresponding unmatched service worker.
   scoped_refptr<SiteInstanceImpl> site_instance2 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
-  if (AreDefaultSiteInstancesEnabled()) {
-    EXPECT_TRUE(site_instance2->IsDefaultSiteInstance());
-    // TODO(acolwell): Remove once CreateForServiceWorker() can return
-    // a default SiteInstance.
-    EXPECT_NE(sw_host1, site_instance2->GetProcess());
-  } else {
-    EXPECT_EQ(sw_host1, site_instance2->GetProcess());
-  }
+  EXPECT_EQ(sw_host1, site_instance2->GetProcess());
 }
 
 TEST_F(RenderProcessHostUnitTest,
@@ -370,28 +338,14 @@
   scoped_refptr<SiteInstanceImpl> sw_site_instance3 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
   RenderProcessHost* sw_host3 = sw_site_instance3->GetProcess();
-  if (AreDefaultSiteInstancesEnabled()) {
-    EXPECT_TRUE(sw_site_instance3->IsDefaultSiteInstance());
-    // TODO(acolwell: Remove once CreateForServiceWorker() can return a
-    // default SiteInstance.
-    EXPECT_NE(sw_host1, sw_host3);
-  } else {
-    EXPECT_EQ(sw_host1, sw_host3);
-  }
+  EXPECT_EQ(sw_host1, sw_host3);
 
   // Getting a RenderProcessHost for a navigation to the same site again with
   // process-per-site flag should reuse the unmatched service worker's process.
   scoped_refptr<SiteInstanceImpl> sw_site_instance4 =
       SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
   RenderProcessHost* sw_host4 = sw_site_instance4->GetProcess();
-  if (AreDefaultSiteInstancesEnabled()) {
-    EXPECT_TRUE(sw_site_instance4->IsDefaultSiteInstance());
-    // TODO(acolwell: Remove once CreateForServiceWorker() can return a
-    // default SiteInstance.
-    EXPECT_NE(sw_host1, sw_host4);
-  } else {
-    EXPECT_EQ(sw_host1, sw_host4);
-  }
+  EXPECT_EQ(sw_host1, sw_host4);
 }
 
 TEST_F(RenderProcessHostUnitTest, DoNotReuseOtherSiteServiceWorkerProcess) {
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index fd3fbc4e0..6053638 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -342,11 +342,6 @@
         local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
             .local_surface_id(),
         GetCompositorViewportPixelSize(), deadline_policy);
-
-    // TODO(ericrk): This can be removed once surface synchronization is
-    // enabled. https://crbug.com/835102
-    delegated_frame_host_->PixelSizeWillChange(
-        GetCompositorViewportPixelSize());
   }
 
   return host()->SynchronizeVisualProperties();
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index ff75742..1befcdd 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -95,7 +95,6 @@
 #include "ui/gfx/skia_util.h"
 #include "ui/touch_selection/touch_selection_controller.h"
 #include "ui/wm/core/coordinate_conversion.h"
-#include "ui/wm/core/window_util.h"
 #include "ui/wm/public/activation_client.h"
 #include "ui/wm/public/scoped_tooltip_disabler.h"
 #include "ui/wm/public/tooltip_client.h"
@@ -347,7 +346,6 @@
       legacy_window_destroyed_(false),
       virtual_keyboard_requested_(false),
 #endif
-      has_snapped_to_boundary_(false),
       is_guest_view_hack_(is_guest_view_hack),
       device_scale_factor_(0.0f),
       event_handler_(new RenderWidgetHostViewEventHandler(host(), this, this)),
@@ -582,7 +580,6 @@
 }
 
 void RenderWidgetHostViewAura::HandleParentBoundsChanged() {
-  SnapToPhysicalPixelBoundary();
 #if defined(OS_WIN)
   if (legacy_render_widget_host_HWND_) {
     legacy_render_widget_host_HWND_->SetBounds(
@@ -1650,7 +1647,6 @@
       display::Screen::GetScreen()->GetDisplayNearestWindow(window_);
   DCHECK_EQ(new_device_scale_factor, display.device_scale_factor());
   current_cursor_.SetDisplayInfo(display);
-  SnapToPhysicalPixelBoundary();
 }
 
 void RenderWidgetHostViewAura::OnWindowDestroying(aura::Window* window) {
@@ -2250,17 +2246,6 @@
   overscroll_controller_ = std::move(controller);
 }
 
-void RenderWidgetHostViewAura::SnapToPhysicalPixelBoundary() {
-  // The top left corner of our view in window coordinates might not land on a
-  // device pixel boundary if we have a non-integer device scale. In that case,
-  // to avoid the web contents area looking blurry we translate the web contents
-  // in the +x, +y direction to land on the nearest pixel boundary. This may
-  // cause the bottom and right edges to be clipped slightly, but that's ok.
-  // We want to snap it to the nearest ancestor.
-  wm::SnapWindowToPixelBoundary(window_);
-  has_snapped_to_boundary_ = true;
-}
-
 void RenderWidgetHostViewAura::SetSelectionControllerClientForTest(
     std::unique_ptr<TouchSelectionControllerClientAura> client) {
   selection_controller_client_.swap(client);
@@ -2268,7 +2253,6 @@
 }
 
 void RenderWidgetHostViewAura::InternalSetBounds(const gfx::Rect& rect) {
-  SnapToPhysicalPixelBoundary();
   // Don't recursively call SetBounds if this bounds update is the result of
   // a Window::SetBoundsInternal call.
   if (!in_bounds_changed_)
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index 451a72c..50b2161 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -494,10 +494,6 @@
   void OnDidUpdateVisualPropertiesComplete(
       const cc::RenderFrameMetadata& metadata);
 
-  // Tracks whether SnapToPhysicalPixelBoundary() has been called.
-  bool has_snapped_to_boundary() { return has_snapped_to_boundary_; }
-  void ResetHasSnappedToBoundary() { has_snapped_to_boundary_ = false; }
-
   // Set the bounds of the window and handle size changes.  Assumes the caller
   // has already adjusted the origin of |rect| to conform to whatever coordinate
   // space is required by the aura::Window.
@@ -668,8 +664,6 @@
   gfx::Point last_mouse_move_location_;
 #endif
 
-  bool has_snapped_to_boundary_;
-
   // The last selection bounds reported to the view.
   gfx::SelectionBound selection_start_;
   gfx::SelectionBound selection_end_;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 2e2c878..8eda400 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -2289,21 +2289,4 @@
   EXPECT_NSEQ([view accessibilityParent], parent_view);
 }
 
-// Tests that when entering mouse lock, the cursor will lock to window center.
-TEST_F(RenderWidgetHostViewMacTest, PointerLockCenterPosition) {
-  NSView* view = rwhv_mac_->GetInProcessNSView();
-
-  NSRect bound = NSMakeRect(123, 234, 456, 678);
-  [view setFrame:bound];
-
-  EXPECT_EQ(gfx::Rect([view bounds]), gfx::Rect(0, 0, 456, 678));
-
-  rwhv_mac_->LockMouse();
-  EXPECT_TRUE(rwhv_mac_->IsMouseLocked());
-
-  gfx::Point mouse_pos =
-      gfx::Point([window_ mouseLocationOutsideOfEventStream]);
-  EXPECT_EQ(mouse_pos, gfx::Point(228, 339));
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/web_database_host_impl.cc b/content/browser/renderer_host/web_database_host_impl.cc
index 5405fef8..195dd66 100644
--- a/content/browser/renderer_host/web_database_host_impl.cc
+++ b/content/browser/renderer_host/web_database_host_impl.cc
@@ -17,7 +17,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/origin_util.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "storage/browser/database/database_util.h"
 #include "storage/browser/database/vfs_backend.h"
 #include "storage/browser/quota/quota_manager.h"
@@ -90,11 +90,11 @@
 void WebDatabaseHostImpl::Create(
     int process_id,
     scoped_refptr<storage::DatabaseTracker> db_tracker,
-    blink::mojom::WebDatabaseHostRequest request) {
+    mojo::PendingReceiver<blink::mojom::WebDatabaseHost> receiver) {
   DCHECK(db_tracker->task_runner()->RunsTasksInCurrentSequence());
-  mojo::MakeStrongBinding(
+  mojo::MakeSelfOwnedReceiver(
       std::make_unique<WebDatabaseHostImpl>(process_id, std::move(db_tracker)),
-      std::move(request));
+      std::move(receiver));
 }
 
 void WebDatabaseHostImpl::OpenFile(const base::string16& vfs_file_name,
@@ -450,15 +450,17 @@
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(
-            [](int process_id, blink::mojom::WebDatabaseRequest request) {
+            [](int process_id,
+               mojo::PendingReceiver<blink::mojom::WebDatabase> receiver) {
               RenderProcessHost* host = RenderProcessHost::FromID(process_id);
               if (host) {
-                content::BindInterface(host, std::move(request));
+                host->BindInterface(blink::mojom::WebDatabase::Name_,
+                                    receiver.PassPipe());
               }
             },
-            process_id_, mojo::MakeRequest(&database_provider_)));
+            process_id_, database_provider_.BindNewPipeAndPassReceiver()));
   }
-  return *database_provider_;
+  return *database_provider_.get();
 }
 
 void WebDatabaseHostImpl::ValidateOrigin(const url::Origin& origin,
diff --git a/content/browser/renderer_host/web_database_host_impl.h b/content/browser/renderer_host/web_database_host_impl.h
index 546eac43..4f4b790 100644
--- a/content/browser/renderer_host/web_database_host_impl.h
+++ b/content/browser/renderer_host/web_database_host_impl.h
@@ -12,6 +12,8 @@
 #include "build/build_config.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "storage/browser/database/database_tracker.h"
 #include "third_party/blink/public/mojom/webdatabase/web_database.mojom.h"
 
@@ -29,9 +31,10 @@
                       scoped_refptr<storage::DatabaseTracker> db_tracker);
   ~WebDatabaseHostImpl() override;
 
-  static void Create(int process_id,
-                     scoped_refptr<storage::DatabaseTracker> db_tracker,
-                     blink::mojom::WebDatabaseHostRequest request);
+  static void Create(
+      int process_id,
+      scoped_refptr<storage::DatabaseTracker> db_tracker,
+      mojo::PendingReceiver<blink::mojom::WebDatabaseHost> receiver);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(WebDatabaseHostImplTest, BadMessagesUnauthorized);
@@ -148,7 +151,7 @@
   storage::DatabaseConnections database_connections_;
 
   // Interface to the render process WebDatabase.
-  blink::mojom::WebDatabasePtr database_provider_;
+  mojo::Remote<blink::mojom::WebDatabase> database_provider_;
 
   // The database tracker for the current browser context.
   const scoped_refptr<storage::DatabaseTracker> db_tracker_;
diff --git a/content/browser/renderer_host/web_database_host_impl_unittest.cc b/content/browser/renderer_host/web_database_host_impl_unittest.cc
index 28a728e..6961f8a 100644
--- a/content/browser/renderer_host/web_database_host_impl_unittest.cc
+++ b/content/browser/renderer_host/web_database_host_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "content/test/fake_mojo_message_dispatch_context.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "storage/common/database/database_identifier.h"
@@ -30,18 +31,6 @@
          base::ASCIIToUTF16("#") + suffix;
 }
 
-class FakeMojoMessageDispatchContext {
- public:
-  FakeMojoMessageDispatchContext()
-      : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) {}
-
- private:
-  mojo::Message dummy_message_;
-  mojo::internal::MessageDispatchContext context_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeMojoMessageDispatchContext);
-};
-
 }  // namespace
 
 class WebDatabaseHostImplTest : public ::testing::Test {
diff --git a/content/browser/scheduler/browser_io_task_environment.cc b/content/browser/scheduler/browser_io_task_environment.cc
index 75a18d9..d9f9cc5 100644
--- a/content/browser/scheduler/browser_io_task_environment.cc
+++ b/content/browser/scheduler/browser_io_task_environment.cc
@@ -33,7 +33,7 @@
   task_queues_ = std::make_unique<BrowserTaskQueues>(
       BrowserThread::IO, sequence_manager,
       sequence_manager->GetRealTimeDomain());
-  default_task_runner_ = task_queues_->CreateHandle().GetDefaultTaskRunner();
+  default_task_runner_ = task_queues_->GetHandle()->GetDefaultTaskRunner();
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/content/browser/scheduler/browser_io_task_environment.h b/content/browser/scheduler/browser_io_task_environment.h
index 7d47e10..ae6760b 100644
--- a/content/browser/scheduler/browser_io_task_environment.h
+++ b/content/browser/scheduler/browser_io_task_environment.h
@@ -48,7 +48,7 @@
   // tests.
   void SetAllowBlockingForTesting() { allow_blocking_for_testing_ = true; }
 
-  Handle CreateHandle() { return task_queues_->CreateHandle(); }
+  scoped_refptr<Handle> CreateHandle() { return task_queues_->GetHandle(); }
 
  private:
   explicit BrowserIOTaskEnvironment(
diff --git a/content/browser/scheduler/browser_io_task_environment_unittest.cc b/content/browser/scheduler/browser_io_task_environment_unittest.cc
index 106a7c0..19f405f81 100644
--- a/content/browser/scheduler/browser_io_task_environment_unittest.cc
+++ b/content/browser/scheduler/browser_io_task_environment_unittest.cc
@@ -21,14 +21,14 @@
 
   auto env = std::make_unique<BrowserIOTaskEnvironment>();
   auto handle = env->CreateHandle();
-  handle.EnableAllQueues();
+  handle->EnableAllQueues();
 
   base::Thread::Options options;
   options.task_environment = env.release();
   thread.StartWithOptions(options);
 
   auto runner =
-      handle.GetBrowserTaskRunner(BrowserTaskQueues::QueueType::kDefault);
+      handle->GetBrowserTaskRunner(BrowserTaskQueues::QueueType::kDefault);
 
   base::WaitableEvent event;
   runner->PostTask(FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
@@ -53,4 +53,4 @@
 }
 
 }  // namespace
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc
index 353d009a..15ae24ad 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -99,7 +99,7 @@
   base::RegisterTaskExecutor(BrowserTaskTraitsExtension::kExtensionId,
                              g_browser_task_executor);
   g_browser_task_executor->browser_ui_thread_handle_
-      .EnableAllExceptBestEffortQueues();
+      ->EnableAllExceptBestEffortQueues();
 
 #if defined(OS_ANDROID)
   base::PostTaskAndroid::SignalNativeSchedulerReady();
@@ -126,9 +126,9 @@
   DCHECK(g_browser_task_executor->browser_ui_thread_scheduler_);
   DCHECK(g_browser_task_executor->browser_io_task_environment_);
   g_browser_task_executor->browser_ui_thread_handle_
-      .PostFeatureListInitializationSetup();
+      ->PostFeatureListInitializationSetup();
   g_browser_task_executor->browser_io_thread_handle_
-      .PostFeatureListInitializationSetup();
+      ->PostFeatureListInitializationSetup();
 }
 
 // static
@@ -158,11 +158,11 @@
   switch (identifier) {
     case BrowserThread::UI:
       g_browser_task_executor->browser_ui_thread_handle_
-          .ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+          ->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
       break;
     case BrowserThread::IO: {
       g_browser_task_executor->browser_io_thread_handle_
-          .ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+          ->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
       break;
     }
     case BrowserThread::ID_COUNT:
@@ -220,11 +220,11 @@
 
   switch (id_and_queue.thread_id) {
     case BrowserThread::UI: {
-      return browser_ui_thread_handle_.GetBrowserTaskRunner(
+      return browser_ui_thread_handle_->GetBrowserTaskRunner(
           id_and_queue.queue_type);
     }
     case BrowserThread::IO:
-      return browser_io_thread_handle_.GetBrowserTaskRunner(
+      return browser_io_thread_handle_->GetBrowserTaskRunner(
           id_and_queue.queue_type);
     case BrowserThread::ID_COUNT:
       NOTREACHED();
@@ -251,15 +251,15 @@
 // static
 void BrowserTaskExecutor::EnableAllQueues() {
   DCHECK(g_browser_task_executor);
-  g_browser_task_executor->browser_ui_thread_handle_.EnableAllQueues();
-  g_browser_task_executor->browser_io_thread_handle_.EnableAllQueues();
+  g_browser_task_executor->browser_ui_thread_handle_->EnableAllQueues();
+  g_browser_task_executor->browser_io_thread_handle_->EnableAllQueues();
 }
 
 // static
 void BrowserTaskExecutor::InitializeIOThread() {
   DCHECK(g_browser_task_executor);
   g_browser_task_executor->browser_io_thread_handle_
-      .EnableAllExceptBestEffortQueues();
+      ->EnableAllExceptBestEffortQueues();
 }
 
 std::unique_ptr<BrowserProcessSubThread> BrowserTaskExecutor::CreateIOThread() {
@@ -288,4 +288,58 @@
   return io_thread;
 }
 
+#if DCHECK_IS_ON()
+
+// static
+void BrowserTaskExecutor::AddValidator(
+    const base::TaskTraits& traits,
+    BrowserTaskQueues::Validator* validator) {
+  if (!g_browser_task_executor)
+    return;
+
+  auto id_and_queue = g_browser_task_executor->GetThreadIdAndQueueType(traits);
+  switch (id_and_queue.thread_id) {
+    case BrowserThread::ID::IO:
+      g_browser_task_executor->browser_io_thread_handle_->AddValidator(
+          id_and_queue.queue_type, validator);
+      break;
+
+    case BrowserThread::ID::UI:
+      g_browser_task_executor->browser_ui_thread_handle_->AddValidator(
+          id_and_queue.queue_type, validator);
+      break;
+
+    case BrowserThread::ID::ID_COUNT:
+      NOTREACHED();
+      break;
+  }
+}
+
+// static
+void BrowserTaskExecutor::RemoveValidator(
+    const base::TaskTraits& traits,
+    BrowserTaskQueues::Validator* validator) {
+  if (!g_browser_task_executor)
+    return;
+
+  auto id_and_queue = g_browser_task_executor->GetThreadIdAndQueueType(traits);
+  switch (id_and_queue.thread_id) {
+    case BrowserThread::ID::IO:
+      g_browser_task_executor->browser_io_thread_handle_->RemoveValidator(
+          id_and_queue.queue_type, validator);
+      break;
+
+    case BrowserThread::ID::UI:
+      g_browser_task_executor->browser_ui_thread_handle_->RemoveValidator(
+          id_and_queue.queue_type, validator);
+      break;
+
+    case BrowserThread::ID::ID_COUNT:
+      NOTREACHED();
+      break;
+  }
+}
+
+#endif
+
 }  // namespace content
diff --git a/content/browser/scheduler/browser_task_executor.h b/content/browser/scheduler/browser_task_executor.h
index e72e31af..36fa0b8 100644
--- a/content/browser/scheduler/browser_task_executor.h
+++ b/content/browser/scheduler/browser_task_executor.h
@@ -130,6 +130,19 @@
       base::SingleThreadTaskRunnerThreadMode thread_mode) override;
 #endif  // defined(OS_WIN)
 
+#if DCHECK_IS_ON()
+  // Adds a Validator for |traits|. It is assumed the lifetime of |validator| is
+  // is longer than that of the BrowserTaskExecutor unless RemoveValidator
+  // is called. Does nothing if the BrowserTaskExecutor is not registered.
+  static void AddValidator(const base::TaskTraits& traits,
+                           BrowserTaskQueues::Validator* validator);
+
+  // Removes a Validator previously added by AddValidator. Does nothing if the
+  // BrowserTaskExecutor is not registered.
+  static void RemoveValidator(const base::TaskTraits& traits,
+                              BrowserTaskQueues::Validator* validator);
+#endif  // DCHECK_IS_ON()
+
  private:
   friend class BrowserTaskExecutorTest;
 
@@ -154,10 +167,10 @@
       const base::TaskTraits& traits) const;
 
   std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler_;
-  BrowserUIThreadScheduler::Handle browser_ui_thread_handle_;
+  scoped_refptr<BrowserUIThreadScheduler::Handle> browser_ui_thread_handle_;
 
   std::unique_ptr<BrowserIOTaskEnvironment> browser_io_task_environment_;
-  BrowserIOTaskEnvironment::Handle browser_io_thread_handle_;
+  scoped_refptr<BrowserIOTaskEnvironment::Handle> browser_io_thread_handle_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserTaskExecutor);
 };
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc
index b18159b..9b7673e 100644
--- a/content/browser/scheduler/browser_task_executor_unittest.cc
+++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -151,7 +151,7 @@
           BrowserUIThreadScheduler::CreateForTesting(sequence_manager(),
                                                      GetTimeDomain());
       DeferredInitFromSubclass(
-          browser_ui_thread_scheduler->GetHandle().GetBrowserTaskRunner(
+          browser_ui_thread_scheduler->GetHandle()->GetBrowserTaskRunner(
               QueueType::kDefault));
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
diff --git a/content/browser/scheduler/browser_task_queues.cc b/content/browser/scheduler/browser_task_queues.cc
index b95e60f..63d273f 100644
--- a/content/browser/scheduler/browser_task_queues.cc
+++ b/content/browser/scheduler/browser_task_queues.cc
@@ -108,13 +108,7 @@
 
 }  // namespace
 
-BrowserTaskQueues::Handle::Handle(Handle&&) = default;
-BrowserTaskQueues::Handle::Handle(const Handle&) = default;
 BrowserTaskQueues::Handle::~Handle() = default;
-BrowserTaskQueues::Handle& BrowserTaskQueues::Handle::operator=(Handle&&) =
-    default;
-BrowserTaskQueues::Handle& BrowserTaskQueues::Handle::operator=(const Handle&) =
-    default;
 
 BrowserTaskQueues::Handle::Handle(BrowserTaskQueues* outer)
     : outer_(outer),
@@ -152,18 +146,66 @@
           base::ScopedClosureRunner(std::move(on_pending_task_ran))));
 }
 
+#if DCHECK_IS_ON()
+
+void BrowserTaskQueues::Handle::AddValidator(QueueType queue_type,
+                                             Validator* validator) {
+  validator_sets_[static_cast<size_t>(queue_type)].AddValidator(validator);
+}
+
+void BrowserTaskQueues::Handle::RemoveValidator(QueueType queue_type,
+                                                Validator* validator) {
+  validator_sets_[static_cast<size_t>(queue_type)].RemoveValidator(validator);
+}
+
+BrowserTaskQueues::ValidatorSet::ValidatorSet() = default;
+
+BrowserTaskQueues::ValidatorSet::~ValidatorSet() {
+  // Note the queue has already been shut down by the time we're deleted so we
+  // don't need to unregister.
+  DCHECK(validators_.empty());
+}
+
+void BrowserTaskQueues::ValidatorSet::AddValidator(Validator* validator) {
+  base::AutoLock lock(lock_);
+  DCHECK_EQ(validators_.count(validator), 0u)
+      << "Validator added more than once";
+  validators_.insert(validator);
+}
+
+void BrowserTaskQueues::ValidatorSet::RemoveValidator(Validator* validator) {
+  base::AutoLock lock(lock_);
+  size_t num_erased = validators_.erase(validator);
+  DCHECK_EQ(num_erased, 1u) << "Validator not in set";
+}
+
+void BrowserTaskQueues::ValidatorSet::OnPostTask(base::Location from_here,
+                                                 base::TimeDelta delay) {
+  base::AutoLock lock(lock_);
+  for (Validator* validator : validators_) {
+    validator->ValidatePostTask(from_here);
+  }
+}
+
+void BrowserTaskQueues::ValidatorSet::OnQueueNextWakeUpChanged(
+    base::TimeTicks next_wake_up) {}
+
+#endif  // DCHECK_IS_ON()
+
+BrowserTaskQueues::QueueData::QueueData() = default;
+BrowserTaskQueues::QueueData::~QueueData() = default;
+
 BrowserTaskQueues::BrowserTaskQueues(
     BrowserThread::ID thread_id,
     base::sequence_manager::SequenceManager* sequence_manager,
     base::sequence_manager::TimeDomain* time_domain) {
-  for (size_t i = 0; i < browser_queues_and_voters_.size(); ++i) {
-    browser_queues_and_voters_[i].first = sequence_manager->CreateTaskQueue(
+  for (size_t i = 0; i < queue_data_.size(); ++i) {
+    queue_data_[i].task_queue = sequence_manager->CreateTaskQueue(
         base::sequence_manager::TaskQueue::Spec(
             GetTaskQueueName(thread_id, static_cast<QueueType>(i)))
             .SetTimeDomain(time_domain));
-    browser_queues_and_voters_[i].second =
-        browser_queues_and_voters_[i].first->CreateQueueEnabledVoter();
-    browser_queues_and_voters_[i].second->SetVoteToEnable(false);
+    queue_data_[i].voter = queue_data_[i].task_queue->CreateQueueEnabledVoter();
+    queue_data_[i].voter->SetVoteToEnable(false);
   }
 
   // Default task queue
@@ -189,11 +231,24 @@
           .SetTimeDomain(time_domain));
   run_all_pending_tasks_queue_->SetQueuePriority(
       QueuePriority::kBestEffortPriority);
+
+  handle_ = base::AdoptRef(new Handle(this));
+
+#if DCHECK_IS_ON()
+  for (size_t i = 0; i < queue_data_.size(); ++i) {
+    queue_data_[i].task_queue->SetObserver(&handle_->validator_sets_[i]);
+  }
+
+  // Treat the |default_task_queue_| the same as the USER_BLOCKING task queue
+  // from a validation point of view.
+  default_task_queue_->SetObserver(
+      &handle_->validator_sets_[static_cast<int>(QueueType::kUserBlocking)]);
+#endif
 }
 
 BrowserTaskQueues::~BrowserTaskQueues() {
-  for (auto& queue : browser_queues_and_voters_) {
-    queue.first->ShutdownTaskQueue();
+  for (auto& queue : queue_data_) {
+    queue.task_queue->ShutdownTaskQueue();
   }
   control_queue_->ShutdownTaskQueue();
   default_task_queue_->ShutdownTaskQueue();
@@ -205,8 +260,8 @@
 BrowserTaskQueues::CreateBrowserTaskRunners() const {
   std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes>
       task_runners;
-  for (size_t i = 0; i < browser_queues_and_voters_.size(); ++i) {
-    task_runners[i] = browser_queues_and_voters_[i].first->task_runner();
+  for (size_t i = 0; i < queue_data_.size(); ++i) {
+    task_runners[i] = queue_data_[i].task_queue->task_runner();
   }
   return task_runners;
 }
@@ -224,15 +279,15 @@
 }
 
 void BrowserTaskQueues::EnableAllQueues() {
-  for (size_t i = 0; i < browser_queues_and_voters_.size(); ++i) {
-    browser_queues_and_voters_[i].second->SetVoteToEnable(true);
+  for (size_t i = 0; i < queue_data_.size(); ++i) {
+    queue_data_[i].voter->SetVoteToEnable(true);
   }
 }
 
 void BrowserTaskQueues::EnableAllExceptBestEffortQueues() {
-  for (size_t i = 0; i < browser_queues_and_voters_.size(); ++i) {
+  for (size_t i = 0; i < queue_data_.size(); ++i) {
     if (i != static_cast<size_t>(QueueType::kBestEffort))
-      browser_queues_and_voters_[i].second->SetVoteToEnable(true);
+      queue_data_[i].voter->SetVoteToEnable(true);
   }
 }
 
@@ -250,8 +305,8 @@
 void BrowserTaskQueues::StartRunAllPendingTasksForTesting(
     base::ScopedClosureRunner on_pending_task_ran) {
   ++run_all_pending_nesting_level_;
-  for (const auto& queue : browser_queues_and_voters_) {
-    queue.first->InsertFence(InsertFencePosition::kNow);
+  for (const auto& queue : queue_data_) {
+    queue.task_queue->InsertFence(InsertFencePosition::kNow);
   }
   default_task_queue_->InsertFence(InsertFencePosition::kNow);
   run_all_pending_tasks_queue_->task_runner()->PostTask(
@@ -264,11 +319,11 @@
     base::ScopedClosureRunner on_pending_task_ran) {
   --run_all_pending_nesting_level_;
   if (run_all_pending_nesting_level_ == 0) {
-    for (const auto& queue : browser_queues_and_voters_) {
-      queue.first->RemoveFence();
+    for (const auto& queue : queue_data_) {
+      queue.task_queue->RemoveFence();
     }
     default_task_queue_->RemoveFence();
   }
 }
 
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/scheduler/browser_task_queues.h b/content/browser/scheduler/browser_task_queues.h
index 5c81ebe..3d2c1a7 100644
--- a/content/browser/scheduler/browser_task_queues.h
+++ b/content/browser/scheduler/browser_task_queues.h
@@ -6,9 +6,11 @@
 #define CONTENT_BROWSER_SCHEDULER_BROWSER_TASK_QUEUES_H_
 
 #include <array>
+#include <set>
 
 #include "base/memory/scoped_refptr.h"
 #include "base/task/sequence_manager/task_queue.h"
+#include "base/thread_annotations.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -63,20 +65,45 @@
   static constexpr size_t kNumQueueTypes =
       static_cast<size_t>(QueueType::kMaxValue) + 1;
 
+  // Per queue interface for validating PostTasks. Validation is only enabled if
+  // DCHECKs are on.
+  class Validator {
+   public:
+    virtual ~Validator() = default;
+
+    // This should check fail if there's a problem.
+    virtual void ValidatePostTask(const base::Location& from_here) = 0;
+  };
+
+#if DCHECK_IS_ON()
+  class CONTENT_EXPORT ValidatorSet
+      : public base::sequence_manager::TaskQueue::Observer {
+   public:
+    ValidatorSet();
+    ~ValidatorSet() override;
+
+    void AddValidator(Validator* validator);
+    void RemoveValidator(Validator* validator);
+
+    // base::sequence_manager::TaskQueue::Observer:
+    void OnPostTask(base::Location from_here, base::TimeDelta delay) override;
+    void OnQueueNextWakeUpChanged(base::TimeTicks next_wake_up) override;
+
+   private:
+    base::Lock lock_;
+    std::set<Validator*> validators_ GUARDED_BY(lock_);
+  };
+#endif
+
   // Handle to a BrowserTaskQueues instance that can be used from any thread
   // as all operations are thread safe.
   //
   // If the underlying BrowserTaskQueues is destroyed all methods of this
   // class become no-ops, that is it is safe for this class to outlive its
   // parent BrowserTaskQueues.
-  class CONTENT_EXPORT Handle {
+  class CONTENT_EXPORT Handle : public base::RefCountedThreadSafe<Handle> {
    public:
-    // Handles can be copied / moved around.
-    Handle(Handle&&) noexcept;
-    Handle(const Handle&);
-    ~Handle();
-    Handle& operator=(Handle&&) noexcept;
-    Handle& operator=(const Handle&);
+    REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
 
     // Returns the task runner that should be returned by
     // ThreadTaskRunnerHandle::Get().
@@ -118,9 +145,20 @@
     void ScheduleRunAllPendingTasksForTesting(
         base::OnceClosure on_pending_task_ran);
 
+    // Adds a Validator for |queue_type|.
+    void AddValidator(QueueType queue_type, Validator* validator);
+
+    // Removes a Validator previously added by AddValidator.
+    void RemoveValidator(QueueType queue_type, Validator* validator);
+
    private:
+    friend base::RefCountedThreadSafe<Handle>;
+
     // Only BrowserTaskQueues can create new instances
     friend class BrowserTaskQueues;
+
+    ~Handle();
+
     explicit Handle(BrowserTaskQueues* task_queues);
 
     // |outer_| can only be safely used from a task posted to one of the
@@ -130,6 +168,9 @@
     scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
     std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes>
         browser_task_runners_;
+#if DCHECK_IS_ON()
+    std::array<ValidatorSet, kNumQueueTypes> validator_sets_;
+#endif
   };
 
   // |sequence_manager| and |time_domain| must outlive this instance.
@@ -141,7 +182,7 @@
   // Destroys all queues.
   ~BrowserTaskQueues();
 
-  Handle CreateHandle() { return Handle(this); }
+  scoped_refptr<Handle> GetHandle() { return handle_; }
 
  private:
   // All these methods can only be called from the associated thread. To make
@@ -156,17 +197,20 @@
   void PostFeatureListInitializationSetup();
 
   base::sequence_manager::TaskQueue* GetBrowserTaskQueue(QueueType type) const {
-    return browser_queues_and_voters_[static_cast<size_t>(type)].first.get();
+    return queue_data_[static_cast<size_t>(type)].task_queue.get();
   }
 
   std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes>
   CreateBrowserTaskRunners() const;
 
-  using QueueVoterPair = std::pair<
-      scoped_refptr<base::sequence_manager::TaskQueue>,
-      std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter>>;
+  struct QueueData {
+    QueueData();
+    ~QueueData();
+    scoped_refptr<base::sequence_manager::TaskQueue> task_queue;
+    std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> voter;
+  };
+  std::array<QueueData, kNumQueueTypes> queue_data_;
 
-  std::array<QueueVoterPair, kNumQueueTypes> browser_queues_and_voters_;
   // Helper queue to make sure private methods run on the associated thread. the
   // control queue has maximum priority and will never be disabled.
   scoped_refptr<base::sequence_manager::TaskQueue> control_queue_;
@@ -180,6 +224,8 @@
   // Helper queue to run all pending tasks.
   scoped_refptr<base::sequence_manager::TaskQueue> run_all_pending_tasks_queue_;
   int run_all_pending_nesting_level_ = 0;
+
+  scoped_refptr<Handle> handle_;
 };
 
 }  // namespace content
diff --git a/content/browser/scheduler/browser_task_queues_unittest.cc b/content/browser/scheduler/browser_task_queues_unittest.cc
index 8575e652..9e5d6739 100644
--- a/content/browser/scheduler/browser_task_queues_unittest.cc
+++ b/content/browser/scheduler/browser_task_queues_unittest.cc
@@ -40,33 +40,33 @@
             BrowserThread::UI,
             sequence_manager_.get(),
             sequence_manager_->GetRealTimeDomain())),
-        handle_(queues_->CreateHandle()) {
-    sequence_manager_->SetDefaultTaskRunner(handle_.GetDefaultTaskRunner());
+        handle_(queues_->GetHandle()) {
+    sequence_manager_->SetDefaultTaskRunner(handle_->GetDefaultTaskRunner());
   }
 
   std::unique_ptr<SequenceManager> sequence_manager_;
   std::unique_ptr<BrowserTaskQueues> queues_;
-  BrowserTaskQueues::Handle handle_;
+  scoped_refptr<BrowserTaskQueues::Handle> handle_;
 };
 
 TEST_F(BrowserTaskQueuesTest, NoTaskRunsUntilQueuesAreEnabled) {
   StrictMockTask task;
   for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) {
-    handle_.GetBrowserTaskRunner(static_cast<QueueType>(i))
+    handle_->GetBrowserTaskRunner(static_cast<QueueType>(i))
         ->PostTask(FROM_HERE, task.Get());
   }
 
   {
     RunLoop run_loop;
-    handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+    handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
     run_loop.Run();
   }
 
-  handle_.EnableAllQueues();
+  handle_->EnableAllQueues();
 
   {
     RunLoop run_loop;
-    handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+    handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
     EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes);
     run_loop.Run();
   }
@@ -75,16 +75,16 @@
 TEST_F(BrowserTaskQueuesTest, OnlyDefaultQueueRunsTasksOnCreation) {
   StrictMockTask task;
   for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) {
-    handle_.GetBrowserTaskRunner(static_cast<QueueType>(i))
+    handle_->GetBrowserTaskRunner(static_cast<QueueType>(i))
         ->PostTask(FROM_HERE, task.Get());
   }
 
   StrictMockTask default_task;
-  handle_.GetDefaultTaskRunner()->PostTask(FROM_HERE, default_task.Get());
+  handle_->GetDefaultTaskRunner()->PostTask(FROM_HERE, default_task.Get());
 
   {
     RunLoop run_loop;
-    handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+    handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
     EXPECT_CALL(default_task, Run);
     run_loop.Run();
   }
@@ -93,30 +93,30 @@
 TEST_F(BrowserTaskQueuesTest, TasksRunWhenQueuesAreEnabled) {
   StrictMockTask task;
   for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) {
-    handle_.GetBrowserTaskRunner(static_cast<QueueType>(i))
+    handle_->GetBrowserTaskRunner(static_cast<QueueType>(i))
         ->PostTask(FROM_HERE, task.Get());
   }
 
   {
     RunLoop run_loop;
-    handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+    handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
     run_loop.Run();
   }
 
-  handle_.EnableAllQueues();
+  handle_->EnableAllQueues();
 
   {
     RunLoop run_loop;
-    handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+    handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
     EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes);
     run_loop.Run();
   }
 }
 
 TEST_F(BrowserTaskQueuesTest, SimplePosting) {
-  handle_.EnableAllQueues();
+  handle_->EnableAllQueues();
   scoped_refptr<base::SingleThreadTaskRunner> tq =
-      handle_.GetBrowserTaskRunner(QueueType::kDefault);
+      handle_->GetBrowserTaskRunner(QueueType::kDefault);
 
   StrictMockTask task_1;
   StrictMockTask task_2;
@@ -137,23 +137,23 @@
 }
 
 TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTesting) {
-  handle_.EnableAllQueues();
+  handle_->EnableAllQueues();
 
   StrictMockTask task;
   StrictMockTask followup_task;
   EXPECT_CALL(task, Run).WillOnce(Invoke([&]() {
     for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) {
-      handle_.GetBrowserTaskRunner(static_cast<QueueType>(i))
+      handle_->GetBrowserTaskRunner(static_cast<QueueType>(i))
           ->PostTask(FROM_HERE, followup_task.Get());
     }
   }));
 
-  handle_.GetBrowserTaskRunner(QueueType::kDefault)
+  handle_->GetBrowserTaskRunner(QueueType::kDefault)
       ->PostTask(FROM_HERE, task.Get());
 
   {
     RunLoop run_loop;
-    handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+    handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
     run_loop.Run();
   }
 
@@ -161,77 +161,77 @@
   EXPECT_CALL(followup_task, Run).Times(BrowserTaskQueues::kNumQueueTypes);
 
   RunLoop run_loop;
-  handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+  handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
   run_loop.Run();
 }
 
 TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTestingRunsAllTasks) {
   constexpr size_t kTasksPerPriority = 100;
-  handle_.EnableAllQueues();
+  handle_->EnableAllQueues();
 
   StrictMockTask task;
   EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes *
                                kTasksPerPriority);
   for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) {
     for (size_t j = 0; j < kTasksPerPriority; ++j) {
-      handle_.GetBrowserTaskRunner(static_cast<QueueType>(i))
+      handle_->GetBrowserTaskRunner(static_cast<QueueType>(i))
           ->PostTask(FROM_HERE, task.Get());
     }
   }
 
   RunLoop run_loop;
-  handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+  handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
   run_loop.Run();
 }
 
 TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTestingIsReentrant) {
-  handle_.EnableAllQueues();
+  handle_->EnableAllQueues();
   StrictMockTask task_1;
   StrictMockTask task_2;
   StrictMockTask task_3;
 
   EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() {
-    handle_.GetBrowserTaskRunner(QueueType::kDefault)
+    handle_->GetBrowserTaskRunner(QueueType::kDefault)
         ->PostTask(FROM_HERE, task_2.Get());
     RunLoop run_loop(RunLoop::Type::kNestableTasksAllowed);
-    handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+    handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
     run_loop.Run();
   }));
 
   EXPECT_CALL(task_2, Run).WillOnce(Invoke([&]() {
-    handle_.GetBrowserTaskRunner(QueueType::kDefault)
+    handle_->GetBrowserTaskRunner(QueueType::kDefault)
         ->PostTask(FROM_HERE, task_3.Get());
   }));
 
-  handle_.GetBrowserTaskRunner(QueueType::kDefault)
+  handle_->GetBrowserTaskRunner(QueueType::kDefault)
       ->PostTask(FROM_HERE, task_1.Get());
 
   RunLoop run_loop;
-  handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+  handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
   run_loop.Run();
 }
 
 TEST_F(BrowserTaskQueuesTest,
        RunAllPendingTasksForTestingIgnoresBestEffortIfNotEnabled) {
-  handle_.EnableAllExceptBestEffortQueues();
+  handle_->EnableAllExceptBestEffortQueues();
   StrictMockTask best_effort_task;
   StrictMockTask default_task;
 
-  handle_.GetBrowserTaskRunner(QueueType::kBestEffort)
+  handle_->GetBrowserTaskRunner(QueueType::kBestEffort)
       ->PostTask(FROM_HERE, best_effort_task.Get());
-  handle_.GetBrowserTaskRunner(QueueType::kDefault)
+  handle_->GetBrowserTaskRunner(QueueType::kDefault)
       ->PostTask(FROM_HERE, default_task.Get());
 
   EXPECT_CALL(default_task, Run);
 
   RunLoop run_loop;
-  handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+  handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
   run_loop.Run();
 }
 
 TEST_F(BrowserTaskQueuesTest,
        RunAllPendingTasksForTestingRunsBestEffortTasksWhenEnabled) {
-  handle_.EnableAllExceptBestEffortQueues();
+  handle_->EnableAllExceptBestEffortQueues();
   StrictMockTask task_1;
   StrictMockTask task_2;
   StrictMockTask task_3;
@@ -239,35 +239,35 @@
   EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() {
     // This task should not run as it is posted after the
     // RunAllPendingTasksForTesting() call
-    handle_.GetBrowserTaskRunner(QueueType::kBestEffort)
+    handle_->GetBrowserTaskRunner(QueueType::kBestEffort)
         ->PostTask(FROM_HERE, task_3.Get());
-    handle_.EnableAllQueues();
+    handle_->EnableAllQueues();
   }));
   EXPECT_CALL(task_2, Run);
 
-  handle_.GetBrowserTaskRunner(QueueType::kDefault)
+  handle_->GetBrowserTaskRunner(QueueType::kDefault)
       ->PostTask(FROM_HERE, task_1.Get());
-  handle_.GetBrowserTaskRunner(QueueType::kBestEffort)
+  handle_->GetBrowserTaskRunner(QueueType::kBestEffort)
       ->PostTask(FROM_HERE, task_2.Get());
 
   RunLoop run_loop;
-  handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+  handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
   run_loop.Run();
 }
 
 TEST_F(BrowserTaskQueuesTest, HandleStillWorksWhenQueuesDestroyed) {
-  handle_.EnableAllQueues();
+  handle_->EnableAllQueues();
   StrictMockTask task;
   queues_.reset();
 
   for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) {
     EXPECT_FALSE(
-        handle_.GetBrowserTaskRunner(static_cast<QueueType>(i))
+        handle_->GetBrowserTaskRunner(static_cast<QueueType>(i))
             ->PostTask(FROM_HERE, base::BindLambdaForTesting([]() {})));
   }
 
   RunLoop run_loop;
-  handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
+  handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
   run_loop.Run();
 }
 
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.cc b/content/browser/scheduler/browser_ui_thread_scheduler.cc
index da0b5cd..4fd4b3f 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.cc
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.cc
@@ -43,9 +43,10 @@
       task_queues_(BrowserThread::UI,
                    owned_sequence_manager_.get(),
                    owned_sequence_manager_->GetRealTimeDomain()),
-      handle_(task_queues_.CreateHandle()) {
+      handle_(task_queues_.GetHandle()) {
   CommonSequenceManagerSetup(owned_sequence_manager_.get());
-  owned_sequence_manager_->SetDefaultTaskRunner(handle_.GetDefaultTaskRunner());
+  owned_sequence_manager_->SetDefaultTaskRunner(
+      handle_->GetDefaultTaskRunner());
 
   owned_sequence_manager_->BindToMessagePump(
       base::MessagePump::Create(base::MessagePump::Type::UI));
@@ -55,7 +56,7 @@
     base::sequence_manager::SequenceManager* sequence_manager,
     base::sequence_manager::TimeDomain* time_domain)
     : task_queues_(BrowserThread::UI, sequence_manager, time_domain),
-      handle_(task_queues_.CreateHandle()) {
+      handle_(task_queues_.GetHandle()) {
   CommonSequenceManagerSetup(sequence_manager);
 }
 
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.h b/content/browser/scheduler/browser_ui_thread_scheduler.h
index 565f0a8..b1681be 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.h
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.h
@@ -39,7 +39,7 @@
 
   using QueueType = BrowserTaskQueues::QueueType;
 
-  Handle GetHandle() const { return handle_; }
+  scoped_refptr<Handle> GetHandle() const { return handle_; }
 
  private:
   friend class BrowserTaskExecutor;
@@ -57,7 +57,7 @@
       owned_sequence_manager_;
 
   BrowserTaskQueues task_queues_;
-  Handle handle_;
+  scoped_refptr<Handle> handle_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserUIThreadScheduler);
 };
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler_unittest.cc b/content/browser/scheduler/browser_ui_thread_scheduler_unittest.cc
index fb88e16..7d88388 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler_unittest.cc
+++ b/content/browser/scheduler/browser_ui_thread_scheduler_unittest.cc
@@ -44,9 +44,9 @@
 TEST(BrowserUIThreadSchedulerTest, DestructorPostChainDuringShutdown) {
   auto browser_ui_thread_scheduler_ =
       std::make_unique<BrowserUIThreadScheduler>();
-  browser_ui_thread_scheduler_->GetHandle().EnableAllQueues();
+  browser_ui_thread_scheduler_->GetHandle()->EnableAllQueues();
   auto task_queue =
-      browser_ui_thread_scheduler_->GetHandle().GetBrowserTaskRunner(
+      browser_ui_thread_scheduler_->GetHandle()->GetBrowserTaskRunner(
           BrowserUIThreadScheduler::QueueType::kDefault);
 
   bool run = false;
diff --git a/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io.cc b/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io.cc
new file mode 100644
index 0000000..79cb049a
--- /dev/null
+++ b/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io.cc
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io.h"
+
+#include "base/bind.h"
+#include "base/trace_event/trace_event.h"
+#include "content/browser/scheduler/browser_task_executor.h"
+
+namespace content {
+
+ScopedDoNotUseUIDefaultQueueFromIO::ScopedDoNotUseUIDefaultQueueFromIO(
+    const base::Location& scoped_location)
+    : scoped_location_(scoped_location) {
+  TRACE_EVENT_BEGIN0("toplevel", "ScopedDoNotUseUIDefaultQueueFromIO");
+#if DCHECK_IS_ON()
+  // Only has an effect in the browser process.
+  BrowserTaskExecutor::AddValidator({BrowserThread::UI}, this);
+#endif
+}
+
+void ScopedDoNotUseUIDefaultQueueFromIO::ValidatePostTask(
+    const base::Location& post_task_location) {
+  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO))
+      << "It's prohibited by ScopedDoNotUseUIDefaultQueueFromIO at "
+      << scoped_location_.ToString()
+      << " to post to the UI thread's default queue at this time due to the "
+         "risk of accidental task reordering. Please specify a non-default "
+         "BrowserTaskType. See PostTask "
+      << post_task_location.ToString();
+}
+
+ScopedDoNotUseUIDefaultQueueFromIO::~ScopedDoNotUseUIDefaultQueueFromIO() {
+  TRACE_EVENT_END0("toplevel", "ScopedDoNotUseUIDefaultQueueFromIO");
+#if DCHECK_IS_ON()
+  BrowserTaskExecutor::RemoveValidator({BrowserThread::UI}, this);
+#endif
+}
+
+}  // namespace content
diff --git a/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io.h b/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io.h
new file mode 100644
index 0000000..340d0d6
--- /dev/null
+++ b/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SCHEDULER_SCOPED_DO_NOT_USE_UI_DEFAULT_QUEUE_FROM_IO_H_
+#define CONTENT_BROWSER_SCHEDULER_SCOPED_DO_NOT_USE_UI_DEFAULT_QUEUE_FROM_IO_H_
+
+#include "base/location.h"
+#include "content/browser/scheduler/browser_task_queues.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// When ScopedDoNotUseUIDefaultQueueFromIO exists, it's prohibited to post to
+// the UI thread's default task runner from the IO thread.
+class CONTENT_EXPORT ScopedDoNotUseUIDefaultQueueFromIO
+    : public BrowserTaskQueues::Validator {
+ public:
+  explicit ScopedDoNotUseUIDefaultQueueFromIO(const base::Location& location);
+
+  ~ScopedDoNotUseUIDefaultQueueFromIO() override;
+
+ private:
+  void ValidatePostTask(const base::Location& post_task_location) override;
+
+  const base::Location scoped_location_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SCHEDULER_SCOPED_DO_NOT_USE_UI_DEFAULT_QUEUE_FROM_IO_H_
diff --git a/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io_unittest.cc b/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io_unittest.cc
new file mode 100644
index 0000000..9c7ac2cc
--- /dev/null
+++ b/content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io_unittest.cc
@@ -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.
+
+#include "content/browser/scheduler/scoped_do_not_use_ui_default_queue_from_io.h"
+
+#include "base/bind_helpers.h"
+#include "base/task/post_task.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/gtest_util.h"
+#include "build/build_config.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+#if defined(THREAD_SANITIZER)
+// The combination of EXPECT_DCHECK_DEATH and TSan doesn't work, although we get
+// the expected DCHECK with TSan builds.
+#define MAYBE_BadPostFromIO DISABLED_BadPostFromIO
+#elif defined(OS_MACOSX)
+// This test fails to DCHECK on mac release builds for reasons unknown.
+#define MAYBE_BadPostFromIO DISABLED_BadPostFromIO
+#else
+#define MAYBE_BadPostFromIO BadPostFromIO
+#endif
+
+TEST(ScopedDoNotUseUIDefaultQueueFromIO, MAYBE_BadPostFromIO) {
+  EXPECT_DCHECK_DEATH({
+    TestBrowserThreadBundle thread_bundle;
+    base::RunLoop run_loop;
+
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+          ScopedDoNotUseUIDefaultQueueFromIO do_not_post_to_ui_default(
+              FROM_HERE);
+
+          // Posting to the UI thread with no other traits is prohibited.
+          base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
+                                   base::DoNothing());
+        }));
+
+    run_loop.Run();
+  });
+}
+
+TEST(ScopedDoNotUseUIDefaultQueueFromIO, PostFromIO) {
+  TestBrowserThreadBundle thread_bundle;
+
+  base::RunLoop run_loop;
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+        {
+          ScopedDoNotUseUIDefaultQueueFromIO do_not_post_to_ui_default(
+              FROM_HERE);
+
+          // Posting with non default BrowserTaskType is OK.
+          base::PostTaskWithTraits(
+              FROM_HERE, {BrowserThread::IO, BrowserTaskType::kNavigation},
+              base::DoNothing());
+
+          // Posting to the IO thread default queue is OK.
+          base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
+                                   base::DoNothing());
+        }
+
+        // After |do_not_post_to_ui_default| has gone out of scope it's fine to
+        // post to the UI thread's default queue again.
+        base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
+                                 run_loop.QuitClosure());
+      }));
+
+  run_loop.Run();
+}
+
+TEST(ScopedDoNotUseUIDefaultQueueFromIO, PostFromUI) {
+  TestBrowserThreadBundle thread_bundle(
+      TestBrowserThreadBundle::REAL_IO_THREAD);
+  base::RunLoop run_loop;
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::UI}, base::BindLambdaForTesting([&]() {
+        ScopedDoNotUseUIDefaultQueueFromIO do_not_post_to_ui_default(FROM_HERE);
+
+        // It's fine to post from the UI thread.
+        base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
+                                 run_loop.QuitClosure());
+      }));
+
+  run_loop.Run();
+}
+
+}  // namespace content
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index 8e833827..213399fe 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -43,6 +43,7 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/gpu_service_registry.h"
 #include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/service_process_host.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
diff --git a/content/browser/service_manager/service_manager_context_browsertest.cc b/content/browser/service_manager/service_manager_context_browsertest.cc
index b853867..ce39c6f 100644
--- a/content/browser/service_manager/service_manager_context_browsertest.cc
+++ b/content/browser/service_manager/service_manager_context_browsertest.cc
@@ -5,41 +5,28 @@
 #include <string>
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/macros.h"
-#include "base/process/launch.h"
 #include "base/run_loop.h"
-#include "base/test/launcher/test_launcher.h"
-#include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "content/public/browser/system_connector.h"
 #include "content/public/test/content_browser_test.h"
-#include "content/public/test/test_launcher.h"
-#include "content/shell/browser/shell_content_browser_client.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/data_decoder/public/mojom/constants.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/mojom/constants.mojom.h"
 #include "services/service_manager/public/mojom/service_manager.mojom.h"
-#include "services/test/echo/public/mojom/echo.mojom.h"
-#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::HasSubstr;
-using testing::Not;
-
 namespace content {
 namespace {
 
-bool ShouldTerminateOnServiceQuit(const service_manager::Identity& id) {
-  return id.name() == echo::mojom::kServiceName;
-}
-
 class ServiceInstanceListener
     : public service_manager::mojom::ServiceManagerListener {
  public:
   explicit ServiceInstanceListener(
       service_manager::mojom::ServiceManagerListenerRequest request)
-      : binding_(this, std::move(request)) {}
+      : receiver_(this, std::move(request)) {}
   ~ServiceInstanceListener() override = default;
 
   void WaitForInit() {
@@ -85,7 +72,7 @@
   base::RunLoop* pid_wait_loop_ = nullptr;
   std::string service_expecting_pid_;
   uint32_t pid_received_ = 0;
-  mojo::Binding<service_manager::mojom::ServiceManagerListener> binding_;
+  mojo::Receiver<service_manager::mojom::ServiceManagerListener> receiver_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceInstanceListener);
 };
@@ -94,55 +81,6 @@
 
 using ServiceManagerContextBrowserTest = ContentBrowserTest;
 
-// "MANUAL" tests only run when kRunManualTestsFlag is set.
-IN_PROC_BROWSER_TEST_F(ServiceManagerContextBrowserTest,
-                       MANUAL_TerminateOnServiceQuit) {
-  ShellContentBrowserClient::Get()
-      ->set_should_terminate_on_service_quit_callback(
-          base::BindOnce(&ShouldTerminateOnServiceQuit));
-
-  // Launch a test service.
-  echo::mojom::EchoPtr echo_ptr;
-  GetSystemConnector()->BindInterface(echo::mojom::kServiceName, &echo_ptr);
-
-  base::RunLoop loop;
-  // Terminate the service. Browser should exit in response with an error.
-  echo_ptr->Quit();
-  loop.Run();
-}
-
-// Flaky timeout on Linux and Chrome OS ASAN: http://crbug.com/803814,
-// crbug.com/804113.
-#if (defined(OS_CHROMEOS) || defined(OS_LINUX)) && defined(ADDRESS_SANITIZER)
-#define MAYBE_TerminateOnServiceQuit DISABLED_TerminateOnServiceQuit
-#elif defined(OS_WIN)
-// crbug.com/804937.  Causes failures when test times out even if retry passes.
-#define MAYBE_TerminateOnServiceQuit DISABLED_TerminateOnServiceQuit
-#else
-#define MAYBE_TerminateOnServiceQuit TerminateOnServiceQuit
-#endif
-TEST(ServiceManagerContextTest, MAYBE_TerminateOnServiceQuit) {
-  // Run the above test and collect the test output.
-  base::CommandLine new_test =
-      base::CommandLine(base::CommandLine::ForCurrentProcess()->GetProgram());
-  new_test.AppendSwitchASCII(
-      base::kGTestFilterFlag,
-      "ServiceManagerContextBrowserTest.MANUAL_TerminateOnServiceQuit");
-  new_test.AppendSwitch(kRunManualTestsFlag);
-  new_test.AppendSwitch(kSingleProcessTestsFlag);
-
-  base::ScopedAllowBlockingForTesting allow;
-  std::string output;
-  base::GetAppOutputAndError(new_test, &output);
-
-#if !defined(OS_ANDROID)
-  // The test output contains the failure message.
-  // TODO(jamescook): The |output| is always empty on Android. I suspect the
-  // test runner does logs collection after the program has exited.
-  EXPECT_THAT(output, HasSubstr("Terminating because service 'echo' quit"));
-#endif
-}
-
 IN_PROC_BROWSER_TEST_F(ServiceManagerContextBrowserTest,
                        ServiceProcessReportsPID) {
   service_manager::mojom::ServiceManagerListenerPtr listener_proxy;
@@ -155,12 +93,12 @@
   service_manager->AddListener(std::move(listener_proxy));
   listener.WaitForInit();
 
-  echo::mojom::EchoPtr echo_ptr;
-  connector->BindInterface(echo::mojom::kServiceName, &echo_ptr);
+  connector->WarmService(service_manager::ServiceFilter::ByName(
+      data_decoder::mojom::kServiceName));
 
   // PID should be non-zero, confirming that it was indeed properly reported to
   // the Service Manager. If not reported at all, this will hang.
-  EXPECT_GT(listener.WaitForServicePID(echo::mojom::kServiceName), 0u);
+  EXPECT_GT(listener.WaitForServicePID(data_decoder::mojom::kServiceName), 0u);
 }
 
 }  // namespace content
diff --git a/content/browser/service_process_host_browsertest.cc b/content/browser/service_process_host_browsertest.cc
new file mode 100644
index 0000000..73e93df
--- /dev/null
+++ b/content/browser/service_process_host_browsertest.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "content/public/browser/service_process_host.h"
+#include "content/public/test/content_browser_test.h"
+#include "services/test/echo/public/mojom/echo.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+using ServiceProcessHostBrowserTest = ContentBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, Launch) {
+  mojo::Remote<echo::mojom::EchoService> echo_service;
+  auto host = ServiceProcessHost::Launch(&echo_service);
+
+  const std::string kTestString =
+      "Aurora borealis! At this time of year? At this time of day? "
+      "In this part of the country? Localized entirely within your kitchen?";
+  base::RunLoop loop;
+  echo_service->EchoString(
+      kTestString,
+      base::BindLambdaForTesting([&](const std::string& echoed_input) {
+        EXPECT_EQ(kTestString, echoed_input);
+        loop.Quit();
+      }));
+  loop.Run();
+}
+
+}  // namespace content
diff --git a/content/browser/service_process_host_impl.cc b/content/browser/service_process_host_impl.cc
new file mode 100644
index 0000000..e1c61ab8
--- /dev/null
+++ b/content/browser/service_process_host_impl.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 "content/browser/service_process_host_impl.h"
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "content/browser/utility_process_host.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
+
+namespace content {
+
+class ServiceProcessHostImpl::IOThreadState {
+ public:
+  IOThreadState(ServiceProcessHostImpl::Options options,
+                const std::string& service_interface_name,
+                mojo::PendingReceiver<mojom::ServiceControl> receiver) {
+    UtilityProcessHost* host = new UtilityProcessHost();
+    host->SetName(!options.display_name.empty()
+                      ? options.display_name
+                      : base::UTF8ToUTF16(service_interface_name));
+    host->SetMetricsName(service_interface_name);
+    host->SetSandboxType(options.sandbox_type);
+    host->Start();
+    host->BindInterface(mojom::ServiceControl::Name_, receiver.PassPipe());
+    utility_process_host_ = host->AsWeakPtr();
+  }
+
+  ~IOThreadState() = default;
+
+ private:
+  base::WeakPtr<UtilityProcessHost> utility_process_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(IOThreadState);
+};
+
+ServiceProcessHostImpl::ServiceProcessHostImpl(
+    base::StringPiece service_interface_name,
+    mojo::ScopedMessagePipeHandle receiving_pipe,
+    Options options)
+    : io_thread_state_(
+          base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}),
+          std::move(options),
+          service_interface_name.as_string(),
+          remote_control_.BindNewPipeAndPassReceiver()) {
+  remote_control_->BindServiceInterface(mojo::GenericPendingReceiver(
+      service_interface_name, std::move(receiving_pipe)));
+}
+
+ServiceProcessHostImpl::~ServiceProcessHostImpl() = default;
+
+// static
+std::unique_ptr<ServiceProcessHost> ServiceProcessHost::Launch(
+    base::StringPiece service_interface_name,
+    mojo::ScopedMessagePipeHandle receiving_pipe,
+    Options options) {
+  return std::make_unique<ServiceProcessHostImpl>(
+      service_interface_name, std::move(receiving_pipe), std::move(options));
+}
+
+}  // namespace content
diff --git a/content/browser/service_process_host_impl.h b/content/browser/service_process_host_impl.h
new file mode 100644
index 0000000..f365d97a
--- /dev/null
+++ b/content/browser/service_process_host_impl.h
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium 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_SERVICE_PROCESS_HOST_IMPL_H_
+#define CONTENT_BROWSER_SERVICE_PROCESS_HOST_IMPL_H_
+
+#include "base/macros.h"
+#include "base/threading/sequence_bound.h"
+#include "content/common/service_control.mojom.h"
+#include "content/public/browser/service_process_host.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace content {
+
+// Implementation of the public ServiceProcessHost API. These objects should
+// only be created on the UI thread via calls to ServiceProcessHost::Create().
+class ServiceProcessHostImpl : public ServiceProcessHost {
+ public:
+  ServiceProcessHostImpl(base::StringPiece service_interface_name,
+                         mojo::ScopedMessagePipeHandle receiving_pipe,
+                         Options options);
+  ~ServiceProcessHostImpl() override;
+
+ private:
+  class IOThreadState;
+
+  mojo::Remote<mojom::ServiceControl> remote_control_;
+  base::SequenceBound<IOThreadState> io_thread_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceProcessHostImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SERVICE_PROCESS_HOST_IMPL_H_
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 02cd39d9..6c9ecfa 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -460,10 +460,19 @@
 
 }  // namespace
 
-class ServiceWorkerBrowserTest : public ContentBrowserTest {
+class ServiceWorkerBrowserTest : public ContentBrowserTest,
+                                 public testing::WithParamInterface<bool> {
  protected:
   using self = ServiceWorkerBrowserTest;
 
+  void SetUp() override {
+    if (ShouldDisableOffMainScriptFetch()) {
+      feature_list_.InitAndDisableFeature(
+          blink::features::kOffMainThreadServiceWorkerScriptFetch);
+    }
+    ContentBrowserTest::SetUp();
+  }
+
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
     StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
@@ -501,7 +510,11 @@
   ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
   ServiceWorkerContext* public_context() { return wrapper(); }
 
+  base::test::ScopedFeatureList feature_list_;
+
  private:
+  bool ShouldDisableOffMainScriptFetch() { return GetParam(); }
+
   scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
 };
 
@@ -1057,7 +1070,7 @@
   std::vector<ServiceWorkerRemoteProviderEndpoint> remote_endpoints_;
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartAndStop) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, StartAndStop) {
   StartServerAndNavigateToSetup();
   RunOnIOThread(base::BindOnce(&self::SetUpRegistrationOnIOThread,
                                base::Unretained(this),
@@ -1084,7 +1097,7 @@
 
 // TODO(lunalu): remove this test when blink side use counter is removed
 // (crbug.com/811948).
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        DropCountsOnBlinkUseCounter) {
   StartServerAndNavigateToSetup();
   RunOnIOThread(base::BindOnce(&self::SetUpRegistrationOnIOThread,
@@ -1116,7 +1129,7 @@
                          "Blink.UseCounter.Features"));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartNotFound) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, StartNotFound) {
   StartServerAndNavigateToSetup();
   RunOnIOThread(base::BindOnce(&self::SetUpRegistrationOnIOThread,
                                base::Unretained(this),
@@ -1126,7 +1139,7 @@
   StartWorker(blink::ServiceWorkerStatusCode::kErrorNetwork);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, ReadResourceFailure) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, ReadResourceFailure) {
   StartServerAndNavigateToSetup();
 
   // Create a registration with an active version.
@@ -1159,7 +1172,7 @@
   EXPECT_TRUE(registration_->is_uninstalled());
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        ReadResourceFailure_WaitingWorker) {
   StartServerAndNavigateToSetup();
   // Create a registration and active version.
@@ -1205,20 +1218,20 @@
   EXPECT_TRUE(registration_->is_uninstalled());
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Install) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, Install) {
   StartServerAndNavigateToSetup();
   InstallTestHelper("/service_worker/worker.js",
                     blink::ServiceWorkerStatusCode::kOk);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        InstallWithWaitUntil_Fulfilled) {
   StartServerAndNavigateToSetup();
   InstallTestHelper("/service_worker/worker_install_fulfilled.js",
                     blink::ServiceWorkerStatusCode::kOk);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        InstallWithFetchHandler) {
   StartServerAndNavigateToSetup();
   InstallTestHelper("/service_worker/fetch_event.js",
@@ -1227,7 +1240,7 @@
             version_->fetch_handler_existence());
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        InstallWithoutFetchHandler) {
   StartServerAndNavigateToSetup();
   InstallTestHelper("/service_worker/worker.js",
@@ -1238,7 +1251,7 @@
 
 // Check that ServiceWorker script requests set a "Service-Worker: script"
 // header.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        ServiceWorkerScriptHeader) {
   embedded_test_server()->RegisterRequestHandler(
       base::BindRepeating(&VerifyServiceWorkerHeaderInRequest));
@@ -1247,7 +1260,7 @@
                     blink::ServiceWorkerStatusCode::kOk);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        Activate_NoEventListener) {
   StartServerAndNavigateToSetup();
   InstallTestHelper("/service_worker/worker.js",
@@ -1256,7 +1269,7 @@
   ASSERT_EQ(ServiceWorkerVersion::ACTIVATING, version_->status());
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Activate_Rejected) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, Activate_Rejected) {
   StartServerAndNavigateToSetup();
   InstallTestHelper("/service_worker/worker_activate_rejected.js",
                     blink::ServiceWorkerStatusCode::kOk);
@@ -1264,7 +1277,7 @@
       blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        InstallWithWaitUntil_Rejected) {
   StartServerAndNavigateToSetup();
   InstallTestHelper(
@@ -1272,7 +1285,7 @@
       blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        InstallWithWaitUntil_RejectConsoleMessage) {
   StartServerAndNavigateToSetup();
   RunOnIOThread(base::BindOnce(&self::SetUpRegistrationOnIOThread,
@@ -1306,7 +1319,7 @@
 }
 
 // Tests starting an installed classic service worker while offline.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        StartInstalledClassicScriptWhileOffline) {
   StartServerAndNavigateToSetup();
 
@@ -1325,7 +1338,7 @@
 }
 
 // Tests starting an installed module service worker while offline.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        StartInstalledModuleScriptWhileOffline) {
   StartServerAndNavigateToSetup();
 
@@ -1357,7 +1370,7 @@
   base::OnceClosure quit_;
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, TimeoutStartingWorker) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, TimeoutStartingWorker) {
   StartServerAndNavigateToSetup();
   RunOnIOThread(base::BindOnce(&self::SetUpRegistrationOnIOThread,
                                base::Unretained(this),
@@ -1394,7 +1407,7 @@
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, status.value());
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, TimeoutWorkerInEvent) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, TimeoutWorkerInEvent) {
   StartServerAndNavigateToSetup();
   RunOnIOThread(
       base::BindOnce(&self::SetUpRegistrationOnIOThread, base::Unretained(this),
@@ -1430,7 +1443,7 @@
             status.value());
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchEvent_Response) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, FetchEvent_Response) {
   StartServerAndNavigateToSetup();
   ServiceWorkerFetchDispatcher::FetchEventResult result;
   blink::mojom::FetchAPIResponsePtr response;
@@ -1461,7 +1474,7 @@
 }
 
 // Tests for response type when a service worker does respondWith(fetch()).
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        FetchEvent_ResponseNetwork) {
   const char* kPath = "/service_worker/http_cache.html";
 
@@ -1493,7 +1506,7 @@
             response2->response_source);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        FetchEvent_ResponseViaCache) {
   const char* kPath = "/service_worker/empty.html";
   StartServerAndNavigateToSetup();
@@ -1525,7 +1538,7 @@
             response2->response_source);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        FetchEvent_respondWithRejection) {
   StartServerAndNavigateToSetup();
   ServiceWorkerFetchDispatcher::FetchEventResult result;
@@ -1568,7 +1581,7 @@
 // clock being reasonable during the test. So it might break on daylight savings
 // leap or something:
 // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/C3EvKPrb0XM/4Jv02SpNYncJ
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        UpdateBypassesCacheAfter24Hours) {
   const char kScope[] = "/service_worker/handle_fetch.html";
   const char kWorkerUrl[] = "/service_worker/update_worker.js";
@@ -1668,7 +1681,7 @@
   bool data_saver_enabled_;
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchWithSaveData) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, FetchWithSaveData) {
   embedded_test_server()->RegisterRequestHandler(
       base::BindRepeating(&VerifySaveDataHeaderInRequest));
   StartServerAndNavigateToSetup();
@@ -1682,7 +1695,7 @@
   SetBrowserClientForTesting(old_client);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
                        RequestWorkerScriptWithSaveData) {
   embedded_test_server()->RegisterRequestHandler(
       base::BindRepeating(&VerifySaveDataHeaderInRequest));
@@ -1697,7 +1710,7 @@
   SetBrowserClientForTesting(old_client);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchWithoutSaveData) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, FetchWithoutSaveData) {
   embedded_test_server()->RegisterRequestHandler(
       base::BindRepeating(&VerifySaveDataHeaderNotInRequest));
   StartServerAndNavigateToSetup();
@@ -1712,7 +1725,7 @@
 // Tests the |top_frame_origin| and |request_initiator| on the main resource and
 // subresource requests from service workers, in order to ensure proper handling
 // by the SplitCache. See https://crbug.com/918868.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, RequestOrigin) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, RequestOrigin) {
   embedded_test_server()->StartAcceptingConnections();
 
   // To make things tricky about |top_frame_origin|, this test navigates to a
@@ -1767,7 +1780,7 @@
   request_origin_expectation_waiter.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, FetchPageWithSaveData) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, FetchPageWithSaveData) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] = "/service_worker/handle_fetch.html";
   const char kWorkerUrl[] = "/service_worker/add_save_data_to_title.js";
@@ -1806,7 +1819,7 @@
 // Tests that when data saver is enabled and a cross-origin fetch by a webpage
 // is intercepted by a serviceworker, and the serviceworker does a fetch, the
 // preflight request does not have save-data in Access-Control-Request-Headers.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, CrossOriginFetchWithSaveData) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, CrossOriginFetchWithSaveData) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] = "/service_worker/fetch_cross_origin.html";
   const char kWorkerUrl[] = "/service_worker/fetch_event_pass_through.js";
@@ -1853,7 +1866,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest,
                        FetchPageWithSaveDataPassThroughOnFetch) {
   const char kPageUrl[] = "/service_worker/pass_through_fetch.html";
   const char kWorkerUrl[] = "/service_worker/fetch_event_pass_through.js";
@@ -1892,7 +1905,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, Reload) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, Reload) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] = "/service_worker/reload.html";
   const char kWorkerUrl[] = "/service_worker/fetch_event_reload.js";
@@ -1932,7 +1945,7 @@
 // renderer should continue processing events on the service worker instead of
 // waiting for termination or an event from the browser. Regression test for
 // https://crbug.com/878667.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, IdleTimerWithDevTools) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, IdleTimerWithDevTools) {
   StartServerAndNavigateToSetup();
 
   // Register a service worker.
@@ -2238,7 +2251,7 @@
         "            result => event.source.postMessage(result)));\n"
         "  });";
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest, NetworkFallback) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest, NetworkFallback) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
   const char kPage[] = "<title>PASS</title>Hello world.";
@@ -2277,7 +2290,7 @@
   EXPECT_GT(fallback_count, 0);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest, SetHeaderValue) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest, SetHeaderValue) {
   const std::string kPageUrl = "/service_worker/navigation_preload.html";
   const std::string kWorkerUrl = "/service_worker/navigation_preload.js";
   const std::string kPage = "<title>FROM_SERVER</title>";
@@ -2362,7 +2375,7 @@
   ASSERT_EQ(0, GetRequestCount(kPageUrl4));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        RespondWithNavigationPreload) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2393,7 +2406,7 @@
             request_log_[kPageUrl][0].headers[kNavigationPreloadHeaderName]);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest, GetResponseText) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest, GetResponseText) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
   const char kPage[] = "<title>PASS</title>Hello world.";
@@ -2421,7 +2434,7 @@
   EXPECT_EQ(1, GetRequestCount(kPageUrl));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        GetLargeResponseText) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2458,7 +2471,7 @@
   EXPECT_EQ(1, GetRequestCount(kPageUrl));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        GetLargeResponseCloneText) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2496,7 +2509,7 @@
   EXPECT_EQ(1, GetRequestCount(kPageUrl));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        GetLargeResponseReadableStream) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2545,7 +2558,7 @@
   EXPECT_EQ(1, GetRequestCount(kPageUrl));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest, NetworkError) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest, NetworkError) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
   const GURL page_url = embedded_test_server()->GetURL(kPageUrl);
@@ -2578,7 +2591,7 @@
   EXPECT_NE(base::string16::npos, messages[0].find(expected));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        PreloadHeadersSimple) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2608,7 +2621,7 @@
                           base::NumberToString(sizeof(kPage) - 1)));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest, NotEnabled) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest, NotEnabled) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
   const char kPage[] = "<title>ERROR</title>Hello world.";
@@ -2625,7 +2638,7 @@
   EXPECT_EQ(0, GetRequestCount(kPageUrl));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        PreloadHeadersCustom) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2668,7 +2681,7 @@
   EXPECT_FALSE(HasHeader(*dict, "set-cookie2"));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        InvalidRedirect_MultiLocation) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2715,7 +2728,7 @@
   EXPECT_EQ(0, GetRequestCount(kRedirectedPageUrl2));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        InvalidRedirect_InvalidLocation) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2745,7 +2758,7 @@
 
 // Tests responding with the navigation preload response when the navigation
 // occurred after a redirect.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        RedirectAndRespondWithNavigationPreload) {
   const std::string kPageUrl = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
@@ -2810,7 +2823,7 @@
   EXPECT_EQ(1, GetRequestCount(kPageUrl + "?3"));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest,
                        ResponseFromHTTPSServiceWorkerIsMarkedAsSecure) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] = "/service_worker/fetch_event_blob.html";
@@ -2852,7 +2865,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest,
                        ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] = "/service_worker/fetch_event_blob.html";
@@ -2889,7 +2902,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, ImportsBustMemcache) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, ImportsBustMemcache) {
   StartServerAndNavigateToSetup();
   const char kScopeUrl[] = "/service_worker/imports_bust_memcache_scope/";
   const char kPageUrl[] = "/service_worker/imports_bust_memcache.html";
@@ -2946,7 +2959,7 @@
   int64_t version_id_ = blink::mojom::kInvalidServiceWorkerVersionId;
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest,
                        GetAllServiceWorkerRunningInfos) {
   StartServerAndNavigateToSetup();
   WorkerRunningStatusObserver observer(public_context());
@@ -2969,7 +2982,7 @@
             infos[0].process_id);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, GetServiceWorkerRunningInfo) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, GetServiceWorkerRunningInfo) {
   StartServerAndNavigateToSetup();
   WorkerRunningStatusObserver observer(public_context());
   EXPECT_TRUE(NavigateToURL(shell(),
@@ -3010,7 +3023,7 @@
   base::OnceClosure quit_closure_;
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, RendererCrash) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, RendererCrash) {
   // Start a worker.
   StartServerAndNavigateToSetup();
   RunOnIOThread(base::BindOnce(&self::SetUpRegistrationOnIOThread,
@@ -3066,7 +3079,7 @@
   return RenderProcessHost::GetCurrentRenderProcessCountForTesting();
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, Registration) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBlackBoxBrowserTest, Registration) {
   StartServerAndNavigateToSetup();
   // Close the only window to be sure we're not re-using its RenderProcessHost.
   shell()->Close();
@@ -3199,7 +3212,7 @@
   size_t metadata_size_ = 0;
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserV8FullCodeCacheTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserV8FullCodeCacheTest,
                        FullCode) {
   StartServerAndNavigateToSetup();
   RunOnIOThread(base::BindOnce(&self::SetUpRegistrationAndListenerOnIOThread,
@@ -3383,7 +3396,7 @@
 const char ServiceWorkerV8CodeCacheForCacheStorageTest::kScriptUrl[] =
     "/service_worker/v8_cache_test.js";
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerV8CodeCacheForCacheStorageTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerV8CodeCacheForCacheStorageTest,
                        V8CacheOnCacheStorage) {
   RegisterAndActivateServiceWorker();
 
@@ -3411,7 +3424,7 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerV8CodeCacheForCacheStorageNoneTest);
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerV8CodeCacheForCacheStorageNoneTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerV8CodeCacheForCacheStorageNoneTest,
                        V8CacheOnCacheStorage) {
   RegisterAndActivateServiceWorker();
 
@@ -3490,8 +3503,6 @@
     }
   }
 
-  base::test::ScopedFeatureList feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerCodeCacheStrategyTestBase);
 };
 
@@ -3515,11 +3526,16 @@
     feature_list_.InitAndEnableFeatureWithParameters(
         blink::features::kServiceWorkerAggressiveCodeCache,
         {{blink::kServiceWorkerEagerCodeCacheStrategy, "dontgenerate"}});
-    ServiceWorkerCodeCacheStrategyTestBase::SetUp();
+    // Call ContentBrowserTest::SetUp() to avoid initializing |feature_list_|
+    // twice.
+    // TODO(crbug.com/967272): Change this to call
+    // ServiceWorkerCodeCacheStrategyTestBase::SetUp() once we fully ship
+    // off-the-main-thread script fetch.
+    ContentBrowserTest::SetUp();
   }
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerCodeCacheStrategyDontGenerateTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerCodeCacheStrategyDontGenerateTest,
                        DontGenerate) {
   NavigateToTestPage();
   InitiateEventsToCacheScript();
@@ -3535,11 +3551,16 @@
     feature_list_.InitAndEnableFeatureWithParameters(
         blink::features::kServiceWorkerAggressiveCodeCache,
         {{blink::kServiceWorkerEagerCodeCacheStrategy, "installevent"}});
-    ServiceWorkerCodeCacheStrategyTestBase::SetUp();
+    // Call ContentBrowserTest::SetUp() to avoid initializing |feature_list_|
+    // twice.
+    // TODO(crbug.com/967272): Change this to call
+    // ServiceWorkerCodeCacheStrategyTestBase::SetUp() once we fully ship
+    // off-the-main-thread script fetch.
+    ContentBrowserTest::SetUp();
   }
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerCodeCacheStrategyInstallEventTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerCodeCacheStrategyInstallEventTest,
                        GenerateInInstallEvent) {
   NavigateToTestPage();
   InitiateEventsToCacheScript();
@@ -3555,11 +3576,16 @@
     feature_list_.InitAndEnableFeatureWithParameters(
         blink::features::kServiceWorkerAggressiveCodeCache,
         {{blink::kServiceWorkerEagerCodeCacheStrategy, "idletask"}});
-    ServiceWorkerCodeCacheStrategyTestBase::SetUp();
+    // Call ContentBrowserTest::SetUp() to avoid initializing |feature_list_|
+    // twice.
+    // TODO(crbug.com/967272): Change this to call
+    // ServiceWorkerCodeCacheStrategyTestBase::SetUp() once we fully ship
+    // off-the-main-thread script fetch.
+    ContentBrowserTest::SetUp();
   }
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerCodeCacheStrategyIdleTaskTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerCodeCacheStrategyIdleTaskTest,
                        GenerateInIdleTask) {
   NavigateToTestPage();
   InitiateEventsToCacheScript();
@@ -3571,7 +3597,7 @@
 
 // Test that generating and storing code cache in idle tasks doesn't corrupt
 // cache entry when Cache#put() is called twice asynchronously.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerCodeCacheStrategyIdleTaskTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerCodeCacheStrategyIdleTaskTest,
                        CacheScriptTwice) {
   const char kCachedTwiceUrl[] =
       "/service_worker/code_cache_strategy_test_script.js?cached_twice";
@@ -3639,7 +3665,7 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDisableWebSecurityTest);
 };
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerDisableWebSecurityTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerDisableWebSecurityTest,
                        GetRegistrationNoCrash) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] =
@@ -3648,14 +3674,14 @@
   RunTestWithCrossOriginURL(kPageUrl, kScopeUrl);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerDisableWebSecurityTest, RegisterNoCrash) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerDisableWebSecurityTest, RegisterNoCrash) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] = "/service_worker/disable_web_security_register.html";
   const char kScopeUrl[] = "/service_worker/";
   RunTestWithCrossOriginURL(kPageUrl, kScopeUrl);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerDisableWebSecurityTest, UnregisterNoCrash) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerDisableWebSecurityTest, UnregisterNoCrash) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] =
       "/service_worker/disable_web_security_unregister.html";
@@ -3665,7 +3691,7 @@
   RunTestWithCrossOriginURL(kPageUrl, kScopeUrl);
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerDisableWebSecurityTest, UpdateNoCrash) {
+IN_PROC_BROWSER_TEST_P(ServiceWorkerDisableWebSecurityTest, UpdateNoCrash) {
   StartServerAndNavigateToSetup();
   const char kPageUrl[] = "/service_worker/disable_web_security_update.html";
   const char kScopeUrl[] = "/service_worker/scope/";
@@ -3751,7 +3777,7 @@
 
 // Test that the throttles can inject headers during navigation that are
 // observable inside the service worker's fetch event.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerURLLoaderThrottleTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerURLLoaderThrottleTest,
                        FetchEventForNavigationHasThrottledRequest) {
   // Add a throttle which injects a header.
   ThrottlingContentBrowserClient content_browser_client;
@@ -3786,7 +3812,7 @@
 }
 
 // Test that redirects by throttles occur before service worker interception.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerURLLoaderThrottleTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerURLLoaderThrottleTest,
                        RedirectOccursBeforeFetchEvent) {
   // Add a throttle which performs a redirect.
   ThrottlingContentBrowserClient content_browser_client;
@@ -3830,7 +3856,7 @@
 
 // Test that the headers injected by throttles during navigation are
 // present in the network request in the case of network fallback.
-IN_PROC_BROWSER_TEST_F(
+IN_PROC_BROWSER_TEST_P(
     ServiceWorkerURLLoaderThrottleTest,
     NavigationHasThrottledRequestHeadersAfterNetworkFallback) {
   // Add a throttle which injects a header.
@@ -3861,7 +3887,7 @@
 
 // Test that the headers injected by throttles during navigation are
 // present in the navigation preload request.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerURLLoaderThrottleTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerURLLoaderThrottleTest,
                        NavigationPreloadHasThrottledRequestHeaders) {
   // Add a throttle which injects a header.
   ThrottlingContentBrowserClient content_browser_client;
@@ -3890,4 +3916,61 @@
   SetBrowserClientForTesting(old_content_browser_client);
 }
 
+// TODO(crbug.com/967272): Remove following parameterized tests once we fully
+// ship off-the-main-thread script fetch.
+
+INSTANTIATE_TEST_SUITE_P(ServiceWorkerBrowserTestOnMainThreadFetch,
+                         ServiceWorkerBrowserTest,
+                         testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(ServiceWorkerVersionBrowserTestOnMainThreadFetch,
+                         ServiceWorkerVersionBrowserTest,
+                         testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(ServiceWorkerNavigationPreloadTestOnMainThreadFetch,
+                         ServiceWorkerNavigationPreloadTest,
+                         testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(ServiceWorkerDisableWebSecurityTestOnMainThreadFetch,
+                         ServiceWorkerDisableWebSecurityTest,
+                         testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch,
+                         ServiceWorkerURLLoaderThrottleTest,
+                         testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(ServiceWorkerBlackBoxBrowserTestOnMainThreadFetch,
+                         ServiceWorkerBlackBoxBrowserTest,
+                         testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(
+    ServiceWorkerCodeCacheStrategyDontGenerateTestOnMainThreadFetch,
+    ServiceWorkerCodeCacheStrategyDontGenerateTest,
+    testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(
+    ServiceWorkerCodeCacheStrategyIdleTaskTestOnMainThreadFetch,
+    ServiceWorkerCodeCacheStrategyIdleTaskTest,
+    testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(
+    ServiceWorkerCodeCacheStrategyInstallEventTestOnMainThreadFetch,
+    ServiceWorkerCodeCacheStrategyInstallEventTest,
+    testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(
+    ServiceWorkerV8CodeCacheForCacheStorageNoneTestOnMainThreadFetch,
+    ServiceWorkerV8CodeCacheForCacheStorageNoneTest,
+    testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(
+    ServiceWorkerV8CodeCacheForCacheStorageTestOnMainThreadFetch,
+    ServiceWorkerV8CodeCacheForCacheStorageTest,
+    testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(
+    ServiceWorkerVersionBrowserV8FullCodeCacheTestOnMainThreadFetch,
+    ServiceWorkerVersionBrowserV8FullCodeCacheTest,
+    testing::Bool());
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_client_utils.cc b/content/browser/service_worker/service_worker_client_utils.cc
index 2762005..bfc25ff 100644
--- a/content/browser/service_worker/service_worker_client_utils.cc
+++ b/content/browser/service_worker/service_worker_client_utils.cc
@@ -575,6 +575,8 @@
   blink::mojom::ServiceWorkerClientType client_type =
       provider_host->client_type();
   DCHECK(client_type == blink::mojom::ServiceWorkerClientType::kWindow ||
+         client_type ==
+             blink::mojom::ServiceWorkerClientType::kDedicatedWorker ||
          client_type == blink::mojom::ServiceWorkerClientType::kSharedWorker)
       << client_type;
 
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 670a93e3..906801a 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -1097,15 +1097,16 @@
 }
 
 base::WeakPtr<ServiceWorkerProviderHost>
-ServiceWorkerContextWrapper::PreCreateHostForSharedWorker(
+ServiceWorkerContextWrapper::PreCreateHostForWorker(
     int process_id,
+    blink::mojom::ServiceWorkerProviderType provider_type,
     blink::mojom::ServiceWorkerProviderInfoForWorkerPtr* out_provider_info) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (!context_core_)
     return nullptr;
-  return ServiceWorkerProviderHost::PreCreateForSharedWorker(
-      context_core_->AsWeakPtr(), process_id, out_provider_info);
+  return ServiceWorkerProviderHost::PreCreateForWebWorker(
+      context_core_->AsWeakPtr(), process_id, provider_type, out_provider_info);
 }
 
 ServiceWorkerContextWrapper::~ServiceWorkerContextWrapper() {
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index 67169101..e5f6fb2 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -295,16 +295,18 @@
 
   bool is_incognito() const { return is_incognito_; }
 
-  // Used for starting a shared worker. Returns a provider host for the shared
-  // worker and fills |out_provider_info| with info to send to the renderer to
-  // connect to the host. The host stays alive as long as this info stays alive
-  // (namely, as long as |out_provider_info->host_ptr_info| stays alive).
+  // Used for starting a web worker (dedicated worker or shared worker). Returns
+  // a provider host for the worker and fills |out_provider_info| with info to
+  // send to the renderer to connect to the host. The host stays alive as long
+  // as this info stays alive (namely, as long as
+  // |out_provider_info->host_ptr_info| stays alive).
   //
   // Returns null if context() is null.
   //
   // Must be called on the IO thread.
-  base::WeakPtr<ServiceWorkerProviderHost> PreCreateHostForSharedWorker(
+  base::WeakPtr<ServiceWorkerProviderHost> PreCreateHostForWorker(
       int process_id,
+      blink::mojom::ServiceWorkerProviderType provider_type,
       blink::mojom::ServiceWorkerProviderInfoForWorkerPtr* out_provider_info);
 
  private:
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index 96bcfc8..45f673e 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -142,6 +142,7 @@
 
 void ServiceWorkerControlleeRequestHandler::MaybeCreateLoader(
     const network::ResourceRequest& tentative_resource_request,
+    BrowserContext* browser_context,
     ResourceContext* resource_context,
     LoaderCallback callback,
     FallbackCallback fallback_callback) {
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.h b/content/browser/service_worker/service_worker_controllee_request_handler.h
index 0421e6a..fb598361 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.h
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -59,6 +59,7 @@
   // cases. (In fallback-to-network cases we basically forward the request
   // to the request to the next request handler)
   void MaybeCreateLoader(const network::ResourceRequest& tentative_request,
+                         BrowserContext* browser_context,
                          ResourceContext* resource_context,
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override;
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
index ec04c735..3858f0ec 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
@@ -70,7 +70,7 @@
       resource_request.url = request_->url();
       resource_request.resource_type = static_cast<int>(resource_type_);
       resource_request.headers = request()->extra_request_headers();
-      handler_->MaybeCreateLoader(resource_request, nullptr,
+      handler_->MaybeCreateLoader(resource_request, nullptr, nullptr,
                                   base::DoNothing(), base::DoNothing());
       return handler_->loader();
     }
diff --git a/content/browser/service_worker/service_worker_object_host.cc b/content/browser/service_worker/service_worker_object_host.cc
index cb2ec44..d6a44485 100644
--- a/content/browser/service_worker/service_worker_object_host.cc
+++ b/content/browser/service_worker/service_worker_object_host.cc
@@ -308,8 +308,9 @@
                          provider_host_->AsWeakPtr()));
       return;
     }
+    case blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker:
     case blink::mojom::ServiceWorkerProviderType::kForSharedWorker:
-    // Shared workers don't yet have access to ServiceWorker objects, so they
+    // Web workers don't yet have access to ServiceWorker objects, so they
     // can't postMessage to one (https://crbug.com/371690).
     case blink::mojom::ServiceWorkerProviderType::kUnknown:
       break;
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index c4adb32b..07e8d6e1 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -73,6 +73,7 @@
 
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      BrowserContext*,
       ResourceContext*,
       LoaderCallback callback,
       FallbackCallback) override {
@@ -217,15 +218,19 @@
 
 // static
 base::WeakPtr<ServiceWorkerProviderHost>
-ServiceWorkerProviderHost::PreCreateForSharedWorker(
+ServiceWorkerProviderHost::PreCreateForWebWorker(
     base::WeakPtr<ServiceWorkerContextCore> context,
     int process_id,
+    blink::mojom::ServiceWorkerProviderType provider_type,
     blink::mojom::ServiceWorkerProviderInfoForWorkerPtr* out_provider_info) {
+  using ServiceWorkerProviderType = blink::mojom::ServiceWorkerProviderType;
+  DCHECK(provider_type == ServiceWorkerProviderType::kForDedicatedWorker ||
+         provider_type == ServiceWorkerProviderType::kForSharedWorker);
   blink::mojom::ServiceWorkerContainerAssociatedPtrInfo client_ptr_info;
   (*out_provider_info)->client_request = mojo::MakeRequest(&client_ptr_info);
   auto host = base::WrapUnique(new ServiceWorkerProviderHost(
-      blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-      true /* is_parent_frame_secure */, FrameTreeNode::kFrameTreeNodeInvalidId,
+      provider_type, true /* is_parent_frame_secure */,
+      FrameTreeNode::kFrameTreeNodeInvalidId,
       mojo::MakeRequest(&((*out_provider_info)->host_ptr_info)),
       std::move(client_ptr_info), context));
   host->SetRenderProcessId(process_id);
@@ -499,6 +504,7 @@
 bool ServiceWorkerProviderHost::IsProviderForClient() const {
   switch (type_) {
     case blink::mojom::ServiceWorkerProviderType::kForWindow:
+    case blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker:
     case blink::mojom::ServiceWorkerProviderType::kForSharedWorker:
       return true;
     case blink::mojom::ServiceWorkerProviderType::kForServiceWorker:
@@ -515,6 +521,8 @@
   switch (type_) {
     case blink::mojom::ServiceWorkerProviderType::kForWindow:
       return blink::mojom::ServiceWorkerClientType::kWindow;
+    case blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker:
+      return blink::mojom::ServiceWorkerClientType::kDedicatedWorker;
     case blink::mojom::ServiceWorkerProviderType::kForSharedWorker:
       return blink::mojom::ServiceWorkerClientType::kSharedWorker;
     case blink::mojom::ServiceWorkerProviderType::kForServiceWorker:
@@ -732,9 +740,10 @@
       std::move(interface_provider_request)));
 }
 
-void ServiceWorkerProviderHost::CompleteSharedWorkerPreparation() {
-  DCHECK_EQ(blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-            provider_type());
+void ServiceWorkerProviderHost::CompleteWebWorkerPreparation() {
+  using ServiceWorkerProviderType = blink::mojom::ServiceWorkerProviderType;
+  DCHECK(provider_type() == ServiceWorkerProviderType::kForDedicatedWorker ||
+         provider_type() == ServiceWorkerProviderType::kForSharedWorker);
   TransitionToClientPhase(ClientPhase::kResponseCommitted);
   SetExecutionReady();
 }
@@ -864,11 +873,12 @@
       // response. The controller will be sent on navigation commit. See
       // CommitNavigation in frame.mojom.
       return false;
+    case blink::mojom::ServiceWorkerClientType::kDedicatedWorker:
     case blink::mojom::ServiceWorkerClientType::kSharedWorker:
       // NetworkService (PlzWorker):
       if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
         // When PlzWorker is enabled, the controller will be sent when the
-        // response is committed to the renderer at SharedWorkerHost::Start().
+        // response is committed to the renderer.
         return false;
       }
       return true;
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 1873aa4..5a62b7c 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -140,13 +140,15 @@
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr*
           out_provider_info);
 
-  // Used for starting a shared worker. Returns a provider host for the shared
-  // worker and fills |out_provider_info| with info to send to the renderer to
-  // connect to the host. The host stays alive as long as this info stays alive
-  // (namely, as long as |out_provider_info->host_ptr_info| stays alive).
-  static base::WeakPtr<ServiceWorkerProviderHost> PreCreateForSharedWorker(
+  // Used for starting a web worker (dedicated worker or shared worker). Returns
+  // a provider host for the worker and fills |out_provider_info| with info to
+  // send to the renderer to connect to the host. The host stays alive as long
+  // as this info stays alive (namely, as long as
+  // |out_provider_info->host_ptr_info| stays alive).
+  static base::WeakPtr<ServiceWorkerProviderHost> PreCreateForWebWorker(
       base::WeakPtr<ServiceWorkerContextCore> context,
       int process_id,
+      blink::mojom::ServiceWorkerProviderType provider_type,
       blink::mojom::ServiceWorkerProviderInfoForWorkerPtr* out_provider_info);
 
   ~ServiceWorkerProviderHost() override;
@@ -352,10 +354,10 @@
       service_manager::mojom::InterfaceProviderRequest
           interface_provider_request);
 
-  // Called when the shared worker main script resource has finished loading.
+  // Called when the web worker main script resource has finished loading.
   // After this is called, is_response_committed() and is_execution_ready()
   // return true.
-  void CompleteSharedWorkerPreparation();
+  void CompleteWebWorkerPreparation();
 
   // For service worker clients. The host keeps track of all the prospective
   // longest-matching registrations, in order to resolve .ready or respond to
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 2a6c866..8076856 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -262,6 +262,13 @@
     return !host->versions_to_update_.empty();
   }
 
+  void TestReservedClientsAreNotExposed(
+      blink::mojom::ServiceWorkerProviderType provider_type,
+      const GURL url);
+  void TestClientPhaseTransition(
+      blink::mojom::ServiceWorkerProviderType provider_type,
+      const GURL url);
+
   TestBrowserThreadBundle thread_bundle_;
 
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
@@ -858,19 +865,19 @@
 // Test that a "reserved" (i.e., not execution ready) client is not included
 // when iterating over client provider hosts. If it were, it'd be undesirably
 // exposed via the Clients API.
-TEST_F(ServiceWorkerProviderHostTest,
-       ReservedClientsAreNotExposedToClientsAPI) {
+void ServiceWorkerProviderHostTest::TestReservedClientsAreNotExposed(
+    blink::mojom::ServiceWorkerProviderType provider_type,
+    const GURL url) {
   {
     auto provider_info =
         blink::mojom::ServiceWorkerProviderInfoForWorker::New();
     base::WeakPtr<ServiceWorkerProviderHost> host =
-        ServiceWorkerProviderHost::PreCreateForSharedWorker(
+        ServiceWorkerProviderHost::PreCreateForWebWorker(
             context_->AsWeakPtr(), helper_->mock_render_process_id(),
-            &provider_info);
-    const GURL url("https://www.example.com/shared_worker.js");
+            provider_type, &provider_info);
     host->UpdateUrls(url, url);
     EXPECT_FALSE(CanFindClientProviderHost(host.get()));
-    host->CompleteSharedWorkerPreparation();
+    host->CompleteWebWorkerPreparation();
     EXPECT_TRUE(CanFindClientProviderHost(host.get()));
   }
 
@@ -894,6 +901,20 @@
   }
 }
 
+TEST_F(ServiceWorkerProviderHostTest,
+       ReservedClientsAreNotExposedToClientsApiForDedicatedWorker) {
+  TestReservedClientsAreNotExposed(
+      blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker,
+      GURL("https://www.example.com/dedicated_worker.js"));
+}
+
+TEST_F(ServiceWorkerProviderHostTest,
+       ReservedClientsAreNotExposedToClientsApiForSharedWorker) {
+  TestReservedClientsAreNotExposed(
+      blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+      GURL("https://www.example.com/shared_worker.js"));
+}
+
 // Tests the client phase transitions for a navigation.
 TEST_F(ServiceWorkerProviderHostTest, ClientPhaseForWindow) {
   auto provider_info = blink::mojom::ServiceWorkerProviderInfoForWindow::New();
@@ -918,24 +939,37 @@
   EXPECT_TRUE(host->is_execution_ready());
 }
 
-// Tests the client phase transitions for a shared worker.
-TEST_F(ServiceWorkerProviderHostTest, ClientPhaseForSharedWorker) {
+// Tests the client phase transitions for workers.
+void ServiceWorkerProviderHostTest::TestClientPhaseTransition(
+    blink::mojom::ServiceWorkerProviderType provider_type,
+    const GURL url) {
   auto provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
   base::WeakPtr<ServiceWorkerProviderHost> host =
-      ServiceWorkerProviderHost::PreCreateForSharedWorker(
+      ServiceWorkerProviderHost::PreCreateForWebWorker(
           context_->AsWeakPtr(), helper_->mock_render_process_id(),
-          &provider_info);
+          provider_type, &provider_info);
   EXPECT_FALSE(host->is_response_committed());
   EXPECT_FALSE(host->is_execution_ready());
 
-  const GURL url("https://www.example.com/shared_worker.js");
   host->UpdateUrls(url, url);
-  host->CompleteSharedWorkerPreparation();
+  host->CompleteWebWorkerPreparation();
 
   EXPECT_TRUE(host->is_response_committed());
   EXPECT_TRUE(host->is_execution_ready());
 }
 
+TEST_F(ServiceWorkerProviderHostTest, ClientPhaseForDedicatedWorker) {
+  TestClientPhaseTransition(
+      blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker,
+      GURL("https://www.example.com/dedicated_worker.js"));
+}
+
+TEST_F(ServiceWorkerProviderHostTest, ClientPhaseForSharedWorker) {
+  TestClientPhaseTransition(
+      blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+      GURL("https://www.example.com/shared_worker.js"));
+}
+
 // Tests that the service worker involved with a navigation (via
 // AddServiceWorkerToUpdate) is updated when the host for the navigation is
 // destroyed.
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index f346b897..e7fadf3 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -194,10 +194,14 @@
   return browsing_instance_->default_process();
 }
 
-bool SiteInstanceImpl::IsDefaultSiteInstance() {
+bool SiteInstanceImpl::IsDefaultSiteInstance() const {
   return browsing_instance_->IsDefaultSiteInstance(this);
 }
 
+bool SiteInstanceImpl::IsSiteInDefaultSiteInstance(const GURL& site_url) const {
+  return browsing_instance_->IsSiteInDefaultSiteInstance(site_url);
+}
+
 void SiteInstanceImpl::MaybeSetBrowsingInstanceDefaultProcess() {
   if (!base::FeatureList::IsEnabled(
           features::kProcessSharingWithStrictSiteInstances)) {
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index b1f7dc24..5f57cd4 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -307,7 +307,11 @@
   RenderProcessHost* GetDefaultProcessIfUsable();
 
   // Returns true if this object was constructed as a default site instance.
-  bool IsDefaultSiteInstance();
+  bool IsDefaultSiteInstance() const;
+
+  // Returns true if |site_url| is a site URL that the BrowsingInstance has
+  // associated with its default SiteInstance.
+  bool IsSiteInDefaultSiteInstance(const GURL& site_url) const;
 
   // Returns true if the the site URL for |url| matches the site URL
   // for this instance (i.e. GetSiteURL()). Otherwise returns false.
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index bfe6c5a..7f4fc1f 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -895,6 +895,9 @@
       base::MakeRefCounted<DevToolsBackgroundServicesContextImpl>(
           context, partition->service_worker_context_);
 
+  partition->content_index_context_ = base::MakeRefCounted<ContentIndexContext>(
+      context, partition->service_worker_context_);
+
   partition->background_fetch_context_ =
       base::MakeRefCounted<BackgroundFetchContext>(
           context, partition->service_worker_context_,
@@ -1138,6 +1141,10 @@
   return native_file_system_manager_.get();
 }
 
+ContentIndexContext* StoragePartitionImpl::GetContentIndexContext() {
+  return content_index_context_.get();
+}
+
 void StoragePartitionImpl::OpenLocalStorage(
     const url::Origin& origin,
     blink::mojom::StorageAreaRequest request) {
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 4d59802..1ed9443 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -21,6 +21,7 @@
 #include "content/browser/bluetooth/bluetooth_allowed_devices_map.h"
 #include "content/browser/broadcast_channel/broadcast_channel_provider.h"
 #include "content/browser/cache_storage/cache_storage_context_impl.h"
+#include "content/browser/content_index/content_index_context.h"
 #include "content/browser/devtools/devtools_background_services_context_impl.h"
 #include "content/browser/dom_storage/dom_storage_context_wrapper.h"
 #include "content/browser/idle/idle_manager.h"
@@ -157,6 +158,7 @@
   PrefetchURLLoaderService* GetPrefetchURLLoaderService();
   CookieStoreContext* GetCookieStoreContext();
   NativeFileSystemManagerImpl* GetNativeFileSystemManager();
+  ContentIndexContext* GetContentIndexContext();
 
   // blink::mojom::StoragePartitionService interface.
   void OpenLocalStorage(const url::Origin& origin,
@@ -375,6 +377,7 @@
   scoped_refptr<DevToolsBackgroundServicesContextImpl>
       devtools_background_services_context_;
   scoped_refptr<NativeFileSystemManagerImpl> native_file_system_manager_;
+  scoped_refptr<ContentIndexContext> content_index_context_;
 
   // BindingSet for StoragePartitionService, using the process id as the
   // binding context type. The process id can subsequently be used during
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index cc0e92dd..474c2317 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -570,6 +570,11 @@
         base::BindOnce(&BackgroundFetchContext::InitializeOnIOThread,
                        partition->GetBackgroundFetchContext()));
 
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&ContentIndexContext::InitializeOnIOThread,
+                       partition->GetContentIndexContext()));
+
     // We do not call InitializeURLRequestContext() for media contexts because,
     // other than the HTTP cache, the media contexts share the same backing
     // objects as their associated "normal" request context.  Thus, the previous
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 5cbb7018..11c83d2 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -47,6 +47,7 @@
 #include "content/browser/browser_plugin/browser_plugin_embedder.h"
 #include "content/browser/browser_plugin/browser_plugin_guest.h"
 #include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/devtools/protocol/page_handler.h"
 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
 #include "content/browser/display_cutout/display_cutout_host_impl.h"
@@ -4115,7 +4116,7 @@
     WebContents::ImageDownloadCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   static int next_image_download_id = 0;
-  const content::mojom::ImageDownloaderPtr& mojo_image_downloader =
+  const blink::mojom::ImageDownloaderPtr& mojo_image_downloader =
       GetMainFrame()->GetMojoImageDownloader();
   const int download_id = ++next_image_download_id;
   if (!mojo_image_downloader) {
@@ -5470,6 +5471,11 @@
   // fullscreen can be used to confuse the user, so drop fullscreen.
   ForSecurityDropFullscreen();
 
+  RenderFrameHostImpl* rfhi =
+      static_cast<RenderFrameHostImpl*>(render_frame_host);
+  if (devtools_instrumentation::InterceptFileChooser(rfhi, &listener, params))
+    return;
+
   if (delegate_)
     delegate_->RunFileChooser(render_frame_host, std::move(listener), params);
   else
diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index fd229b8..fd4f3730 100644
--- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -584,14 +584,7 @@
 
   std::unique_ptr<aura::Window> window(new aura::Window(nullptr));
   window->Init(ui::LAYER_NOT_DRAWN);
-
-  RenderWidgetHostViewAura* rwhva =
-      static_cast<RenderWidgetHostViewAura*>(
-          shell()->web_contents()->GetRenderWidgetHostView());
-  rwhva->ResetHasSnappedToBoundary();
-  EXPECT_FALSE(rwhva->has_snapped_to_boundary());
   window->AddChild(shell()->web_contents()->GetNativeView());
-  EXPECT_TRUE(rwhva->has_snapped_to_boundary());
 }
 
 // Flaky on some platforms, likely for the same reason as other flaky overscroll
diff --git a/content/browser/web_package/http_structured_header.h b/content/browser/web_package/http_structured_header.h
deleted file mode 100644
index 8515445a..0000000
--- a/content/browser/web_package/http_structured_header.h
+++ /dev/null
@@ -1,43 +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_WEB_PACKAGE_HTTP_STRUCTURED_HEADER_H_
-#define CONTENT_BROWSER_WEB_PACKAGE_HTTP_STRUCTURED_HEADER_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/optional.h"
-#include "base/strings/string_piece.h"
-#include "content/common/content_export.h"
-
-namespace content {
-namespace http_structured_header {
-
-struct CONTENT_EXPORT ParameterisedIdentifier {
-  using Parameters = std::map<std::string, std::string>;
-
-  std::string identifier;
-  Parameters params;
-
-  ParameterisedIdentifier(const ParameterisedIdentifier&);
-  ParameterisedIdentifier(const std::string&, const Parameters&);
-  ~ParameterisedIdentifier();
-};
-
-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
-
-#endif  // CONTENT_BROWSER_WEB_PACKAGE_HTTP_STRUCTURED_HEADER_H_
diff --git a/content/browser/web_package/prefetched_signed_exchange_cache.cc b/content/browser/web_package/prefetched_signed_exchange_cache.cc
index e2d3351..d279d47 100644
--- a/content/browser/web_package/prefetched_signed_exchange_cache.cc
+++ b/content/browser/web_package/prefetched_signed_exchange_cache.cc
@@ -373,6 +373,7 @@
 
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      BrowserContext* browser_context,
       ResourceContext* resource_context,
       LoaderCallback callback,
       FallbackCallback fallback_callback) override {
diff --git a/content/browser/web_package/signed_exchange_handler.cc b/content/browser/web_package/signed_exchange_handler.cc
index 95d3641b3..88a4de4 100644
--- a/content/browser/web_package/signed_exchange_handler.cc
+++ b/content/browser/web_package/signed_exchange_handler.cc
@@ -24,7 +24,6 @@
 #include "content/browser/web_package/signed_exchange_envelope.h"
 #include "content/browser/web_package/signed_exchange_prologue.h"
 #include "content/browser/web_package/signed_exchange_reporter.h"
-#include "content/browser/web_package/signed_exchange_request_matcher.h"
 #include "content/browser/web_package/signed_exchange_signature_verifier.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -51,6 +50,7 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/url_loader_completion_status.h"
 #include "services/network/public/mojom/network_context.mojom.h"
+#include "third_party/blink/public/common/web_package/signed_exchange_request_matcher.h"
 
 namespace content {
 
@@ -188,7 +188,7 @@
     ExchangeHeadersCallback headers_callback,
     std::unique_ptr<SignedExchangeCertFetcherFactory> cert_fetcher_factory,
     int load_flags,
-    std::unique_ptr<SignedExchangeRequestMatcher> request_matcher,
+    std::unique_ptr<blink::SignedExchangeRequestMatcher> request_matcher,
     std::unique_ptr<SignedExchangeDevToolsProxy> devtools_proxy,
     SignedExchangeReporter* reporter,
     base::RepeatingCallback<int(void)> frame_tree_node_id_getter)
diff --git a/content/browser/web_package/signed_exchange_handler.h b/content/browser/web_package/signed_exchange_handler.h
index 7ac5fd7..0fc5029 100644
--- a/content/browser/web_package/signed_exchange_handler.h
+++ b/content/browser/web_package/signed_exchange_handler.h
@@ -23,6 +23,10 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+namespace blink {
+class SignedExchangeRequestMatcher;
+}  // namespace blink
+
 namespace net {
 class CertVerifyResult;
 class DrainableIOBuffer;
@@ -45,7 +49,6 @@
 class SignedExchangeCertificateChain;
 class SignedExchangeDevToolsProxy;
 class SignedExchangeReporter;
-class SignedExchangeRequestMatcher;
 
 // SignedExchangeHandler reads "application/signed-exchange" format from a
 // net::SourceStream, parses and verifies the signed exchange, and reports
@@ -93,7 +96,7 @@
       ExchangeHeadersCallback headers_callback,
       std::unique_ptr<SignedExchangeCertFetcherFactory> cert_fetcher_factory,
       int load_flags,
-      std::unique_ptr<SignedExchangeRequestMatcher> request_matcher,
+      std::unique_ptr<blink::SignedExchangeRequestMatcher> request_matcher,
       std::unique_ptr<SignedExchangeDevToolsProxy> devtools_proxy,
       SignedExchangeReporter* reporter,
       base::RepeatingCallback<int(void)> frame_tree_node_id_getter);
@@ -169,7 +172,7 @@
 
   std::unique_ptr<SignedExchangeCertificateChain> unverified_cert_chain_;
 
-  std::unique_ptr<SignedExchangeRequestMatcher> request_matcher_;
+  std::unique_ptr<blink::SignedExchangeRequestMatcher> request_matcher_;
 
   std::unique_ptr<SignedExchangeDevToolsProxy> devtools_proxy_;
 
diff --git a/content/browser/web_package/signed_exchange_handler_unittest.cc b/content/browser/web_package/signed_exchange_handler_unittest.cc
index b1bf2628c..ca783ed7 100644
--- a/content/browser/web_package/signed_exchange_handler_unittest.cc
+++ b/content/browser/web_package/signed_exchange_handler_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "content/browser/web_package/signed_exchange_cert_fetcher_factory.h"
 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
-#include "content/browser/web_package/signed_exchange_request_matcher.h"
 #include "content/browser/web_package/signed_exchange_signature_verifier.h"
 #include "content/browser/web_package/signed_exchange_test_utils.h"
 #include "content/public/browser/content_browser_client.h"
@@ -37,6 +36,7 @@
 #include "services/network/public/cpp/network_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/web_package/signed_exchange_request_matcher.h"
 
 using testing::_;
 using testing::DoAll;
@@ -331,7 +331,7 @@
         base::BindOnce(&SignedExchangeHandlerTest::OnHeaderFound,
                        base::Unretained(this)),
         std::move(cert_fetcher_factory_), net::LOAD_NORMAL,
-        std::make_unique<SignedExchangeRequestMatcher>(
+        std::make_unique<blink::SignedExchangeRequestMatcher>(
             net::HttpRequestHeaders(), std::string() /* accept_langs */),
         nullptr /* devtools_proxy */, nullptr /* reporter */,
         base::RepeatingCallback<int(void)>());
diff --git a/content/browser/web_package/signed_exchange_loader.cc b/content/browser/web_package/signed_exchange_loader.cc
index 4788305..2269f79 100644
--- a/content/browser/web_package/signed_exchange_loader.cc
+++ b/content/browser/web_package/signed_exchange_loader.cc
@@ -18,7 +18,6 @@
 #include "content/browser/web_package/signed_exchange_handler.h"
 #include "content/browser/web_package/signed_exchange_prefetch_metric_recorder.h"
 #include "content/browser/web_package/signed_exchange_reporter.h"
-#include "content/browser/web_package/signed_exchange_request_matcher.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/browser/web_package/signed_exchange_validity_pinger.h"
 #include "content/public/common/content_features.h"
@@ -34,6 +33,7 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/url_loader_completion_status.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/common/web_package/signed_exchange_request_matcher.h"
 
 namespace content {
 
@@ -173,8 +173,8 @@
       base::BindOnce(&SignedExchangeLoader::OnHTTPExchangeFound,
                      weak_factory_.GetWeakPtr()),
       std::move(cert_fetcher_factory), outer_request_.load_flags,
-      std::make_unique<SignedExchangeRequestMatcher>(outer_request_.headers,
-                                                     accept_langs_),
+      std::make_unique<blink::SignedExchangeRequestMatcher>(
+          outer_request_.headers, accept_langs_),
       std::move(devtools_proxy_), reporter_.get(), frame_tree_node_id_getter_);
 }
 
diff --git a/content/browser/web_package/signed_exchange_request_handler.cc b/content/browser/web_package/signed_exchange_request_handler.cc
index e57bb16..853cb832 100644
--- a/content/browser/web_package/signed_exchange_request_handler.cc
+++ b/content/browser/web_package/signed_exchange_request_handler.cc
@@ -52,6 +52,7 @@
 
 void SignedExchangeRequestHandler::MaybeCreateLoader(
     const network::ResourceRequest& tentative_resource_request,
+    BrowserContext* browser_context,
     ResourceContext* resource_context,
     LoaderCallback callback,
     FallbackCallback fallback_callback) {
diff --git a/content/browser/web_package/signed_exchange_request_handler.h b/content/browser/web_package/signed_exchange_request_handler.h
index 428bb857..3bd2f12 100644
--- a/content/browser/web_package/signed_exchange_request_handler.h
+++ b/content/browser/web_package/signed_exchange_request_handler.h
@@ -44,6 +44,7 @@
   // NavigationLoaderInterceptor implementation
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      BrowserContext* browser_context,
       ResourceContext* resource_context,
       LoaderCallback callback,
       FallbackCallback fallback_callback) override;
diff --git a/content/browser/web_package/signed_exchange_signature_header_field.cc b/content/browser/web_package/signed_exchange_signature_header_field.cc
index 5be0413..6d2464d 100644
--- a/content/browser/web_package/signed_exchange_signature_header_field.cc
+++ b/content/browser/web_package/signed_exchange_signature_header_field.cc
@@ -8,10 +8,10 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
-#include "content/browser/web_package/http_structured_header.h"
 #include "content/browser/web_package/signed_exchange_consts.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "crypto/sha2.h"
+#include "third_party/blink/public/common/web_package/http_structured_header.h"
 
 namespace content {
 
@@ -23,8 +23,8 @@
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"),
                "SignedExchangeSignatureHeaderField::ParseSignature");
 
-  base::Optional<http_structured_header::ParameterisedList> values =
-      http_structured_header::ParseParameterisedList(signature_str);
+  base::Optional<blink::http_structured_header::ParameterisedList> values =
+      blink::http_structured_header::ParseParameterisedList(signature_str);
   if (!values) {
     signed_exchange_utils::ReportErrorAndTraceEvent(
         devtools_proxy, "Failed to parse signature header.");
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index ec25704..4d273f0d 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -962,6 +962,8 @@
   ctap_get_assertion_request_ = CreateCtapGetAssertionRequest(
       client_data_json_, std::move(options), app_id_,
       browser_context()->IsOffTheRecord());
+  ctap_get_assertion_request_->is_u2f_only =
+      OriginIsCryptoTokenExtension(caller_origin_);
 
   StartGetAssertionRequest();
 }
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 0783fe3..e890924 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -25,6 +25,7 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
 #include "url/origin.h"
 
@@ -69,14 +70,20 @@
       const GURL& script_url,
       const url::Origin& request_initiator_origin,
       network::mojom::CredentialsMode credentials_mode,
+      blink::mojom::FetchClientSettingsObjectPtr
+          outside_fetch_client_settings_object,
       blink::mojom::BlobURLTokenPtr blob_url_token,
       blink::mojom::DedicatedWorkerHostFactoryClientPtr client) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(blink::features::IsPlzDedicatedWorkerEnabled());
 
+    DCHECK(!client_);
+    DCHECK(client);
+    client_ = std::move(client);
+
     auto* render_process_host = RenderProcessHost::FromID(process_id_);
     if (!render_process_host) {
-      client->OnScriptLoadStartFailed();
+      client_->OnScriptLoadStartFailed();
       return;
     }
     auto* storage_partition_impl = static_cast<StoragePartitionImpl*>(
@@ -102,12 +109,12 @@
 
     WorkerScriptFetchInitiator::Start(
         process_id_, script_url, request_initiator_origin, credentials_mode,
-        ResourceType::kWorker,
+        std::move(outside_fetch_client_settings_object), ResourceType::kWorker,
         storage_partition_impl->GetServiceWorkerContext(),
         appcache_handle_->core(), std::move(blob_url_loader_factory), nullptr,
         storage_partition_impl,
         base::BindOnce(&DedicatedWorkerHost::DidStartScriptLoad,
-                       weak_factory_.GetWeakPtr(), std::move(client)));
+                       weak_factory_.GetWeakPtr()));
   }
 
  private:
@@ -145,7 +152,6 @@
   // a ServiceWorker object about the controller is prepared, it is registered
   // to |controller_service_worker_object_host|.
   void DidStartScriptLoad(
-      blink::mojom::DedicatedWorkerHostFactoryClientPtr client,
       blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
           service_worker_provider_info,
       network::mojom::URLLoaderFactoryPtr main_script_loader_factory,
@@ -162,13 +168,13 @@
     DCHECK(!main_script_loader_factory);
 
     if (!success) {
-      client->OnScriptLoadStartFailed();
+      client_->OnScriptLoadStartFailed();
       return;
     }
 
     auto* render_process_host = RenderProcessHost::FromID(process_id_);
     if (!render_process_host) {
-      client->OnScriptLoadStartFailed();
+      client_->OnScriptLoadStartFailed();
       return;
     }
 
@@ -191,10 +197,10 @@
       service_worker_state = controller->object_info->state;
     }
 
-    client->OnScriptLoadStarted(std::move(service_worker_provider_info),
-                                std::move(main_script_load_params),
-                                std::move(subresource_loader_factories),
-                                std::move(controller));
+    client_->OnScriptLoadStarted(std::move(service_worker_provider_info),
+                                 std::move(main_script_load_params),
+                                 std::move(subresource_loader_factories),
+                                 std::move(controller));
 
     // |service_worker_remote_object| is an associated interface ptr, so calls
     // can't be made on it until its request endpoint is sent. Now that the
@@ -282,6 +288,12 @@
   const int ancestor_render_frame_id_;
   const url::Origin origin_;
 
+  // This is kept alive during the lifetime of the dedicated worker, since it's
+  // associated with Mojo interfaces (ServiceWorkerContainer and
+  // URLLoaderFactory) that are needed to stay alive while the worker is
+  // starting or running.
+  blink::mojom::DedicatedWorkerHostFactoryClientPtr client_;
+
   std::unique_ptr<AppCacheNavigationHandle> appcache_handle_;
 
   service_manager::BinderRegistry registry_;
@@ -331,6 +343,8 @@
       const GURL& script_url,
       const url::Origin& request_initiator_origin,
       network::mojom::CredentialsMode credentials_mode,
+      blink::mojom::FetchClientSettingsObjectPtr
+          outside_fetch_client_settings_object,
       blink::mojom::BlobURLTokenPtr blob_url_token,
       blink::mojom::DedicatedWorkerHostFactoryClientPtr client) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -354,8 +368,9 @@
             mojo::MakeRequest(&interface_provider)));
     client->OnWorkerHostCreated(std::move(interface_provider));
     host_raw->StartScriptLoad(script_url, request_initiator_origin,
-                              credentials_mode, std::move(blob_url_token),
-                              std::move(client));
+                              credentials_mode,
+                              std::move(outside_fetch_client_settings_object),
+                              std::move(blob_url_token), std::move(client));
   }
 
  private:
diff --git a/content/browser/worker_host/shared_worker_connector_impl.cc b/content/browser/worker_host/shared_worker_connector_impl.cc
index fb9aa3bb..afb77f06 100644
--- a/content/browser/worker_host/shared_worker_connector_impl.cc
+++ b/content/browser/worker_host/shared_worker_connector_impl.cc
@@ -34,6 +34,8 @@
 
 void SharedWorkerConnectorImpl::Connect(
     blink::mojom::SharedWorkerInfoPtr info,
+    blink::mojom::FetchClientSettingsObjectPtr
+        outside_fetch_client_settings_object,
     blink::mojom::SharedWorkerClientPtr client,
     blink::mojom::SharedWorkerCreationContextType creation_context_type,
     mojo::ScopedMessagePipeHandle message_port,
@@ -58,6 +60,7 @@
       static_cast<StoragePartitionImpl*>(host->GetStoragePartition())
           ->GetSharedWorkerService();
   service->ConnectToWorker(process_id_, frame_id_, std::move(info),
+                           std::move(outside_fetch_client_settings_object),
                            std::move(client), creation_context_type,
                            blink::MessagePortChannel(std::move(message_port)),
                            std::move(blob_url_loader_factory));
diff --git a/content/browser/worker_host/shared_worker_connector_impl.h b/content/browser/worker_host/shared_worker_connector_impl.h
index 6c6d629..3f48c8c 100644
--- a/content/browser/worker_host/shared_worker_connector_impl.h
+++ b/content/browser/worker_host/shared_worker_connector_impl.h
@@ -25,6 +25,8 @@
   // blink::mojom::SharedWorkerConnector methods:
   void Connect(
       blink::mojom::SharedWorkerInfoPtr info,
+      blink::mojom::FetchClientSettingsObjectPtr
+          outside_fetch_client_settings_object,
       blink::mojom::SharedWorkerClientPtr client,
       blink::mojom::SharedWorkerCreationContextType creation_context_type,
       mojo::ScopedMessagePipeHandle message_port,
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index 4b8cd49..89ab9aa 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -87,8 +87,9 @@
     // comment on SharedWorkerHost::Start() for details.
     if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
       provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
-      ServiceWorkerProviderHost::PreCreateForSharedWorker(
+      ServiceWorkerProviderHost::PreCreateForWebWorker(
           helper_->context()->AsWeakPtr(), mock_render_process_host_.GetID(),
+          blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
           &provider_info);
 
       main_script_load_params = blink::mojom::WorkerMainScriptLoadParams::New();
@@ -105,8 +106,9 @@
           loader_factory_ptr.PassInterface();
     } else {
       provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
-      ServiceWorkerProviderHost::PreCreateForSharedWorker(
+      ServiceWorkerProviderHost::PreCreateForWebWorker(
           helper_->context()->AsWeakPtr(), mock_render_process_host_.GetID(),
+          blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
           &provider_info);
 
       mojo::MakeStrongBinding(
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index 522adf3..213e7758 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -37,6 +37,7 @@
 #include "services/network/loader_util.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
+#include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_client.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_info.mojom.h"
@@ -112,6 +113,8 @@
     int process_id,
     int frame_id,
     blink::mojom::SharedWorkerInfoPtr info,
+    blink::mojom::FetchClientSettingsObjectPtr
+        outside_fetch_client_settings_object,
     blink::mojom::SharedWorkerClientPtr client,
     blink::mojom::SharedWorkerCreationContextType creation_context_type,
     const blink::MessagePortChannel& message_port,
@@ -121,8 +124,8 @@
   RenderFrameHostImpl* render_frame_host =
       RenderFrameHostImpl::FromID(process_id, frame_id);
   if (!render_frame_host) {
-    // TODO(nhiroki): Support the case where the requester is a worker (i.e.,
-    // nested worker) (https://crbug.com/31666).
+    // TODO(crbug.com/31666): Support the case where the requester is a worker
+    // (i.e., nested worker).
     client->OnScriptLoadFailed();
     return;
   }
@@ -166,8 +169,10 @@
     DestroyHost(host);
   }
 
-  CreateWorker(std::move(instance), std::move(client), process_id, frame_id,
-               message_port, std::move(blob_url_loader_factory));
+  CreateWorker(std::move(instance),
+               std::move(outside_fetch_client_settings_object),
+               std::move(client), process_id, frame_id, message_port,
+               std::move(blob_url_loader_factory));
 }
 
 void SharedWorkerServiceImpl::DestroyHost(SharedWorkerHost* host) {
@@ -181,6 +186,8 @@
 
 void SharedWorkerServiceImpl::CreateWorker(
     std::unique_ptr<SharedWorkerInstance> instance,
+    blink::mojom::FetchClientSettingsObjectPtr
+        outside_fetch_client_settings_object,
     blink::mojom::SharedWorkerClientPtr client,
     int process_id,
     int frame_id,
@@ -213,14 +220,14 @@
   // Fetch classic shared worker script with "same-origin" credentials mode.
   // https://html.spec.whatwg.org/C/#fetch-a-classic-worker-script
   //
-  // TODO(nhiroki): The document's renderer should provide credentials mode
-  // specified by WorkerOptions for module script.
-  // (https://crbug.com/824646, https://crbug.com/907749)
+  // TODO(crbug.com/824646, crbug.com/907749): The document should provide the
+  // credentials mode specified by WorkerOptions for module script.
   const auto credentials_mode = network::mojom::CredentialsMode::kSameOrigin;
 
   WorkerScriptFetchInitiator::Start(
       process_id, weak_host->instance()->url(),
       weak_host->instance()->constructor_origin(), credentials_mode,
+      std::move(outside_fetch_client_settings_object),
       ResourceType::kSharedWorker, service_worker_context_,
       appcache_handle_core, std::move(blob_url_loader_factory),
       url_loader_factory_override_, storage_partition_,
diff --git a/content/browser/worker_host/shared_worker_service_impl.h b/content/browser/worker_host/shared_worker_service_impl.h
index 6105d487..572e71a 100644
--- a/content/browser/worker_host/shared_worker_service_impl.h
+++ b/content/browser/worker_host/shared_worker_service_impl.h
@@ -17,6 +17,7 @@
 #include "content/public/browser/shared_worker_service.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_connector.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_factory.mojom.h"
@@ -63,6 +64,8 @@
       int process_id,
       int frame_id,
       blink::mojom::SharedWorkerInfoPtr info,
+      blink::mojom::FetchClientSettingsObjectPtr
+          outside_fetch_client_settings_object,
       blink::mojom::SharedWorkerClientPtr client,
       blink::mojom::SharedWorkerCreationContextType creation_context_type,
       const blink::MessagePortChannel& port,
@@ -79,6 +82,8 @@
 
   void CreateWorker(
       std::unique_ptr<SharedWorkerInstance> instance,
+      blink::mojom::FetchClientSettingsObjectPtr
+          outside_fetch_client_settings_object,
       blink::mojom::SharedWorkerClientPtr client,
       int process_id,
       int frame_id,
diff --git a/content/browser/worker_host/shared_worker_service_impl_unittest.cc b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
index 0165b53..99b6ad0 100644
--- a/content/browser/worker_host/shared_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
@@ -57,7 +57,9 @@
   blink::mojom::SharedWorkerClientPtr client_proxy;
   client->Bind(mojo::MakeRequest(&client_proxy));
 
-  connector->Connect(std::move(info), std::move(client_proxy),
+  connector->Connect(std::move(info),
+                     blink::mojom::FetchClientSettingsObject::New(),
+                     std::move(client_proxy),
                      blink::mojom::SharedWorkerCreationContextType::kSecure,
                      std::move(message_pipe.handle1), nullptr);
 }
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index 01dadb8..06cc8084 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -35,6 +35,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/origin_util.h"
+#include "content/public/common/referrer.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/load_flags.h"
 #include "net/base/network_isolation_key.h"
@@ -53,6 +54,8 @@
     const GURL& script_url,
     const url::Origin& request_initiator,
     network::mojom::CredentialsMode credentials_mode,
+    blink::mojom::FetchClientSettingsObjectPtr
+        outside_fetch_client_settings_object,
     ResourceType resource_type,
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
     AppCacheNavigationHandleCore* appcache_handle_core,
@@ -89,16 +92,24 @@
           process_id, storage_partition, constructor_uses_file_url);
 
   // NetworkService (PlzWorker):
-  // Create a resource request for initiating shared worker script fetch from
-  // the browser process.
+  // Create a resource request for initiating worker script fetch from the
+  // browser process.
   std::unique_ptr<network::ResourceRequest> resource_request;
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    // TODO(nhiroki): Populate more fields like referrer.
-    // (https://crbug.com/715632)
+    // Determine the referrer for the worker script request based on the spec.
+    // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
+    Referrer sanitized_referrer = Referrer::SanitizeForRequest(
+        script_url,
+        Referrer(outside_fetch_client_settings_object->outgoing_referrer,
+                 outside_fetch_client_settings_object->referrer_policy));
+
     resource_request = std::make_unique<network::ResourceRequest>();
     resource_request->url = script_url;
     resource_request->site_for_cookies = script_url;
     resource_request->request_initiator = request_initiator;
+    resource_request->referrer = sanitized_referrer.url,
+    resource_request->referrer_policy = Referrer::ReferrerPolicyForUrlRequest(
+        outside_fetch_client_settings_object->referrer_policy);
     resource_request->resource_type = static_cast<int>(resource_type);
 
     // When the credentials mode is "omit", clear |allow_credentials| and set
@@ -307,11 +318,27 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(resource_context);
 
+  auto resource_type =
+      static_cast<ResourceType>(resource_request->resource_type);
+  auto provider_type = blink::mojom::ServiceWorkerProviderType::kUnknown;
+  switch (resource_type) {
+    case ResourceType::kWorker:
+      provider_type =
+          blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker;
+      break;
+    case ResourceType::kSharedWorker:
+      provider_type = blink::mojom::ServiceWorkerProviderType::kForSharedWorker;
+      break;
+    default:
+      NOTREACHED() << resource_request->resource_type;
+      break;
+  }
+
   // Set up for service worker.
   auto provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
   base::WeakPtr<ServiceWorkerProviderHost> service_worker_host =
-      service_worker_context->PreCreateHostForSharedWorker(process_id,
-                                                           &provider_info);
+      service_worker_context->PreCreateHostForWorker(process_id, provider_type,
+                                                     &provider_info);
 
   // Create the URL loader factory for WorkerScriptLoaderFactory to use to load
   // the main script.
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.h b/content/browser/worker_host/worker_script_fetch_initiator.h
index e481e8f..7143ea31 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.h
+++ b/content/browser/worker_host/worker_script_fetch_initiator.h
@@ -16,6 +16,7 @@
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/mojom/loader/fetch_client_settings_object.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/worker_main_script_load_params.mojom.h"
@@ -61,6 +62,8 @@
       const GURL& script_url,
       const url::Origin& request_initiator,
       network::mojom::CredentialsMode credentials_mode,
+      blink::mojom::FetchClientSettingsObjectPtr
+          outside_fetch_client_settings_object,
       ResourceType resource_type,
       scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
       AppCacheNavigationHandleCore* appcache_handle_core,
diff --git a/content/browser/worker_host/worker_script_loader.cc b/content/browser/worker_host/worker_script_loader.cc
index c0a33e7..70a86e0d 100644
--- a/content/browser/worker_host/worker_script_loader.cc
+++ b/content/browser/worker_host/worker_script_loader.cc
@@ -74,10 +74,11 @@
     return;
   }
 
+  // TODO(http://crbug.com/824840): Support interceptors on the UI thread.
   if (interceptor_index_ < interceptors_.size()) {
     auto* interceptor = interceptors_[interceptor_index_++].get();
     interceptor->MaybeCreateLoader(
-        resource_request_, resource_context,
+        resource_request_, nullptr, resource_context,
         base::BindOnce(&WorkerScriptLoader::MaybeStartLoader,
                        weak_factory_.GetWeakPtr(), interceptor),
         base::BindOnce(&WorkerScriptLoader::LoadFromNetwork,
@@ -287,7 +288,7 @@
   completed_ = true;
 
   if (service_worker_provider_host_ && status.error_code == net::OK)
-    service_worker_provider_host_->CompleteSharedWorkerPreparation();
+    service_worker_provider_host_->CompleteWebWorkerPreparation();
   client_->OnComplete(status);
 
   // We're done. Ensure we no longer send messages to our client, and no longer
diff --git a/content/browser/worker_host/worker_script_loader_factory.cc b/content/browser/worker_host/worker_script_loader_factory.cc
index b74841f4..c84d028 100644
--- a/content/browser/worker_host/worker_script_loader_factory.cc
+++ b/content/browser/worker_host/worker_script_loader_factory.cc
@@ -31,13 +31,11 @@
       loader_factory_(std::move(loader_factory)) {
 #if DCHECK_IS_ON()
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  // 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).
   if (service_worker_provider_host_) {
-    DCHECK_EQ(service_worker_provider_host_->provider_type(),
-              blink::mojom::ServiceWorkerProviderType::kForSharedWorker);
+    DCHECK(service_worker_provider_host_->provider_type() ==
+               blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker ||
+           service_worker_provider_host_->provider_type() ==
+               blink::mojom::ServiceWorkerProviderType::kForSharedWorker);
   }
 #endif  // DCHECK_IS_ON()
 }
diff --git a/content/browser/worker_host/worker_script_loader_factory_unittest.cc b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
index 2e4b8cab..b0ba4ee 100644
--- a/content/browser/worker_host/worker_script_loader_factory_unittest.cc
+++ b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
@@ -55,8 +55,9 @@
     service_worker_provider_info_ =
         blink::mojom::ServiceWorkerProviderInfoForWorker::New();
     service_worker_provider_host_ =
-        ServiceWorkerProviderHost::PreCreateForSharedWorker(
+        ServiceWorkerProviderHost::PreCreateForWebWorker(
             helper_->context()->AsWeakPtr(), kProcessId,
+            blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
             &service_worker_provider_info_);
   }
 
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 353e35a..2d4ed5a6 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -656,38 +656,6 @@
   return IsSchemeSupportedForAppCache(url);
 }
 
-base::File BlinkPlatformImpl::DatabaseOpenFile(
-    const blink::WebString& vfs_file_name,
-    int desired_flags) {
-  return base::File();
-}
-
-int BlinkPlatformImpl::DatabaseDeleteFile(const blink::WebString& vfs_file_name,
-                                          bool sync_dir) {
-  return -1;
-}
-
-int32_t BlinkPlatformImpl::DatabaseGetFileAttributes(
-    const blink::WebString& vfs_file_name) {
-  return 0;
-}
-
-int64_t BlinkPlatformImpl::DatabaseGetFileSize(
-    const blink::WebString& vfs_file_name) {
-  return 0;
-}
-
-int64_t BlinkPlatformImpl::DatabaseGetSpaceAvailableForOrigin(
-    const blink::WebSecurityOrigin& origin) {
-  return 0;
-}
-
-bool BlinkPlatformImpl::DatabaseSetFileSize(
-    const blink::WebString& vfs_file_name,
-    int64_t size) {
-  return false;
-}
-
 size_t BlinkPlatformImpl::MaxDecodedImageBytes() {
   const int kMB = 1024 * 1024;
   const int kMaxNumberOfBytesPerPixel = 4;
diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h
index 7bde51bf3..ce73e6f 100644
--- a/content/child/blink_platform_impl.h
+++ b/content/child/blink_platform_impl.h
@@ -37,17 +37,6 @@
   // Platform methods (partial implementation):
   blink::WebThemeEngine* ThemeEngine() override;
   bool IsURLSupportedForAppCache(const blink::WebURL& url) override;
-  base::File DatabaseOpenFile(const blink::WebString& vfs_file_name,
-                              int desired_flags) override;
-  int DatabaseDeleteFile(const blink::WebString& vfs_file_name,
-                         bool sync_dir) override;
-  int32_t DatabaseGetFileAttributes(
-      const blink::WebString& vfs_file_name) override;
-  int64_t DatabaseGetFileSize(const blink::WebString& vfs_file_name) override;
-  int64_t DatabaseGetSpaceAvailableForOrigin(
-      const blink::WebSecurityOrigin& origin) override;
-  bool DatabaseSetFileSize(const blink::WebString& vfs_file_name,
-                           int64_t size) override;
 
   size_t MaxDecodedImageBytes() override;
   bool IsLowEndDevice() override;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 462d40d03..3f8702c 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -475,7 +475,6 @@
     "frame_sink_provider.mojom",
     "histogram_fetcher.mojom",
     "host_zoom.mojom",
-    "image_downloader/image_downloader.mojom",
     "input/input_handler.mojom",
     "input/input_injector.mojom",
     "input/synchronous_compositor.mojom",
@@ -492,6 +491,7 @@
     "render_message_filter.mojom",
     "renderer.mojom",
     "renderer_host.mojom",
+    "service_control.mojom",
     "widget.mojom",
   ]
 
diff --git a/content/common/service_control.mojom b/content/common/service_control.mojom
new file mode 100644
index 0000000..d096b0c
--- /dev/null
+++ b/content/common/service_control.mojom
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "mojo/public/mojom/base/generic_pending_receiver.mojom";
+
+// ServiceControl is the interface the browser uses to control service behavior
+// within child processes.
+interface ServiceControl {
+  // Requests that the process bind a receiving pipe targeting the named service
+  // interface.
+  BindServiceInterface(mojo_base.mojom.GenericPendingReceiver receiver);
+};
diff --git a/content/gpu/gpu_child_thread.h b/content/gpu/gpu_child_thread.h
index 3b40d5a5..6e584b0 100644
--- a/content/gpu/gpu_child_thread.h
+++ b/content/gpu/gpu_child_thread.h
@@ -22,6 +22,7 @@
 #include "components/viz/service/gl/gpu_service_impl.h"
 #include "components/viz/service/main/viz_main_impl.h"
 #include "content/child/child_thread_impl.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/config/gpu_preferences.h"
diff --git a/content/ppapi_plugin/ppapi_blink_platform_impl.cc b/content/ppapi_plugin/ppapi_blink_platform_impl.cc
index 921d326f..7378136 100644
--- a/content/ppapi_plugin/ppapi_blink_platform_impl.cc
+++ b/content/ppapi_plugin/ppapi_blink_platform_impl.cc
@@ -90,11 +90,4 @@
   return blink::WebData();
 }
 
-int PpapiBlinkPlatformImpl::DatabaseDeleteFile(
-    const blink::WebString& vfs_file_name,
-    bool sync_dir) {
-  NOTREACHED();
-  return 0;
-}
-
 }  // namespace content
diff --git a/content/ppapi_plugin/ppapi_blink_platform_impl.h b/content/ppapi_plugin/ppapi_blink_platform_impl.h
index 2227892..f426392 100644
--- a/content/ppapi_plugin/ppapi_blink_platform_impl.h
+++ b/content/ppapi_plugin/ppapi_blink_platform_impl.h
@@ -35,8 +35,6 @@
   blink::WebString DefaultLocale() override;
   blink::WebThemeEngine* ThemeEngine() override;
   blink::WebData GetDataResource(const char* name) override;
-  int DatabaseDeleteFile(const blink::WebString& vfs_file_name,
-                         bool sync_dir) override;
 
  private:
 #if defined(OS_LINUX) || defined(OS_MACOSX)
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 120f34c3..54f9eea 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -549,7 +549,6 @@
     "javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java",
     "javatests/src/org/chromium/content/browser/webcontents/AccessibilitySnapshotTest.java",
     "javatests/src/org/chromium/content/browser/webcontents/WebContentsTest.java",
-    "javatests/src/org/chromium/content/common/ServiceManagerConnectionImplTest.java",
   ]
 
   data = [
diff --git a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
index a44e524..2340cbda 100644
--- a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
+++ b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
@@ -118,7 +118,6 @@
             }
         }
         boolean isLoaded = false;
-        boolean loadAtFixedAddressFailed = false;
         try {
             LibraryLoader.getInstance().loadNowOverrideApplicationContext(hostContext);
             isLoaded = true;
@@ -127,7 +126,6 @@
                 Log.w(TAG,
                         "Failed to load native library with shared RELRO, "
                                 + "retrying without");
-                loadAtFixedAddressFailed = true;
             } else {
                 Log.e(TAG, "Failed to load native library", e);
             }
@@ -144,8 +142,7 @@
         if (!isLoaded) {
             return false;
         }
-        LibraryLoader.getInstance().registerRendererProcessHistogram(
-                requestedSharedRelro, loadAtFixedAddressFailed);
+        LibraryLoader.getInstance().registerRendererProcessHistogram();
 
         return initializeLibrary();
     }
diff --git a/content/public/android/javatests/src/org/chromium/content/common/ServiceManagerConnectionImplTest.java b/content/public/android/javatests/src/org/chromium/content/common/ServiceManagerConnectionImplTest.java
deleted file mode 100644
index eacfa9d..0000000
--- a/content/public/android/javatests/src/org/chromium/content/common/ServiceManagerConnectionImplTest.java
+++ /dev/null
@@ -1,87 +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.content.common;
-
-import android.support.test.filters.SmallTest;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.content_shell_apk.ContentShellActivity;
-import org.chromium.content_shell_apk.ContentShellActivityTestRule;
-import org.chromium.echo.mojom.Echo;
-import org.chromium.echo.mojom.Echo.EchoStringResponse;
-import org.chromium.echo.mojom.EchoConstants;
-import org.chromium.mojo.bindings.InterfaceRequest;
-import org.chromium.mojo.system.Core;
-import org.chromium.mojo.system.MessagePipeHandle;
-import org.chromium.mojo.system.Pair;
-import org.chromium.mojo.system.impl.CoreImpl;
-import org.chromium.services.service_manager.Connector;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Use the Connector and ServiceManagerConnectionImpl to connect echo service.
- */
-@RunWith(BaseJUnit4ClassRunner.class)
-public class ServiceManagerConnectionImplTest {
-    @Rule
-    public ContentShellActivityTestRule mActivityTestRule = new ContentShellActivityTestRule();
-
-    private static final String TEST_URL = "about://blank";
-    private static final String TEST_STRING = "abcdefghijklmnopqrstuvwxyz";
-
-    private CallbackHelper mCallbackHelper;
-
-    private class EchoStringResponseImpl implements EchoStringResponse {
-        @Override
-        public void call(String str) {
-            // Verify they're equal.
-            Assert.assertEquals("The sent out string should be same as received", str, TEST_STRING);
-            mCallbackHelper.notifyCalled();
-        }
-    }
-
-    /**
-     * Connect Echo service, verify the sent and received data are same.
-     * @throws InterruptedException
-     * @throws ExecutionException
-     */
-    @Test
-    @SmallTest
-    public void testConnectEchoService()
-            throws InterruptedException, ExecutionException, TimeoutException {
-        final ContentShellActivity activity = mActivityTestRule.launchContentShellWithUrl(TEST_URL);
-        mActivityTestRule.waitForActiveShellToBeDoneLoading();
-
-        mCallbackHelper = new CallbackHelper();
-        final int callCount = mCallbackHelper.getCallCount();
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            MessagePipeHandle handle = ServiceManagerConnectionImpl.getConnectorMessagePipeHandle();
-            Core core = CoreImpl.getInstance();
-            Pair<Echo.Proxy, InterfaceRequest<Echo>> pair = Echo.MANAGER.getInterfaceRequest(core);
-
-            // Connect the Echo service via Connector.
-            Connector connector = new Connector(handle);
-            connector.bindInterface(
-                    EchoConstants.SERVICE_NAME, Echo.MANAGER.getName(), pair.second);
-
-            // Fire the echoString() mojo call.
-            EchoStringResponse callback = new EchoStringResponseImpl();
-            pair.first.echoString(TEST_STRING, callback);
-        });
-
-        // Wait the response from Echo service.
-        mCallbackHelper.waitForCallback(callCount);
-    }
-}
diff --git a/content/public/app/content_renderer_manifest.cc b/content/public/app/content_renderer_manifest.cc
index 27ffa5c..fb0fc07 100644
--- a/content/public/app/content_renderer_manifest.cc
+++ b/content/public/app/content_renderer_manifest.cc
@@ -66,6 +66,7 @@
               std::set<const char*>{
                   "blink.mojom.AppBannerController",
                   "blink.mojom.EngagementClient",
+                  "blink.mojom.ImageDownloader",
                   "blink.mojom.InstallationService",
                   "blink.mojom.ManifestManager",
                   "blink.mojom.MediaDevicesListener",
diff --git a/content/public/app/content_utility_manifest.cc b/content/public/app/content_utility_manifest.cc
index e3eafb7..322dbd0 100644
--- a/content/public/app/content_utility_manifest.cc
+++ b/content/public/app/content_utility_manifest.cc
@@ -23,6 +23,7 @@
                                 "content.mojom.ChildHistogramFetcher",
                                 "content.mojom.ChildHistogramFetcherFactory",
                                 "content.mojom.ResourceUsageReporter",
+                                "content.mojom.ServiceControl",
                                 "IPC.mojom.ChannelBootstrap",
                                 "printing.mojom.PdfToEmfConverterFactory",
                                 "printing.mojom.PdfToPwgRasterConverter",
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index c3551f5..b3a677c 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -111,6 +111,8 @@
     "console_message.h",
     "content_browser_client.cc",
     "content_browser_client.h",
+    "content_index_provider.cc",
+    "content_index_provider.h",
     "cookie_store_factory.h",
     "cors_exempt_headers.cc",
     "cors_exempt_headers.h",
@@ -270,6 +272,7 @@
     "resource_throttle.cc",
     "resource_throttle.h",
     "restore_type.h",
+    "sandbox_type.h",
     "save_page_type.h",
     "screen_orientation_delegate.h",
     "screenlock_observer.h",
@@ -280,6 +283,8 @@
     "serial_chooser.cc",
     "serial_chooser.h",
     "serial_delegate.h",
+    "service_process_host.cc",
+    "service_process_host.h",
     "service_worker_context.h",
     "service_worker_context_observer.h",
     "service_worker_running_info.h",
diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h
index 05a78a2..e745a29 100644
--- a/content/public/browser/browser_context.h
+++ b/content/public/browser/browser_context.h
@@ -80,6 +80,7 @@
 class BrowsingDataRemoverDelegate;
 class DownloadManager;
 class ClientHintsControllerDelegate;
+class ContentIndexProvider;
 class DownloadManagerDelegate;
 class NativeFileSystemPermissionContext;
 class PermissionController;
@@ -321,8 +322,7 @@
       base::OnceClosure closure);
 
   // Returns a SharedCorsOriginAccessList instance.
-  virtual const SharedCorsOriginAccessList* GetSharedCorsOriginAccessList()
-      const;
+  virtual const SharedCorsOriginAccessList* GetSharedCorsOriginAccessList();
 
   // Handles a service request for a service expected to run an instance per
   // BrowserContext.
@@ -362,6 +362,10 @@
   virtual NativeFileSystemPermissionContext*
   GetNativeFileSystemPermissionContext();
 
+  // Returns the ContentIndexProvider associated with that context if any,
+  // nullptr otherwise.
+  virtual ContentIndexProvider* GetContentIndexProvider();
+
  private:
   const std::string unique_id_;
   bool was_notify_will_be_destroyed_called_ = false;
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 2ff5541..fae53a9 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -133,11 +133,6 @@
   return nullptr;
 }
 
-void ContentBrowserClient::LogInitiatorSchemeBypassingDocumentBlocking(
-    const url::Origin& initiator_origin,
-    int render_process_id,
-    ResourceType resource_type) {}
-
 network::mojom::URLLoaderFactoryPtrInfo
 ContentBrowserClient::CreateURLLoaderFactoryForNetworkRequests(
     RenderProcessHost* process,
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 56f0da1..d1d54b95 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -326,17 +326,6 @@
   // exceptions should be granted based on initiator's scheme.
   virtual const char* GetInitiatorSchemeBypassingDocumentBlocking();
 
-  // Gives the embedder a chance to log that CORB would have blocked a response
-  // if it wasn't for GetInitatorSchemeBypassingDocumentBlocking above.  Called
-  // only after all the other CORB checks (potentially including sniffing) have
-  // been already run / right before blocking would have otherwise happened (and
-  // only for non-empty, non-4xx responses).
-  // TODO(lukasza): Remove once we gather enough data.
-  virtual void LogInitiatorSchemeBypassingDocumentBlocking(
-      const url::Origin& initiator_origin,
-      int render_process_id,
-      ResourceType resource_type);
-
   // Called to create a URLLoaderFactory for network requests in the following
   // cases:
   // - The default factory to be used by a frame.  In this case
diff --git a/content/public/browser/content_index_provider.cc b/content/public/browser/content_index_provider.cc
new file mode 100644
index 0000000..da93d39
--- /dev/null
+++ b/content/public/browser/content_index_provider.cc
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/browser/content_index_provider.h"
+
+namespace content {
+
+ContentIndexEntry::ContentIndexEntry(
+    int64_t service_worker_registration_id,
+    blink::mojom::ContentDescriptionPtr description,
+    base::Time registration_time)
+    : service_worker_registration_id(service_worker_registration_id),
+      description(std::move(description)),
+      registration_time(registration_time) {}
+
+ContentIndexEntry::ContentIndexEntry(ContentIndexEntry&& other) = default;
+
+ContentIndexEntry::~ContentIndexEntry() = default;
+
+ContentIndexProvider::ContentIndexProvider() = default;
+
+ContentIndexProvider::~ContentIndexProvider() = default;
+
+ContentIndexProvider::Client::~Client() = default;
+
+}  // namespace content
diff --git a/content/public/browser/content_index_provider.h b/content/public/browser/content_index_provider.h
new file mode 100644
index 0000000..425cef9
--- /dev/null
+++ b/content/public/browser/content_index_provider.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 CONTENT_PUBLIC_BROWSER_CONTENT_INDEX_PROVIDER_H_
+#define CONTENT_PUBLIC_BROWSER_CONTENT_INDEX_PROVIDER_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "content/common/content_export.h"
+#include "third_party/blink/public/mojom/content_index/content_index.mojom.h"
+
+class SkBitmap;
+
+namespace content {
+
+struct CONTENT_EXPORT ContentIndexEntry {
+  ContentIndexEntry(int64_t service_worker_registration_id,
+                    blink::mojom::ContentDescriptionPtr description,
+                    base::Time registration_time);
+  ContentIndexEntry(ContentIndexEntry&& other);
+  ~ContentIndexEntry();
+
+  // Part of the key for an entry since different service workers can use the
+  // same ID.
+  int64_t service_worker_registration_id;
+
+  // All the developer provided information.
+  blink::mojom::ContentDescriptionPtr description;
+
+  // The time the registration was created.
+  base::Time registration_time;
+};
+
+// Interface for content providers to receive content-related updates.
+class CONTENT_EXPORT ContentIndexProvider {
+ public:
+  // Interface for the client that updates the provider with entries.
+  class Client {
+   public:
+    virtual ~Client();
+
+    // The client will need to provide an icon for the entry when requested.
+    virtual void GetIcon(int64_t service_worker_registration_id,
+                         const std::string& description_id,
+                         base::OnceCallback<void(SkBitmap)> icon_callback) = 0;
+  };
+
+  ContentIndexProvider();
+  virtual ~ContentIndexProvider();
+
+  // Called when a new entry is registered. |client| is passed for when the
+  // provider will require additional information relating to the entry.
+  virtual void OnContentAdded(
+      ContentIndexEntry entry,
+      base::WeakPtr<ContentIndexProvider::Client> client) = 0;
+
+  // Called when an entry is unregistered.
+  virtual void OnContentDeleted(int64_t service_worker_registration_id,
+                                const std::string& description_id) = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentIndexProvider);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_CONTENT_INDEX_PROVIDER_H_
diff --git a/content/public/browser/download_manager_delegate.cc b/content/public/browser/download_manager_delegate.cc
index 6e2dbdd0..a4f9e36 100644
--- a/content/public/browser/download_manager_delegate.cc
+++ b/content/public/browser/download_manager_delegate.cc
@@ -44,6 +44,7 @@
     const std::string& mime_type,
     const std::string& request_origin,
     int64_t content_length,
+    bool is_transient,
     WebContents* web_contents) {
   return false;
 }
diff --git a/content/public/browser/download_manager_delegate.h b/content/public/browser/download_manager_delegate.h
index 4d94346..adf813f0 100644
--- a/content/public/browser/download_manager_delegate.h
+++ b/content/public/browser/download_manager_delegate.h
@@ -131,6 +131,7 @@
       const std::string& mime_type,
       const std::string& request_origin,
       int64_t content_length,
+      bool is_transient,
       WebContents* web_contents);
 
   // Retrieve the directories to save html pages and downloads to.
diff --git a/content/public/browser/sandbox_type.h b/content/public/browser/sandbox_type.h
new file mode 100644
index 0000000..cc6d830
--- /dev/null
+++ b/content/public/browser/sandbox_type.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 CONTENT_PUBLIC_BROWSER_SANDBOX_TYPE_H_
+#define CONTENT_PUBLIC_BROWSER_SANDBOX_TYPE_H_
+
+#include "services/service_manager/sandbox/sandbox_type.h"
+
+namespace content {
+
+// TODO(crbug.com/977637): Move the definition into this header.
+using SandboxType = service_manager::SandboxType;
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_SANDBOX_TYPE_H_
diff --git a/content/public/browser/service_process_host.cc b/content/public/browser/service_process_host.cc
new file mode 100644
index 0000000..fcf655f8
--- /dev/null
+++ b/content/public/browser/service_process_host.cc
@@ -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.
+
+#include "content/public/browser/service_process_host.h"
+#include "content/public/common/content_client.h"
+
+namespace content {
+
+ServiceProcessHost::Options::Options() = default;
+
+ServiceProcessHost::Options::~Options() = default;
+
+ServiceProcessHost::Options::Options(Options&&) = default;
+
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithSandboxType(
+    SandboxType type) {
+  sandbox_type = type;
+  return *this;
+}
+
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithDisplayName(
+    const base::string16& name) {
+  display_name = name;
+  return *this;
+}
+
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithDisplayName(
+    int resource_id) {
+  display_name = GetContentClient()->GetLocalizedString(resource_id);
+  return *this;
+}
+
+}  // namespace content
diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
new file mode 100644
index 0000000..39738d0c
--- /dev/null
+++ b/content/public/browser/service_process_host.h
@@ -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.
+
+#ifndef CONTENT_PUBLIC_BROWSER_SERVICE_PROCESS_HOST_H_
+#define CONTENT_PUBLIC_BROWSER_SERVICE_PROCESS_HOST_H_
+
+#include <memory>
+#include <tuple>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/sandbox_type.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace content {
+
+// A ServiceProcessHost instance corresponds to a single service process in the
+// system. ServiceProcessHost instances are not thread-safe, but they may be
+// created on any thread.
+class CONTENT_EXPORT ServiceProcessHost {
+ public:
+  struct CONTENT_EXPORT Options {
+    Options();
+    ~Options();
+
+    Options(Options&&);
+
+    Options& WithSandboxType(SandboxType type);
+    Options& WithDisplayName(const base::string16& name);
+    Options& WithDisplayName(int resource_id);
+
+    SandboxType sandbox_type = service_manager::SANDBOX_TYPE_UTILITY;
+    base::string16 display_name;
+  };
+
+  virtual ~ServiceProcessHost() {}
+
+  // Launches a new service process for asks it to bind the given interface
+  // receiver. |Interface| must be a service interface known to utility process
+  // code. See content/utility/services.cc and/or
+  // ContentUtilityClient::Run{Main,IO}ThreadService() methods.
+  //
+  // NOTE: The Interface type can be inferred from from the |receiver| argument.
+  template <typename Interface>
+  static std::unique_ptr<ServiceProcessHost> Launch(
+      mojo::PendingReceiver<Interface> receiver,
+      Options options = {}) {
+    return Launch(Interface::Name_, receiver.PassPipe(), std::move(options));
+  }
+
+  // Same as above but expects |remote| to point to an unbound Remote. The
+  // interface type is inferred from |remote| and upon return |*remote| is
+  // bound. The process will launch with a receiver for that interface type.
+  template <typename Interface>
+  static std::unique_ptr<ServiceProcessHost> Launch(
+      mojo::Remote<Interface>* unbound_remote,
+      Options options = {}) {
+    return Launch(unbound_remote->BindNewPipeAndPassReceiver(),
+                  std::move(options));
+  }
+
+ protected:
+  // Launches a new service process for asks it to bind a receiver for the
+  // service interface named by |interface_name|. The receiving pipe must be
+  // given in |pipe| and should be connected to a Remote of the same interface
+  // type.
+  static std::unique_ptr<ServiceProcessHost> Launch(
+      base::StringPiece service_interface_name,
+      mojo::ScopedMessagePipeHandle receiving_pipe,
+      Options options = {});
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_SERVICE_PROCESS_HOST_H_
diff --git a/content/public/browser/url_loader_request_interceptor.h b/content/public/browser/url_loader_request_interceptor.h
index bcc52786..a676a1fa 100644
--- a/content/public/browser/url_loader_request_interceptor.h
+++ b/content/public/browser/url_loader_request_interceptor.h
@@ -16,6 +16,7 @@
 
 namespace content {
 
+class BrowserContext;
 class ResourceContext;
 
 // URLLoaderRequestInterceptor is given a chance to create a URLLoader and
@@ -43,8 +44,16 @@
   // request later passed to the RequestHandler given to |callback| may not be
   // exactly the same. See documentation for
   // NavigationLoaderInterceptor::MaybeCreateLoader.
+  //
+  // |browser_context| will only be non-null when this interceptor is running on
+  // the UI thread, and |resource_context| will only be non-null when running on
+  // the IO thread.
+  //
+  // TODO(http://crbug.com/824840): Once all interceptors support
+  // running on UI, remove |resource_context|.
   virtual void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
+      BrowserContext* browser_context,
       ResourceContext* resource_context,
       LoaderCallback callback) = 0;
 };
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestRule.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestRule.java
index 50434b7..2fd94e7947 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestRule.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestRule.java
@@ -9,7 +9,6 @@
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
-import org.chromium.base.PathUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
@@ -24,8 +23,6 @@
  * NativeLibraryTestRule does not interact with any Activity.
  */
 public class NativeLibraryTestRule implements TestRule {
-    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "content";
-
     /**
      * Loads the native library on the activity UI thread (must not be called from the UI thread).
      */
@@ -44,8 +41,6 @@
     private void handleNativeInitialization(final boolean initBrowserProcess) {
         Assert.assertFalse(ThreadUtils.runningOnUiThread());
 
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
-
         // LibraryLoader is not in general multithreaded; as other InstrumentationTestCase code
         // (specifically, ChromeBrowserProvider) uses it from the main thread we must do
         // likewise.
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index da8cc87..c86780b 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -26,6 +26,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
@@ -526,6 +527,19 @@
 #endif
 
 void BrowserTestBase::ProxyRunTestOnMainThreadLoop() {
+  // Install a RunLoop timeout if none is present but do not override tests that
+  // set a ScopedRunTimeoutForTest from their fixture's constructor (which
+  // happens as part of setting up the test factory in gtest while
+  // ProxyRunTestOnMainThreadLoop() happens later as part of SetUp()).
+  base::Optional<base::RunLoop::ScopedRunTimeoutForTest> scoped_run_timeout;
+  if (!base::RunLoop::ScopedRunTimeoutForTest::Current()) {
+    // TODO(https://crbug.com/918724): determine whether the timeout can be
+    // reduced from action_max_timeout() to action_timeout().
+    scoped_run_timeout.emplace(TestTimeouts::action_max_timeout(),
+                               base::MakeExpectedNotRunClosure(
+                                   FROM_HERE, "RunLoop::Run() timed out."));
+  }
+
 #if defined(OS_POSIX)
   g_browser_process_pid = base::GetCurrentProcId();
   signal(SIGSEGV, DumpStackTraceSignalHandler);
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc
index 8e21d72..5997fb83 100644
--- a/content/public/test/test_browser_thread_bundle.cc
+++ b/content/public/test/test_browser_thread_bundle.cc
@@ -90,7 +90,7 @@
   auto browser_ui_thread_scheduler = BrowserUIThreadScheduler::CreateForTesting(
       sequence_manager(), GetTimeDomain());
   auto default_ui_task_runner =
-      browser_ui_thread_scheduler->GetHandle().GetDefaultTaskRunner();
+      browser_ui_thread_scheduler->GetHandle()->GetDefaultTaskRunner();
   auto browser_io_task_environment =
       real_io_thread_
           ? std::make_unique<BrowserIOTaskEnvironment>()
diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc
index b9d52aab..7d86df4a 100644
--- a/content/public/test/test_launcher.cc
+++ b/content/public/test/test_launcher.cc
@@ -357,9 +357,12 @@
 
   auto observer = std::make_unique<ChildProcessLifetimeObserver>(
       this, test_launcher, std::move(test_names_copy), test_name, output_file);
+
+  // Must use test_launcher_timeout() here because this process is allowed to
+  // assume that it can use an entire action_max_timeout() in its lifespan.
   test_launcher->LaunchChildGTestProcess(
       new_cmd_line, browser_wrapper ? browser_wrapper : std::string(),
-      TestTimeouts::action_max_timeout(), test_launch_options,
+      TestTimeouts::test_launcher_timeout(), test_launch_options,
       std::move(observer));
 }
 
diff --git a/content/public/utility/content_utility_client.cc b/content/public/utility/content_utility_client.cc
index 714acf67..354911d 100644
--- a/content/public/utility/content_utility_client.cc
+++ b/content/public/utility/content_utility_client.cc
@@ -16,4 +16,10 @@
   return false;
 }
 
+void ContentUtilityClient::RunIOThreadService(
+    mojo::GenericPendingReceiver* receiver) {}
+
+void ContentUtilityClient::RunMainThreadService(
+    mojo::GenericPendingReceiver receiver) {}
+
 }  // namespace content
diff --git a/content/public/utility/content_utility_client.h b/content/public/utility/content_utility_client.h
index 09a1190..48b297d 100644
--- a/content/public/utility/content_utility_client.h
+++ b/content/public/utility/content_utility_client.h
@@ -10,6 +10,7 @@
 
 #include "base/callback_forward.h"
 #include "content/public/common/content_client.h"
+#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
 #include "services/service_manager/public/cpp/binder_map.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service.h"
@@ -42,6 +43,17 @@
       const std::string& service_name,
       service_manager::mojom::ServiceRequest request);
 
+  // Allows the embedder to handle an incoming service interface request to run
+  // a service on the IO thread. |*receiver| is always valid when this called,
+  // and the embedder is free to take ownership if handling the request. If the
+  // embedder does not wish to handle this request on the I/O thread, it must
+  // not modify |*receiver|.
+  virtual void RunIOThreadService(mojo::GenericPendingReceiver* receiver);
+
+  // Allows the embedder to handle an incoming service interface request to run
+  // a service on the main thread. |receiver| is always valid when this called.
+  virtual void RunMainThreadService(mojo::GenericPendingReceiver receiver);
+
   virtual void RegisterNetworkBinders(
       service_manager::BinderRegistry* registry) {}
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 295b0bd2..9125eae 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -427,8 +427,6 @@
     "top_level_blame_context.h",
     "v8_value_converter_impl.cc",
     "v8_value_converter_impl.h",
-    "web_database_observer_impl.cc",
-    "web_database_observer_impl.h",
     "web_ui_extension.cc",
     "web_ui_extension.h",
     "web_ui_extension_data.cc",
@@ -439,8 +437,8 @@
     "worker/dedicated_worker_host_factory_client.h",
     "worker/embedded_shared_worker_stub.cc",
     "worker/embedded_shared_worker_stub.h",
-    "worker/service_worker_network_provider_for_worker.cc",
-    "worker/service_worker_network_provider_for_worker.h",
+    "worker/service_worker_network_provider_for_shared_worker.cc",
+    "worker/service_worker_network_provider_for_shared_worker.h",
     "worker/shared_worker_factory_impl.cc",
     "worker/shared_worker_factory_impl.h",
     "worker/worker_thread_registry.cc",
diff --git a/content/renderer/OWNERS b/content/renderer/OWNERS
index 002af66..1ead6343 100644
--- a/content/renderer/OWNERS
+++ b/content/renderer/OWNERS
@@ -29,6 +29,3 @@
 per-file child_frame_compositor*=jonross@chromium.org
 per-file child_frame_compositing_helper*=jonross@chromium.org
 per-file render_frame_metadata*=jonross@chromium.org
-
-# For AppCache.
-per-file renderer_webapplicationcachehost_impl*=pwnall@chromium.org
diff --git a/content/renderer/image_downloader/image_downloader_impl.cc b/content/renderer/image_downloader/image_downloader_impl.cc
index 164406a..7d2d9370 100644
--- a/content/renderer/image_downloader/image_downloader_impl.cc
+++ b/content/renderer/image_downloader/image_downloader_impl.cc
@@ -85,8 +85,9 @@
 
 namespace content {
 
-ImageDownloaderImpl::ImageDownloaderImpl(RenderFrame* render_frame,
-                                         mojom::ImageDownloaderRequest request)
+ImageDownloaderImpl::ImageDownloaderImpl(
+    RenderFrame* render_frame,
+    blink::mojom::ImageDownloaderRequest request)
     : ImageDownloaderBase(render_frame), binding_(this, std::move(request)) {
   DCHECK(render_frame);
   binding_.set_connection_error_handler(
@@ -98,7 +99,7 @@
 // static
 void ImageDownloaderImpl::CreateMojoService(
     RenderFrame* render_frame,
-    mojom::ImageDownloaderRequest request) {
+    blink::mojom::ImageDownloaderRequest request) {
   DVLOG(1) << "ImageDownloaderImpl::CreateMojoService";
   DCHECK(render_frame);
 
diff --git a/content/renderer/image_downloader/image_downloader_impl.h b/content/renderer/image_downloader/image_downloader_impl.h
index e244dc4..1570ba4 100644
--- a/content/renderer/image_downloader/image_downloader_impl.h
+++ b/content/renderer/image_downloader/image_downloader_impl.h
@@ -5,23 +5,23 @@
 #ifndef CONTENT_RENDERER_IMAGE_DOWNLOADER_IMAGE_DOWNLOADER_IMPL_H_
 #define CONTENT_RENDERER_IMAGE_DOWNLOADER_IMAGE_DOWNLOADER_IMPL_H_
 
-#include "content/common/image_downloader/image_downloader.mojom.h"
 #include "content/renderer/image_downloader/image_downloader_base.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/public/mojom/image_downloader/image_downloader.mojom.h"
 
 namespace content {
 
-class ImageDownloaderImpl : public mojom::ImageDownloader,
+class ImageDownloaderImpl : public blink::mojom::ImageDownloader,
                             public ImageDownloaderBase {
  public:
   ~ImageDownloaderImpl() override;
 
   static void CreateMojoService(RenderFrame* render_frame,
-                                mojom::ImageDownloaderRequest request);
+                                blink::mojom::ImageDownloaderRequest request);
 
  private:
   ImageDownloaderImpl(RenderFrame* render_frame,
-                      mojom::ImageDownloaderRequest request);
+                      blink::mojom::ImageDownloaderRequest request);
 
   // Override ImageDownloaderBase::OnDestruct().
   void OnDestruct() override;
@@ -43,7 +43,7 @@
                         int32_t http_status_code,
                         const std::vector<SkBitmap>& images);
 
-  mojo::Binding<mojom::ImageDownloader> binding_;
+  mojo::Binding<blink::mojom::ImageDownloader> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageDownloaderImpl);
 };
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 5eaf5ac..fea44b2 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -10,6 +10,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -104,30 +105,6 @@
 
 using HeadersVector = network::HttpRawRequestResponseInfo::HeadersVector;
 
-// TODO(estark): Figure out a way for the embedder to provide the
-// security style for a resource. Ideally, the logic for assigning
-// per-resource security styles should live in the same place as the
-// logic for assigning per-page security styles (which lives in the
-// embedder). It would also be nice for the embedder to have the chance
-// to control the per-resource security style beyond the simple logic
-// here. (For example, the embedder might want to mark certain resources
-// differently if they use SHA1 signatures.) https://crbug.com/648326
-blink::WebSecurityStyle GetSecurityStyleForResource(
-    const GURL& url,
-    net::CertStatus cert_status) {
-  if (!url.SchemeIsCryptographic())
-    return blink::kWebSecurityStyleNeutral;
-
-  // Minor errors don't lower the security style to
-  // WebSecurityStyleAuthenticationBroken.
-  if (net::IsCertStatusError(cert_status) &&
-      !net::IsCertStatusMinorError(cert_status)) {
-    return blink::kWebSecurityStyleInsecure;
-  }
-
-  return blink::kWebSecurityStyleSecure;
-}
-
 // Converts timing data from |load_timing| to the format used by WebKit.
 void PopulateURLLoadTiming(const net::LoadTimingInfo& load_timing,
                            WebURLLoadTiming* url_timing) {
@@ -270,8 +247,13 @@
     }
   }
 
-  response->SetSecurityStyle(
-      GetSecurityStyleForResource(url, info.cert_status));
+  // Minor errors don't lower the security style to WebSecurityStyleInsecure.
+  if (net::IsCertStatusError(info.cert_status) &&
+      !net::IsCertStatusMinorError(info.cert_status)) {
+    response->SetSecurityStyle(blink::kWebSecurityStyleInsecure);
+  } else {
+    response->SetSecurityStyle(blink::kWebSecurityStyleSecure);
+  }
 
   blink::WebURLResponse::SignedCertificateTimestampList sct_list(
       ssl_info.signed_certificate_timestamps.size());
diff --git a/content/renderer/loader/web_url_loader_impl_unittest.cc b/content/renderer/loader/web_url_loader_impl_unittest.cc
index 3f7db944..bf01751c 100644
--- a/content/renderer/loader/web_url_loader_impl_unittest.cc
+++ b/content/renderer/loader/web_url_loader_impl_unittest.cc
@@ -45,6 +45,7 @@
 #include "third_party/blink/public/platform/web_url_loader_client.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/public/platform/web_vector.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -235,7 +236,7 @@
       int64_t totalEncodedBodyLength,
       int64_t totalDecodedBodyLength,
       bool should_report_corb_blocking,
-      const std::vector<network::cors::PreflightTimingInfo>&) override {
+      const blink::WebVector<network::cors::PreflightTimingInfo>&) override {
     EXPECT_TRUE(loader_);
     EXPECT_TRUE(did_receive_response_);
     EXPECT_FALSE(did_finish_);
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.cc b/content/renderer/loader/web_worker_fetch_context_impl.cc
index 861df1d..2da6fee 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.cc
+++ b/content/renderer/loader/web_worker_fetch_context_impl.cc
@@ -28,7 +28,6 @@
 #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/worker/service_worker_network_provider_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"
diff --git a/content/renderer/media/android/stream_texture_factory.cc b/content/renderer/media/android/stream_texture_factory.cc
index a2ee9ff..6fcea5f 100644
--- a/content/renderer/media/android/stream_texture_factory.cc
+++ b/content/renderer/media/android/stream_texture_factory.cc
@@ -6,11 +6,8 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/ipc/client/command_buffer_proxy_impl.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/gpu_messages.h"
-#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace content {
@@ -71,63 +68,60 @@
     received_frame_cb_.Run();
 }
 
-void StreamTextureProxy::SetStreamTextureSize(const gfx::Size& size) {
-  host_->SetStreamTextureSize(size);
-}
-
 void StreamTextureProxy::ForwardStreamTextureForSurfaceRequest(
     const base::UnguessableToken& request_token) {
   host_->ForwardStreamTextureForSurfaceRequest(request_token);
 }
 
+void StreamTextureProxy::CreateSharedImage(
+    const gfx::Size& size,
+    gpu::Mailbox* mailbox,
+    gpu::SyncToken* unverified_sync_token) {
+  *mailbox = host_->CreateSharedImage(size);
+  if (!mailbox)
+    return;
+  *unverified_sync_token = host_->GenUnverifiedSyncToken();
+}
+
 // static
 scoped_refptr<StreamTextureFactory> StreamTextureFactory::Create(
-    scoped_refptr<viz::ContextProviderCommandBuffer> context_provider) {
-  return new StreamTextureFactory(std::move(context_provider));
+    scoped_refptr<gpu::GpuChannelHost> channel) {
+  return new StreamTextureFactory(std::move(channel));
 }
 
 StreamTextureFactory::StreamTextureFactory(
-    scoped_refptr<viz::ContextProviderCommandBuffer> context_provider)
-    : context_provider_(std::move(context_provider)),
-      channel_(context_provider_->GetCommandBufferProxy()->channel()) {
+    scoped_refptr<gpu::GpuChannelHost> channel)
+    : channel_(std::move(channel)) {
   DCHECK(channel_);
 }
 
-StreamTextureFactory::~StreamTextureFactory() {}
+StreamTextureFactory::~StreamTextureFactory() = default;
 
-ScopedStreamTextureProxy StreamTextureFactory::CreateProxy(
-    unsigned* texture_id,
-    gpu::Mailbox* texture_mailbox) {
-  int32_t route_id = CreateStreamTexture(texture_id, texture_mailbox);
+ScopedStreamTextureProxy StreamTextureFactory::CreateProxy() {
+  int32_t route_id = CreateStreamTexture();
   if (!route_id)
     return ScopedStreamTextureProxy();
   return ScopedStreamTextureProxy(new StreamTextureProxy(
       std::make_unique<StreamTextureHost>(channel_, route_id)));
 }
 
-unsigned StreamTextureFactory::CreateStreamTexture(
-    unsigned* texture_id,
-    gpu::Mailbox* texture_mailbox) {
-  GLuint route_id = 0;
-  gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
-  gl->GenTextures(1, texture_id);
-  gl->ShallowFlushCHROMIUM();
-  route_id = context_provider_->GetCommandBufferProxy()->CreateStreamTexture(
-      *texture_id);
-  if (!route_id) {
-    gl->DeleteTextures(1, texture_id);
-    // Flush to ensure that the stream texture gets deleted in a timely fashion.
-    gl->ShallowFlushCHROMIUM();
-    *texture_id = 0;
-    *texture_mailbox = gpu::Mailbox();
-  } else {
-    gl->ProduceTextureDirectCHROMIUM(*texture_id, texture_mailbox->name);
-  }
-  return route_id;
+bool StreamTextureFactory::IsLost() const {
+  return channel_->IsLost();
 }
 
-gpu::gles2::GLES2Interface* StreamTextureFactory::ContextGL() {
-  return context_provider_->ContextGL();
+unsigned StreamTextureFactory::CreateStreamTexture() {
+  int32_t stream_id = channel_->GenerateRouteID();
+  bool succeeded = false;
+  channel_->Send(new GpuChannelMsg_CreateStreamTexture(stream_id, &succeeded));
+  if (!succeeded) {
+    DLOG(ERROR) << "GpuChannelMsg_CreateStreamTexture returned failure";
+    return 0;
+  }
+  return stream_id;
+}
+
+gpu::SharedImageInterface* StreamTextureFactory::SharedImageInterface() {
+  return channel_->shared_image_interface();
 }
 
 }  // namespace content
diff --git a/content/renderer/media/android/stream_texture_factory.h b/content/renderer/media/android/stream_texture_factory.h
index 38ce94d..e65bf4e11 100644
--- a/content/renderer/media/android/stream_texture_factory.h
+++ b/content/renderer/media/android/stream_texture_factory.h
@@ -20,16 +20,11 @@
 #include "ui/gfx/geometry/size.h"
 
 namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}  // namespace gles2
 class GpuChannelHost;
+class SharedImageInterface;
+struct SyncToken;
 }  // namespace gpu
 
-namespace viz {
-class ContextProviderCommandBuffer;
-}
-
 namespace content {
 
 class StreamTextureFactory;
@@ -50,15 +45,21 @@
   // StreamTextureHost::Listener implementation:
   void OnFrameAvailable() override;
 
-  // Set the streamTexture size.
-  void SetStreamTextureSize(const gfx::Size& size);
-
   // Sends an IPC to the GPU process.
   // Asks the StreamTexture to forward its SurfaceTexture to the
   // ScopedSurfaceRequestManager, using the gpu::ScopedSurfaceRequestConduit.
   void ForwardStreamTextureForSurfaceRequest(
       const base::UnguessableToken& request_token);
 
+  // Creates a SharedImage for the provided texture size. Returns the
+  // |mailbox| for the SharedImage, as well as an |unverified_sync_token|
+  // representing SharedImage creation.
+  // If creation fails, returns an empty |mailbox| and does not modify
+  // |unverified_sync_token|.
+  void CreateSharedImage(const gfx::Size& size,
+                         gpu::Mailbox* mailbox,
+                         gpu::SyncToken* unverified_sync_token);
+
   // Clears |received_frame_cb_| in a thread safe way.
   void ClearReceivedFrameCB();
 
@@ -90,33 +91,26 @@
     : public base::RefCounted<StreamTextureFactory> {
  public:
   static scoped_refptr<StreamTextureFactory> Create(
-      scoped_refptr<viz::ContextProviderCommandBuffer> context_provider);
+      scoped_refptr<gpu::GpuChannelHost> channel);
 
-  // Create the StreamTextureProxy object. This internally calls
-  // CreateSteamTexture with the recieved arguments. CreateSteamTexture
-  // generates a texture and stores it in  *texture_id, the texture is produced
-  // into a mailbox so it can be shipped in a VideoFrame, it creates a
-  // gpu::StreamTexture and returns its route_id. If this route_id is  invalid
-  // nullptr is returned and *texture_id will be set to 0. If the route_id is
-  // valid it returns StreamTextureProxy object. The caller needs to take care
-  // of cleaning up the texture_id.
-  ScopedStreamTextureProxy CreateProxy(unsigned* texture_id,
-                                       gpu::Mailbox* texture_mailbox);
+  // Create the StreamTextureProxy object. This internally creates a
+  // gpu::StreamTexture and returns its route_id. If this route_id is invalid
+  // nullptr is returned. If the route_id is valid it returns
+  // StreamTextureProxy object.
+  ScopedStreamTextureProxy CreateProxy();
 
-  gpu::gles2::GLES2Interface* ContextGL();
+  // Returns true if the StreamTextureFactory's channel is lost.
+  bool IsLost() const;
+
+  gpu::SharedImageInterface* SharedImageInterface();
 
  private:
   friend class base::RefCounted<StreamTextureFactory>;
-  StreamTextureFactory(
-      scoped_refptr<viz::ContextProviderCommandBuffer> context_provider);
+  StreamTextureFactory(scoped_refptr<gpu::GpuChannelHost> channel);
   ~StreamTextureFactory();
-  // Creates a gpu::StreamTexture and returns its id.  Sets |*texture_id| to the
-  // client-side id of the gpu::StreamTexture. The texture is produced into
-  // a mailbox so it can be shipped in a VideoFrame.
-  unsigned CreateStreamTexture(unsigned* texture_id,
-                               gpu::Mailbox* texture_mailbox);
+  // Creates a gpu::StreamTexture and returns its id.
+  unsigned CreateStreamTexture();
 
-  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider_;
   scoped_refptr<gpu::GpuChannelHost> channel_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(StreamTextureFactory);
diff --git a/content/renderer/media/android/stream_texture_wrapper_impl.cc b/content/renderer/media/android/stream_texture_wrapper_impl.cc
index 8a0eceb9..256ea79 100644
--- a/content/renderer/media/android/stream_texture_wrapper_impl.cc
+++ b/content/renderer/media/android/stream_texture_wrapper_impl.cc
@@ -8,21 +8,17 @@
 #include "base/callback.h"
 #include "cc/layers/video_frame_provider.h"
 #include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/shared_image_interface.h"
 #include "media/base/bind_to_current_loop.h"
 
-using gpu::gles2::GLES2Interface;
-
 namespace {
 // Non-member function to allow it to run even after this class is deleted.
-void OnReleaseTexture(scoped_refptr<content::StreamTextureFactory> factories,
-                      uint32_t texture_id,
-                      const gpu::SyncToken& sync_token) {
-  GLES2Interface* gl = factories->ContextGL();
-  gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
-  gl->DeleteTextures(1, &texture_id);
-  // Flush to ensure that the stream texture gets deleted in a timely fashion.
-  gl->ShallowFlushCHROMIUM();
+void OnReleaseVideoFrame(scoped_refptr<content::StreamTextureFactory> factories,
+                         gpu::Mailbox mailbox,
+                         const gpu::SyncToken& sync_token) {
+  gpu::SharedImageInterface* sii = factories->SharedImageInterface();
+  sii->DestroySharedImage(sync_token, mailbox);
+  sii->Flush();
 }
 }
 
@@ -33,21 +29,12 @@
     scoped_refptr<StreamTextureFactory> factory,
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
     : enable_texture_copy_(enable_texture_copy),
-      texture_id_(0),
       factory_(factory),
       main_task_runner_(main_task_runner),
       weak_factory_(this) {}
 
 StreamTextureWrapperImpl::~StreamTextureWrapperImpl() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
-
-  if (texture_id_) {
-    GLES2Interface* gl = factory_->ContextGL();
-    gl->DeleteTextures(1, &texture_id_);
-    // Flush to ensure that the stream texture gets deleted in a timely fashion.
-    gl->ShallowFlushCHROMIUM();
-  }
-
   SetCurrentFrameInternal(nullptr);
 }
 
@@ -64,37 +51,24 @@
   return current_frame_;
 }
 
-void StreamTextureWrapperImpl::ReallocateVideoFrame(
-    const gfx::Size& natural_size) {
+void StreamTextureWrapperImpl::ReallocateVideoFrame() {
   DVLOG(2) << __func__;
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
-  GLES2Interface* gl = factory_->ContextGL();
-  GLuint texture_target = GL_TEXTURE_EXTERNAL_OES;
-  GLuint texture_id_ref =
-      gl->CreateAndConsumeTextureCHROMIUM(texture_mailbox_.name);
-  gl->Flush();
-
+  gpu::Mailbox mailbox;
   gpu::SyncToken texture_mailbox_sync_token;
-  if (texture_mailbox_sync_token.namespace_id() ==
-      gpu::CommandBufferNamespace::IN_PROCESS) {
-    // TODO(boliu): Remove this once Android WebView switches to IPC-based
-    // command buffer for video.
-    gl->GenSyncTokenCHROMIUM(texture_mailbox_sync_token.GetData());
-  } else {
-    gl->GenUnverifiedSyncTokenCHROMIUM(texture_mailbox_sync_token.GetData());
-  }
-
+  stream_texture_proxy_->CreateSharedImage(natural_size_, &mailbox,
+                                           &texture_mailbox_sync_token);
   gpu::MailboxHolder holders[media::VideoFrame::kMaxPlanes] = {
-      gpu::MailboxHolder(texture_mailbox_, texture_mailbox_sync_token,
-                         texture_target)};
+      gpu::MailboxHolder(mailbox, texture_mailbox_sync_token,
+                         GL_TEXTURE_EXTERNAL_OES)};
 
   scoped_refptr<media::VideoFrame> new_frame =
       media::VideoFrame::WrapNativeTextures(
           media::PIXEL_FORMAT_ARGB, holders,
           media::BindToCurrentLoop(
-              base::BindOnce(&OnReleaseTexture, factory_, texture_id_ref)),
-          natural_size, gfx::Rect(natural_size), natural_size,
+              base::BindOnce(&OnReleaseVideoFrame, factory_, mailbox)),
+          natural_size_, gfx::Rect(natural_size_), natural_size_,
           base::TimeDelta());
 
   if (enable_texture_copy_) {
@@ -140,9 +114,7 @@
     return;
 
   natural_size_ = new_size;
-
-  ReallocateVideoFrame(new_size);
-  stream_texture_proxy_->SetStreamTextureSize(new_size);
+  ReallocateVideoFrame();
 }
 
 void StreamTextureWrapperImpl::Initialize(
@@ -175,14 +147,13 @@
     return;
   }
 
-  stream_texture_proxy_ =
-      factory_->CreateProxy(&texture_id_, &texture_mailbox_);
+  stream_texture_proxy_ = factory_->CreateProxy();
   if (!stream_texture_proxy_) {
     init_cb.Run(false);
     return;
   }
 
-  ReallocateVideoFrame(natural_size_);
+  ReallocateVideoFrame();
 
   stream_texture_proxy_->BindToTaskRunner(received_frame_cb,
                                           compositor_task_runner_);
diff --git a/content/renderer/media/android/stream_texture_wrapper_impl.h b/content/renderer/media/android/stream_texture_wrapper_impl.h
index bb14d61..153eb9f5 100644
--- a/content/renderer/media/android/stream_texture_wrapper_impl.h
+++ b/content/renderer/media/android/stream_texture_wrapper_impl.h
@@ -20,26 +20,27 @@
 // any thread, but additional threading considerations are listed in the
 // comments of individual methods.
 //
-// The StreamTexture is an abstraction allowing Chrome to wrap a SurfaceTexture
+// The StreamTexture is an abstraction allowing Chrome to wrap a SurfaceOwner
 // living in the GPU process. It allows VideoFrames to be created from the
-// SurfaceTexture's texture, in the Renderer process.
+// SurfaceOwner's texture, in the Renderer process.
 //
 // The general idea behind our use of StreamTexture is as follows:
-// - We create a client GL texture in the Renderer process.
-// - We request the creation of a StreamTexture via the StreamTextureFactory,
-// passing the client texture ID. The call is sent to the GPU process via the
-// CommandBuffer. The "platform" GL texture reference associated with the client
-// texture ID is looked up in the TextureManager. A StreamTexture is then
-// created, wrapping a SurfaceTexture created from the texture reference. The
-// SurfaceTexture's OnFrameAvailable() callback is tied to StreamTexture's
-// OnFrameAvailable(), which fires an IPC accross the GPU channel.
+// - We request the creation of a StreamTexture via the StreamTextureFactory.
+// The call is sent to the GPU process via the CommandBuffer. A StreamTexture is
+// then created, wrapping a SurfaceOwner. The SurfaceOwner's
+// OnFrameAvailable() callback is tied to StreamTexture's OnFrameAvailable(),
+// which fires an IPC across the GPU channel.
 // - We create a StreamTextureProxy in the Renderer process which listens for
 // the IPC fired by the StreamTexture's OnFrameAvailable() callback.
 // - We bind the StreamTextureProxy's lifetime to the |compositor_task_runner_|.
-// - We wrap the client texture into a VideoFrame.
-// - When the SurfaceTexture's OnFrameAvailable() callback is fired (and routed
+// - We create a SharedImage mailbox representing the StreamTexture at a given
+// size.
+// - We create a VideoFrame which takes ownership of this SharedImage mailbox.
+// - When the SurfaceOwner's OnFrameAvailable() callback is fired (and routed
 // to the StreamTextureProxy living on the compositor thread), we notify
 // |client_| that a new frame is available, via the DidReceiveFrame() callback.
+// - When the StreamTextureProxy is destroyed, it delivers a notification over
+// the channel, cleaning up the StreamTexture ref in the GPU process.
 class CONTENT_EXPORT StreamTextureWrapperImpl
     : public media::StreamTextureWrapper {
  public:
@@ -100,18 +101,12 @@
   void InitializeOnMainThread(const base::RepeatingClosure& received_frame_cb,
                               const StreamTextureWrapperInitCB& init_cb);
 
-  void ReallocateVideoFrame(const gfx::Size& natural_size);
+  void ReallocateVideoFrame();
 
   void SetCurrentFrameInternal(scoped_refptr<media::VideoFrame> video_frame);
 
   bool enable_texture_copy_;
 
-  // Client GL texture ID allocated to the StreamTexture.
-  unsigned texture_id_;
-
-  // GL texture mailbox for |texture_id_|.
-  gpu::Mailbox texture_mailbox_;
-
   // Object for calling back the compositor thread to repaint the video when a
   // frame is available. It should be bound to |compositor_task_runner_|.
   ScopedStreamTextureProxy stream_texture_proxy_;
diff --git a/content/renderer/media/audio/audio_device_factory.cc b/content/renderer/media/audio/audio_device_factory.cc
index 5d3f60e..869349c0 100644
--- a/content/renderer/media/audio/audio_device_factory.cc
+++ b/content/renderer/media/audio/audio_device_factory.cc
@@ -66,13 +66,13 @@
 
 // This is where we decide which audio will go to mixers and which one to
 // AudioOutputDevice directly.
-bool IsMixable(AudioDeviceFactory::SourceType source_type) {
+bool IsMixable(blink::WebAudioDeviceSourceType source_type) {
   // Media element must ALWAYS go through mixer.
-  return source_type == AudioDeviceFactory::kSourceMediaElement;
+  return source_type == blink::WebAudioDeviceSourceType::kMediaElement;
 }
 
 scoped_refptr<media::SwitchableAudioRendererSink> NewMixableSink(
-    AudioDeviceFactory::SourceType source_type,
+    blink::WebAudioDeviceSourceType source_type,
     int render_frame_id,
     const media::AudioSinkParameters& params) {
   RenderThreadImpl* render_thread = RenderThreadImpl::current();
@@ -87,19 +87,19 @@
 }  // namespace
 
 media::AudioLatency::LatencyType AudioDeviceFactory::GetSourceLatencyType(
-    AudioDeviceFactory::SourceType source) {
+    blink::WebAudioDeviceSourceType source) {
   switch (source) {
-    case AudioDeviceFactory::kSourceWebAudioInteractive:
+    case blink::WebAudioDeviceSourceType::kWebAudioInteractive:
       return media::AudioLatency::LATENCY_INTERACTIVE;
-    case AudioDeviceFactory::kSourceNone:
-    case AudioDeviceFactory::kSourceWebRtc:
-    case AudioDeviceFactory::kSourceNonRtcAudioTrack:
-    case AudioDeviceFactory::kSourceWebAudioBalanced:
+    case blink::WebAudioDeviceSourceType::kNone:
+    case blink::WebAudioDeviceSourceType::kWebRtc:
+    case blink::WebAudioDeviceSourceType::kNonRtcAudioTrack:
+    case blink::WebAudioDeviceSourceType::kWebAudioBalanced:
       return media::AudioLatency::LATENCY_RTC;
-    case AudioDeviceFactory::kSourceMediaElement:
-    case AudioDeviceFactory::kSourceWebAudioPlayback:
+    case blink::WebAudioDeviceSourceType::kMediaElement:
+    case blink::WebAudioDeviceSourceType::kWebAudioPlayback:
       return media::AudioLatency::LATENCY_PLAYBACK;
-    case AudioDeviceFactory::kSourceWebAudioExact:
+    case blink::WebAudioDeviceSourceType::kWebAudioExact:
       return media::AudioLatency::LATENCY_EXACT_MS;
   }
   NOTREACHED();
@@ -118,7 +118,7 @@
 // static
 scoped_refptr<media::AudioRendererSink>
 AudioDeviceFactory::NewAudioRendererSink(
-    SourceType source_type,
+    blink::WebAudioDeviceSourceType source_type,
     int render_frame_id,
     const media::AudioSinkParameters& params) {
   if (factory_) {
@@ -144,7 +144,7 @@
 // static
 scoped_refptr<media::SwitchableAudioRendererSink>
 AudioDeviceFactory::NewSwitchableAudioRendererSink(
-    SourceType source_type,
+    blink::WebAudioDeviceSourceType source_type,
     int render_frame_id,
     const media::AudioSinkParameters& params) {
   if (factory_) {
@@ -199,7 +199,7 @@
           {base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}),
       base::BindRepeating(&AudioDeviceFactory::NewAudioRendererSink,
-                          AudioDeviceFactory::kSourceNone),
+                          blink::WebAudioDeviceSourceType::kNone),
       kDeleteTimeout);
   return cache->GetSinkInfo(render_frame_id, params.session_id,
                             params.device_id);
diff --git a/content/renderer/media/audio/audio_device_factory.h b/content/renderer/media/audio/audio_device_factory.h
index 79ec56d..20ecbe4 100644
--- a/content/renderer/media/audio/audio_device_factory.h
+++ b/content/renderer/media/audio/audio_device_factory.h
@@ -15,6 +15,7 @@
 #include "media/audio/audio_source_parameters.h"
 #include "media/base/audio_latency.h"
 #include "media/base/output_device_info.h"
+#include "third_party/blink/public/platform/audio/web_audio_device_source_type.h"
 
 namespace media {
 class AudioRendererSink;
@@ -31,25 +32,9 @@
 // AudioCapturerSourceFactory.
 class CONTENT_EXPORT AudioDeviceFactory {
  public:
-  // Types of audio sources. Each source can have individual mixing and/or
-  // latency requirements for output. The source is specified by the client when
-  // requesting output sink from the factory, and the factory creates the output
-  // sink basing on those requirements.
-  enum SourceType {
-    kSourceNone = 0,
-    kSourceMediaElement,
-    kSourceWebRtc,
-    kSourceNonRtcAudioTrack,
-    kSourceWebAudioInteractive,
-    kSourceWebAudioBalanced,
-    kSourceWebAudioPlayback,
-    kSourceWebAudioExact,
-    kSourceLast = kSourceWebAudioExact  // Only used for validation of format.
-  };
-
   // Maps the source type to the audio latency it requires.
   static media::AudioLatency::LatencyType GetSourceLatencyType(
-      SourceType source);
+      blink::WebAudioDeviceSourceType source);
 
   // Creates a sink for AudioRendererMixer. |render_frame_id| refers to the
   // RenderFrame containing the entity producing the audio. Note: These sinks do
@@ -66,7 +51,7 @@
   // TODO(olka): merge it with NewRestartableOutputDevice() as soon as
   // AudioOutputDevice is fixed to be restartable.
   static scoped_refptr<media::AudioRendererSink> NewAudioRendererSink(
-      SourceType source_type,
+      blink::WebAudioDeviceSourceType source_type,
       int render_frame_id,
       const media::AudioSinkParameters& params);
 
@@ -74,7 +59,7 @@
   // Basing on |source_type| and build configuration, audio played out through
   // the sink goes to AOD directly or can be mixed with other audio before that.
   static scoped_refptr<media::SwitchableAudioRendererSink>
-  NewSwitchableAudioRendererSink(SourceType source_type,
+  NewSwitchableAudioRendererSink(blink::WebAudioDeviceSourceType source_type,
                                  int render_frame_id,
                                  const media::AudioSinkParameters& params);
 
@@ -109,13 +94,13 @@
       base::TimeDelta auth_timeout) = 0;
 
   virtual scoped_refptr<media::AudioRendererSink> CreateAudioRendererSink(
-      SourceType source_type,
+      blink::WebAudioDeviceSourceType source_type,
       int render_frame_id,
       const media::AudioSinkParameters& params) = 0;
 
   virtual scoped_refptr<media::SwitchableAudioRendererSink>
   CreateSwitchableAudioRendererSink(
-      SourceType source_type,
+      blink::WebAudioDeviceSourceType source_type,
       int render_frame_id,
       const media::AudioSinkParameters& params) = 0;
 
diff --git a/content/renderer/media/audio/mock_audio_device_factory.h b/content/renderer/media/audio/mock_audio_device_factory.h
index 36a2dd3..faf49c1 100644
--- a/content/renderer/media/audio/mock_audio_device_factory.h
+++ b/content/renderer/media/audio/mock_audio_device_factory.h
@@ -53,12 +53,12 @@
                    base::TimeDelta auth_timeout));
   MOCK_METHOD3(CreateAudioRendererSink,
                scoped_refptr<media::AudioRendererSink>(
-                   SourceType source_type,
+                   blink::WebAudioDeviceSourceType source_type,
                    int render_frame_id,
                    const media::AudioSinkParameters& params));
   MOCK_METHOD3(CreateSwitchableAudioRendererSink,
                scoped_refptr<media::SwitchableAudioRendererSink>(
-                   SourceType source_type,
+                   blink::WebAudioDeviceSourceType source_type,
                    int render_frame_id,
                    const media::AudioSinkParameters& params));
 
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 77832a3..fd14cecd 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -287,7 +287,7 @@
 
   scoped_refptr<media::SwitchableAudioRendererSink> audio_renderer_sink =
       AudioDeviceFactory::NewSwitchableAudioRendererSink(
-          AudioDeviceFactory::kSourceMediaElement,
+          blink::WebAudioDeviceSourceType::kMediaElement,
           render_frame_->GetRoutingID(),
           media::AudioSinkParameters(0, sink_id.Utf8()));
 
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.cc b/content/renderer/media/renderer_webaudiodevice_impl.cc
index 8d5a31c..b78da2c 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl.cc
@@ -19,6 +19,7 @@
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/limits.h"
 #include "media/base/silent_sink_suspender.h"
+#include "third_party/blink/public/platform/audio/web_audio_device_source_type.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_view.h"
 
@@ -32,20 +33,20 @@
 
 namespace {
 
-AudioDeviceFactory::SourceType GetLatencyHintSourceType(
+blink::WebAudioDeviceSourceType GetLatencyHintSourceType(
     WebAudioLatencyHint::AudioContextLatencyCategory latency_category) {
   switch (latency_category) {
     case WebAudioLatencyHint::kCategoryInteractive:
-      return AudioDeviceFactory::kSourceWebAudioInteractive;
+      return blink::WebAudioDeviceSourceType::kWebAudioInteractive;
     case WebAudioLatencyHint::kCategoryBalanced:
-      return AudioDeviceFactory::kSourceWebAudioBalanced;
+      return blink::WebAudioDeviceSourceType::kWebAudioBalanced;
     case WebAudioLatencyHint::kCategoryPlayback:
-      return AudioDeviceFactory::kSourceWebAudioPlayback;
+      return blink::WebAudioDeviceSourceType::kWebAudioPlayback;
     case WebAudioLatencyHint::kCategoryExact:
-      return AudioDeviceFactory::kSourceWebAudioExact;
+      return blink::WebAudioDeviceSourceType::kWebAudioExact;
   }
   NOTREACHED();
-  return AudioDeviceFactory::kSourceWebAudioInteractive;
+  return blink::WebAudioDeviceSourceType::kWebAudioInteractive;
 }
 
 int GetOutputBufferSize(const blink::WebAudioLatencyHint& latency_hint,
diff --git a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
index d6d47e3..3f82850 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
@@ -95,12 +95,12 @@
                                               base::TimeDelta));
   MOCK_METHOD3(CreateSwitchableAudioRendererSink,
                scoped_refptr<media::SwitchableAudioRendererSink>(
-                   SourceType,
+                   blink::WebAudioDeviceSourceType,
                    int,
                    const media::AudioSinkParameters&));
 
   scoped_refptr<media::AudioRendererSink> CreateAudioRendererSink(
-      SourceType source_type,
+      blink::WebAudioDeviceSourceType source_type,
       int render_frame_id,
       const media::AudioSinkParameters& params) override {
     scoped_refptr<media::MockAudioRendererSink> mock_sink =
diff --git a/content/renderer/media/stream/media_stream_audio_processor.cc b/content/renderer/media/stream/media_stream_audio_processor.cc
index 05603d0..57d4738 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor.cc
@@ -271,7 +271,7 @@
 
 MediaStreamAudioProcessor::MediaStreamAudioProcessor(
     const blink::AudioProcessingProperties& properties,
-    WebRtcPlayoutDataSource* playout_data_source)
+    blink::WebRtcPlayoutDataSource* playout_data_source)
     : render_delay_ms_(0),
       audio_delay_stats_reporter_(kBuffersPerSecond),
       playout_data_source_(playout_data_source),
diff --git a/content/renderer/media/stream/media_stream_audio_processor.h b/content/renderer/media/stream/media_stream_audio_processor.h
index 04da8552..5945a35 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.h
+++ b/content/renderer/media/stream/media_stream_audio_processor.h
@@ -49,7 +49,7 @@
 // on the getUserMedia constraints, processes the data and outputs it in a unit
 // of 10 ms data chunk.
 class CONTENT_EXPORT MediaStreamAudioProcessor
-    : public WebRtcPlayoutDataSource::Sink,
+    : public blink::WebRtcPlayoutDataSource::Sink,
       public AudioProcessorInterface,
       public AecDumpAgentImpl::Delegate {
  public:
@@ -59,8 +59,9 @@
   //
   // Threading note: The constructor assumes it is being run on the main render
   // thread.
-  MediaStreamAudioProcessor(const blink::AudioProcessingProperties& properties,
-                            WebRtcPlayoutDataSource* playout_data_source);
+  MediaStreamAudioProcessor(
+      const blink::AudioProcessingProperties& properties,
+      blink::WebRtcPlayoutDataSource* playout_data_source);
 
   // Called when the format of the capture data has changed.
   // Called on the main render thread. The caller is responsible for stopping
@@ -126,7 +127,7 @@
   FRIEND_TEST_ALL_PREFIXES(MediaStreamAudioProcessorTest,
                            GetAecDumpMessageFilter);
 
-  // WebRtcPlayoutDataSource::Sink implementation.
+  // blink::WebRtcPlayoutDataSource::Sink implementation.
   void OnPlayoutData(media::AudioBus* audio_bus,
                      int sample_rate,
                      int audio_delay_milliseconds) override;
@@ -182,9 +183,9 @@
   media::AudioParameters input_format_;
   media::AudioParameters output_format_;
 
-  // Raw pointer to the WebRtcPlayoutDataSource, which is valid for the
+  // Raw pointer to the blink::WebRtcPlayoutDataSource, which is valid for the
   // lifetime of RenderThread.
-  WebRtcPlayoutDataSource* playout_data_source_;
+  blink::WebRtcPlayoutDataSource* playout_data_source_;
 
   // Task runner for the main render thread.
   const scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner_;
diff --git a/content/renderer/media/stream/track_audio_renderer.cc b/content/renderer/media/stream/track_audio_renderer.cc
index a01b60f..baee3a3 100644
--- a/content/renderer/media/stream/track_audio_renderer.cc
+++ b/content/renderer/media/stream/track_audio_renderer.cc
@@ -150,8 +150,8 @@
   // ...and |sink_| will get audio data from us.
   DCHECK(!sink_);
   sink_ = AudioDeviceFactory::NewAudioRendererSink(
-      AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_,
-      {session_id_, output_device_id_});
+      blink::WebAudioDeviceSourceType::kNonRtcAudioTrack,
+      playout_render_frame_id_, {session_id_, output_device_id_});
 
   base::AutoLock auto_lock(thread_lock_);
   prior_elapsed_render_time_ = base::TimeDelta();
@@ -247,8 +247,8 @@
 
   scoped_refptr<media::AudioRendererSink> new_sink =
       AudioDeviceFactory::NewAudioRendererSink(
-          AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_,
-          {session_id_, device_id});
+          blink::WebAudioDeviceSourceType::kNonRtcAudioTrack,
+          playout_render_frame_id_, {session_id_, device_id});
 
   media::OutputDeviceStatus new_sink_status =
       new_sink->GetOutputDeviceInfo().device_status();
@@ -318,7 +318,7 @@
 
   // Specify the latency info to be passed to the browser side.
   sink_params.set_latency_tag(AudioDeviceFactory::GetSourceLatencyType(
-      AudioDeviceFactory::kSourceNonRtcAudioTrack));
+      blink::WebAudioDeviceSourceType::kNonRtcAudioTrack));
 
   sink_->Initialize(sink_params, this);
   sink_->Start();
@@ -348,8 +348,8 @@
   sink_->Stop();
   sink_started_ = false;
   sink_ = AudioDeviceFactory::NewAudioRendererSink(
-      AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_,
-      {session_id_, output_device_id_});
+      blink::WebAudioDeviceSourceType::kNonRtcAudioTrack,
+      playout_render_frame_id_, {session_id_, output_device_id_});
   MaybeStartSink();
 }
 
diff --git a/content/renderer/media/stream/webmediaplayer_ms.cc b/content/renderer/media/stream/webmediaplayer_ms.cc
index 36963d86..c54ff69 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms.cc
@@ -828,6 +828,10 @@
   return 0;
 }
 
+bool WebMediaPlayerMS::HasAvailableVideoFrame() const {
+  return has_first_frame_;
+}
+
 void WebMediaPlayerMS::OnFrameHidden() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // This method is called when the RenderFrame is sent to background or
@@ -1072,6 +1076,7 @@
   DVLOG(1) << __func__;
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+  has_first_frame_ = true;
   OnRotationChanged(video_rotation);
   OnOpacityChanged(is_opaque);
 
diff --git a/content/renderer/media/stream/webmediaplayer_ms.h b/content/renderer/media/stream/webmediaplayer_ms.h
index 1214c08..d46ad60e 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.h
+++ b/content/renderer/media/stream/webmediaplayer_ms.h
@@ -167,6 +167,8 @@
   uint64_t AudioDecodedByteCount() const override;
   uint64_t VideoDecodedByteCount() const override;
 
+  bool HasAvailableVideoFrame() const override;
+
   // WebMediaPlayerDelegate::Observer implementation.
   void OnFrameHidden() override;
   void OnFrameClosed() override;
@@ -355,6 +357,8 @@
   // Whether the video is known to be opaque or not.
   bool opaque_ = true;
 
+  bool has_first_frame_ = false;
+
   base::WeakPtr<WebMediaPlayerMS> weak_this_;
   base::WeakPtrFactory<WebMediaPlayerMS> weak_factory_;
 
diff --git a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc b/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc
index 9263166..000842e 100644
--- a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc
+++ b/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc
@@ -104,7 +104,7 @@
 
 void FakeRTCRtpSender::GetStats(
     blink::WebRTCStatsReportCallback,
-    const std::vector<webrtc::NonStandardGroupId>&) {
+    const blink::WebVector<webrtc::NonStandardGroupId>&) {
   NOTIMPLEMENTED();
 }
 
@@ -171,7 +171,7 @@
 
 void FakeRTCRtpReceiver::GetStats(
     blink::WebRTCStatsReportCallback,
-    const std::vector<webrtc::NonStandardGroupId>&) {
+    const blink::WebVector<webrtc::NonStandardGroupId>&) {
   NOTIMPLEMENTED();
 }
 
diff --git a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h b/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h
index bb7e968..46e5cca 100644
--- a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h
+++ b/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h
@@ -51,7 +51,7 @@
                      webrtc::DegradationPreference,
                      blink::WebRTCVoidRequest) override;
   void GetStats(blink::WebRTCStatsReportCallback,
-                const std::vector<webrtc::NonStandardGroupId>&) override;
+                const blink::WebVector<webrtc::NonStandardGroupId>&) override;
   void SetStreams(
       const blink::WebVector<blink::WebString>& stream_ids) override;
 
@@ -79,7 +79,7 @@
   blink::WebVector<std::unique_ptr<blink::WebRTCRtpSource>> GetSources()
       override;
   void GetStats(blink::WebRTCStatsReportCallback,
-                const std::vector<webrtc::NonStandardGroupId>&) override;
+                const blink::WebVector<webrtc::NonStandardGroupId>&) override;
   std::unique_ptr<webrtc::RtpParameters> GetParameters() const override;
   void SetJitterBufferMinimumDelay(
       base::Optional<double> delay_seconds) override;
diff --git a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h b/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
index a0ec318..74a5333 100644
--- a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
+++ b/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
@@ -50,7 +50,7 @@
   MOCK_METHOD1(DidModifySctpTransport,
                void(blink::WebRTCSctpTransportSnapshot snapshot));
   void DidModifyTransceivers(
-      std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>>
+      blink::WebVector<std::unique_ptr<blink::WebRTCRtpTransceiver>>
           web_transceivers,
       bool is_remote_description) override {
     DidModifyTransceiversForMock(&web_transceivers, is_remote_description);
@@ -66,9 +66,10 @@
                void(std::unique_ptr<blink::WebRTCRtpReceiver>*));
   MOCK_METHOD1(DidRemoveReceiverPlanBForMock,
                void(std::unique_ptr<blink::WebRTCRtpReceiver>*));
-  MOCK_METHOD2(DidModifyTransceiversForMock,
-               void(std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>>*,
-                    bool));
+  MOCK_METHOD2(
+      DidModifyTransceiversForMock,
+      void(blink::WebVector<std::unique_ptr<blink::WebRTCRtpTransceiver>>*,
+           bool));
 
   void didGenerateICECandidateWorker(
       scoped_refptr<blink::WebRTCICECandidate> candidate);
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 67f0cf99..28cf525 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -506,7 +506,7 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
     scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
     blink::WebRTCStatsReportCallback callback,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   TRACE_EVENT0("webrtc", "GetRTCStatsOnSignalingThread");
 
   native_peer_connection->GetStats(RTCStatsCollectorCallbackImpl::Create(
@@ -1067,7 +1067,7 @@
   return true;
 }
 
-std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>>
+blink::WebVector<std::unique_ptr<blink::WebRTCRtpTransceiver>>
 RTCPeerConnectionHandler::CreateOffer(
     const blink::WebRTCSessionDescriptionRequest& request,
     const blink::WebMediaConstraints& options) {
@@ -1082,7 +1082,7 @@
   return CreateOfferInternal(request, std::move(webrtc_options));
 }
 
-std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>>
+blink::WebVector<std::unique_ptr<blink::WebRTCRtpTransceiver>>
 RTCPeerConnectionHandler::CreateOffer(
     const blink::WebRTCSessionDescriptionRequest& request,
     const blink::WebRTCOfferOptions& options) {
@@ -1559,7 +1559,7 @@
 
 void RTCPeerConnectionHandler::GetStats(
     blink::WebRTCStatsReportCallback callback,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   signaling_thread()->PostTask(
       FROM_HERE, base::BindOnce(&GetRTCStatsOnSignalingThread, task_runner_,
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.h b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
index 93d5bf9..633d68df 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.h
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
@@ -113,10 +113,10 @@
                       server_configuration,
                   const blink::WebMediaConstraints& options) override;
 
-  std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>> CreateOffer(
+  blink::WebVector<std::unique_ptr<blink::WebRTCRtpTransceiver>> CreateOffer(
       const blink::WebRTCSessionDescriptionRequest& request,
       const blink::WebMediaConstraints& options) override;
-  std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>> CreateOffer(
+  blink::WebVector<std::unique_ptr<blink::WebRTCRtpTransceiver>> CreateOffer(
       const blink::WebRTCSessionDescriptionRequest& request,
       const blink::WebRTCOfferOptions& options) override;
 
@@ -154,7 +154,7 @@
 
   void GetStats(const blink::WebRTCStatsRequest& request) override;
   void GetStats(blink::WebRTCStatsReportCallback callback,
-                const std::vector<webrtc::NonStandardGroupId>&
+                const blink::WebVector<webrtc::NonStandardGroupId>&
                     exposed_group_ids) override;
   webrtc::RTCErrorOr<std::unique_ptr<blink::WebRTCRtpTransceiver>>
   AddTransceiverWithTrack(const blink::WebMediaStreamTrack& web_track,
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver.cc b/content/renderer/media/webrtc/rtc_rtp_receiver.cc
index 2af21e9..651480e4 100644
--- a/content/renderer/media/webrtc/rtc_rtp_receiver.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_receiver.cc
@@ -170,7 +170,7 @@
 
   void GetStats(
       blink::WebRTCStatsReportCallback callback,
-      const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+      const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
     signaling_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&RTCRtpReceiverInternal::GetStatsOnSignalingThread, this,
@@ -196,7 +196,7 @@
 
   void GetStatsOnSignalingThread(
       blink::WebRTCStatsReportCallback callback,
-      const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+      const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
     native_peer_connection_->GetStats(
         webrtc_receiver_.get(),
         RTCStatsCollectorCallbackImpl::Create(
@@ -298,7 +298,7 @@
 
 void RTCRtpReceiver::GetStats(
     blink::WebRTCStatsReportCallback callback,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   internal_->GetStats(std::move(callback), exposed_group_ids);
 }
 
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver.h b/content/renderer/media/webrtc/rtc_rtp_receiver.h
index f0e0ca0..54e2ead 100644
--- a/content/renderer/media/webrtc/rtc_rtp_receiver.h
+++ b/content/renderer/media/webrtc/rtc_rtp_receiver.h
@@ -130,7 +130,7 @@
   blink::WebVector<std::unique_ptr<blink::WebRTCRtpSource>> GetSources()
       override;
   void GetStats(blink::WebRTCStatsReportCallback,
-                const std::vector<webrtc::NonStandardGroupId>&) override;
+                const blink::WebVector<webrtc::NonStandardGroupId>&) override;
   std::unique_ptr<webrtc::RtpParameters> GetParameters() const override;
   void SetJitterBufferMinimumDelay(
       base::Optional<double> delay_seconds) override;
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.cc b/content/renderer/media/webrtc/rtc_rtp_sender.cc
index c751444..040c3701 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_sender.cc
@@ -257,7 +257,7 @@
 
   void GetStats(
       blink::WebRTCStatsReportCallback callback,
-      const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+      const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
     signaling_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(
@@ -321,7 +321,7 @@
 
   void GetStatsOnSignalingThread(
       blink::WebRTCStatsReportCallback callback,
-      const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+      const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
     native_peer_connection_->GetStats(
         webrtc_sender_.get(),
         RTCStatsCollectorCallbackImpl::Create(
@@ -470,7 +470,7 @@
 
 void RTCRtpSender::GetStats(
     blink::WebRTCStatsReportCallback callback,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   internal_->GetStats(std::move(callback), exposed_group_ids);
 }
 
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.h b/content/renderer/media/webrtc/rtc_rtp_sender.h
index c8babf9..041b1ad0 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.h
+++ b/content/renderer/media/webrtc/rtc_rtp_sender.h
@@ -141,7 +141,7 @@
                      webrtc::DegradationPreference,
                      blink::WebRTCVoidRequest) override;
   void GetStats(blink::WebRTCStatsReportCallback,
-                const std::vector<webrtc::NonStandardGroupId>&) override;
+                const blink::WebVector<webrtc::NonStandardGroupId>&) override;
   void SetStreams(
       const blink::WebVector<blink::WebString>& stream_ids) override;
 
diff --git a/content/renderer/media/webrtc/rtc_stats.cc b/content/renderer/media/webrtc/rtc_stats.cc
index 7ac45d2..921a0fa5 100644
--- a/content/renderer/media/webrtc/rtc_stats.cc
+++ b/content/renderer/media/webrtc/rtc_stats.cc
@@ -70,7 +70,7 @@
 // including one of its group IDs in |exposed_group_ids|.
 std::vector<const webrtc::RTCStatsMemberInterface*> FilterMembers(
     std::vector<const webrtc::RTCStatsMemberInterface*> stats_members,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   // Note that using "is_standarized" avoids having to maintain a whitelist of
   // every single standardized member, as we do at the "stats object" level
   // with "RTCStatsWhitelist".
@@ -81,7 +81,7 @@
           return false;
         }
 
-        const std::vector<webrtc::NonStandardGroupId>& ids =
+        const blink::WebVector<webrtc::NonStandardGroupId>& ids =
             member->group_ids();
         for (const webrtc::NonStandardGroupId& id : exposed_group_ids) {
           if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
@@ -97,7 +97,7 @@
 
 RTCStatsReport::RTCStatsReport(
     const scoped_refptr<const webrtc::RTCStatsReport>& stats_report,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids)
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids)
     : stats_report_(stats_report),
       it_(stats_report_->begin()),
       end_(stats_report_->end()),
@@ -144,7 +144,7 @@
 RTCStats::RTCStats(
     const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner,
     const webrtc::RTCStats* stats,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids)
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids)
     : stats_owner_(stats_owner),
       stats_(stats),
       stats_members_(FilterMembers(stats->Members(), exposed_group_ids)) {
@@ -328,7 +328,7 @@
 RTCStatsCollectorCallbackImpl::Create(
     scoped_refptr<base::SingleThreadTaskRunner> main_thread,
     blink::WebRTCStatsReportCallback callback,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   return rtc::scoped_refptr<RTCStatsCollectorCallbackImpl>(
       new rtc::RefCountedObject<RTCStatsCollectorCallbackImpl>(
           std::move(main_thread), std::move(callback), exposed_group_ids));
@@ -337,7 +337,7 @@
 RTCStatsCollectorCallbackImpl::RTCStatsCollectorCallbackImpl(
     scoped_refptr<base::SingleThreadTaskRunner> main_thread,
     blink::WebRTCStatsReportCallback callback,
-    const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids)
+    const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids)
     : main_thread_(std::move(main_thread)),
       callback_(std::move(callback)),
       exposed_group_ids_(exposed_group_ids) {}
diff --git a/content/renderer/media/webrtc/rtc_stats.h b/content/renderer/media/webrtc/rtc_stats.h
index 6a017207..478302a 100644
--- a/content/renderer/media/webrtc/rtc_stats.h
+++ b/content/renderer/media/webrtc/rtc_stats.h
@@ -23,7 +23,7 @@
  public:
   RTCStatsReport(
       const scoped_refptr<const webrtc::RTCStatsReport>& stats_report,
-      const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids);
+      const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids);
   ~RTCStatsReport() override;
   std::unique_ptr<blink::WebRTCStatsReport> CopyHandle() const override;
 
@@ -36,14 +36,15 @@
   const scoped_refptr<const webrtc::RTCStatsReport> stats_report_;
   webrtc::RTCStatsReport::ConstIterator it_;
   const webrtc::RTCStatsReport::ConstIterator end_;
-  std::vector<webrtc::NonStandardGroupId> exposed_group_ids_;
+  blink::WebVector<webrtc::NonStandardGroupId> exposed_group_ids_;
 };
 
 class CONTENT_EXPORT RTCStats : public blink::WebRTCStats {
  public:
-  RTCStats(const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner,
-           const webrtc::RTCStats* stats,
-           const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids);
+  RTCStats(
+      const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner,
+      const webrtc::RTCStats* stats,
+      const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids);
   ~RTCStats() override;
 
   blink::WebString Id() const override;
@@ -103,7 +104,7 @@
   static rtc::scoped_refptr<RTCStatsCollectorCallbackImpl> Create(
       scoped_refptr<base::SingleThreadTaskRunner> main_thread,
       blink::WebRTCStatsReportCallback callback,
-      const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids);
+      const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids);
 
   void OnStatsDelivered(
       const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) override;
@@ -112,7 +113,7 @@
   RTCStatsCollectorCallbackImpl(
       scoped_refptr<base::SingleThreadTaskRunner> main_thread,
       blink::WebRTCStatsReportCallback callback2,
-      const std::vector<webrtc::NonStandardGroupId>& exposed_group_ids);
+      const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids);
   ~RTCStatsCollectorCallbackImpl() override;
 
   void OnStatsDeliveredOnMainThread(
@@ -120,7 +121,7 @@
 
   const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
   blink::WebRTCStatsReportCallback callback_;
-  std::vector<webrtc::NonStandardGroupId> exposed_group_ids_;
+  blink::WebVector<webrtc::NonStandardGroupId> exposed_group_ids_;
 };
 
 CONTENT_EXPORT void WhitelistStatsForTesting(const char* type);
diff --git a/content/renderer/media/webrtc/rtc_stats_unittest.cc b/content/renderer/media/webrtc/rtc_stats_unittest.cc
index 29a1725f..d4c165a 100644
--- a/content/renderer/media/webrtc/rtc_stats_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_stats_unittest.cc
@@ -100,7 +100,8 @@
 
   // Include both standard and non-standard member.
   RTCStatsReport report(webrtc_report.get(),
-                        {webrtc::NonStandardGroupId::kGroupIdForTesting});
+                        std::vector<webrtc::NonStandardGroupId>{
+                            webrtc::NonStandardGroupId::kGroupIdForTesting});
   std::unique_ptr<blink::WebRTCStats> stats = report.GetStats("id");
   ASSERT_NE(nullptr, stats);
   ASSERT_EQ(2u, stats->MembersCount());
@@ -123,7 +124,8 @@
   ASSERT_EQ(1u, standard_members_copy->GetStats("id")->MembersCount());
 
   RTCStatsReport all_members_report(
-      webrtc_report.get(), {webrtc::NonStandardGroupId::kGroupIdForTesting});
+      webrtc_report.get(), std::vector<webrtc::NonStandardGroupId>{
+                               webrtc::NonStandardGroupId::kGroupIdForTesting});
   std::unique_ptr<blink::WebRTCStatsReport> all_members_copy =
       all_members_report.CopyHandle();
   ASSERT_EQ(2u, all_members_report.GetStats("id")->MembersCount());
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
index 9df2383..8766545b 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
@@ -52,10 +52,6 @@
 // Any reasonable size, will be overridden by the decoder anyway.
 const gfx::Size kDefaultSize(640, 480);
 
-// Assumed pixel format of the encoded content. WebRTC doesn't tell us, and in
-// practice the decoders ignore it. We're going with generic 4:2:0.
-const media::VideoPixelFormat kDefaultPixelFormat = media::PIXEL_FORMAT_I420;
-
 // Maximum number of buffers that we will queue in |pending_buffers_|.
 const int32_t kMaxPendingBuffers = 8;
 
@@ -159,10 +155,10 @@
   // TODO(sandersd): Predict size from level.
   media::VideoDecoderConfig config(
       ToVideoCodec(webrtc::PayloadStringToCodecType(format.name)),
-      GuessVideoCodecProfile(format), kDefaultPixelFormat,
-      media::VideoColorSpace(), media::kNoTransformation, kDefaultSize,
-      gfx::Rect(kDefaultSize), kDefaultSize, media::EmptyExtraData(),
-      media::Unencrypted());
+      GuessVideoCodecProfile(format),
+      media::VideoDecoderConfig::AlphaMode::kIsOpaque, media::VideoColorSpace(),
+      media::kNoTransformation, kDefaultSize, gfx::Rect(kDefaultSize),
+      kDefaultSize, media::EmptyExtraData(), media::Unencrypted());
   if (!gpu_factories->IsDecoderConfigSupported(kImplementation, config))
     return nullptr;
 
diff --git a/content/renderer/media/webrtc/webrtc_audio_device_impl.cc b/content/renderer/media/webrtc/webrtc_audio_device_impl.cc
index 9363e2d..1cb3dfb 100644
--- a/content/renderer/media/webrtc/webrtc_audio_device_impl.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_device_impl.cc
@@ -394,7 +394,7 @@
 }
 
 void WebRtcAudioDeviceImpl::AddPlayoutSink(
-    WebRtcPlayoutDataSource::Sink* sink) {
+    blink::WebRtcPlayoutDataSource::Sink* sink) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   DCHECK(sink);
   base::AutoLock auto_lock(lock_);
@@ -403,7 +403,7 @@
 }
 
 void WebRtcAudioDeviceImpl::RemovePlayoutSink(
-    WebRtcPlayoutDataSource::Sink* sink) {
+    blink::WebRtcPlayoutDataSource::Sink* sink) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   DCHECK(sink);
   base::AutoLock auto_lock(lock_);
diff --git a/content/renderer/media/webrtc/webrtc_audio_device_impl.h b/content/renderer/media/webrtc/webrtc_audio_device_impl.h
index 10dd96cb..ddd1d137 100644
--- a/content/renderer/media/webrtc/webrtc_audio_device_impl.h
+++ b/content/renderer/media/webrtc/webrtc_audio_device_impl.h
@@ -23,6 +23,7 @@
 #include "content/common/content_export.h"
 #include "content/renderer/media/webrtc/webrtc_audio_device_not_impl.h"
 #include "ipc/ipc_platform_file.h"
+#include "third_party/blink/public/platform/modules/webrtc/webrtc_source.h"
 
 // A WebRtcAudioDeviceImpl instance implements the abstract interface
 // webrtc::AudioDeviceModule which makes it possible for a user (e.g. webrtc::
@@ -77,50 +78,13 @@
   virtual ~WebRtcAudioRendererSource() {}
 };
 
-// TODO(xians): Merge this interface with WebRtcAudioRendererSource.
-// The reason why we could not do it today is that WebRtcAudioRendererSource
-// gets the data by pulling, while the data is pushed into
-// WebRtcPlayoutDataSource::Sink.
-class WebRtcPlayoutDataSource {
- public:
-  class Sink {
-   public:
-    // Callback to get the playout data.
-    // Called on the audio render thread.
-    // |audio_bus| must have buffer size |sample_rate/100| and 1-2 channels.
-    virtual void OnPlayoutData(media::AudioBus* audio_bus,
-                               int sample_rate,
-                               int audio_delay_milliseconds) = 0;
-
-    // Callback to notify the sink that the source has changed.
-    // Called on the main render thread.
-    virtual void OnPlayoutDataSourceChanged() = 0;
-
-    // Called to notify that the audio render thread has changed, and
-    // OnPlayoutData() will from now on be called on the new thread.
-    // Called on the new audio render thread.
-    virtual void OnRenderThreadChanged() = 0;
-
-   protected:
-    virtual ~Sink() {}
-  };
-
-  // Adds/Removes the sink of WebRtcAudioRendererSource to the ADM.
-  // These methods are used by the MediaStreamAudioProcesssor to get the
-  // rendered data for AEC.
-  virtual void AddPlayoutSink(Sink* sink) = 0;
-  virtual void RemovePlayoutSink(Sink* sink) = 0;
-
- protected:
-  virtual ~WebRtcPlayoutDataSource() {}
-};
-
 // Note that this class inherits from webrtc::AudioDeviceModule but due to
 // the high number of non-implemented methods, we move the cruft over to the
 // WebRtcAudioDeviceNotImpl.
-class CONTENT_EXPORT WebRtcAudioDeviceImpl : public WebRtcAudioDeviceNotImpl,
-                                             public WebRtcAudioRendererSource,
-                                             public WebRtcPlayoutDataSource {
+class CONTENT_EXPORT WebRtcAudioDeviceImpl
+    : public WebRtcAudioDeviceNotImpl,
+      public WebRtcAudioRendererSource,
+      public blink::WebRtcPlayoutDataSource {
  public:
   // The maximum volume value WebRtc uses.
   static const int kMaxVolumeLevel = 255;
@@ -204,13 +168,13 @@
   void SetOutputDeviceForAec(const std::string& output_device_id) override;
   base::UnguessableToken GetAudioProcessingId() const override;
 
-  // WebRtcPlayoutDataSource implementation.
-  void AddPlayoutSink(WebRtcPlayoutDataSource::Sink* sink) override;
-  void RemovePlayoutSink(WebRtcPlayoutDataSource::Sink* sink) override;
+  // blink::WebRtcPlayoutDataSource implementation.
+  void AddPlayoutSink(blink::WebRtcPlayoutDataSource::Sink* sink) override;
+  void RemovePlayoutSink(blink::WebRtcPlayoutDataSource::Sink* sink) override;
 
  private:
   using CapturerList = std::list<ProcessedLocalAudioSource*>;
-  using PlayoutDataSinkList = std::list<WebRtcPlayoutDataSource::Sink*>;
+  using PlayoutDataSinkList = std::list<blink::WebRtcPlayoutDataSource::Sink*>;
 
   class RenderBuffer;
 
@@ -232,8 +196,8 @@
   // Provides access to the audio renderer in the browser process.
   scoped_refptr<WebRtcAudioRenderer> renderer_ GUARDED_BY(lock_);
 
-  // A list of raw pointer of WebRtcPlayoutDataSource::Sink objects which want
-  // to get the playout data, the sink need to call RemovePlayoutSink()
+  // A list of raw pointer of blink::WebRtcPlayoutDataSource::Sink objects which
+  // want to get the playout data, the sink need to call RemovePlayoutSink()
   // before it goes away.
   PlayoutDataSinkList playout_sinks_ GUARDED_BY(lock_);
 
diff --git a/content/renderer/media/webrtc/webrtc_audio_renderer.cc b/content/renderer/media/webrtc/webrtc_audio_renderer.cc
index 6d88005..c7278b9b 100644
--- a/content/renderer/media/webrtc/webrtc_audio_renderer.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_renderer.cc
@@ -190,7 +190,8 @@
   media::AudioSinkParameters sink_params(session_id_, output_device_id_);
   sink_params.processing_id = source->GetAudioProcessingId();
   sink_ = AudioDeviceFactory::NewAudioRendererSink(
-      AudioDeviceFactory::kSourceWebRtc, source_render_frame_id_, sink_params);
+      blink::WebAudioDeviceSourceType::kWebRtc, source_render_frame_id_,
+      sink_params);
 
   media::OutputDeviceStatus sink_status =
       sink_->GetOutputDeviceInfo().device_status();
@@ -378,7 +379,7 @@
   sink_params.processing_id = source_->GetAudioProcessingId();
   scoped_refptr<media::AudioRendererSink> new_sink =
       AudioDeviceFactory::NewAudioRendererSink(
-          AudioDeviceFactory::kSourceWebRtc, source_render_frame_id_,
+          blink::WebAudioDeviceSourceType::kWebRtc, source_render_frame_id_,
           sink_params);
   media::OutputDeviceStatus status =
       new_sink->GetOutputDeviceInfo().device_status();
@@ -693,7 +694,7 @@
 
   // Specify the latency info to be passed to the browser side.
   new_sink_params.set_latency_tag(AudioDeviceFactory::GetSourceLatencyType(
-      AudioDeviceFactory::AudioDeviceFactory::kSourceWebRtc));
+      blink::WebAudioDeviceSourceType::kWebRtc));
   sink_->Initialize(new_sink_params, this);
 }
 
diff --git a/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc b/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
index 7fcb52e..f58b9773 100644
--- a/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
@@ -19,6 +19,7 @@
 #include "media/base/mock_audio_renderer_sink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/audio/web_audio_device_source_type.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_audio_renderer.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
@@ -79,9 +80,9 @@
     renderer_ = new WebRtcAudioRenderer(
         blink::scheduler::GetSingleThreadTaskRunnerForTesting(), stream_, 1, 1,
         device_id);
-    EXPECT_CALL(
-        *this, MockCreateAudioRendererSink(AudioDeviceFactory::kSourceWebRtc, _,
-                                           _, device_id, _));
+    EXPECT_CALL(*this, MockCreateAudioRendererSink(
+                           blink::WebAudioDeviceSourceType::kWebRtc, _, _,
+                           device_id, _));
     EXPECT_CALL(*source_.get(), SetOutputDeviceForAec(device_id));
     EXPECT_TRUE(renderer_->Initialize(source_.get()));
 
@@ -98,18 +99,18 @@
                                               base::TimeDelta));
   MOCK_METHOD3(CreateSwitchableAudioRendererSink,
                scoped_refptr<media::SwitchableAudioRendererSink>(
-                   SourceType,
+                   blink::WebAudioDeviceSourceType,
                    int,
                    const media::AudioSinkParameters&));
   MOCK_METHOD5(MockCreateAudioRendererSink,
-               void(SourceType,
+               void(blink::WebAudioDeviceSourceType,
                     int,
                     int,
                     const std::string&,
                     const base::Optional<base::UnguessableToken>&));
 
   scoped_refptr<media::AudioRendererSink> CreateAudioRendererSink(
-      SourceType source_type,
+      blink::WebAudioDeviceSourceType source_type,
       int render_frame_id,
       const media::AudioSinkParameters& params) override {
     mock_sink_ = new media::MockAudioRendererSink(
@@ -250,7 +251,7 @@
 
   EXPECT_CALL(*mock_sink_.get(), Stop());
   EXPECT_CALL(*this, MockCreateAudioRendererSink(
-                         AudioDeviceFactory::kSourceWebRtc, _, _,
+                         blink::WebAudioDeviceSourceType::kWebRtc, _, _,
                          kOtherOutputDeviceId, kAudioProcessingId));
   EXPECT_CALL(*source_.get(), AudioRendererThreadStopped());
   EXPECT_CALL(*source_.get(), SetOutputDeviceForAec(kOtherOutputDeviceId));
@@ -277,7 +278,7 @@
   renderer_proxy_->Start();
 
   EXPECT_CALL(*this, MockCreateAudioRendererSink(
-                         AudioDeviceFactory::kSourceWebRtc, _, _,
+                         blink::WebAudioDeviceSourceType::kWebRtc, _, _,
                          kInvalidOutputDeviceId, kAudioProcessingId));
   EXPECT_CALL(*this, MockSwitchDeviceCallback(
                          media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL));
@@ -301,7 +302,7 @@
       kInvalidOutputDeviceId);
 
   EXPECT_CALL(*this, MockCreateAudioRendererSink(
-                         AudioDeviceFactory::kSourceWebRtc, _, _,
+                         blink::WebAudioDeviceSourceType::kWebRtc, _, _,
                          kInvalidOutputDeviceId, kAudioProcessingId));
 
   EXPECT_FALSE(renderer_->Initialize(source_.get()));
diff --git a/content/renderer/pepper/ppb_broker_impl.cc b/content/renderer/pepper/ppb_broker_impl.cc
index c9275e7a..3488c65 100644
--- a/content/renderer/pepper/ppb_broker_impl.cc
+++ b/content/renderer/pepper/ppb_broker_impl.cc
@@ -58,9 +58,6 @@
     scoped_refptr<TrackedCallback> connect_callback) {
   // TODO(ddorwin): Return PP_ERROR_FAILED if plugin is in-process.
 
-  UMA_HISTOGRAM_ENUMERATION("Pepper.BrokerAction", PepperBrokerAction::CONNECT,
-                            PepperBrokerAction::NUM);
-
   if (broker_) {
     // May only be called once.
     return PP_ERROR_FAILED;
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc
index 00359563..22e4ce3 100644
--- a/content/renderer/pepper/video_decoder_shim.cc
+++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -892,8 +892,9 @@
     return false;
 
   media::VideoDecoderConfig video_decoder_config(
-      codec, vda_config.profile, media::PIXEL_FORMAT_I420,
-      media::VideoColorSpace(), media::kNoTransformation,
+      codec, vda_config.profile,
+      media::VideoDecoderConfig::AlphaMode::kIsOpaque, media::VideoColorSpace(),
+      media::kNoTransformation,
       gfx::Size(32, 24),  // Small sizes that won't fail.
       gfx::Rect(32, 24), gfx::Size(32, 24),
       // TODO(bbudge): Verify extra data isn't needed.
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index fb6ecb2..0a90a00f 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1003,9 +1003,6 @@
   friend class RenderAccessibilityImplTest;
   friend class TestRenderFrame;
 
-  // To update AppCache loader factory via GetLoaderFactoryBundle().
-  friend class RendererWebApplicationCacheHostImpl;
-
   FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuDisplayNoneTest, SelectItem);
   FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuRemoveTest, RemoveFrameOnChange);
   FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuRemoveTest, RemoveOnChange);
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 4b5092c..28b5e17d 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -102,7 +102,6 @@
 #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/web_database_observer_impl.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"
@@ -1412,19 +1411,13 @@
 
 scoped_refptr<StreamTextureFactory> RenderThreadImpl::GetStreamTexureFactory() {
   DCHECK(IsMainThread());
-  if (!stream_texture_factory_.get() ||
-      stream_texture_factory_->ContextGL()->GetGraphicsResetStatusKHR() !=
-          GL_NO_ERROR) {
-    scoped_refptr<viz::ContextProviderCommandBuffer> shared_context_provider =
-        SharedMainThreadContextProvider();
-    if (!shared_context_provider) {
+  if (!stream_texture_factory_ || stream_texture_factory_->IsLost()) {
+    scoped_refptr<gpu::GpuChannelHost> channel = EstablishGpuChannelSync();
+    if (!channel) {
       stream_texture_factory_ = nullptr;
       return nullptr;
     }
-    DCHECK(shared_context_provider->GetCommandBufferProxy());
-    DCHECK(shared_context_provider->GetCommandBufferProxy()->channel());
-    stream_texture_factory_ =
-        StreamTextureFactory::Create(std::move(shared_context_provider));
+    stream_texture_factory_ = StreamTextureFactory::Create(std::move(channel));
   }
   return stream_texture_factory_;
 }
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index f34ae97..70a3e14 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -3157,7 +3157,7 @@
 #if defined(OS_ANDROID)
   if (features::IsSurfaceSynchronizationEnabled()) {
     // TODO(crbug.com/933846): LatencyRecovery is causing jank on Android.
-    // Disable in viz mode for now, with plant o disable more widely once
+    // Disable in viz mode for now, with plan to disable more widely once
     // viz launches.
     settings.enable_latency_recovery = false;
   }
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 91a1929..e722b08 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -53,7 +53,6 @@
 #include "content/renderer/p2p/port_allocator.h"
 #include "content/renderer/render_thread_impl.h"
 #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/dedicated_worker_host_factory_client.h"
 #include "content/renderer/worker/worker_thread_registry.h"
@@ -117,7 +116,6 @@
 using blink::WebAudioDevice;
 using blink::WebAudioLatencyHint;
 using blink::WebBlobRegistry;
-using blink::WebDatabaseObserver;
 using blink::WebMediaStreamCenter;
 using blink::WebMediaStreamTrack;
 using blink::WebRTCPeerConnectionHandler;
@@ -215,8 +213,6 @@
   main_thread_scheduler_->SetTopLevelBlameContext(&top_level_blame_context_);
 
   GetInterfaceProvider()->GetInterface(
-      mojo::MakeRequest(&web_database_host_info_));
-  GetInterfaceProvider()->GetInterface(
       mojo::MakeRequest(&code_cache_host_info_));
 }
 
@@ -454,51 +450,6 @@
 
 //------------------------------------------------------------------------------
 
-base::File RendererBlinkPlatformImpl::DatabaseOpenFile(
-    const WebString& vfs_file_name,
-    int desired_flags) {
-  base::File file;
-  GetWebDatabaseHost().OpenFile(vfs_file_name.Utf16(), desired_flags, &file);
-  return file;
-}
-
-int RendererBlinkPlatformImpl::DatabaseDeleteFile(
-    const WebString& vfs_file_name,
-    bool sync_dir) {
-  int rv = SQLITE_IOERR_DELETE;
-  GetWebDatabaseHost().DeleteFile(vfs_file_name.Utf16(), sync_dir, &rv);
-  return rv;
-}
-
-int32_t RendererBlinkPlatformImpl::DatabaseGetFileAttributes(
-    const WebString& vfs_file_name) {
-  int32_t rv = -1;
-  GetWebDatabaseHost().GetFileAttributes(vfs_file_name.Utf16(), &rv);
-  return rv;
-}
-
-int64_t RendererBlinkPlatformImpl::DatabaseGetFileSize(
-    const WebString& vfs_file_name) {
-  int64_t rv = 0LL;
-  GetWebDatabaseHost().GetFileSize(vfs_file_name.Utf16(), &rv);
-  return rv;
-}
-
-int64_t RendererBlinkPlatformImpl::DatabaseGetSpaceAvailableForOrigin(
-    const blink::WebSecurityOrigin& origin) {
-  int64_t rv = 0LL;
-  GetWebDatabaseHost().GetSpaceAvailable(origin, &rv);
-  return rv;
-}
-
-bool RendererBlinkPlatformImpl::DatabaseSetFileSize(
-    const WebString& vfs_file_name,
-    int64_t size) {
-  bool rv = false;
-  GetWebDatabaseHost().SetFileSize(vfs_file_name.Utf16(), size, &rv);
-  return rv;
-}
-
 WebString RendererBlinkPlatformImpl::DatabaseCreateOriginIdentifier(
     const blink::WebSecurityOrigin& origin) {
   return WebString::FromUTF8(
@@ -542,15 +493,6 @@
   return GetAudioHardwareParams().channels();
 }
 
-WebDatabaseObserver* RendererBlinkPlatformImpl::DatabaseObserver() {
-  if (!web_database_observer_impl_) {
-    InitializeWebDatabaseHostIfNeeded();
-    web_database_observer_impl_ =
-        std::make_unique<WebDatabaseObserverImpl>(web_database_host_);
-  }
-  return web_database_observer_impl_.get();
-}
-
 std::unique_ptr<WebAudioDevice> RendererBlinkPlatformImpl::CreateAudioDevice(
     unsigned input_channels,
     unsigned channels,
@@ -713,6 +655,21 @@
   return base::nullopt;
 }
 
+scoped_refptr<media::AudioRendererSink>
+RendererBlinkPlatformImpl::NewAudioRendererSink(
+    blink::WebAudioDeviceSourceType source_type,
+    blink::WebLocalFrame* web_frame,
+    const media::AudioSinkParameters& params) {
+  return AudioDeviceFactory::NewAudioRendererSink(
+      source_type, RenderFrame::GetRoutingIdForWebFrame(web_frame), params);
+}
+
+media::AudioLatency::LatencyType
+RendererBlinkPlatformImpl::GetAudioSourceLatencyType(
+    blink::WebAudioDeviceSourceType source_type) {
+  return AudioDeviceFactory::GetSourceLatencyType(source_type);
+}
+
 //------------------------------------------------------------------------------
 
 std::unique_ptr<blink::WebSpeechSynthesizer>
@@ -971,20 +928,6 @@
 
 //------------------------------------------------------------------------------
 
-void RendererBlinkPlatformImpl::InitializeWebDatabaseHostIfNeeded() {
-  if (!web_database_host_) {
-    web_database_host_ = blink::mojom::ThreadSafeWebDatabaseHostPtr::Create(
-        std::move(web_database_host_info_),
-        base::CreateSequencedTaskRunnerWithTraits(
-            {base::WithBaseSyncPrimitives()}));
-  }
-}
-
-blink::mojom::WebDatabaseHost& RendererBlinkPlatformImpl::GetWebDatabaseHost() {
-  InitializeWebDatabaseHostIfNeeded();
-  return **web_database_host_;
-}
-
 blink::mojom::CodeCacheHost& RendererBlinkPlatformImpl::GetCodeCacheHost() {
   if (!code_cache_host_) {
     code_cache_host_ = blink::mojom::ThreadSafeCodeCacheHostPtr::Create(
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 8649e75..07344fa3 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -28,7 +28,6 @@
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom.h"
-#include "third_party/blink/public/mojom/webdatabase/web_database.mojom.h"
 
 #if defined(OS_LINUX)
 #include "components/services/font/public/cpp/font_loader.h"  // nogncheck
@@ -55,7 +54,6 @@
 class BlinkInterfaceProviderImpl;
 class ChildURLLoaderFactoryBundle;
 class ThreadSafeSender;
-class WebDatabaseObserverImpl;
 
 class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl {
  public:
@@ -100,17 +98,6 @@
       const blink::WebString& cacheStorageCacheName) override;
   blink::WebString DefaultLocale() override;
   void SuddenTerminationChanged(bool enabled) override;
-  base::File DatabaseOpenFile(const blink::WebString& vfs_file_name,
-                              int desired_flags) override;
-  int DatabaseDeleteFile(const blink::WebString& vfs_file_name,
-                         bool sync_dir) override;
-  int32_t DatabaseGetFileAttributes(
-      const blink::WebString& vfs_file_name) override;
-  int64_t DatabaseGetFileSize(const blink::WebString& vfs_file_name) override;
-  int64_t DatabaseGetSpaceAvailableForOrigin(
-      const blink::WebSecurityOrigin& origin) override;
-  bool DatabaseSetFileSize(const blink::WebString& vfs_file_name,
-                           int64_t size) override;
   blink::WebString DatabaseCreateOriginIdentifier(
       const blink::WebSecurityOrigin& origin) override;
   viz::FrameSinkId GenerateFrameSinkId() override;
@@ -124,7 +111,6 @@
   double AudioHardwareSampleRate() override;
   size_t AudioHardwareBufferSize() override;
   unsigned AudioHardwareOutputChannels() override;
-  blink::WebDatabaseObserver* DatabaseObserver() override;
 
   std::unique_ptr<blink::WebAudioDevice> CreateAudioDevice(
       unsigned input_channels,
@@ -158,6 +144,12 @@
       const blink::WebString& kind) override;
   void UpdateWebRTCAPICount(blink::WebRTCAPIName api_name) override;
   base::Optional<double> GetWebRtcMaxCaptureFrameRate() override;
+  scoped_refptr<media::AudioRendererSink> NewAudioRendererSink(
+      blink::WebAudioDeviceSourceType source_type,
+      blink::WebLocalFrame* web_frame,
+      const media::AudioSinkParameters& params) override;
+  media::AudioLatency::LatencyType GetAudioSourceLatencyType(
+      blink::WebAudioDeviceSourceType source_type) override;
 
   std::unique_ptr<blink::WebGraphicsContext3DProvider>
   CreateOffscreenGraphicsContext3DProvider(
@@ -203,10 +195,6 @@
   // Returns the previous |enable| value.
   static bool SetSandboxEnabledForTesting(bool enable);
 
-  WebDatabaseObserverImpl* web_database_observer_impl() {
-    return web_database_observer_impl_.get();
-  }
-
   std::unique_ptr<blink::WebURLLoaderFactory> CreateDefaultURLLoaderFactory()
       override;
   std::unique_ptr<blink::CodeCacheLoader> CreateCodeCacheLoader() override;
@@ -234,12 +222,6 @@
  private:
   bool CheckPreparsedJsCachingEnabled() const;
 
-  // Ensure that the WebDatabaseHost has been initialized.
-  void InitializeWebDatabaseHostIfNeeded();
-
-  // Return the mojo interface for making WebDatabaseHost calls.
-  blink::mojom::WebDatabaseHost& GetWebDatabaseHost();
-
   // Return the mojo interface for making CodeCache calls.
   blink::mojom::CodeCacheHost& GetCodeCacheHost();
 
@@ -264,8 +246,6 @@
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
   scoped_refptr<ThreadSafeSender> thread_safe_sender_;
 
-  std::unique_ptr<WebDatabaseObserverImpl> web_database_observer_impl_;
-
   // NOT OWNED
   blink::scheduler::WebThreadScheduler* main_thread_scheduler_;
 
@@ -273,9 +253,6 @@
 
   std::unique_ptr<BlinkInterfaceProviderImpl> blink_interface_provider_;
 
-  blink::mojom::WebDatabaseHostPtrInfo web_database_host_info_;
-  scoped_refptr<blink::mojom::ThreadSafeWebDatabaseHostPtr> web_database_host_;
-
   blink::mojom::CodeCacheHostPtrInfo code_cache_host_info_;
   scoped_refptr<blink::mojom::ThreadSafeCodeCacheHostPtr> code_cache_host_;
 
diff --git a/content/renderer/stream_texture_host_android.cc b/content/renderer/stream_texture_host_android.cc
index ca8003cf..b4f28bc 100644
--- a/content/renderer/stream_texture_host_android.cc
+++ b/content/renderer/stream_texture_host_android.cc
@@ -7,6 +7,7 @@
 #include "base/unguessable_token.h"
 #include "content/renderer/render_thread_impl.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
+#include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "ipc/ipc_message_macros.h"
 
@@ -23,8 +24,15 @@
 }
 
 StreamTextureHost::~StreamTextureHost() {
-  if (channel_)
+  if (channel_) {
+    // We destroy the StreamTexture as a deferred message followed by a flush
+    // to ensure this is ordered correctly with regards to previous deferred
+    // messages, such as CreateSharedImage.
+    uint32_t flush_id = channel_->EnqueueDeferredMessage(
+        GpuStreamTextureMsg_Destroy(route_id_));
+    channel_->EnsureFlush(flush_id);
     channel_->RemoveRoute(route_id_);
+  }
 }
 
 bool StreamTextureHost::BindToCurrentThread(Listener* listener) {
@@ -58,11 +66,6 @@
     listener_->OnFrameAvailable();
 }
 
-void StreamTextureHost::SetStreamTextureSize(const gfx::Size& size) {
-  if (channel_)
-    channel_->Send(new GpuStreamTextureMsg_SetSize(route_id_, size));
-}
-
 void StreamTextureHost::ForwardStreamTextureForSurfaceRequest(
     const base::UnguessableToken& request_token) {
   if (channel_) {
@@ -71,4 +74,21 @@
   }
 }
 
+gpu::Mailbox StreamTextureHost::CreateSharedImage(const gfx::Size& size) {
+  if (!channel_)
+    return gpu::Mailbox();
+
+  auto mailbox = gpu::Mailbox::GenerateForSharedImage();
+  channel_->EnqueueDeferredMessage(GpuStreamTextureMsg_CreateSharedImage(
+      route_id_, mailbox, size, ++release_id_));
+  return mailbox;
+}
+
+gpu::SyncToken StreamTextureHost::GenUnverifiedSyncToken() {
+  return gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
+                        gpu::CommandBufferIdFromChannelAndRoute(
+                            channel_->channel_id(), route_id_),
+                        release_id_);
+}
+
 }  // namespace content
diff --git a/content/renderer/stream_texture_host_android.h b/content/renderer/stream_texture_host_android.h
index 0959300..62f8f7b0 100644
--- a/content/renderer/stream_texture_host_android.h
+++ b/content/renderer/stream_texture_host_android.h
@@ -22,6 +22,8 @@
 
 namespace gpu {
 class GpuChannelHost;
+struct Mailbox;
+struct SyncToken;
 }
 
 namespace content {
@@ -48,9 +50,10 @@
   bool OnMessageReceived(const IPC::Message& message) override;
   void OnChannelError() override;
 
-  void SetStreamTextureSize(const gfx::Size& size);
   void ForwardStreamTextureForSurfaceRequest(
       const base::UnguessableToken& request_token);
+  gpu::Mailbox CreateSharedImage(const gfx::Size& size);
+  gpu::SyncToken GenUnverifiedSyncToken();
 
  private:
   // Message handlers:
@@ -59,6 +62,8 @@
   int32_t route_id_;
   Listener* listener_;
   scoped_refptr<gpu::GpuChannelHost> channel_;
+  uint32_t release_id_ = 0;
+
   base::WeakPtrFactory<StreamTextureHost> weak_ptr_factory_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(StreamTextureHost);
diff --git a/content/renderer/web_database_observer_impl.cc b/content/renderer/web_database_observer_impl.cc
deleted file mode 100644
index c9dfc83..0000000
--- a/content/renderer/web_database_observer_impl.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/web_database_observer_impl.h"
-
-#include "base/bind.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string16.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "third_party/blink/public/platform/web_security_origin.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/sqlite/sqlite3.h"
-
-using blink::WebSecurityOrigin;
-using blink::WebString;
-
-namespace content {
-
-WebDatabaseObserverImpl::WebDatabaseObserverImpl(
-    scoped_refptr<blink::mojom::ThreadSafeWebDatabaseHostPtr> web_database_host)
-    : web_database_host_(std::move(web_database_host)),
-      main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
-  DCHECK(main_thread_task_runner_);
-}
-
-WebDatabaseObserverImpl::~WebDatabaseObserverImpl() = default;
-
-void WebDatabaseObserverImpl::DatabaseOpened(
-    const WebSecurityOrigin& origin,
-    const WebString& database_name,
-    const WebString& database_display_name,
-    uint32_t estimated_size) {
-  (*web_database_host_)
-      ->Opened(origin, database_name.Utf16(), database_display_name.Utf16(),
-               estimated_size);
-}
-
-void WebDatabaseObserverImpl::DatabaseModified(const WebSecurityOrigin& origin,
-                                               const WebString& database_name) {
-  (*web_database_host_)->Modified(origin, database_name.Utf16());
-}
-
-void WebDatabaseObserverImpl::DatabaseClosed(const WebSecurityOrigin& origin,
-                                             const WebString& database_name) {
-  DCHECK(!main_thread_task_runner_->RunsTasksInCurrentSequence());
-  (*web_database_host_)->Closed(origin, database_name.Utf16());
-}
-
-void WebDatabaseObserverImpl::ReportSqliteError(const WebSecurityOrigin& origin,
-                                                const WebString& database_name,
-                                                int error) {
-  // We filter out errors which the backend doesn't act on to avoid
-  // a unnecessary ipc traffic, this method can get called at a fairly
-  // high frequency (per-sqlstatement).
-  if (error != SQLITE_CORRUPT && error != SQLITE_NOTADB)
-    return;
-
-  (*web_database_host_)
-      ->HandleSqliteError(origin, database_name.Utf16(), error);
-}
-
-}  // namespace content
diff --git a/content/renderer/web_database_observer_impl.h b/content/renderer/web_database_observer_impl.h
deleted file mode 100644
index c3739b7d..0000000
--- a/content/renderer/web_database_observer_impl.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_WEB_DATABASE_OBSERVER_IMPL_H_
-#define CONTENT_RENDERER_WEB_DATABASE_OBSERVER_IMPL_H_
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "third_party/blink/public/mojom/webdatabase/web_database.mojom.h"
-#include "third_party/blink/public/platform/web_database_observer.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace content {
-
-class WebDatabaseObserverImpl : public blink::WebDatabaseObserver {
- public:
-  explicit WebDatabaseObserverImpl(
-      scoped_refptr<blink::mojom::ThreadSafeWebDatabaseHostPtr>
-          web_database_host);
-  virtual ~WebDatabaseObserverImpl();
-
-  void DatabaseOpened(const blink::WebSecurityOrigin& origin,
-                      const blink::WebString& database_name,
-                      const blink::WebString& database_display_name,
-                      uint32_t estimated_size) override;
-  void DatabaseModified(const blink::WebSecurityOrigin& origin,
-                        const blink::WebString& database_name) override;
-  void DatabaseClosed(const blink::WebSecurityOrigin& origin,
-                      const blink::WebString& database_name) override;
-  void ReportSqliteError(const blink::WebSecurityOrigin& origin,
-                         const blink::WebString& database_name,
-                         int error) override;
-
- private:
-  scoped_refptr<blink::mojom::ThreadSafeWebDatabaseHostPtr> web_database_host_;
-  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebDatabaseObserverImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_WEB_DATABASE_OBSERVER_IMPL_H_
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.cc b/content/renderer/worker/dedicated_worker_host_factory_client.cc
index cb77ceef..f437c8d 100644
--- a/content/renderer/worker/dedicated_worker_host_factory_client.cc
+++ b/content/renderer/worker/dedicated_worker_host_factory_client.cc
@@ -11,6 +11,7 @@
 #include "content/renderer/service_worker/service_worker_provider_context.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h"
+#include "third_party/blink/public/mojom/loader/fetch_client_settings_object.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/worker_main_script_load_params.mojom.h"
@@ -43,13 +44,24 @@
     const blink::WebURL& script_url,
     const blink::WebSecurityOrigin& script_origin,
     network::mojom::CredentialsMode credentials_mode,
+    const blink::WebSecurityOrigin& fetch_client_security_origin,
+    network::mojom::ReferrerPolicy fetch_client_referrer_policy,
+    const blink::WebURL& fetch_client_outgoing_referrer,
     mojo::ScopedMessagePipeHandle blob_url_token) {
   DCHECK(blink::features::IsPlzDedicatedWorkerEnabled());
   blink::mojom::DedicatedWorkerHostFactoryClientPtr client_ptr;
   binding_.Bind(mojo::MakeRequest(&client_ptr));
 
+  auto outside_fetch_client_settings_object =
+      blink::mojom::FetchClientSettingsObject::New();
+  outside_fetch_client_settings_object->referrer_policy =
+      fetch_client_referrer_policy;
+  outside_fetch_client_settings_object->outgoing_referrer =
+      fetch_client_outgoing_referrer;
+
   factory_->CreateWorkerHostAndStartScriptLoad(
       script_url, script_origin, credentials_mode,
+      std::move(outside_fetch_client_settings_object),
       blink::mojom::BlobURLTokenPtr(blink::mojom::BlobURLTokenPtrInfo(
           std::move(blob_url_token), blink::mojom::BlobURLToken::Version_)),
       std::move(client_ptr));
@@ -119,11 +131,10 @@
           std::make_unique<ChildURLLoaderFactoryBundleInfo>(
               std::move(subresource_loader_factory_bundle_info)));
 
-  // TODO(nhiroki): Support ServiceWorkerProviderType::kForDedicatedWorker.
   DCHECK(!service_worker_provider_context_);
   service_worker_provider_context_ =
       base::MakeRefCounted<ServiceWorkerProviderContext>(
-          blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+          blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker,
           std::move(service_worker_provider_info->client_request),
           std::move(service_worker_provider_info->host_ptr_info),
           std::move(controller_info), subresource_loader_factory_bundle_);
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.h b/content/renderer/worker/dedicated_worker_host_factory_client.h
index 54ceac6..76d0e561 100644
--- a/content/renderer/worker/dedicated_worker_host_factory_client.h
+++ b/content/renderer/worker/dedicated_worker_host_factory_client.h
@@ -42,10 +42,14 @@
   // Implements blink::WebDedicatedWorkerHostFactoryClient.
   void CreateWorkerHostDeprecated(
       const blink::WebSecurityOrigin& script_origin) override;
-  void CreateWorkerHost(const blink::WebURL& script_url,
-                        const blink::WebSecurityOrigin& script_origin,
-                        network::mojom::CredentialsMode credentials_mode,
-                        mojo::ScopedMessagePipeHandle blob_url_token) override;
+  void CreateWorkerHost(
+      const blink::WebURL& script_url,
+      const blink::WebSecurityOrigin& script_origin,
+      network::mojom::CredentialsMode credentials_mode,
+      const blink::WebSecurityOrigin& fetch_client_security_origin,
+      network::mojom::ReferrerPolicy fetch_client_referrer_policy,
+      const blink::WebURL& fetch_client_outgoing_referrer,
+      mojo::ScopedMessagePipeHandle blob_url_token) override;
   scoped_refptr<blink::WebWorkerFetchContext> CloneWorkerFetchContext(
       blink::WebWorkerFetchContext* web_worker_fetch_context,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
diff --git a/content/renderer/worker/embedded_shared_worker_stub.cc b/content/renderer/worker/embedded_shared_worker_stub.cc
index afa9ebc..3b6b069 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/worker/embedded_shared_worker_stub.cc
@@ -20,7 +20,7 @@
 #include "content/renderer/loader/web_worker_fetch_context_impl.h"
 #include "content/renderer/renderer_blink_platform_impl.h"
 #include "content/renderer/service_worker/service_worker_provider_context.h"
-#include "content/renderer/worker/service_worker_network_provider_for_worker.h"
+#include "content/renderer/worker/service_worker_network_provider_for_shared_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"
@@ -175,7 +175,7 @@
     // CreateWorkerFetchContext() and consumed during off-the-main-thread
     // shared worker script fetch.
     DCHECK(response_override_);
-    return ServiceWorkerNetworkProviderForWorker::Create(
+    return ServiceWorkerNetworkProviderForSharedWorker::Create(
         std::move(service_worker_provider_info_),
         std::move(main_script_loader_factory_), std::move(controller_info_),
         subresource_loader_factory_bundle_, IsOriginSecure(url_),
@@ -196,7 +196,7 @@
   }
 #endif  // DCHECK_IS_ON()
 
-  return ServiceWorkerNetworkProviderForWorker::Create(
+  return ServiceWorkerNetworkProviderForSharedWorker::Create(
       std::move(service_worker_provider_info_),
       std::move(main_script_loader_factory_), std::move(controller_info_),
       subresource_loader_factory_bundle_, IsOriginSecure(url_),
@@ -207,7 +207,8 @@
     blink::WebServiceWorkerNetworkProvider* web_network_provider,
     base::OnceClosure callback) {
   ServiceWorkerProviderContext* context =
-      static_cast<ServiceWorkerNetworkProviderForWorker*>(web_network_provider)
+      static_cast<ServiceWorkerNetworkProviderForSharedWorker*>(
+          web_network_provider)
           ->context();
   context->PingContainerHost(std::move(callback));
 }
@@ -216,8 +217,9 @@
 EmbeddedSharedWorkerStub::CreateWorkerFetchContext(
     blink::WebServiceWorkerNetworkProvider* web_network_provider) {
   DCHECK(web_network_provider);
-  ServiceWorkerNetworkProviderForWorker* network_provider =
-      static_cast<ServiceWorkerNetworkProviderForWorker*>(web_network_provider);
+  auto* network_provider =
+      static_cast<ServiceWorkerNetworkProviderForSharedWorker*>(
+          web_network_provider);
 
   // Make the factory used for service worker network fallback (that should
   // skip AppCache if it is provided).
diff --git a/content/renderer/worker/service_worker_network_provider_for_worker.cc b/content/renderer/worker/service_worker_network_provider_for_shared_worker.cc
similarity index 80%
rename from content/renderer/worker/service_worker_network_provider_for_worker.cc
rename to content/renderer/worker/service_worker_network_provider_for_shared_worker.cc
index 5353878..e15bb3d 100644
--- a/content/renderer/worker/service_worker_network_provider_for_worker.cc
+++ b/content/renderer/worker/service_worker_network_provider_for_shared_worker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/worker/service_worker_network_provider_for_worker.h"
+#include "content/renderer/worker/service_worker_network_provider_for_shared_worker.h"
 
 #include <utility>
 
@@ -21,8 +21,8 @@
 
 namespace content {
 
-std::unique_ptr<ServiceWorkerNetworkProviderForWorker>
-ServiceWorkerNetworkProviderForWorker::Create(
+std::unique_ptr<ServiceWorkerNetworkProviderForSharedWorker>
+ServiceWorkerNetworkProviderForSharedWorker::Create(
     blink::mojom::ServiceWorkerProviderInfoForWorkerPtr info,
     network::mojom::URLLoaderFactoryPtr script_loader_factory,
     blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
@@ -30,8 +30,9 @@
     bool is_secure_context,
     std::unique_ptr<NavigationResponseOverrideParameters> response_override) {
   DCHECK(info);
-  auto provider = base::WrapUnique(new ServiceWorkerNetworkProviderForWorker(
-      is_secure_context, std::move(response_override)));
+  auto provider =
+      base::WrapUnique(new ServiceWorkerNetworkProviderForSharedWorker(
+          is_secure_context, std::move(response_override)));
   provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
       blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
       std::move(info->client_request), std::move(info->host_ptr_info),
@@ -42,13 +43,13 @@
   return provider;
 }
 
-ServiceWorkerNetworkProviderForWorker::
-    ~ServiceWorkerNetworkProviderForWorker() {
+ServiceWorkerNetworkProviderForSharedWorker::
+    ~ServiceWorkerNetworkProviderForSharedWorker() {
   if (context())
     context()->OnNetworkProviderDestroyed();
 }
 
-void ServiceWorkerNetworkProviderForWorker::WillSendRequest(
+void ServiceWorkerNetworkProviderForSharedWorker::WillSendRequest(
     blink::WebURLRequest& request) {
   auto extra_data = std::make_unique<RequestExtraData>();
   extra_data->set_initiated_in_secure_context(is_secure_context_);
@@ -60,7 +61,7 @@
 }
 
 std::unique_ptr<blink::WebURLLoader>
-ServiceWorkerNetworkProviderForWorker::CreateURLLoader(
+ServiceWorkerNetworkProviderForSharedWorker::CreateURLLoader(
     const blink::WebURLRequest& request,
     std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
         task_runner_handle) {
@@ -95,23 +96,25 @@
 }
 
 blink::mojom::ControllerServiceWorkerMode
-ServiceWorkerNetworkProviderForWorker::GetControllerServiceWorkerMode() {
+ServiceWorkerNetworkProviderForSharedWorker::GetControllerServiceWorkerMode() {
   if (!context())
     return blink::mojom::ControllerServiceWorkerMode::kNoController;
   return context()->GetControllerServiceWorkerMode();
 }
 
-int64_t ServiceWorkerNetworkProviderForWorker::ControllerServiceWorkerID() {
+int64_t
+ServiceWorkerNetworkProviderForSharedWorker::ControllerServiceWorkerID() {
   if (!context())
     return blink::mojom::kInvalidServiceWorkerVersionId;
   return context()->GetControllerVersionId();
 }
 
-void ServiceWorkerNetworkProviderForWorker::DispatchNetworkQuiet() {}
+void ServiceWorkerNetworkProviderForSharedWorker::DispatchNetworkQuiet() {}
 
-ServiceWorkerNetworkProviderForWorker::ServiceWorkerNetworkProviderForWorker(
-    bool is_secure_context,
-    std::unique_ptr<NavigationResponseOverrideParameters> response_override)
+ServiceWorkerNetworkProviderForSharedWorker::
+    ServiceWorkerNetworkProviderForSharedWorker(
+        bool is_secure_context,
+        std::unique_ptr<NavigationResponseOverrideParameters> response_override)
     : is_secure_context_(is_secure_context),
       response_override_(std::move(response_override)) {}
 
diff --git a/content/renderer/worker/service_worker_network_provider_for_worker.h b/content/renderer/worker/service_worker_network_provider_for_shared_worker.h
similarity index 91%
rename from content/renderer/worker/service_worker_network_provider_for_worker.h
rename to content/renderer/worker/service_worker_network_provider_for_shared_worker.h
index 5a908da..1d7944e 100644
--- a/content/renderer/worker/service_worker_network_provider_for_worker.h
+++ b/content/renderer/worker/service_worker_network_provider_for_shared_worker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_WORKER_SERVICE_WORKER_NETWORK_PROVIDER_FOR_WORKER_H_
-#define CONTENT_RENDERER_WORKER_SERVICE_WORKER_NETWORK_PROVIDER_FOR_WORKER_H_
+#ifndef CONTENT_RENDERER_WORKER_SERVICE_WORKER_NETWORK_PROVIDER_FOR_SHARED_WORKER_H_
+#define CONTENT_RENDERER_WORKER_SERVICE_WORKER_NETWORK_PROVIDER_FOR_SHARED_WORKER_H_
 
 #include <memory>
 
@@ -23,7 +23,7 @@
 //
 // 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 ServiceWorkerNetworkProviderForWorker final
+class ServiceWorkerNetworkProviderForSharedWorker final
     : public blink::WebServiceWorkerNetworkProvider {
  public:
   // Creates a new instance.
@@ -36,7 +36,7 @@
   //   AppCache)
   // - |is_secure_context|: whether this context is secure
   // - |response_override|: the main script response
-  static std::unique_ptr<ServiceWorkerNetworkProviderForWorker> Create(
+  static std::unique_ptr<ServiceWorkerNetworkProviderForSharedWorker> Create(
       blink::mojom::ServiceWorkerProviderInfoForWorkerPtr info,
       network::mojom::URLLoaderFactoryPtr script_loader_factory,
       blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
@@ -44,10 +44,10 @@
       bool is_secure_context,
       std::unique_ptr<NavigationResponseOverrideParameters> response_override);
 
-  ServiceWorkerNetworkProviderForWorker(
+  ServiceWorkerNetworkProviderForSharedWorker(
       bool is_secure_context,
       std::unique_ptr<NavigationResponseOverrideParameters> response_override);
-  ~ServiceWorkerNetworkProviderForWorker() override;
+  ~ServiceWorkerNetworkProviderForSharedWorker() override;
 
   // Implements WebServiceWorkerNetworkProvider.
   void WillSendRequest(blink::WebURLRequest& request) override;
@@ -76,4 +76,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_RENDERER_WORKER_SERVICE_WORKER_NETWORK_PROVIDER_FOR_WORKER_H_
+#endif  // CONTENT_RENDERER_WORKER_SERVICE_WORKER_NETWORK_PROVIDER_FOR_SHARED_WORKER_H_
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index f89f506..08d7b85 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -328,7 +328,6 @@
     "//services/service_manager/embedder:embedder_result_codes",
     "//services/service_manager/public/cpp",
     "//services/test/echo:lib",
-    "//services/test/echo/public/cpp:manifest",
     "//services/test/echo/public/mojom",
     "//skia",
     "//skia:test_fonts",
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 9ee532d..22624464 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -58,8 +58,6 @@
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/service_manager/public/cpp/manifest.h"
 #include "services/service_manager/public/cpp/manifest_builder.h"
-#include "services/test/echo/public/cpp/manifest.h"
-#include "services/test/echo/public/mojom/echo.mojom.h"
 #include "storage/browser/quota/quota_settings.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
@@ -163,7 +161,6 @@
                   mojom::FakeBluetoothChooserFactory,
                   mojom::WebTestBluetoothFakeAdapterSetter,
                   bluetooth::mojom::FakeBluetooth>())
-          .RequireCapability(echo::mojom::kServiceName, "echo")
           .ExposeInterfaceFilterCapability_Deprecated(
               "navigation:frame", "renderer",
               service_manager::Manifest::InterfaceList<
@@ -326,12 +323,6 @@
   return base::nullopt;
 }
 
-std::vector<service_manager::Manifest>
-ShellContentBrowserClient::GetExtraServiceManifests() {
-  return std::vector<service_manager::Manifest>{echo::GetManifest(
-      service_manager::Manifest::ExecutionMode::kOutOfProcessBuiltin)};
-}
-
 void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
     base::CommandLine* command_line,
     int child_process_id) {
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index a8112f7..e5ab275 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -49,7 +49,6 @@
       const service_manager::Identity& id) override;
   base::Optional<service_manager::Manifest> GetServiceManifestOverlay(
       base::StringPiece name) override;
-  std::vector<service_manager::Manifest> GetExtraServiceManifests() override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
   std::string GetAcceptLangs(BrowserContext* context) override;
diff --git a/content/shell/browser/shell_views.cc b/content/shell/browser/shell_views.cc
index 4b42cfa..cae6ae8 100644
--- a/content/shell/browser/shell_views.cc
+++ b/content/shell/browser/shell_views.cc
@@ -63,17 +63,15 @@
     STOP_BUTTON
   };
 
-  ShellWindowDelegateView(Shell* shell)
-    : shell_(shell),
-      toolbar_view_(new View),
-      contents_view_(new View) {
-  }
+  ShellWindowDelegateView(Shell* shell) : shell_(shell) {}
+
   ~ShellWindowDelegateView() override {}
 
   // Update the state of UI controls
   void SetAddressBarURL(const GURL& url) {
     url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
   }
+
   void SetWebContents(WebContents* web_contents, const gfx::Size& size) {
     contents_view_->SetLayoutManager(std::make_unique<views::FillLayout>());
     // If there was a previous WebView in this Shell it should be removed and
@@ -82,11 +80,12 @@
       contents_view_->RemoveChildView(web_view_);
       delete web_view_;
     }
-    web_view_ = new views::WebView(web_contents->GetBrowserContext());
-    web_view_->SetWebContents(web_contents);
-    web_view_->SetPreferredSize(size);
+    auto web_view =
+        std::make_unique<views::WebView>(web_contents->GetBrowserContext());
+    web_view->SetWebContents(web_contents);
+    web_view->SetPreferredSize(size);
     web_contents->Focus();
-    contents_view_->AddChildView(web_view_);
+    web_view_ = contents_view_->AddChildView(std::move(web_view));
     Layout();
 
     // Resize the widget, keeping the same origin.
@@ -120,6 +119,9 @@
   void InitShellWindow() {
     SetBackground(views::CreateStandardPanelBackground());
 
+    auto contents_view = std::make_unique<views::View>();
+    auto toolbar_view = std::make_unique<views::View>();
+
     views::GridLayout* layout =
         SetLayoutManager(std::make_unique<views::GridLayout>());
 
@@ -135,46 +137,42 @@
     if (!shell_->hide_toolbar()) {
       layout->AddPaddingRow(0, 2);
       layout->StartRow(0, 0);
-      views::GridLayout* toolbar_layout = toolbar_view_->SetLayoutManager(
-          std::make_unique<views::GridLayout>());
+      views::GridLayout* toolbar_layout =
+          toolbar_view->SetLayoutManager(std::make_unique<views::GridLayout>());
 
       views::ColumnSet* toolbar_column_set =
           toolbar_layout->AddColumnSet(0);
       // Back button
-      back_button_ =
-          views::MdTextButton::Create(this, base::ASCIIToUTF16("Back"))
-              .release();
-      gfx::Size back_button_size = back_button_->GetPreferredSize();
+      auto back_button =
+          views::MdTextButton::Create(this, base::ASCIIToUTF16("Back"));
+      gfx::Size back_button_size = back_button->GetPreferredSize();
       toolbar_column_set->AddColumn(views::GridLayout::CENTER,
                                     views::GridLayout::CENTER, 0,
                                     views::GridLayout::FIXED,
                                     back_button_size.width(),
                                     back_button_size.width() / 2);
       // Forward button
-      forward_button_ =
-          views::MdTextButton::Create(this, base::ASCIIToUTF16("Forward"))
-              .release();
-      gfx::Size forward_button_size = forward_button_->GetPreferredSize();
+      auto forward_button =
+          views::MdTextButton::Create(this, base::ASCIIToUTF16("Forward"));
+      gfx::Size forward_button_size = forward_button->GetPreferredSize();
       toolbar_column_set->AddColumn(views::GridLayout::CENTER,
                                     views::GridLayout::CENTER, 0,
                                     views::GridLayout::FIXED,
                                     forward_button_size.width(),
                                     forward_button_size.width() / 2);
       // Refresh button
-      refresh_button_ =
-          views::MdTextButton::Create(this, base::ASCIIToUTF16("Refresh"))
-              .release();
-      gfx::Size refresh_button_size = refresh_button_->GetPreferredSize();
+      auto refresh_button =
+          views::MdTextButton::Create(this, base::ASCIIToUTF16("Refresh"));
+      gfx::Size refresh_button_size = refresh_button->GetPreferredSize();
       toolbar_column_set->AddColumn(views::GridLayout::CENTER,
                                     views::GridLayout::CENTER, 0,
                                     views::GridLayout::FIXED,
                                     refresh_button_size.width(),
                                     refresh_button_size.width() / 2);
       // Stop button
-      stop_button_ =
-          views::MdTextButton::Create(this, base::ASCIIToUTF16("Stop"))
-              .release();
-      gfx::Size stop_button_size = stop_button_->GetPreferredSize();
+      auto stop_button =
+          views::MdTextButton::Create(this, base::ASCIIToUTF16("Stop"));
+      gfx::Size stop_button_size = stop_button->GetPreferredSize();
       toolbar_column_set->AddColumn(views::GridLayout::CENTER,
                                     views::GridLayout::CENTER, 0,
                                     views::GridLayout::FIXED,
@@ -182,10 +180,10 @@
                                     stop_button_size.width() / 2);
       toolbar_column_set->AddPaddingColumn(0, 2);
       // URL entry
-      url_entry_ = new views::Textfield();
-      url_entry_->SetAccessibleName(base::ASCIIToUTF16("Enter URL"));
-      url_entry_->set_controller(this);
-      url_entry_->SetTextInputType(ui::TextInputType::TEXT_INPUT_TYPE_URL);
+      auto url_entry = std::make_unique<views::Textfield>();
+      url_entry->SetAccessibleName(base::ASCIIToUTF16("Enter URL"));
+      url_entry->set_controller(this);
+      url_entry->SetTextInputType(ui::TextInputType::TEXT_INPUT_TYPE_URL);
       toolbar_column_set->AddColumn(views::GridLayout::FILL,
                                     views::GridLayout::FILL, 1,
                                     views::GridLayout::USE_PREF, 0, 0);
@@ -193,13 +191,13 @@
 
       // Fill up the first row
       toolbar_layout->StartRow(0, 0);
-      toolbar_layout->AddView(back_button_);
-      toolbar_layout->AddView(forward_button_);
-      toolbar_layout->AddView(refresh_button_);
-      toolbar_layout->AddView(stop_button_);
-      toolbar_layout->AddView(url_entry_);
+      back_button_ = toolbar_layout->AddView(std::move(back_button));
+      forward_button_ = toolbar_layout->AddView(std::move(forward_button));
+      refresh_button_ = toolbar_layout->AddView(std::move(refresh_button));
+      stop_button_ = toolbar_layout->AddView(std::move(stop_button));
+      url_entry_ = toolbar_layout->AddView(std::move(url_entry));
 
-      layout->AddView(toolbar_view_);
+      toolbar_view_ = layout->AddView(std::move(toolbar_view));
 
       layout->AddPaddingRow(0, 5);
     }
@@ -207,7 +205,7 @@
     // Add web contents view as the second row
     {
       layout->StartRow(1, 0);
-      layout->AddView(contents_view_);
+      contents_view_ = layout->AddView(std::move(contents_view));
     }
 
     if (!shell_->hide_toolbar())
@@ -307,15 +305,15 @@
   base::string16 title_;
 
   // Toolbar view contains forward/backward/reload button and URL entry
-  View* toolbar_view_;
-  views::Button* back_button_;
-  views::Button* forward_button_;
-  views::Button* refresh_button_;
-  views::Button* stop_button_;
-  views::Textfield* url_entry_;
+  View* toolbar_view_ = nullptr;
+  views::Button* back_button_ = nullptr;
+  views::Button* forward_button_ = nullptr;
+  views::Button* refresh_button_ = nullptr;
+  views::Button* stop_button_ = nullptr;
+  views::Textfield* url_entry_ = nullptr;
 
   // Contents view contains the web contents view
-  View* contents_view_;
+  View* contents_view_ = nullptr;
   views::WebView* web_view_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView);
diff --git a/content/shell/test_runner/test_runner_for_specific_view.cc b/content/shell/test_runner/test_runner_for_specific_view.cc
index 13c95efe..6fd628d7 100644
--- a/content/shell/test_runner/test_runner_for_specific_view.cc
+++ b/content/shell/test_runner/test_runner_for_specific_view.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
 #include "cc/paint/paint_canvas.h"
+#include "content/public/common/isolated_world_ids.h"
 #include "content/renderer/compositor/layer_tree_view.h"
 #include "content/shell/test_runner/layout_dump.h"
 #include "content/shell/test_runner/mock_content_settings_client.h"
@@ -626,8 +627,11 @@
     int world_id,
     v8::Local<v8::Value> security_origin,
     v8::Local<v8::Value> content_security_policy) {
-  if (world_id >= blink::IsolatedWorldId::kEmbedderWorldIdLimit)
+  if (world_id <= content::ISOLATED_WORLD_ID_GLOBAL &&
+      world_id >= blink::IsolatedWorldId::kEmbedderWorldIdLimit) {
     return;
+  }
+
   if (!security_origin->IsString() && !security_origin->IsNull())
     return;
 
diff --git a/content/shell/utility/shell_content_utility_client.cc b/content/shell/utility/shell_content_utility_client.cc
index 263ce049..c94539e 100644
--- a/content/shell/utility/shell_content_utility_client.cc
+++ b/content/shell/utility/shell_content_utility_client.cc
@@ -20,6 +20,7 @@
 #include "content/public/test/test_service.mojom.h"
 #include "content/public/utility/utility_thread.h"
 #include "content/shell/common/power_monitor_test_impl.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
@@ -110,9 +111,7 @@
     const std::string& service_name,
     service_manager::mojom::ServiceRequest request) {
   std::unique_ptr<service_manager::Service> service;
-  if (service_name == echo::mojom::kServiceName) {
-    service = std::make_unique<echo::EchoService>(std::move(request));
-  } else if (service_name == kTestServiceUrl) {
+  if (service_name == kTestServiceUrl) {
     service = std::make_unique<TestService>(std::move(request));
   }
 
@@ -127,6 +126,15 @@
   return false;
 }
 
+void ShellContentUtilityClient::RunIOThreadService(
+    mojo::GenericPendingReceiver* receiver) {
+  if (auto echo_receiver = receiver->As<echo::mojom::EchoService>()) {
+    mojo::MakeSelfOwnedReceiver(std::make_unique<echo::EchoService>(),
+                                std::move(echo_receiver));
+    return;
+  }
+}
+
 void ShellContentUtilityClient::RegisterNetworkBinders(
     service_manager::BinderRegistry* registry) {
   network_service_test_helper_->RegisterNetworkBinders(registry);
diff --git a/content/shell/utility/shell_content_utility_client.h b/content/shell/utility/shell_content_utility_client.h
index a1343c268..6d67275 100644
--- a/content/shell/utility/shell_content_utility_client.h
+++ b/content/shell/utility/shell_content_utility_client.h
@@ -22,6 +22,7 @@
   bool HandleServiceRequest(
       const std::string& service_name,
       service_manager::mojom::ServiceRequest request) override;
+  void RunIOThreadService(mojo::GenericPendingReceiver* receiver) override;
   void RegisterNetworkBinders(
       service_manager::BinderRegistry* registry) override;
   void RegisterAudioBinders(service_manager::BinderMap* binders) override;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 993c881..bf7d9067 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -240,6 +240,7 @@
     "fake_compositor_dependencies.h",
     "fake_leveldb_database.cc",
     "fake_leveldb_database.h",
+    "fake_mojo_message_dispatch_context.h",
     "fake_network_url_loader_factory.cc",
     "fake_network_url_loader_factory.h",
     "fake_plugin_service.cc",
@@ -945,6 +946,7 @@
     "../browser/screen_orientation/screen_orientation_browsertest.cc",
     "../browser/security_exploit_browsertest.cc",
     "../browser/service_manager/service_manager_context_browsertest.cc",
+    "../browser/service_process_host_browsertest.cc",
     "../browser/service_worker/service_worker_auth_browsertest.cc",
     "../browser/service_worker/service_worker_browsertest.cc",
     "../browser/service_worker/service_worker_clients_api_browsertest.cc",
@@ -1080,6 +1082,7 @@
     "//services/audio/public/cpp",
     "//services/content/public/cpp",
     "//services/content/public/mojom",
+    "//services/data_decoder/public/mojom",
     "//services/device/public/cpp:device_features",
     "//services/device/public/cpp:test_support",
     "//services/device/public/cpp/generic_sensor",
@@ -1493,6 +1496,7 @@
     "../browser/cocoa/system_hotkey_map_unittest.mm",
     "../browser/code_cache/generated_code_cache_unittest.cc",
     "../browser/compositor/reflector_impl_unittest.cc",
+    "../browser/content_index/content_index_database_unittest.cc",
     "../browser/cookie_store/cookie_store_manager_unittest.cc",
     "../browser/devtools/devtools_background_services_context_impl_unittest.cc",
     "../browser/devtools/devtools_http_handler_unittest.cc",
@@ -1741,6 +1745,7 @@
     "../browser/scheduler/responsiveness/jank_monitor_unittest.cc",
     "../browser/scheduler/responsiveness/metric_source_unittest.cc",
     "../browser/scheduler/responsiveness/watcher_unittest.cc",
+    "../browser/scheduler/scoped_do_not_use_ui_default_queue_from_io_unittest.cc",
     "../browser/screen_orientation/screen_orientation_provider_unittest.cc",
     "../browser/screenlock_monitor/screenlock_monitor_unittest.cc",
     "../browser/service_worker/embedded_worker_instance_unittest.cc",
@@ -1783,7 +1788,6 @@
     "../browser/web_contents/web_contents_view_mac_unittest.mm",
     "../browser/web_contents/web_drag_dest_mac_unittest.mm",
     "../browser/web_contents/web_drag_source_mac_unittest.mm",
-    "../browser/web_package/http_structured_header_unittest.cc",
     "../browser/web_package/signed_exchange_cert_fetcher_unittest.cc",
     "../browser/web_package/signed_exchange_certificate_chain_unittest.cc",
     "../browser/web_package/signed_exchange_envelope_unittest.cc",
@@ -1791,7 +1795,6 @@
     "../browser/web_package/signed_exchange_loader_unittest.cc",
     "../browser/web_package/signed_exchange_prefetch_metric_recorder_unittest.cc",
     "../browser/web_package/signed_exchange_prologue_unittest.cc",
-    "../browser/web_package/signed_exchange_request_matcher_unittest.cc",
     "../browser/web_package/signed_exchange_signature_header_field_unittest.cc",
     "../browser/web_package/signed_exchange_signature_verifier_unittest.cc",
     "../browser/web_package/signed_exchange_utils_unittest.cc",
diff --git a/content/test/content_browser_test_test.cc b/content/test/content_browser_test_test.cc
index 372cb147..109d74d 100644
--- a/content/test/content_browser_test_test.cc
+++ b/content/test/content_browser_test_test.cc
@@ -10,10 +10,12 @@
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/process/launch.h"
+#include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/launcher/test_launcher.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_timeouts.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
@@ -28,6 +30,7 @@
 #include "content/shell/browser/shell.h"
 #include "content/shell/common/shell_switches.h"
 #include "services/service_manager/sandbox/switches.h"
+#include "testing/gtest/include/gtest/gtest-spi.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
@@ -222,4 +225,15 @@
   ASSERT_TRUE(non_nested_task_ran);
 }
 
+IN_PROC_BROWSER_TEST_F(ContentBrowserTest, RunTimeoutInstalled) {
+  // Verify that a RunLoop timeout is installed and shorter than the test
+  // timeout itself.
+  const auto* run_timeout = base::RunLoop::ScopedRunTimeoutForTest::Current();
+  EXPECT_TRUE(run_timeout);
+  EXPECT_LT(run_timeout->timeout(), TestTimeouts::test_launcher_timeout());
+
+  EXPECT_NONFATAL_FAILURE({ run_timeout->on_timeout().Run(); },
+                          "RunLoop::Run() timed out");
+}
+
 }  // namespace content
diff --git a/content/test/data/accessibility/html/a-expected-uia-win.txt b/content/test/data/accessibility/html/a-expected-uia-win.txt
index d0c07890..dd0fb32 100644
--- a/content/test/data/accessibility/html/a-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/a-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++link Name='normal link'
+document
+++group
+++++link Name='normal link'
diff --git a/content/test/data/accessibility/html/a-name-calc-expected-uia-win.txt b/content/test/data/accessibility/html/a-name-calc-expected-uia-win.txt
index 35893ef..bc1dea4 100644
--- a/content/test/data/accessibility/html/a-name-calc-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/a-name-calc-expected-uia-win.txt
@@ -1,12 +1,11 @@
-region
-++document
-++++link Name='InnerText0'
-++++description Name=' '
-++++link Name='InnerText1' FullDescription='Title1'
-++++description Name=' '
-++++link Name='Title2'
-++++description Name=' '
-++++link Name='LabelledBy3'
-++++link Name='Title4'
-++++link Name='Label5'
-++++link Name='LabelledBy6'
+document
+++link Name='InnerText0'
+++description Name=' '
+++link Name='InnerText1' FullDescription='Title1'
+++description Name=' '
+++link Name='Title2'
+++description Name=' '
+++link Name='LabelledBy3'
+++link Name='Title4'
+++link Name='Label5'
+++link Name='LabelledBy6'
diff --git a/content/test/data/accessibility/html/a-name-calc-expected-uia-win7.txt b/content/test/data/accessibility/html/a-name-calc-expected-uia-win7.txt
index 9d9b1ffd..e71bf618 100644
--- a/content/test/data/accessibility/html/a-name-calc-expected-uia-win7.txt
+++ b/content/test/data/accessibility/html/a-name-calc-expected-uia-win7.txt
@@ -1,12 +1,11 @@
-region
-++document
-++++link Name='InnerText0'
-++++description Name=' '
-++++link Name='InnerText1'
-++++description Name=' '
-++++link Name='Title2'
-++++description Name=' '
-++++link Name='LabelledBy3'
-++++link Name='Title4'
-++++link Name='Label5'
-++++link Name='LabelledBy6'
+document
+++link Name='InnerText0'
+++description Name=' '
+++link Name='InnerText1'
+++description Name=' '
+++link Name='Title2'
+++description Name=' '
+++link Name='LabelledBy3'
+++link Name='Title4'
+++link Name='Label5'
+++link Name='LabelledBy6'
diff --git a/content/test/data/accessibility/html/a-name-expected-uia-win.txt b/content/test/data/accessibility/html/a-name-expected-uia-win.txt
index 7388bb6..e374c1cb 100644
--- a/content/test/data/accessibility/html/a-name-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/a-name-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++link Name='named anchor'
-++++++description Name='named anchor'
-++++link Name='both a named anchor and a link'
+document
+++link Name='named anchor'
+++++description Name='named anchor'
+++link Name='both a named anchor and a link'
diff --git a/content/test/data/accessibility/html/a-onclick-expected-uia-win.txt b/content/test/data/accessibility/html/a-onclick-expected-uia-win.txt
index 758e1df..9d30ebe9 100644
--- a/content/test/data/accessibility/html/a-onclick-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/a-onclick-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++link Name='link with no href but onclick'
-++++link Name='link with no href and click handler added via script'
+document
+++link Name='link with no href but onclick'
+++link Name='link with no href and click handler added via script'
diff --git a/content/test/data/accessibility/html/a-with-img-expected-uia-win.txt b/content/test/data/accessibility/html/a-with-img-expected-uia-win.txt
index e65dd517..a267bf4 100644
--- a/content/test/data/accessibility/html/a-with-img-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/a-with-img-expected-uia-win.txt
@@ -1,14 +1,13 @@
-region
-++document
-++++group
-++++++link Name='Link with image at start.'
-++++++++img Name='Link'
-++++++description Name=' '
-++++++link Name='Link with image in the middle.'
-++++++++img Name='image'
-++++++description Name=' '
-++++++link Name='Link with broken in the middle.'
-++++++++img Name='broken'
-++++++description Name=' '
-++++++link Name='Link with image at the end'
-++++++++img Name='end'
+document
+++group
+++++link Name='Link with image at start.'
+++++++img Name='Link'
+++++description Name=' '
+++++link Name='Link with image in the middle.'
+++++++img Name='image'
+++++description Name=' '
+++++link Name='Link with broken in the middle.'
+++++++img Name='broken'
+++++description Name=' '
+++++link Name='Link with image at the end'
+++++++img Name='end'
diff --git a/content/test/data/accessibility/html/abbr-expected-uia-win.txt b/content/test/data/accessibility/html/abbr-expected-uia-win.txt
index 2e37717..113efff 100644
--- a/content/test/data/accessibility/html/abbr-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/abbr-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++group
-++++++description Name='The '
-++++++description Name='World Health Organization'
-++++++++description Name='WHO'
-++++++description Name=' was founded in 1948.'
+document
+++group
+++++description Name='The '
+++++description Name='World Health Organization'
+++++++description Name='WHO'
+++++description Name=' was founded in 1948.'
diff --git a/content/test/data/accessibility/html/action-verbs-expected-uia-win.txt b/content/test/data/accessibility/html/action-verbs-expected-uia-win.txt
index 2811303..186e85f 100644
--- a/content/test/data/accessibility/html/action-verbs-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/action-verbs-expected-uia-win.txt
@@ -1,29 +1,28 @@
-region
-++document Name='Action verbs'
+document Name='Action verbs'
+++group
+++++description Name='Generic div'
+++heading Name='Heading'
+++button Name='Button'
+++link Name='Link'
+++textbox
+++textbox
+++textbox
+++textbox
+++checkbox Toggle.ToggleState='Off'
+++checkbox Toggle.ToggleState='On'
+++radio SelectionItem.IsSelected=false
+++checkbox Name='ARIA Switch' Toggle.ToggleState='Off'
+++group
+++++button Name='Summary' ExpandCollapse.ExpandCollapseState='Collapsed'
+++++++description Name='Summary'
+++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Pop-up button'
+++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++listitem Name='Pop-up button' SelectionItem.IsSelected=true
+++group
+++++description Name='Div with click handler'
+++group
 ++++group
-++++++description Name='Generic div'
-++++heading Name='Heading'
-++++button Name='Button'
-++++link Name='Link'
-++++textbox
-++++textbox
-++++textbox
-++++textbox
-++++checkbox Toggle.ToggleState='Off'
-++++checkbox Toggle.ToggleState='On'
-++++radio SelectionItem.IsSelected=false
-++++checkbox Name='ARIA Switch' Toggle.ToggleState='Off'
-++++group
-++++++button Name='Summary' ExpandCollapse.ExpandCollapseState='Collapsed'
-++++++++description Name='Summary'
-++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Pop-up button'
-++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++listitem Name='Pop-up button' SelectionItem.IsSelected=true
-++++group
-++++++description Name='Div with click handler'
-++++group
-++++++group
-++++menu Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++menuitem Name='Menu item 1'
-++++++menuitemcheckbox Name='Menu item 2' Toggle.ToggleState='On'
-++++++menuitemradio Name='Menu item 3' SelectionItem.IsSelected=false
+++menu Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++menuitem Name='Menu item 1'
+++++menuitemcheckbox Name='Menu item 2' Toggle.ToggleState='On'
+++++menuitemradio Name='Menu item 3' SelectionItem.IsSelected=false
diff --git a/content/test/data/accessibility/html/actions-expected-uia-win.txt b/content/test/data/accessibility/html/actions-expected-uia-win.txt
index 159be3fcd..e191919 100644
--- a/content/test/data/accessibility/html/actions-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/actions-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document Name='Actions'
-++++group
-++++++slider RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=100.00 RangeValue.Minimum=1.00 RangeValue.Value=50.00 Value.Value='50'
-++++++textbox Name='Test textfield'
+document Name='Actions'
+++group
+++++slider RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=100.00 RangeValue.Minimum=1.00 RangeValue.Value=50.00 Value.Value='50'
+++++textbox Name='Test textfield'
diff --git a/content/test/data/accessibility/html/address-expected-uia-win.txt b/content/test/data/accessibility/html/address-expected-uia-win.txt
index 7c23c6b..dccbc9a 100644
--- a/content/test/data/accessibility/html/address-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/address-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++description Name='Please contact John Citizen for more information.'
+document
+++group
+++++description Name='Please contact John Citizen for more information.'
diff --git a/content/test/data/accessibility/html/area-expected-uia-win.txt b/content/test/data/accessibility/html/area-expected-uia-win.txt
index 5a2a29c..71f43c0 100644
--- a/content/test/data/accessibility/html/area-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/area-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++document Name='pipe'
-++++++++link Name='pipe1'
-++++++++link Name='pipe2'
+document
+++group
+++++document Name='pipe'
+++++++link Name='pipe1'
+++++++link Name='pipe2'
diff --git a/content/test/data/accessibility/html/article-expected-uia-win.txt b/content/test/data/accessibility/html/article-expected-uia-win.txt
index 9340c11..efe01316 100644
--- a/content/test/data/accessibility/html/article-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/article-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++article
-++++++description Name='This is an article element.'
+document
+++article
+++++description Name='This is an article element.'
diff --git a/content/test/data/accessibility/html/aside-expected-uia-win.txt b/content/test/data/accessibility/html/aside-expected-uia-win.txt
index 547aac2e..cf2ceab7 100644
--- a/content/test/data/accessibility/html/aside-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/aside-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
+document
+++group
+++++description Name='The aside tag defines some content aside from the content it is placed in.'
+++complementary
+++++heading Name='Aside tag'
 ++++group
-++++++description Name='The aside tag defines some content aside from the content it is placed in.'
-++++complementary
-++++++heading Name='Aside tag'
-++++++group
-++++++++description Name='The aside content should be related to the surrounding content.'
+++++++description Name='The aside content should be related to the surrounding content.'
diff --git a/content/test/data/accessibility/html/b-expected-uia-win.txt b/content/test/data/accessibility/html/b-expected-uia-win.txt
index 31ef39c..149b059 100644
--- a/content/test/data/accessibility/html/b-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/b-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++description Name='Some '
-++++++description Name='bold'
-++++++description Name=' text'
+document
+++group
+++++description Name='Some '
+++++description Name='bold'
+++++description Name=' text'
diff --git a/content/test/data/accessibility/html/bdo-expected-uia-win.txt b/content/test/data/accessibility/html/bdo-expected-uia-win.txt
index 09eaa31f..d1dbd89 100644
--- a/content/test/data/accessibility/html/bdo-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/bdo-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++group
-++++++description Name='Some LTR text'
-++++++description Name=' '
-++++++description Name='Some RTL text '
-++++++description Name='with some LTR text embedded'
+document
+++group
+++++description Name='Some LTR text'
+++++description Name=' '
+++++description Name='Some RTL text '
+++++description Name='with some LTR text embedded'
diff --git a/content/test/data/accessibility/html/blockquote-expected-uia-win.txt b/content/test/data/accessibility/html/blockquote-expected-uia-win.txt
index b3bf836..f5c37da 100644
--- a/content/test/data/accessibility/html/blockquote-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/blockquote-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
+document
+++group
 ++++group
-++++++group
-++++++++description Name='First blockquote has a child element.'
-++++group
-++++++description Name='Second blockquote has no child.'
+++++++description Name='First blockquote has a child element.'
+++group
+++++description Name='Second blockquote has no child.'
diff --git a/content/test/data/accessibility/html/body-expected-uia-win.txt b/content/test/data/accessibility/html/body-expected-uia-win.txt
index 948099d..83d6d2b7 100644
--- a/content/test/data/accessibility/html/body-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/body-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++description Name='This test is for body tag'
+document
+++group
+++++description Name='This test is for body tag'
diff --git a/content/test/data/accessibility/html/br-expected-uia-win.txt b/content/test/data/accessibility/html/br-expected-uia-win.txt
index 39fca70..f4a6204f 100644
--- a/content/test/data/accessibility/html/br-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/br-expected-uia-win.txt
@@ -1,9 +1,8 @@
-region
-++document
+document
+++separator Name='<newline>'
+++description Name='Text line 1'
+++group
+++++description Name='Text line 2'
 ++++separator Name='<newline>'
-++++description Name='Text line 1'
-++++group
-++++++description Name='Text line 2'
-++++++separator Name='<newline>'
-++++++description Name='Text line 3'
-++++separator Name='<newline>'
+++++description Name='Text line 3'
+++separator Name='<newline>'
diff --git a/content/test/data/accessibility/html/button-expected-uia-win.txt b/content/test/data/accessibility/html/button-expected-uia-win.txt
index 77ddd37..9d4c319 100644
--- a/content/test/data/accessibility/html/button-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/button-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++button Name='Click me!'
+document
+++group
+++++button Name='Click me!'
diff --git a/content/test/data/accessibility/html/button-name-calc-expected-uia-win.txt b/content/test/data/accessibility/html/button-name-calc-expected-uia-win.txt
index 841621a..fb076def 100644
--- a/content/test/data/accessibility/html/button-name-calc-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/button-name-calc-expected-uia-win.txt
@@ -1,11 +1,10 @@
-region
-++document
-++++button Name='InnerText0'
-++++button Name='InnerText1' FullDescription='Title1'
-++++button Name='AriaLabel2' FullDescription='Title2'
-++++button Name='LabelledBy3' FullDescription='Title3'
-++++button Name='LabelledBy4' FullDescription='DescribedBy4'
-++++button Name='InnerText5' FullDescription='DescribedBy5'
-++++button Name='Outer inner'
-++++button Name='Outer inner1'
-++++button Name='Outer grandchild'
+document
+++button Name='InnerText0'
+++button Name='InnerText1' FullDescription='Title1'
+++button Name='AriaLabel2' FullDescription='Title2'
+++button Name='LabelledBy3' FullDescription='Title3'
+++button Name='LabelledBy4' FullDescription='DescribedBy4'
+++button Name='InnerText5' FullDescription='DescribedBy5'
+++button Name='Outer inner'
+++button Name='Outer inner1'
+++button Name='Outer grandchild'
diff --git a/content/test/data/accessibility/html/button-name-calc-expected-uia-win7.txt b/content/test/data/accessibility/html/button-name-calc-expected-uia-win7.txt
index 38ae4cf..061fd78 100644
--- a/content/test/data/accessibility/html/button-name-calc-expected-uia-win7.txt
+++ b/content/test/data/accessibility/html/button-name-calc-expected-uia-win7.txt
@@ -1,11 +1,10 @@
-region
-++document
-++++button Name='InnerText0'
-++++button Name='InnerText1'
-++++button Name='AriaLabel2'
-++++button Name='LabelledBy3'
-++++button Name='LabelledBy4'
-++++button Name='InnerText5'
-++++button Name='Outer inner'
-++++button Name='Outer inner1'
-++++button Name='Outer grandchild'
\ No newline at end of file
+document
+++button Name='InnerText0'
+++button Name='InnerText1'
+++button Name='AriaLabel2'
+++button Name='LabelledBy3'
+++button Name='LabelledBy4'
+++button Name='InnerText5'
+++button Name='Outer inner'
+++button Name='Outer inner1'
+++button Name='Outer grandchild'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-uia-win.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-uia-win.txt
index 96676848..d937af2f 100644
--- a/content/test/data/accessibility/html/button-with-listbox-popup-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-uia-win.txt
@@ -1,11 +1,10 @@
-region
-++document
+document
+++group
 ++++group
-++++++group
-++++++++description Name='Choose one:'
-++++++menu Name='Choose one: Foo' ExpandCollapse.ExpandCollapseState='LeafNode'
-++++++++description Name='Foo'
-++++++listbox Name='Choose one:' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++option Name='Baz' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
-++++++++option Name='Bar' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
-++++++++option Name='Foo' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
+++++++description Name='Choose one:'
+++++menu Name='Choose one: Foo' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++description Name='Foo'
+++++listbox Name='Choose one:' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++option Name='Baz' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
+++++++option Name='Bar' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
+++++++option Name='Foo' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
diff --git a/content/test/data/accessibility/html/canvas-expected-uia-win.txt b/content/test/data/accessibility/html/canvas-expected-uia-win.txt
index 2c94cda..88f1a3b 100644
--- a/content/test/data/accessibility/html/canvas-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/canvas-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++group
-++++++img
-++++++++description Name='Static fallback'
-++++++img
-++++++++link Name='Interactive fallback'
+document
+++group
+++++img
+++++++description Name='Static fallback'
+++++img
+++++++link Name='Interactive fallback'
diff --git a/content/test/data/accessibility/html/caption-expected-uia-win.txt b/content/test/data/accessibility/html/caption-expected-uia-win.txt
index 89607249..d3e3a310 100644
--- a/content/test/data/accessibility/html/caption-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/caption-expected-uia-win.txt
@@ -1,14 +1,13 @@
-region
-++document
-++++grid Name='Browser and Engine' Grid.ColumnCount=2 Grid.RowCount=3 Table.RowOrColumnMajor='RowMajor'
-++++++description
-++++++++description Name='Browser and Engine'
-++++++row
-++++++++columnheader Name='Browser' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
-++++++++columnheader Name='Engine' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
-++++++row
-++++++++gridcell Name='Chrome' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
-++++++++gridcell Name='Blink' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
-++++++row
-++++++++gridcell Name='Safari' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=2 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
-++++++++gridcell Name='WebKit' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=2 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
+document
+++grid Name='Browser and Engine' Grid.ColumnCount=2 Grid.RowCount=3 Table.RowOrColumnMajor='RowMajor'
+++++description
+++++++description Name='Browser and Engine'
+++++row
+++++++columnheader Name='Browser' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
+++++++columnheader Name='Engine' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
+++++row
+++++++gridcell Name='Chrome' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
+++++++gridcell Name='Blink' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
+++++row
+++++++gridcell Name='Safari' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=2 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
+++++++gridcell Name='WebKit' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=2 GridItem.RowSpan=1 GridItem.ContainingGrid='Browser and Engine'
diff --git a/content/test/data/accessibility/html/checkbox-name-calc-expected-uia-win.txt b/content/test/data/accessibility/html/checkbox-name-calc-expected-uia-win.txt
index 9a86e5e..f097f3db 100644
--- a/content/test/data/accessibility/html/checkbox-name-calc-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/checkbox-name-calc-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
-++++checkbox Name='Title0' Toggle.ToggleState='Off'
-++++checkbox Name='Label1' Toggle.ToggleState='Off'
-++++checkbox Name='AriaLabel2' Toggle.ToggleState='Off'
-++++checkbox Name='LabelledBy3' Toggle.ToggleState='Off'
-++++checkbox Name='LabelledBy4' Toggle.ToggleState='Off'
-++++checkbox Toggle.ToggleState='Off'
+document
+++checkbox Name='Title0' Toggle.ToggleState='Off'
+++checkbox Name='Label1' Toggle.ToggleState='Off'
+++checkbox Name='AriaLabel2' Toggle.ToggleState='Off'
+++checkbox Name='LabelledBy3' Toggle.ToggleState='Off'
+++checkbox Name='LabelledBy4' Toggle.ToggleState='Off'
+++checkbox Toggle.ToggleState='Off'
diff --git a/content/test/data/accessibility/html/cite-expected-uia-win.txt b/content/test/data/accessibility/html/cite-expected-uia-win.txt
index fd4f19f5..4a7f98b 100644
--- a/content/test/data/accessibility/html/cite-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/cite-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++img Name='Pipe'
-++++group
-++++++description Name='The pipe'
-++++++description Name=' clicked by SomeOne.'
+document
+++img Name='Pipe'
+++group
+++++description Name='The pipe'
+++++description Name=' clicked by SomeOne.'
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-expected-uia-win.txt b/content/test/data/accessibility/html/contenteditable-descendants-expected-uia-win.txt
index 16bc338..8d7f715 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-expected-uia-win.txt
@@ -1,21 +1,20 @@
-region
-++document
+document
+++group
 ++++group
-++++++group
-++++++++description Name='A contenteditable with a '
-++++++++link Name='link'
-++++++++description Name=' and an '
-++++++++img Name='Image'
-++++++++description Name=' and a '
-++++++++button Name='Button'
-++++++++description Name='.'
-++++++grid Grid.ColumnCount=1 Grid.RowCount=1 Table.RowOrColumnMajor='RowMajor'
-++++++++row
-++++++++++gridcell Name='Always expose editable tables as tables.' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++list
-++++++++listitem
-++++++++++description Name='1. '
-++++group
-++++++description Name='Non-editable paragraph.'
-++++group
-++++++description Name='Should keep the role but change the state.'
+++++++description Name='A contenteditable with a '
+++++++link Name='link'
+++++++description Name=' and an '
+++++++img Name='Image'
+++++++description Name=' and a '
+++++++button Name='Button'
+++++++description Name='.'
+++++grid Grid.ColumnCount=1 Grid.RowCount=1 Table.RowOrColumnMajor='RowMajor'
+++++++row
+++++++++gridcell Name='Always expose editable tables as tables.' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++list
+++++++listitem
+++++++++description Name='1. '
+++group
+++++description Name='Non-editable paragraph.'
+++group
+++++description Name='Should keep the role but change the state.'
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-uia-win.txt b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-uia-win.txt
index 4455e2b..a97696f 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-uia-win.txt
@@ -1,17 +1,16 @@
-region
-++document
+document
+++group
 ++++group
-++++++group
-++++++++description Name='A contenteditable with a '
-++++++++link Name='link'
-++++++++description Name=' and an '
-++++++++img Name='Image'
-++++++++description Name=' and a '
-++++++++button Name='Button'
-++++++++description Name='.'
-++++++grid Grid.ColumnCount=1 Grid.RowCount=1 Table.RowOrColumnMajor='RowMajor'
-++++++++row
-++++++++++gridcell Name='Always expose editable tables as tables.' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++list
-++++++++listitem
-++++++++++description Name='1. '
+++++++description Name='A contenteditable with a '
+++++++link Name='link'
+++++++description Name=' and an '
+++++++img Name='Image'
+++++++description Name=' and a '
+++++++button Name='Button'
+++++++description Name='.'
+++++grid Grid.ColumnCount=1 Grid.RowCount=1 Table.RowOrColumnMajor='RowMajor'
+++++++row
+++++++++gridcell Name='Always expose editable tables as tables.' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++list
+++++++listitem
+++++++++description Name='1. '
diff --git a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-uia-win.txt b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-uia-win.txt
index 4320d41..194ca20 100644
--- a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-uia-win.txt
@@ -1,11 +1,10 @@
-region
-++document
+document
+++group
 ++++group
-++++++group
-++++++++description Name='This is editable.'
-++++++description Name='This is not editable.'
-++++++separator Name='<newline>'
-++++++group
-++++++++description Name='But this one is.'
-++++++group
-++++++++description Name='So is this one.'
+++++++description Name='This is editable.'
+++++description Name='This is not editable.'
+++++separator Name='<newline>'
+++++group
+++++++description Name='But this one is.'
+++++group
+++++++description Name='So is this one.'
diff --git a/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-uia-win.txt b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-uia-win.txt
index d78ec7d..9412a3a 100644
--- a/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++group Name='label'
-++++group
-++++group Name='title'
-++++group
-++++++description Name='description'
+document
+++group Name='label'
+++group
+++group Name='title'
+++group
+++++description Name='description'
diff --git a/content/test/data/accessibility/html/dd-expected-uia-win.txt b/content/test/data/accessibility/html/dd-expected-uia-win.txt
index d5aed60..decbb19 100644
--- a/content/test/data/accessibility/html/dd-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/dd-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++list
-++++++listitem
-++++++++description Name='Coffee'
-++++++description
-++++++++description Name='Black hot drink'
+document
+++list
+++++listitem
+++++++description Name='Coffee'
+++++description
+++++++description Name='Black hot drink'
diff --git a/content/test/data/accessibility/html/del-expected-uia-win.txt b/content/test/data/accessibility/html/del-expected-uia-win.txt
index 5f12502..4d34966 100644
--- a/content/test/data/accessibility/html/del-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/del-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
+document
+++group
+++++description Name='I am '
 ++++group
-++++++description Name='I am '
-++++++group
-++++++++description Name='vegetarian'
+++++++description Name='vegetarian'
diff --git a/content/test/data/accessibility/html/details-expected-uia-win.txt b/content/test/data/accessibility/html/details-expected-uia-win.txt
index 8fe0e66..cafe146e 100644
--- a/content/test/data/accessibility/html/details-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/details-expected-uia-win.txt
@@ -1,10 +1,9 @@
-region
-++document
+document
+++group
+++++button Name='details tag' ExpandCollapse.ExpandCollapseState='Collapsed'
+++++++description Name='details tag'
+++group
+++++button Name='details tag open' ExpandCollapse.ExpandCollapseState='Expanded'
+++++++description Name='details tag open'
 ++++group
-++++++button Name='details tag' ExpandCollapse.ExpandCollapseState='Collapsed'
-++++++++description Name='details tag'
-++++group
-++++++button Name='details tag open' ExpandCollapse.ExpandCollapseState='Expanded'
-++++++++description Name='details tag open'
-++++++group
-++++++++description Name='The details tag with open specifies that the details should be visible (open) to the user.'
+++++++description Name='The details tag with open specifies that the details should be visible (open) to the user.'
diff --git a/content/test/data/accessibility/html/dfn-expected-uia-win.txt b/content/test/data/accessibility/html/dfn-expected-uia-win.txt
index 79cc5108..b818887a 100644
--- a/content/test/data/accessibility/html/dfn-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/dfn-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++group
-++++++description Name='Web Browser'
-++++++description Name=' A computer program with a graphical user interface for displaying HTML files, used to navigate the World Wide Web.'
+document
+++group
+++++description Name='Web Browser'
+++++description Name=' A computer program with a graphical user interface for displaying HTML files, used to navigate the World Wide Web.'
diff --git a/content/test/data/accessibility/html/dialog-expected-uia-win.txt b/content/test/data/accessibility/html/dialog-expected-uia-win.txt
index 9b7d00935..01cc608 100644
--- a/content/test/data/accessibility/html/dialog-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/dialog-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++dialog Window.IsModal=false
-++++++description Name='Text in dialog'
+document
+++dialog Window.IsModal=false
+++++description Name='Text in dialog'
diff --git a/content/test/data/accessibility/html/div-expected-uia-win.txt b/content/test/data/accessibility/html/div-expected-uia-win.txt
index 5cd26653..8f153e6 100644
--- a/content/test/data/accessibility/html/div-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/div-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++description Name='Unfocusable div'
-++++group Name='Focusable div'
-++++++description Name='Focusable div'
+document
+++group
+++++description Name='Unfocusable div'
+++group Name='Focusable div'
+++++description Name='Focusable div'
diff --git a/content/test/data/accessibility/html/dl-expected-uia-win.txt b/content/test/data/accessibility/html/dl-expected-uia-win.txt
index 7bdffdf3..694e4ee 100644
--- a/content/test/data/accessibility/html/dl-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/dl-expected-uia-win.txt
@@ -1,9 +1,8 @@
-region
-++document
-++++list
-++++++listitem
-++++++++description Name='Term'
-++++++description
-++++++++description Name='Description'
-++++definition
-++++++description Name='Definition'
+document
+++list
+++++listitem
+++++++description Name='Term'
+++++description
+++++++description Name='Description'
+++definition
+++++description Name='Definition'
diff --git a/content/test/data/accessibility/html/dt-expected-uia-win.txt b/content/test/data/accessibility/html/dt-expected-uia-win.txt
index d5aed60..decbb19 100644
--- a/content/test/data/accessibility/html/dt-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/dt-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++list
-++++++listitem
-++++++++description Name='Coffee'
-++++++description
-++++++++description Name='Black hot drink'
+document
+++list
+++++listitem
+++++++description Name='Coffee'
+++++description
+++++++description Name='Black hot drink'
diff --git a/content/test/data/accessibility/html/element-class-id-src-attr-expected-uia-win.txt b/content/test/data/accessibility/html/element-class-id-src-attr-expected-uia-win.txt
index 8fe2b36d..4202a9d 100644
--- a/content/test/data/accessibility/html/element-class-id-src-attr-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/element-class-id-src-attr-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++heading Name='Image'
-++++img Name='ImageAlt'
+document
+++heading Name='Image'
+++img Name='ImageAlt'
diff --git a/content/test/data/accessibility/html/em-expected-uia-win.txt b/content/test/data/accessibility/html/em-expected-uia-win.txt
index 5afe9e1..f9bf405f 100644
--- a/content/test/data/accessibility/html/em-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/em-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++description Name='One word is '
-++++++description Name='emphasized'
-++++++description Name='.'
+document
+++group
+++++description Name='One word is '
+++++description Name='emphasized'
+++++description Name='.'
diff --git a/content/test/data/accessibility/html/embed-expected-uia-win.txt b/content/test/data/accessibility/html/embed-expected-uia-win.txt
index 17f58ff..e74482a 100644
--- a/content/test/data/accessibility/html/embed-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/embed-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++document
+document
+++group
+++++document
diff --git a/content/test/data/accessibility/html/fieldset-expected-uia-win.txt b/content/test/data/accessibility/html/fieldset-expected-uia-win.txt
index 28853b7..409dc83 100644
--- a/content/test/data/accessibility/html/fieldset-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/fieldset-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++form
-++++++group Name='Browser Engines:'
-++++++++description
+document
+++form
+++++group Name='Browser Engines:'
+++++++description
diff --git a/content/test/data/accessibility/html/figcaption-expected-uia-win.txt b/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
index 96367050..aeb5424 100644
--- a/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group Name='Fig.1 - A green Box'
-++++++img Name='This is a green box.'
-++++++group
-++++++++description Name='Fig.1 - A green Box'
+document
+++group Name='Fig.1 - A green Box'
+++++img Name='This is a green box.'
+++++group
+++++++description Name='Fig.1 - A green Box'
diff --git a/content/test/data/accessibility/html/figure-expected-uia-win.txt b/content/test/data/accessibility/html/figure-expected-uia-win.txt
index 47f7962d..0c4353e3 100644
--- a/content/test/data/accessibility/html/figure-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/figure-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++img Name='Sunspots'
+document
+++group
+++++img Name='Sunspots'
diff --git a/content/test/data/accessibility/html/footer-expected-uia-win.txt b/content/test/data/accessibility/html/footer-expected-uia-win.txt
index cc7a716a..2cb67be 100644
--- a/content/test/data/accessibility/html/footer-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/footer-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++contentinfo
-++++++description Name='Footer element'
+document
+++contentinfo
+++++description Name='Footer element'
diff --git a/content/test/data/accessibility/html/footer-inside-other-section-expected-uia-win.txt b/content/test/data/accessibility/html/footer-inside-other-section-expected-uia-win.txt
index da098c7..33ba75c 100644
--- a/content/test/data/accessibility/html/footer-inside-other-section-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/footer-inside-other-section-expected-uia-win.txt
@@ -1,14 +1,13 @@
-region
-++document
-++++article
-++++++group
-++++++++group
-++++++++++description Name='footer inside article.'
+document
+++article
 ++++group
 ++++++group
-++++++++group
-++++++++++description Name='footer inside section.'
-++++main
+++++++++description Name='footer inside article.'
+++group
+++++group
 ++++++group
-++++++++group
-++++++++++description Name='footer inside main.'
+++++++++description Name='footer inside section.'
+++main
+++++group
+++++++group
+++++++++description Name='footer inside main.'
diff --git a/content/test/data/accessibility/html/form-expected-uia-win.txt b/content/test/data/accessibility/html/form-expected-uia-win.txt
index 2ab4334..57456f6 100644
--- a/content/test/data/accessibility/html/form-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/form-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++form
-++++++button Name='Submit'
+document
+++form
+++++button Name='Submit'
diff --git a/content/test/data/accessibility/html/form-validation-message-expected-uia-win.txt b/content/test/data/accessibility/html/form-validation-message-expected-uia-win.txt
index b510051..80466af 100644
--- a/content/test/data/accessibility/html/form-validation-message-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/form-validation-message-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++form
-++++++description
-++++++++description Name='Pet name:'
-++++++textbox Name='Pet name:' IsRequiredForForm=true
-++++alert Name='Please enter pet name'
+document
+++form
+++++description
+++++++description Name='Pet name:'
+++++textbox Name='Pet name:' IsRequiredForForm=true
+++alert Name='Please enter pet name'
diff --git a/content/test/data/accessibility/html/frameset-expected-uia-win.txt b/content/test/data/accessibility/html/frameset-expected-uia-win.txt
index 18ca445..331f811 100644
--- a/content/test/data/accessibility/html/frameset-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/frameset-expected-uia-win.txt
@@ -1,19 +1,18 @@
-region
+document
 ++document
 ++++document
-++++++document
+++++++group
+++++++++description Name='My favorite browser is '
 ++++++++group
-++++++++++description Name='My favorite browser is '
-++++++++++group
-++++++++++++description Name='ABC'
-++++++++++description Name=' '
-++++++++++group
-++++++++++++description Name='Chrome'
-++++++++++description Name='!'
+++++++++++description Name='ABC'
+++++++++description Name=' '
+++++++++group
+++++++++++description Name='Chrome'
+++++++++description Name='!'
+++document
 ++++document
-++++++document
-++++++++group
-++++++++++description Name='This test is to check '
-++++++++++description
-++++++++++++description Name='mark tag'
-++++++++++description Name='.'
+++++++group
+++++++++description Name='This test is to check '
+++++++++description
+++++++++++description Name='mark tag'
+++++++++description Name='.'
diff --git a/content/test/data/accessibility/html/header-expected-uia-win.txt b/content/test/data/accessibility/html/header-expected-uia-win.txt
index 6fad97a..d2f5e54 100644
--- a/content/test/data/accessibility/html/header-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/header-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++banner
-++++++description Name='Chromium Browser'
+document
+++banner
+++++description Name='Chromium Browser'
diff --git a/content/test/data/accessibility/html/header-inside-other-section-expected-uia-win.txt b/content/test/data/accessibility/html/header-inside-other-section-expected-uia-win.txt
index 6df13f6..87d51eaf 100644
--- a/content/test/data/accessibility/html/header-inside-other-section-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/header-inside-other-section-expected-uia-win.txt
@@ -1,14 +1,13 @@
-region
-++document
-++++article
-++++++group
-++++++++group
-++++++++++description Name='Header inside article.'
+document
+++article
 ++++group
 ++++++group
-++++++++group
-++++++++++description Name='Header inside section.'
-++++main
+++++++++description Name='Header inside article.'
+++group
+++++group
 ++++++group
-++++++++group
-++++++++++description Name='Header inside main.'
+++++++++description Name='Header inside section.'
+++main
+++++group
+++++++group
+++++++++description Name='Header inside main.'
diff --git a/content/test/data/accessibility/html/heading-expected-uia-win.txt b/content/test/data/accessibility/html/heading-expected-uia-win.txt
index 36e289a0..bdb1d2c 100644
--- a/content/test/data/accessibility/html/heading-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/heading-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
-++++heading Name='Heading 1'
-++++heading Name='Heading 2'
-++++heading Name='Heading 3'
-++++heading Name='Heading 4'
-++++heading Name='Heading 5'
-++++heading Name='Heading 6'
+document
+++heading Name='Heading 1'
+++heading Name='Heading 2'
+++heading Name='Heading 3'
+++heading Name='Heading 4'
+++heading Name='Heading 5'
+++heading Name='Heading 6'
diff --git a/content/test/data/accessibility/html/hr-expected-uia-win.txt b/content/test/data/accessibility/html/hr-expected-uia-win.txt
index fc38522..e074f7f 100644
--- a/content/test/data/accessibility/html/hr-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/hr-expected-uia-win.txt
@@ -1,10 +1,9 @@
-region
-++document
-++++group
-++++++description Name='Before.'
-++++separator Name='Dividing line'
-++++group
-++++++description Name='Middle.'
-++++separator
-++++group
-++++++description Name='After.'
+document
+++group
+++++description Name='Before.'
+++separator Name='Dividing line'
+++group
+++++description Name='Middle.'
+++separator
+++group
+++++description Name='After.'
diff --git a/content/test/data/accessibility/html/i-expected-uia-win.txt b/content/test/data/accessibility/html/i-expected-uia-win.txt
index 1e0ade2..b6c4afc 100644
--- a/content/test/data/accessibility/html/i-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/i-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++description Name='This is to check '
-++++++description Name='italic property'
-++++++description Name=' using i tag.'
+document
+++group
+++++description Name='This is to check '
+++++description Name='italic property'
+++++description Name=' using i tag.'
diff --git a/content/test/data/accessibility/html/iframe-coordinates-expected-uia-win.txt b/content/test/data/accessibility/html/iframe-coordinates-expected-uia-win.txt
index 8383b4e..2380fe7 100644
--- a/content/test/data/accessibility/html/iframe-coordinates-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/iframe-coordinates-expected-uia-win.txt
@@ -1,16 +1,15 @@
-region
-++document
-++++group
-++++++button Name='Button'
-++++group
-++++++button Name='Button'
-++++group
+document
+++group
+++++button Name='Button'
+++group
+++++button Name='Button'
+++group
+++++document
 ++++++document
-++++++++document
-++++++++++group
-++++++++++++button Name='Ordinary Button'
-++++group
+++++++++group
+++++++++++button Name='Ordinary Button'
+++group
+++++document
 ++++++document
-++++++++document
-++++++++++group
-++++++++++++button Name='Scrolled Button'
+++++++++group
+++++++++++button Name='Scrolled Button'
diff --git a/content/test/data/accessibility/html/iframe-cross-process-expected-uia-win.txt b/content/test/data/accessibility/html/iframe-cross-process-expected-uia-win.txt
index 001d9e41..49e99c7 100644
--- a/content/test/data/accessibility/html/iframe-cross-process-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/iframe-cross-process-expected-uia-win.txt
@@ -1,11 +1,10 @@
-region
-++document
-++++group
-++++++description Name='Before frame'
-++++group
-++++++document Name='Cross-process iframe'
-++++++++document
-++++++++++group
-++++++++++++description Name='Text in iframe'
-++++group
-++++++description Name='After frame'
+document
+++group
+++++description Name='Before frame'
+++group
+++++document Name='Cross-process iframe'
+++++++document
+++++++++group
+++++++++++description Name='Text in iframe'
+++group
+++++description Name='After frame'
diff --git a/content/test/data/accessibility/html/iframe-expected-uia-win.txt b/content/test/data/accessibility/html/iframe-expected-uia-win.txt
index 5a9cd13..f60fc7ab 100644
--- a/content/test/data/accessibility/html/iframe-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/iframe-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++group
-++++++document Name='Empty iframe'
-++++++++document
+document
+++group
+++++document Name='Empty iframe'
+++++++document
diff --git a/content/test/data/accessibility/html/iframe-presentational-expected-uia-win.txt b/content/test/data/accessibility/html/iframe-presentational-expected-uia-win.txt
index 9470dbe..f6c37cb9 100644
--- a/content/test/data/accessibility/html/iframe-presentational-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/iframe-presentational-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
+document
+++group
 ++++group
 ++++++group
-++++++++group
diff --git a/content/test/data/accessibility/html/iframe-transform-expected-uia-win.txt b/content/test/data/accessibility/html/iframe-transform-expected-uia-win.txt
index efc23c2..8290e16a 100644
--- a/content/test/data/accessibility/html/iframe-transform-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/iframe-transform-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
+document
 ++document
 ++++document
-++++++document
-++++++++img
+++++++img
+++document
 ++++document
-++++++document
-++++++++img
+++++++img
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-uia-win.txt b/content/test/data/accessibility/html/img-empty-alt-expected-uia-win.txt
index 0c77191..221a798d2 100644
--- a/content/test/data/accessibility/html/img-empty-alt-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++group
-++++++img
-++++++img
-++++++img
-++++++img Name='full'
+document
+++group
+++++img
+++++img
+++++img
+++++img Name='full'
diff --git a/content/test/data/accessibility/html/img-expected-uia-win.txt b/content/test/data/accessibility/html/img-expected-uia-win.txt
index 6107521..56321fb 100644
--- a/content/test/data/accessibility/html/img-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/img-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
-++++group
-++++++img Name='pipe'
-++++++description Name=' '
-++++++img
-++++++description Name=' '
-++++++img Name='  '
+document
+++group
+++++img Name='pipe'
+++++description Name=' '
+++++img
+++++description Name=' '
+++++img Name='  '
diff --git a/content/test/data/accessibility/html/img-link-empty-alt-expected-uia-win.txt b/content/test/data/accessibility/html/img-link-empty-alt-expected-uia-win.txt
index b9c26ed..a44e743 100644
--- a/content/test/data/accessibility/html/img-link-empty-alt-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/img-link-empty-alt-expected-uia-win.txt
@@ -1,10 +1,9 @@
-region
-++document
-++++group
-++++++link Name='unread '
-++++++link Name='read '
-++++++++img
-++++++link Name='read '
-++++++++img
-++++++link Name='read'
-++++++++img
+document
+++group
+++++link Name='unread '
+++++link Name='read '
+++++++img
+++++link Name='read '
+++++++img
+++++link Name='read'
+++++++img
diff --git a/content/test/data/accessibility/html/in-page-links-expected-uia-win.txt b/content/test/data/accessibility/html/in-page-links-expected-uia-win.txt
index 37207bae..b5ca6ee 100644
--- a/content/test/data/accessibility/html/in-page-links-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/in-page-links-expected-uia-win.txt
@@ -1,30 +1,29 @@
-region
-++document
-++++link Name='Empty anchor'
-++++description Name=' '
+document
+++link Name='Empty anchor'
+++description Name=' '
+++link Name='Anchor with content'
+++description Name=' '
+++link Name='Anchor with ID'
+++description Name=' '
+++link Name='Empty span'
+++description Name=' '
+++link Name='Span with content'
+++description Name=' '
+++link Name='Paragraph with content'
+++group
+++++link
+++++description Name='After empty anchor'
+++group
 ++++link Name='Anchor with content'
-++++description Name=' '
+++++++description Name='Anchor with content'
+++group
 ++++link Name='Anchor with ID'
-++++description Name=' '
-++++link Name='Empty span'
-++++description Name=' '
-++++link Name='Span with content'
-++++description Name=' '
-++++link Name='Paragraph with content'
+++++++description Name='Anchor with ID'
+++group
 ++++group
-++++++link
-++++++description Name='After empty anchor'
+++++description Name='After empty span'
+++group
 ++++group
-++++++link Name='Anchor with content'
-++++++++description Name='Anchor with content'
-++++group
-++++++link Name='Anchor with ID'
-++++++++description Name='Anchor with ID'
-++++group
-++++++group
-++++++description Name='After empty span'
-++++group
-++++++group
-++++++++description Name='Span with content'
-++++group
-++++++description Name='Paragraph with content'
+++++++description Name='Span with content'
+++group
+++++description Name='Paragraph with content'
diff --git a/content/test/data/accessibility/html/input-button-expected-uia-win.txt b/content/test/data/accessibility/html/input-button-expected-uia-win.txt
index fb2d2b9..cae6f304 100644
--- a/content/test/data/accessibility/html/input-button-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-button-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++button Name='Button'
+document
+++group
+++++button Name='Button'
diff --git a/content/test/data/accessibility/html/input-button-in-menu-expected-uia-win.txt b/content/test/data/accessibility/html/input-button-in-menu-expected-uia-win.txt
index d0006c2..0d4ec90 100644
--- a/content/test/data/accessibility/html/input-button-in-menu-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-button-in-menu-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++menuitem Name='Button in menu element'
-++++menu Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++menuitem Name='Button in element with menu role'
+document
+++group
+++++menuitem Name='Button in menu element'
+++menu Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++menuitem Name='Button in element with menu role'
diff --git a/content/test/data/accessibility/html/input-checkbox-expected-uia-win.txt b/content/test/data/accessibility/html/input-checkbox-expected-uia-win.txt
index ed8ea3f..b5b30ddf 100644
--- a/content/test/data/accessibility/html/input-checkbox-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-checkbox-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
-++++group
-++++++checkbox Name='Checkbox0' Toggle.ToggleState='Off'
-++++++checkbox Name='Checkbox1' Toggle.ToggleState='On'
-++++++checkbox Name='Checkbox2' Toggle.ToggleState='On'
-++++++checkbox Name='Checkbox3' Toggle.ToggleState='On'
-++++++checkbox Name='Checkbox4' Toggle.ToggleState='Indeterminate'
+document
+++group
+++++checkbox Name='Checkbox0' Toggle.ToggleState='Off'
+++++checkbox Name='Checkbox1' Toggle.ToggleState='On'
+++++checkbox Name='Checkbox2' Toggle.ToggleState='On'
+++++checkbox Name='Checkbox3' Toggle.ToggleState='On'
+++++checkbox Name='Checkbox4' Toggle.ToggleState='Indeterminate'
diff --git a/content/test/data/accessibility/html/input-checkbox-in-menu-expected-uia-win.txt b/content/test/data/accessibility/html/input-checkbox-in-menu-expected-uia-win.txt
index 22ce224..8d45d959 100644
--- a/content/test/data/accessibility/html/input-checkbox-in-menu-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-checkbox-in-menu-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
-++++group
-++++++checkbox Name='Checkbox1' Toggle.ToggleState='Off'
-++++++checkbox Name='Checkbox2' Toggle.ToggleState='On'
-++++menu Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++checkbox Name='Checkbox3' Toggle.ToggleState='On'
-++++++checkbox Name='Checkbox4' Toggle.ToggleState='Indeterminate'
+document
+++group
+++++checkbox Name='Checkbox1' Toggle.ToggleState='Off'
+++++checkbox Name='Checkbox2' Toggle.ToggleState='On'
+++menu Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++checkbox Name='Checkbox3' Toggle.ToggleState='On'
+++++checkbox Name='Checkbox4' Toggle.ToggleState='Indeterminate'
diff --git a/content/test/data/accessibility/html/input-color-expected-uia-win.txt b/content/test/data/accessibility/html/input-color-expected-uia-win.txt
index 558e4420..412f2fb 100644
--- a/content/test/data/accessibility/html/input-color-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-color-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='100% red 60% green 0% blue'
+document
+++group
+++++textbox Value.Value='100% red 60% green 0% blue'
diff --git a/content/test/data/accessibility/html/input-date-expected-uia-win.txt b/content/test/data/accessibility/html/input-date-expected-uia-win.txt
index 1a41c9a1..1b03c4b 100644
--- a/content/test/data/accessibility/html/input-date-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-date-expected-uia-win.txt
@@ -1,15 +1,14 @@
-region
-++document
-++++group
-++++++list
+document
+++group
+++++list
+++++++group
 ++++++++group
-++++++++++group
-++++++++++++spinbutton Name='Month' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=9.00 Value.Value='09'
-++++++++++++++description Name='09'
-++++++++++++description Name='/'
-++++++++++++spinbutton Name='Day' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='01'
-++++++++++++++description Name='01'
-++++++++++++description Name='/'
-++++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=2008.00 Value.Value='2008'
-++++++++++++++description Name='2008'
-++++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++++++spinbutton Name='Month' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=9.00 Value.Value='09'
+++++++++++++description Name='09'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Day' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='01'
+++++++++++++description Name='01'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=2008.00 Value.Value='2008'
+++++++++++++description Name='2008'
+++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='LeafNode'
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-expected-uia-win.txt b/content/test/data/accessibility/html/input-date-with-popup-open-expected-uia-win.txt
index 0e8b157c..69e875e 100644
--- a/content/test/data/accessibility/html/input-date-with-popup-open-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-expected-uia-win.txt
@@ -1,41 +1,40 @@
-region
-++document
-++++group
-++++++list ControllerFor='{document}'
+document
+++group
+++++list ControllerFor='{document}'
+++++++group
+++++++++group
+++++++++++spinbutton Name='Month' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=9.00 Value.Value='09'
+++++++++++++description Name='09'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Day' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='01'
+++++++++++++description Name='01'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=2008.00 Value.Value='2008'
+++++++++++++description Name='2008'
+++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++document
 ++++++++group
 ++++++++++group
-++++++++++++spinbutton Name='Month' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=9.00 Value.Value='09'
-++++++++++++++description Name='09'
-++++++++++++description Name='/'
-++++++++++++spinbutton Name='Day' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='01'
-++++++++++++++description Name='01'
-++++++++++++description Name='/'
-++++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=2008.00 Value.Value='2008'
-++++++++++++++description Name='2008'
-++++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='LeafNode'
-++++++++document
-++++++++++group
 ++++++++++++group
+++++++++++++++button Name='Show month selection panel'
+++++++++++++button Name='Show previous month'
+++++++++++++button Name='Today'
+++++++++++++button Name='Show next month'
+++++++++++++grid Grid.ColumnCount=0 Grid.RowCount=0 Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Table.RowOrColumnMajor='RowMajor'
 ++++++++++++++group
-++++++++++++++++button Name='Show month selection panel'
-++++++++++++++button Name='Show previous month'
-++++++++++++++button Name='Today'
-++++++++++++++button Name='Show next month'
-++++++++++++++grid Grid.ColumnCount=0 Grid.RowCount=0 Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Table.RowOrColumnMajor='RowMajor'
 ++++++++++++++++group
-++++++++++++++++++group
-++++++++++++++++++++description Name='Sun'
-++++++++++++++++++group
-++++++++++++++++++++description Name='Mon'
-++++++++++++++++++group
-++++++++++++++++++++description Name='Tue'
-++++++++++++++++++group
-++++++++++++++++++++description Name='Wed'
-++++++++++++++++++group
-++++++++++++++++++++description Name='Thu'
-++++++++++++++++++group
-++++++++++++++++++++description Name='Fri'
-++++++++++++++++++group
-++++++++++++++++++++description Name='Sat'
+++++++++++++++++++description Name='Sun'
 ++++++++++++++++group
-++++++++++++++++++group
+++++++++++++++++++description Name='Mon'
+++++++++++++++++group
+++++++++++++++++++description Name='Tue'
+++++++++++++++++group
+++++++++++++++++++description Name='Wed'
+++++++++++++++++group
+++++++++++++++++++description Name='Thu'
+++++++++++++++++group
+++++++++++++++++++description Name='Fri'
+++++++++++++++++group
+++++++++++++++++++description Name='Sat'
+++++++++++++++group
+++++++++++++++++group
diff --git a/content/test/data/accessibility/html/input-datetime-expected-uia-win.txt b/content/test/data/accessibility/html/input-datetime-expected-uia-win.txt
index 5896a63..45b4b43 100644
--- a/content/test/data/accessibility/html/input-datetime-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-datetime-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='1/1/2015 1:00AM'
+document
+++group
+++++textbox Value.Value='1/1/2015 1:00AM'
diff --git a/content/test/data/accessibility/html/input-datetime-local-expected-uia-win.txt b/content/test/data/accessibility/html/input-datetime-local-expected-uia-win.txt
index 4184626..2dbdd78 100644
--- a/content/test/data/accessibility/html/input-datetime-local-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-datetime-local-expected-uia-win.txt
@@ -1,24 +1,23 @@
-region
-++document
-++++group
-++++++list
+document
+++group
+++++list
+++++++group
 ++++++++group
-++++++++++group
-++++++++++++spinbutton Name='Month' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
-++++++++++++++description Name='mm'
-++++++++++++description Name='/'
-++++++++++++spinbutton Name='Day' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
-++++++++++++++description Name='dd'
-++++++++++++description Name='/'
-++++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
-++++++++++++++description Name='yyyy'
-++++++++++++description Name=' '
-++++++++++++spinbutton Name='Hours' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
-++++++++++++++description Name='--'
-++++++++++++description Name=':'
-++++++++++++spinbutton Name='Minutes' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=59.00 RangeValue.Minimum=0.00 RangeValue.Value=0.00 Value.Value='0'
-++++++++++++++description Name='--'
-++++++++++++description Name=' '
-++++++++++++spinbutton Name='AM/PM' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=2.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
-++++++++++++++description Name='--'
-++++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++++++spinbutton Name='Month' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
+++++++++++++description Name='mm'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Day' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
+++++++++++++description Name='dd'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
+++++++++++++description Name='yyyy'
+++++++++++description Name=' '
+++++++++++spinbutton Name='Hours' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
+++++++++++++description Name='--'
+++++++++++description Name=':'
+++++++++++spinbutton Name='Minutes' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=59.00 RangeValue.Minimum=0.00 RangeValue.Value=0.00 Value.Value='0'
+++++++++++++description Name='--'
+++++++++++description Name=' '
+++++++++++spinbutton Name='AM/PM' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=2.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
+++++++++++++description Name='--'
+++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='LeafNode'
diff --git a/content/test/data/accessibility/html/input-email-expected-uia-win.txt b/content/test/data/accessibility/html/input-email-expected-uia-win.txt
index fdcab882..13da42cd 100644
--- a/content/test/data/accessibility/html/input-email-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-email-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='someone@example.com'
+document
+++group
+++++textbox Value.Value='someone@example.com'
diff --git a/content/test/data/accessibility/html/input-file-expected-uia-win.txt b/content/test/data/accessibility/html/input-file-expected-uia-win.txt
index f09be57..f246b4e 100644
--- a/content/test/data/accessibility/html/input-file-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-file-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++button Name='Choose File'
+document
+++group
+++++button Name='Choose File'
diff --git a/content/test/data/accessibility/html/input-image-expected-uia-win.txt b/content/test/data/accessibility/html/input-image-expected-uia-win.txt
index 3a477fc1..a3314b3 100644
--- a/content/test/data/accessibility/html/input-image-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-image-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++button Name='Submit'
+document
+++group
+++++button Name='Submit'
diff --git a/content/test/data/accessibility/html/input-list-expected-uia-win.txt b/content/test/data/accessibility/html/input-list-expected-uia-win.txt
index ce6a63e..813dbdd 100644
--- a/content/test/data/accessibility/html/input-list-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-list-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++description
-++++++++description Name='Choose a pokemon '
-++++++++combobox Name='Choose a pokemon' ExpandCollapse.ExpandCollapseState='LeafNode'
+document
+++group
+++++description
+++++++description Name='Choose a pokemon '
+++++++combobox Name='Choose a pokemon' ExpandCollapse.ExpandCollapseState='LeafNode'
diff --git a/content/test/data/accessibility/html/input-number-expected-uia-win.txt b/content/test/data/accessibility/html/input-number-expected-uia-win.txt
index a029e67..89e30bac 100644
--- a/content/test/data/accessibility/html/input-number-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-number-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++group
-++++++spinbutton RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=0.00 RangeValue.Minimum=0.00 RangeValue.Value=1.00 Value.Value='1'
-++++++spinbutton RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=10.00 RangeValue.Minimum=5.00 RangeValue.Value=6.00 Value.Value='6'
+document
+++group
+++++spinbutton RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=0.00 RangeValue.Minimum=0.00 RangeValue.Value=1.00 Value.Value='1'
+++++spinbutton RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=10.00 RangeValue.Minimum=5.00 RangeValue.Value=6.00 Value.Value='6'
diff --git a/content/test/data/accessibility/html/input-password-expected-uia-win.txt b/content/test/data/accessibility/html/input-password-expected-uia-win.txt
index 1b5b1ba..5fdfb67 100644
--- a/content/test/data/accessibility/html/input-password-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-password-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox IsPassword=true Value.Value='••••••'
+document
+++group
+++++textbox IsPassword=true Value.Value='••••••'
diff --git a/content/test/data/accessibility/html/input-radio-expected-uia-win.txt b/content/test/data/accessibility/html/input-radio-expected-uia-win.txt
index fc55ffd9..d07b98f 100644
--- a/content/test/data/accessibility/html/input-radio-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-radio-expected-uia-win.txt
@@ -1,14 +1,13 @@
-region
-++document
-++++form
-++++++radio SelectionItem.IsSelected=false
-++++++description Name='Radio1'
-++++++separator Name='<newline>'
-++++++radio SelectionItem.IsSelected=false
-++++++description Name='Radio2'
-++++form
-++++++radio Name='Radio3' SelectionItem.IsSelected=false
-++++++radio Name='Radio4' SelectionItem.IsSelected=true
-++++form
-++++++radio Name='Radio5' SelectionItem.IsSelected=false
-++++++radio Name='Radio6' SelectionItem.IsSelected=true
+document
+++form
+++++radio SelectionItem.IsSelected=false
+++++description Name='Radio1'
+++++separator Name='<newline>'
+++++radio SelectionItem.IsSelected=false
+++++description Name='Radio2'
+++form
+++++radio Name='Radio3' SelectionItem.IsSelected=false
+++++radio Name='Radio4' SelectionItem.IsSelected=true
+++form
+++++radio Name='Radio5' SelectionItem.IsSelected=false
+++++radio Name='Radio6' SelectionItem.IsSelected=true
diff --git a/content/test/data/accessibility/html/input-radio-in-menu-expected-uia-win.txt b/content/test/data/accessibility/html/input-radio-in-menu-expected-uia-win.txt
index 328001a..7fe0bc39 100644
--- a/content/test/data/accessibility/html/input-radio-in-menu-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-radio-in-menu-expected-uia-win.txt
@@ -1,12 +1,11 @@
-region
-++document
-++++group
-++++++menuitemradio SelectionItem.IsSelected=true
-++++++description Name='Radio0 '
-++++++menuitemradio SelectionItem.IsSelected=false
-++++++description Name='Radio1 '
-++++++radio Name='Radio2' SelectionItem.IsSelected=false
-++++menu Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++radio Name='Radio3' SelectionItem.IsSelected=false
-++++++menuitemradio SelectionItem.IsSelected=false
-++++++menuitemradio SelectionItem.IsSelected=true
+document
+++group
+++++menuitemradio SelectionItem.IsSelected=true
+++++description Name='Radio0 '
+++++menuitemradio SelectionItem.IsSelected=false
+++++description Name='Radio1 '
+++++radio Name='Radio2' SelectionItem.IsSelected=false
+++menu Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++radio Name='Radio3' SelectionItem.IsSelected=false
+++++menuitemradio SelectionItem.IsSelected=false
+++++menuitemradio SelectionItem.IsSelected=true
diff --git a/content/test/data/accessibility/html/input-range-expected-uia-win.txt b/content/test/data/accessibility/html/input-range-expected-uia-win.txt
index 8d525219..aa63326 100644
--- a/content/test/data/accessibility/html/input-range-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-range-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++slider RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=10.00 RangeValue.Minimum=1.00 RangeValue.Value=5.00 Value.Value='5'
+document
+++group
+++++slider RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=10.00 RangeValue.Minimum=1.00 RangeValue.Value=5.00 Value.Value='5'
diff --git a/content/test/data/accessibility/html/input-reset-expected-uia-win.txt b/content/test/data/accessibility/html/input-reset-expected-uia-win.txt
index 9fd9664..5c2c6b87 100644
--- a/content/test/data/accessibility/html/input-reset-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-reset-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++group
-++++++textbox
-++++++button Name='Reset'
+document
+++group
+++++textbox
+++++button Name='Reset'
diff --git a/content/test/data/accessibility/html/input-search-expected-uia-win.txt b/content/test/data/accessibility/html/input-search-expected-uia-win.txt
index 7391ecea..8a36694 100644
--- a/content/test/data/accessibility/html/input-search-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-search-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='Search terms'
+document
+++group
+++++textbox Value.Value='Search terms'
diff --git a/content/test/data/accessibility/html/input-submit-expected-uia-win.txt b/content/test/data/accessibility/html/input-submit-expected-uia-win.txt
index 89434a2a..51d775b 100644
--- a/content/test/data/accessibility/html/input-submit-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-submit-expected-uia-win.txt
@@ -1,11 +1,10 @@
-region
-++document
-++++form
-++++++textbox
-++++++button Name='First submit in a form is a valid default button'
-++++++button Name='Second submit in a form not a valid default button'
-++++form
-++++++textbox
-++++++button Name='First image button in a form is a valid default button'
-++++++button Name='Second image button in a form not a valid default button'
-++++button Name='Submit outside of form not a valid default button'
+document
+++form
+++++textbox
+++++button Name='First submit in a form is a valid default button'
+++++button Name='Second submit in a form not a valid default button'
+++form
+++++textbox
+++++button Name='First image button in a form is a valid default button'
+++++button Name='Second image button in a form not a valid default button'
+++button Name='Submit outside of form not a valid default button'
diff --git a/content/test/data/accessibility/html/input-suggestions-source-element-expected-uia-win.txt b/content/test/data/accessibility/html/input-suggestions-source-element-expected-uia-win.txt
index 75d9219..3a7fbd0 100644
--- a/content/test/data/accessibility/html/input-suggestions-source-element-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-suggestions-source-element-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++combobox ExpandCollapse.ExpandCollapseState='LeafNode'
+document
+++group
+++++combobox ExpandCollapse.ExpandCollapseState='LeafNode'
diff --git a/content/test/data/accessibility/html/input-tel-expected-uia-win.txt b/content/test/data/accessibility/html/input-tel-expected-uia-win.txt
index 771e386..9a17b528 100644
--- a/content/test/data/accessibility/html/input-tel-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-tel-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='123-456-7890'
+document
+++group
+++++textbox Value.Value='123-456-7890'
diff --git a/content/test/data/accessibility/html/input-text-expected-uia-win.txt b/content/test/data/accessibility/html/input-text-expected-uia-win.txt
index f71dd0c..70a2cd1 100644
--- a/content/test/data/accessibility/html/input-text-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-text-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Name='Name'
+document
+++group
+++++textbox Name='Name'
diff --git a/content/test/data/accessibility/html/input-text-name-calc-expected-uia-win.txt b/content/test/data/accessibility/html/input-text-name-calc-expected-uia-win.txt
index b5e79b4..73556fee 100644
--- a/content/test/data/accessibility/html/input-text-name-calc-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-text-name-calc-expected-uia-win.txt
@@ -1,12 +1,11 @@
-region
-++document
-++++textbox Name='Title0' HelpText='Title0'
-++++textbox Name='Label1' HelpText='Title1'
-++++textbox Name='AriaLabel2' HelpText='Title2'
-++++textbox Name='LabelledBy3' HelpText='Title3'
-++++textbox Name='Placeholder4' HelpText='Placeholder4'
-++++textbox Name='ARIA Placeholder4a' HelpText='ARIA Placeholder4a'
-++++textbox Name='Placeholder4b' HelpText='Placeholder4b'
-++++textbox Name='Placeholder5' HelpText='Placeholder5'
-++++textbox Name='LabelledBy6' HelpText='Title6'
-++++textbox Name='AriaLabel7' HelpText='Placeholder7'
+document
+++textbox Name='Title0' HelpText='Title0'
+++textbox Name='Label1' HelpText='Title1'
+++textbox Name='AriaLabel2' HelpText='Title2'
+++textbox Name='LabelledBy3' HelpText='Title3'
+++textbox Name='Placeholder4' HelpText='Placeholder4'
+++textbox Name='ARIA Placeholder4a' HelpText='ARIA Placeholder4a'
+++textbox Name='Placeholder4b' HelpText='Placeholder4b'
+++textbox Name='Placeholder5' HelpText='Placeholder5'
+++textbox Name='LabelledBy6' HelpText='Title6'
+++textbox Name='AriaLabel7' HelpText='Placeholder7'
diff --git a/content/test/data/accessibility/html/input-text-read-only-expected-uia-win.txt b/content/test/data/accessibility/html/input-text-read-only-expected-uia-win.txt
index f71dd0c..70a2cd1 100644
--- a/content/test/data/accessibility/html/input-text-read-only-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-text-read-only-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Name='Name'
+document
+++group
+++++textbox Name='Name'
diff --git a/content/test/data/accessibility/html/input-text-value-expected-uia-win.txt b/content/test/data/accessibility/html/input-text-value-expected-uia-win.txt
index b957ee6..ff9b09f 100644
--- a/content/test/data/accessibility/html/input-text-value-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-text-value-expected-uia-win.txt
@@ -1,19 +1,18 @@
-region
-++document
+document
+++group
+++++description
+++++++description Name='l1'
+++++textbox Name='l1'
+++++description
+++++++description Name='l2'
+++++textbox Name='l2' Value.Value='value'
+++++textbox Name='l2'
+++++textbox Name='l2' Value.Value='value  *'
+++++description
+++++++description Name='Email'
 ++++group
-++++++description
-++++++++description Name='l1'
-++++++textbox Name='l1'
-++++++description
-++++++++description Name='l2'
-++++++textbox Name='l2' Value.Value='value'
-++++++textbox Name='l2'
-++++++textbox Name='l2' Value.Value='value  *'
-++++++description
-++++++++description Name='Email'
-++++++group
-++++++textbox Name='Email'
-++++++textbox Name='Email' Value.Value='value'
-++++++textbox Name='l5'
-++++++textbox Name='l6' Value.Value='Value'
-++++++textbox Name='Name' Value.Value='value'
+++++textbox Name='Email'
+++++textbox Name='Email' Value.Value='value'
+++++textbox Name='l5'
+++++textbox Name='l6' Value.Value='Value'
+++++textbox Name='Name' Value.Value='value'
diff --git a/content/test/data/accessibility/html/input-text-with-selection-expected-uia-win.txt b/content/test/data/accessibility/html/input-text-with-selection-expected-uia-win.txt
index 2ca93021..6bc56d35 100644
--- a/content/test/data/accessibility/html/input-text-with-selection-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-text-with-selection-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='Selection'
+document
+++group
+++++textbox Value.Value='Selection'
diff --git a/content/test/data/accessibility/html/input-time-expected-uia-win.txt b/content/test/data/accessibility/html/input-time-expected-uia-win.txt
index a1ff3c4..b84a31e 100644
--- a/content/test/data/accessibility/html/input-time-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-time-expected-uia-win.txt
@@ -1,14 +1,13 @@
-region
-++document
+document
+++group
 ++++group
 ++++++group
 ++++++++group
-++++++++++group
-++++++++++++spinbutton Name='Hours' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=12.00 Value.Value='12'
-++++++++++++++description Name='12'
-++++++++++++description Name=':'
-++++++++++++spinbutton Name='Minutes' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=59.00 RangeValue.Minimum=0.00 RangeValue.Value=0.00 Value.Value='00'
-++++++++++++++description Name='00'
-++++++++++++description Name=' '
-++++++++++++spinbutton Name='AM/PM' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=2.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='AM'
-++++++++++++++description Name='AM'
+++++++++++spinbutton Name='Hours' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=12.00 Value.Value='12'
+++++++++++++description Name='12'
+++++++++++description Name=':'
+++++++++++spinbutton Name='Minutes' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=59.00 RangeValue.Minimum=0.00 RangeValue.Value=0.00 Value.Value='00'
+++++++++++++description Name='00'
+++++++++++description Name=' '
+++++++++++spinbutton Name='AM/PM' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=2.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='AM'
+++++++++++++description Name='AM'
diff --git a/content/test/data/accessibility/html/input-types-expected-uia-win.txt b/content/test/data/accessibility/html/input-types-expected-uia-win.txt
index fe3dff58..815dcec 100644
--- a/content/test/data/accessibility/html/input-types-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-types-expected-uia-win.txt
@@ -1,50 +1,49 @@
-region
-++document
-++++group
-++++++description
-++++++++description Name='Default: '
-++++++++textbox Name='Default:'
-++++++description
-++++++++description Name='Button: '
-++++++++button Name='Button:'
-++++++checkbox Name='Checkbox:' Toggle.ToggleState='Off'
-++++++description
-++++++++description Name='Color: '
-++++++++textbox Name='Color:' Value.Value='0% red 0% green 0% blue'
-++++++description
-++++++++description Name='Email: '
-++++++++textbox Name='Email:'
-++++++description
-++++++++description Name='File: '
-++++++++button Name='File:'
-++++++description
-++++++++description Name='Image: '
-++++++++button Name='Image:'
-++++++description
-++++++++description Name='Number: '
-++++++++spinbutton Name='Number:' RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=0.00 RangeValue.Minimum=0.00 RangeValue.Value=0.00
-++++++description
-++++++++description Name='Password: '
-++++++++textbox Name='Password:' IsPassword=true
-++++++radio Name='Radio:' SelectionItem.IsSelected=false
-++++++description
-++++++++description Name='Range: '
-++++++++slider Name='Range:' RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=100.00 RangeValue.Minimum=0.00 RangeValue.Value=50.00 Value.Value='50'
-++++++description
-++++++++description Name='Reset: '
-++++++++button Name='Reset:'
-++++++description
-++++++++description Name='Search: '
-++++++++textbox Name='Search:'
-++++++description
-++++++++description Name='Submit: '
-++++++++button Name='Submit:'
-++++++description
-++++++++description Name='Tel: '
-++++++++textbox Name='Tel:'
-++++++description
-++++++++description Name='Text: '
-++++++++textbox Name='Text:'
-++++++description
-++++++++description Name='Url: '
-++++++++textbox Name='Url:'
+document
+++group
+++++description
+++++++description Name='Default: '
+++++++textbox Name='Default:'
+++++description
+++++++description Name='Button: '
+++++++button Name='Button:'
+++++checkbox Name='Checkbox:' Toggle.ToggleState='Off'
+++++description
+++++++description Name='Color: '
+++++++textbox Name='Color:' Value.Value='0% red 0% green 0% blue'
+++++description
+++++++description Name='Email: '
+++++++textbox Name='Email:'
+++++description
+++++++description Name='File: '
+++++++button Name='File:'
+++++description
+++++++description Name='Image: '
+++++++button Name='Image:'
+++++description
+++++++description Name='Number: '
+++++++spinbutton Name='Number:' RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=0.00 RangeValue.Minimum=0.00 RangeValue.Value=0.00
+++++description
+++++++description Name='Password: '
+++++++textbox Name='Password:' IsPassword=true
+++++radio Name='Radio:' SelectionItem.IsSelected=false
+++++description
+++++++description Name='Range: '
+++++++slider Name='Range:' RangeValue.IsReadOnly=false RangeValue.LargeChange=10.00 RangeValue.SmallChange=1.00 RangeValue.Maximum=100.00 RangeValue.Minimum=0.00 RangeValue.Value=50.00 Value.Value='50'
+++++description
+++++++description Name='Reset: '
+++++++button Name='Reset:'
+++++description
+++++++description Name='Search: '
+++++++textbox Name='Search:'
+++++description
+++++++description Name='Submit: '
+++++++button Name='Submit:'
+++++description
+++++++description Name='Tel: '
+++++++textbox Name='Tel:'
+++++description
+++++++description Name='Text: '
+++++++textbox Name='Text:'
+++++description
+++++++description Name='Url: '
+++++++textbox Name='Url:'
diff --git a/content/test/data/accessibility/html/input-url-expected-uia-win.txt b/content/test/data/accessibility/html/input-url-expected-uia-win.txt
index 79d6bfc5..c455b77 100644
--- a/content/test/data/accessibility/html/input-url-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-url-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='example.com'
+document
+++group
+++++textbox Value.Value='example.com'
diff --git a/content/test/data/accessibility/html/input-week-expected-uia-win.txt b/content/test/data/accessibility/html/input-week-expected-uia-win.txt
index 4c139905..a665972 100644
--- a/content/test/data/accessibility/html/input-week-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/input-week-expected-uia-win.txt
@@ -1,13 +1,12 @@
-region
-++document
-++++group
-++++++list
+document
+++group
+++++list
+++++++group
 ++++++++group
-++++++++++group
-++++++++++++description Name='Week '
-++++++++++++spinbutton Name='Week' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=53.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
-++++++++++++++description Name='--'
-++++++++++++description Name=', '
-++++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
-++++++++++++++description Name='----'
-++++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++++++description Name='Week '
+++++++++++spinbutton Name='Week' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=53.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
+++++++++++++description Name='--'
+++++++++++description Name=', '
+++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=0.00 Value.Value='0'
+++++++++++++description Name='----'
+++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='LeafNode'
diff --git a/content/test/data/accessibility/html/ins-expected-uia-win.txt b/content/test/data/accessibility/html/ins-expected-uia-win.txt
index 0fd804a..b3a9459 100644
--- a/content/test/data/accessibility/html/ins-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/ins-expected-uia-win.txt
@@ -1,10 +1,9 @@
-region
-++document
+document
+++group
+++++description Name='My favorite browser is '
 ++++group
-++++++description Name='My favorite browser is '
-++++++group
-++++++++description Name='ABC'
-++++++description Name=' '
-++++++group
-++++++++description Name='Chrome'
-++++++description Name='!'
+++++++description Name='ABC'
+++++description Name=' '
+++++group
+++++++description Name='Chrome'
+++++description Name='!'
diff --git a/content/test/data/accessibility/html/label-expected-uia-win.txt b/content/test/data/accessibility/html/label-expected-uia-win.txt
index e1ceb71..00610c6 100644
--- a/content/test/data/accessibility/html/label-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/label-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++group
-++++++description
-++++++++description Name='Label'
+document
+++group
+++++description
+++++++description Name='Label'
diff --git a/content/test/data/accessibility/html/landmark-expected-uia-win.txt b/content/test/data/accessibility/html/landmark-expected-uia-win.txt
index 925facf..44f4c75 100644
--- a/content/test/data/accessibility/html/landmark-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/landmark-expected-uia-win.txt
@@ -1,144 +1,143 @@
-region
-++document
-++++banner
-++++++description Name='This is a header element.'
-++++complementary
-++++++description Name='This is an aside element.'
+document
+++banner
+++++description Name='This is a header element.'
+++complementary
+++++description Name='This is an aside element.'
+++group
+++++description Name='This is an address element.'
+++contentinfo
+++++description Name='This is a footer element.'
+++form
+++++description Name='This is a form element.'
+++main
+++++description Name='This is a main element.'
+++navigation
+++++description Name='This is a nav element.'
+++application
+++++description Name='This is an ARIA application landmark.'
+++banner
+++++description Name='This is an ARIA banner landmark.'
+++complementary
+++++description Name='This is an ARIA complementary landmark.'
+++contentinfo
+++++description Name='This is an ARIA contentinfo landmark.'
+++form
+++++description Name='This is an ARIA form landmark.'
+++main
+++++description Name='This is an ARIA main landmark.'
+++navigation
+++++description Name='This is an ARIA navigation landmark.'
+++search
+++++description Name='This is an ARIA search landmark.'
+++article
 ++++group
-++++++description Name='This is an address element.'
-++++contentinfo
-++++++description Name='This is a footer element.'
-++++form
-++++++description Name='This is a form element.'
-++++main
-++++++description Name='This is a main element.'
-++++navigation
-++++++description Name='This is a nav element.'
-++++application
-++++++description Name='This is an ARIA application landmark.'
-++++banner
-++++++description Name='This is an ARIA banner landmark.'
-++++complementary
-++++++description Name='This is an ARIA complementary landmark.'
-++++contentinfo
-++++++description Name='This is an ARIA contentinfo landmark.'
-++++form
-++++++description Name='This is an ARIA form landmark.'
-++++main
-++++++description Name='This is an ARIA main landmark.'
-++++navigation
-++++++description Name='This is an ARIA navigation landmark.'
-++++search
-++++++description Name='This is an ARIA search landmark.'
-++++article
-++++++group
-++++++++description Name='This should NOT banner role.'
-++++complementary
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++navigation
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++group
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++group
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++group
-++++++button Name='Details' ExpandCollapse.ExpandCollapseState='LeafNode'
-++++++++description Name='Details'
-++++group
-++++++group
-++++group
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++group
-++++++group
-++++main
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++article
-++++++group
-++++++++description Name='This should NOT banner role.'
-++++complementary
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++navigation
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++group
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++group
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++group
-++++++button Name='Details' ExpandCollapse.ExpandCollapseState='LeafNode'
-++++++++description Name='Details'
-++++group
-++++++group
-++++group
-++++++group
-++++++++description Name='This should NOT have banner role.'
+++++++description Name='This should NOT banner role.'
+++complementary
 ++++group
 ++++++description Name='This should NOT have banner role.'
-++++main
-++++++group
-++++++++description Name='This should NOT have banner role.'
-++++article
-++++++group
-++++++++description Name='This should NOT footer role.'
-++++complementary
-++++++group
-++++++++description Name='This should NOT have footer role.'
-++++navigation
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++navigation
 ++++group
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++++++description Name='This should NOT have banner role.'
+++group
 ++++group
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++++++description Name='This should NOT have banner role.'
+++group
 ++++group
-++++++button Name='Details' ExpandCollapse.ExpandCollapseState='LeafNode'
-++++++++description Name='Details'
+++++++description Name='This should NOT have banner role.'
+++group
+++++button Name='Details' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++description Name='Details'
+++group
 ++++group
-++++++group
+++group
 ++++group
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++++++description Name='This should NOT have banner role.'
+++group
+++++group
+++main
+++++group
+++++++description Name='This should NOT have banner role.'
+++article
+++++group
+++++++description Name='This should NOT banner role.'
+++complementary
+++++group
+++++++description Name='This should NOT have banner role.'
+++navigation
+++++group
+++++++description Name='This should NOT have banner role.'
+++group
+++++group
+++++++description Name='This should NOT have banner role.'
+++group
+++++group
+++++++description Name='This should NOT have banner role.'
+++group
+++++button Name='Details' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++description Name='Details'
+++group
+++++group
+++group
+++++group
+++++++description Name='This should NOT have banner role.'
+++group
+++++description Name='This should NOT have banner role.'
+++main
+++++group
+++++++description Name='This should NOT have banner role.'
+++article
+++++group
+++++++description Name='This should NOT footer role.'
+++complementary
 ++++group
 ++++++description Name='This should NOT have footer role.'
-++++main
-++++++group
-++++++++description Name='This should NOT have footer role.'
-++++article
-++++++group
-++++++++description Name='This should NOT footer role.'
-++++complementary
-++++++group
-++++++++description Name='This should NOT have footer role.'
-++++navigation
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++navigation
 ++++group
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++++++description Name='This should NOT have footer role.'
+++group
 ++++group
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++++++description Name='This should NOT have footer role.'
+++group
 ++++group
-++++++button Name='Details' ExpandCollapse.ExpandCollapseState='LeafNode'
-++++++++description Name='Details'
+++++++description Name='This should NOT have footer role.'
+++group
+++++button Name='Details' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++description Name='Details'
+++group
 ++++group
-++++++group
+++group
 ++++group
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++++++description Name='This should NOT have footer role.'
+++group
+++++description Name='This should NOT have footer role.'
+++main
 ++++group
-++++++group
-++++main
-++++++group
-++++++++description Name='This should NOT have footer role.'
+++++++description Name='This should NOT have footer role.'
+++article
+++++group
+++++++description Name='This should NOT footer role.'
+++complementary
+++++group
+++++++description Name='This should NOT have footer role.'
+++navigation
+++++group
+++++++description Name='This should NOT have footer role.'
+++group
+++++group
+++++++description Name='This should NOT have footer role.'
+++group
+++++group
+++++++description Name='This should NOT have footer role.'
+++group
+++++button Name='Details' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++description Name='Details'
+++group
+++++group
+++group
+++++group
+++++++description Name='This should NOT have footer role.'
+++group
+++++group
+++main
+++++group
+++++++description Name='This should NOT have footer role.'
diff --git a/content/test/data/accessibility/html/legend-expected-uia-win.txt b/content/test/data/accessibility/html/legend-expected-uia-win.txt
index 665aa1ff..01d366b 100644
--- a/content/test/data/accessibility/html/legend-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/legend-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++form
-++++++group Name='Browser Engines:'
-++++++++description
-++++++++textbox
-++++++++textbox
+document
+++form
+++++group Name='Browser Engines:'
+++++++description
+++++++textbox
+++++++textbox
diff --git a/content/test/data/accessibility/html/li-expected-uia-win.txt b/content/test/data/accessibility/html/li-expected-uia-win.txt
index 8b08296..c538fb9 100644
--- a/content/test/data/accessibility/html/li-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/li-expected-uia-win.txt
@@ -1,9 +1,8 @@
-region
-++document
-++++list
-++++++listitem
-++++++++description Name='• '
-++++++listitem
-++++++++description Name='• '
-++++++listitem
-++++++++description Name='• '
+document
+++list
+++++listitem
+++++++description Name='• '
+++++listitem
+++++++description Name='• '
+++++listitem
+++++++description Name='• '
diff --git a/content/test/data/accessibility/html/link-inside-heading-expected-uia-win.txt b/content/test/data/accessibility/html/link-inside-heading-expected-uia-win.txt
index 430ae266..0702781 100644
--- a/content/test/data/accessibility/html/link-inside-heading-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/link-inside-heading-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++heading Name='Link In Heading'
-++++++link Name='Link In Heading'
+document
+++heading Name='Link In Heading'
+++++link Name='Link In Heading'
diff --git a/content/test/data/accessibility/html/list-expected-uia-win.txt b/content/test/data/accessibility/html/list-expected-uia-win.txt
index ccc8eb2..10ae0262 100644
--- a/content/test/data/accessibility/html/list-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/list-expected-uia-win.txt
@@ -1,15 +1,14 @@
-region
-++document
-++++list
-++++++listitem
-++++++++description Name='• '
-++++++listitem
-++++++++description Name='• '
-++++++listitem
-++++++++description Name='• '
-++++list
-++++++listitem
-++++++description Name=' '
-++++++listitem
-++++++description Name=' '
-++++++listitem
+document
+++list
+++++listitem
+++++++description Name='• '
+++++listitem
+++++++description Name='• '
+++++listitem
+++++++description Name='• '
+++list
+++++listitem
+++++description Name=' '
+++++listitem
+++++description Name=' '
+++++listitem
diff --git a/content/test/data/accessibility/html/list-markers-expected-uia-win.txt b/content/test/data/accessibility/html/list-markers-expected-uia-win.txt
index 8b08296..c538fb9 100644
--- a/content/test/data/accessibility/html/list-markers-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/list-markers-expected-uia-win.txt
@@ -1,9 +1,8 @@
-region
-++document
-++++list
-++++++listitem
-++++++++description Name='• '
-++++++listitem
-++++++++description Name='• '
-++++++listitem
-++++++++description Name='• '
+document
+++list
+++++listitem
+++++++description Name='• '
+++++listitem
+++++++description Name='• '
+++++listitem
+++++++description Name='• '
diff --git a/content/test/data/accessibility/html/main-expected-uia-win.txt b/content/test/data/accessibility/html/main-expected-uia-win.txt
index 606fc69..6ebd73b 100644
--- a/content/test/data/accessibility/html/main-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/main-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++main
-++++++description Name='This is main element.'
-++++main
-++++++description Name='This is an ARIA role main.'
+document
+++main
+++++description Name='This is main element.'
+++main
+++++description Name='This is an ARIA role main.'
diff --git a/content/test/data/accessibility/html/mark-expected-uia-win.txt b/content/test/data/accessibility/html/mark-expected-uia-win.txt
index 5a5b6042..ae2b0eb4 100644
--- a/content/test/data/accessibility/html/mark-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/mark-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++group
-++++++description Name='This test is to check '
-++++++description
-++++++++description Name='mark tag'
-++++++description Name='.'
+document
+++group
+++++description Name='This test is to check '
+++++description
+++++++description Name='mark tag'
+++++description Name='.'
diff --git a/content/test/data/accessibility/html/math-expected-uia-win.txt b/content/test/data/accessibility/html/math-expected-uia-win.txt
index 750bd9b..93d1ee9 100644
--- a/content/test/data/accessibility/html/math-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/math-expected-uia-win.txt
@@ -1,11 +1,10 @@
-region
-++document
+document
+++group
 ++++group
-++++++group
-++++++++description Name='a'
-++++++++description Name='2'
-++++++++description Name=' '
-++++++++description Name='+'
-++++++++description Name=' '
-++++++++description Name='b'
-++++++++description Name='2'
+++++++description Name='a'
+++++++description Name='2'
+++++++description Name=' '
+++++++description Name='+'
+++++++description Name=' '
+++++++description Name='b'
+++++++description Name='2'
diff --git a/content/test/data/accessibility/html/meter-expected-uia-win.txt b/content/test/data/accessibility/html/meter-expected-uia-win.txt
index fc550be6..bc073bb8 100644
--- a/content/test/data/accessibility/html/meter-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/meter-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++progressbar RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=10.00 RangeValue.Minimum=1.00 RangeValue.Value=2.00 Value.Value='2'
+document
+++group
+++++progressbar RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=10.00 RangeValue.Minimum=1.00 RangeValue.Value=2.00 Value.Value='2'
diff --git a/content/test/data/accessibility/html/modal-dialog-closed-expected-uia-win.txt b/content/test/data/accessibility/html/modal-dialog-closed-expected-uia-win.txt
index b11c71e..084444e 100644
--- a/content/test/data/accessibility/html/modal-dialog-closed-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-closed-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
-++++description Name='Test that elements respawn in the accessibility tree after a modal dialog closes.'
-++++group
-++++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='This should be in the tree.'
-++++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++++listitem Name='This should be in the tree.' SelectionItem.IsSelected=true
-++++textbox Value.Value='0% red 0% green 0% blue'
+document
+++description Name='Test that elements respawn in the accessibility tree after a modal dialog closes.'
+++group
+++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='This should be in the tree.'
+++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++++listitem Name='This should be in the tree.' SelectionItem.IsSelected=true
+++textbox Value.Value='0% red 0% green 0% blue'
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
index 286148e..380e5b2 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++dialog Window.IsModal=true
-++++++description Name='The dialog subtree should be the only text content in the accessibility tree. '
-++++++link Name='Link inside the dialog.'
+document
+++group
+++dialog Window.IsModal=true
+++++description Name='The dialog subtree should be the only text content in the accessibility tree. '
+++++link Name='Link inside the dialog.'
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
index a5a6537..7275201 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++dialog Window.IsModal=true
-++++++description Name='This is the now active dialog. Of course it should be in the tree. '
-++++++button Name='This is in the active dialog and should be in the tree.'
+document
+++group
+++dialog Window.IsModal=true
+++++description Name='This is the now active dialog. Of course it should be in the tree. '
+++++button Name='This is in the active dialog and should be in the tree.'
diff --git a/content/test/data/accessibility/html/navigation-expected-uia-win.txt b/content/test/data/accessibility/html/navigation-expected-uia-win.txt
index b0254e3..6a75839 100644
--- a/content/test/data/accessibility/html/navigation-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/navigation-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++navigation
-++++++link Name='Don't click on me'
+document
+++navigation
+++++link Name='Don't click on me'
diff --git a/content/test/data/accessibility/html/object-expected-uia-win.txt b/content/test/data/accessibility/html/object-expected-uia-win.txt
index 17f58ff..e74482a 100644
--- a/content/test/data/accessibility/html/object-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/object-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++document
+document
+++group
+++++document
diff --git a/content/test/data/accessibility/html/offscreen-iframe-expected-uia-win.txt b/content/test/data/accessibility/html/offscreen-iframe-expected-uia-win.txt
index 352bbed6..3e01b27f 100644
--- a/content/test/data/accessibility/html/offscreen-iframe-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/offscreen-iframe-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++group
+document
+++group
+++++document
 ++++++document
-++++++++document
-++++++++++group Name='iframe_onscreen'
-++++++++++group Name='iframe_offscreen'
+++++++++group Name='iframe_onscreen'
+++++++++group Name='iframe_offscreen'
diff --git a/content/test/data/accessibility/html/offscreen-select-expected-uia-win.txt b/content/test/data/accessibility/html/offscreen-select-expected-uia-win.txt
index 9d67e8c..c7fc3e5 100644
--- a/content/test/data/accessibility/html/offscreen-select-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/offscreen-select-expected-uia-win.txt
@@ -1,12 +1,11 @@
-region
-++document
-++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Onscreen 1'
-++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++listitem Name='Onscreen 1' SelectionItem.IsSelected=true
-++++++++listitem Name='Onscreen 2' SelectionItem.IsSelected=false
-++++++++listitem Name='Onscreen 3' SelectionItem.IsSelected=false
-++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Offscreen 1'
-++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++listitem Name='Offscreen 1' SelectionItem.IsSelected=true
-++++++++listitem Name='Offscreen 2' SelectionItem.IsSelected=false
-++++++++listitem Name='Offscreen 3' SelectionItem.IsSelected=false
+document
+++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Onscreen 1'
+++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++listitem Name='Onscreen 1' SelectionItem.IsSelected=true
+++++++listitem Name='Onscreen 2' SelectionItem.IsSelected=false
+++++++listitem Name='Onscreen 3' SelectionItem.IsSelected=false
+++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Offscreen 1'
+++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++listitem Name='Offscreen 1' SelectionItem.IsSelected=true
+++++++listitem Name='Offscreen 2' SelectionItem.IsSelected=false
+++++++listitem Name='Offscreen 3' SelectionItem.IsSelected=false
diff --git a/content/test/data/accessibility/html/ol-expected-uia-win.txt b/content/test/data/accessibility/html/ol-expected-uia-win.txt
index d2724c3..75944fd9 100644
--- a/content/test/data/accessibility/html/ol-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/ol-expected-uia-win.txt
@@ -1,16 +1,15 @@
-region
-++document
-++++list
-++++++listitem
-++++++++description Name='1. '
-++++++listitem
-++++++++description Name='2. '
-++++++listitem
-++++++++description Name='3. '
-++++list
-++++++listitem
-++++++++description Name='10. '
-++++++listitem
-++++++++description Name='11. '
-++++++listitem
-++++++++description Name='12. '
+document
+++list
+++++listitem
+++++++description Name='1. '
+++++listitem
+++++++description Name='2. '
+++++listitem
+++++++description Name='3. '
+++list
+++++listitem
+++++++description Name='10. '
+++++listitem
+++++++description Name='11. '
+++++listitem
+++++++description Name='12. '
diff --git a/content/test/data/accessibility/html/optgroup-expected-uia-win.txt b/content/test/data/accessibility/html/optgroup-expected-uia-win.txt
index 3c31ba7..e7f7009 100644
--- a/content/test/data/accessibility/html/optgroup-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/optgroup-expected-uia-win.txt
@@ -1,14 +1,13 @@
-region
-++document
-++++group
-++++++listbox Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Value.IsReadOnly=false
-++++++++group Name='Enabled'
-++++++++option Name='One' SelectionItem.IsSelected=false
-++++++++option Name='Two' SelectionItem.IsSelected=false
-++++++++option Name='Three' SelectionItem.IsSelected=false
-++++++++option Name='Four' SelectionItem.IsSelected=false
-++++++++group Name='Disabled'
-++++++++option Name='One' IsEnabled=false
-++++++++option Name='Two' IsEnabled=false
-++++++++option Name='Three' IsEnabled=false
-++++++++option Name='Four' IsEnabled=false
+document
+++group
+++++listbox Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Value.IsReadOnly=false
+++++++group Name='Enabled'
+++++++option Name='One' SelectionItem.IsSelected=false
+++++++option Name='Two' SelectionItem.IsSelected=false
+++++++option Name='Three' SelectionItem.IsSelected=false
+++++++option Name='Four' SelectionItem.IsSelected=false
+++++++group Name='Disabled'
+++++++option Name='One' IsEnabled=false
+++++++option Name='Two' IsEnabled=false
+++++++option Name='Three' IsEnabled=false
+++++++option Name='Four' IsEnabled=false
diff --git a/content/test/data/accessibility/html/p-expected-uia-win.txt b/content/test/data/accessibility/html/p-expected-uia-win.txt
index a446be51..ee67a64 100644
--- a/content/test/data/accessibility/html/p-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/p-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++description Name='Before'
-++++group
-++++++description Name='Paragraph'
-++++description Name='After'
+document
+++description Name='Before'
+++group
+++++description Name='Paragraph'
+++description Name='After'
diff --git a/content/test/data/accessibility/html/pre-expected-uia-win.txt b/content/test/data/accessibility/html/pre-expected-uia-win.txt
index f364d75..85bd64d 100644
--- a/content/test/data/accessibility/html/pre-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/pre-expected-uia-win.txt
@@ -1,10 +1,9 @@
-region
-++document
-++++region
-++++++description Name='This test is to check   pre<newline>formatting.'
-++++group
-++++++description Name='This test is to check   pre<newline>formatting'
-++++group
-++++++description Name='This test is to check   pre<newline>formatting.'
-++++group
-++++++description Name='This test is to check pre formatting.'
+document
+++region
+++++description Name='This test is to check   pre<newline>formatting.'
+++group
+++++description Name='This test is to check   pre<newline>formatting'
+++group
+++++description Name='This test is to check   pre<newline>formatting.'
+++group
+++++description Name='This test is to check pre formatting.'
diff --git a/content/test/data/accessibility/html/progress-expected-uia-win.txt b/content/test/data/accessibility/html/progress-expected-uia-win.txt
index 4cac35c7..02e9645 100644
--- a/content/test/data/accessibility/html/progress-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/progress-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++group
-++++++progressbar RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=100.00 RangeValue.Minimum=0.00 RangeValue.Value=22.00 Value.Value='22'
-++++++progressbar RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=1.00 RangeValue.Minimum=0.00 RangeValue.Value=0.00
+document
+++group
+++++progressbar RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=100.00 RangeValue.Minimum=0.00 RangeValue.Value=22.00 Value.Value='22'
+++++progressbar RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=1.00 RangeValue.Minimum=0.00 RangeValue.Value=0.00
diff --git a/content/test/data/accessibility/html/q-expected-uia-win.txt b/content/test/data/accessibility/html/q-expected-uia-win.txt
index afbbeee..c85b7bb 100644
--- a/content/test/data/accessibility/html/q-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/q-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
-++++group
-++++++description Name='This is '
-++++++description Name='"'
-++++++description Name='Chromium Blink'
-++++++description Name='"'
-++++++description Name=' based browser.'
+document
+++group
+++++description Name='This is '
+++++description Name='"'
+++++description Name='Chromium Blink'
+++++description Name='"'
+++++description Name=' based browser.'
diff --git a/content/test/data/accessibility/html/ruby-expected-uia-win.txt b/content/test/data/accessibility/html/ruby-expected-uia-win.txt
index 9037765..2ad5cab 100644
--- a/content/test/data/accessibility/html/ruby-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/ruby-expected-uia-win.txt
@@ -1,7 +1,6 @@
-region
-++document
-++++group
-++++++region
-++++++++description
-++++++++++description Name='ruby text'
-++++++++description Name='ruby base'
+document
+++group
+++++region
+++++++description
+++++++++description Name='ruby text'
+++++++description Name='ruby base'
diff --git a/content/test/data/accessibility/html/scrollable-expected-uia-win.txt b/content/test/data/accessibility/html/scrollable-expected-uia-win.txt
index ac15890..c7cd62b 100644
--- a/content/test/data/accessibility/html/scrollable-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/scrollable-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region Scroll.HorizontalScrollPercent=0.00 Scroll.HorizontalViewSize=100.00 Scroll.HorizontallyScrollable=false Scroll.VerticalScrollPercent=0.00 Scroll.VerticalViewSize=100.00 Scroll.VerticallyScrollable=false
-++document Scroll.HorizontalScrollPercent=-1.00 Scroll.HorizontalViewSize=100.00 Scroll.HorizontallyScrollable=false Scroll.VerticalScrollPercent=-1.00 Scroll.VerticalViewSize=100.00 Scroll.VerticallyScrollable=false
-++++group Name='not'
-++++++description Name='not scrollable'
-++++group Name='x' Scroll.HorizontalScrollPercent=0.00 Scroll.HorizontalViewSize=50.00 Scroll.HorizontallyScrollable=true Scroll.VerticalScrollPercent=-1.00 Scroll.VerticalViewSize=100.00 Scroll.VerticallyScrollable=false
-++++group Name='y' Scroll.HorizontalScrollPercent=-1.00 Scroll.HorizontalViewSize=100.00 Scroll.HorizontallyScrollable=false Scroll.VerticalScrollPercent=0.00 Scroll.VerticalViewSize=50.00 Scroll.VerticallyScrollable=true
+document Scroll.HorizontalScrollPercent=-1.00 Scroll.HorizontalViewSize=100.00 Scroll.HorizontallyScrollable=false Scroll.VerticalScrollPercent=-1.00 Scroll.VerticalViewSize=100.00 Scroll.VerticallyScrollable=false
+++group Name='not'
+++++description Name='not scrollable'
+++group Name='x' Scroll.HorizontalScrollPercent=0.00 Scroll.HorizontalViewSize=50.00 Scroll.HorizontallyScrollable=true Scroll.VerticalScrollPercent=-1.00 Scroll.VerticalViewSize=100.00 Scroll.VerticallyScrollable=false
+++group Name='y' Scroll.HorizontalScrollPercent=-1.00 Scroll.HorizontalViewSize=100.00 Scroll.HorizontallyScrollable=false Scroll.VerticalScrollPercent=0.00 Scroll.VerticalViewSize=50.00 Scroll.VerticallyScrollable=true
diff --git a/content/test/data/accessibility/html/scrollable-textarea-expected-uia-win.txt b/content/test/data/accessibility/html/scrollable-textarea-expected-uia-win.txt
index 3863aa83..dd1ed8a 100644
--- a/content/test/data/accessibility/html/scrollable-textarea-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/scrollable-textarea-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++group
-++++++textbox Value.Value='little'
-++++++textbox Value.Value='lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text'
+document
+++group
+++++textbox Value.Value='little'
+++++textbox Value.Value='lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text'
diff --git a/content/test/data/accessibility/html/section-expected-uia-win.txt b/content/test/data/accessibility/html/section-expected-uia-win.txt
index 9b6898b..7ca7de3 100644
--- a/content/test/data/accessibility/html/section-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/section-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++description Name='This is a section element.'
+document
+++group
+++++description Name='This is a section element.'
diff --git a/content/test/data/accessibility/html/select-expected-uia-win.txt b/content/test/data/accessibility/html/select-expected-uia-win.txt
index 3c940aa..7bfe644 100644
--- a/content/test/data/accessibility/html/select-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/select-expected-uia-win.txt
@@ -1,26 +1,25 @@
-region
-++document
-++++group
-++++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Placeholder option'
-++++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++++listitem Name='Placeholder option' SelectionItem.IsSelected=true
-++++++++++listitem Name='Option 1' SelectionItem.IsSelected=false
-++++++++++listitem Name='Option 2' SelectionItem.IsSelected=false
-++++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Option 2'
-++++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++++listitem Name='Option 1' SelectionItem.IsSelected=false
-++++++++++listitem Name='Option 2' SelectionItem.IsSelected=true
-++++++++++listitem Name='Option 3' SelectionItem.IsSelected=false
-++++++combobox IsRequiredForForm=true ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Option 1'
-++++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++++listitem Name='Option 1' SelectionItem.IsSelected=true
-++++++++++listitem Name='Option 2' SelectionItem.IsSelected=false
-++++++++++listitem Name='Option 3' SelectionItem.IsSelected=false
-++++++listbox Selection.CanSelectMultiple=true Selection.IsSelectionRequired=false
-++++++++option Name='Option 1' SelectionItem.IsSelected=false
-++++++++option Name='Option 2' SelectionItem.IsSelected=false
-++++++++option Name='Option 3' SelectionItem.IsSelected=false
-++++++listbox Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++option Name='Option 1' SelectionItem.IsSelected=false
-++++++++option Name='Option 2' SelectionItem.IsSelected=false
-++++++++option Name='Option 3' SelectionItem.IsSelected=false
+document
+++group
+++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Placeholder option'
+++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++++listitem Name='Placeholder option' SelectionItem.IsSelected=true
+++++++++listitem Name='Option 1' SelectionItem.IsSelected=false
+++++++++listitem Name='Option 2' SelectionItem.IsSelected=false
+++++combobox ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Option 2'
+++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++++listitem Name='Option 1' SelectionItem.IsSelected=false
+++++++++listitem Name='Option 2' SelectionItem.IsSelected=true
+++++++++listitem Name='Option 3' SelectionItem.IsSelected=false
+++++combobox IsRequiredForForm=true ExpandCollapse.ExpandCollapseState='Collapsed' Value.Value='Option 1'
+++++++list Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++++listitem Name='Option 1' SelectionItem.IsSelected=true
+++++++++listitem Name='Option 2' SelectionItem.IsSelected=false
+++++++++listitem Name='Option 3' SelectionItem.IsSelected=false
+++++listbox Selection.CanSelectMultiple=true Selection.IsSelectionRequired=false
+++++++option Name='Option 1' SelectionItem.IsSelected=false
+++++++option Name='Option 2' SelectionItem.IsSelected=false
+++++++option Name='Option 3' SelectionItem.IsSelected=false
+++++listbox Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++option Name='Option 1' SelectionItem.IsSelected=false
+++++++option Name='Option 2' SelectionItem.IsSelected=false
+++++++option Name='Option 3' SelectionItem.IsSelected=false
diff --git a/content/test/data/accessibility/html/selection-container-expected-uia-win.txt b/content/test/data/accessibility/html/selection-container-expected-uia-win.txt
index b8b36ee..21e34873 100644
--- a/content/test/data/accessibility/html/selection-container-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/selection-container-expected-uia-win.txt
@@ -1,9 +1,8 @@
-region
-++document
-++++group
-++++++listbox Name='selection_list' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
-++++++++group Name='Enabled'
-++++++++option Name='One' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='selection_list'
-++++++++option Name='Two' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='selection_list'
-++++++++option Name='Three' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='selection_list'
-++++++++option Name='Four' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='selection_list'
+document
+++group
+++++listbox Name='selection_list' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++group Name='Enabled'
+++++++option Name='One' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='selection_list'
+++++++option Name='Two' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='selection_list'
+++++++option Name='Three' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='selection_list'
+++++++option Name='Four' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='selection_list'
diff --git a/content/test/data/accessibility/html/span-expected-uia-win.txt b/content/test/data/accessibility/html/span-expected-uia-win.txt
index 38c6923..07125669 100644
--- a/content/test/data/accessibility/html/span-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/span-expected-uia-win.txt
@@ -1,65 +1,64 @@
-region
-++document
-++++group
-++++++description Name='This'
-++++++description Name=' '
-++++++description Name='paragraph has '
-++++++description Name='text'
-++++++description Name=' '
-++++++description Name='in'
-++++++description Name=' '
-++++++description Name='spans'
-++++++description Name='.'
-++++group
-++++++description Name='E1. Eat'
-++++++description Name=' '
-++++++link Name='space'
-++++group
-++++++description Name='E2. Eat'
-++++++description Name=' '
-++++++link Name='space'
-++++group
-++++++description Name='E3. Eat'
-++++++description Name=' '
-++++++link Name='space'
-++++group
-++++++link Name='E4. Eat'
-++++++description Name=' '
-++++++description Name='space'
-++++group
-++++++link Name='E5. Eat'
-++++++description Name=' '
-++++++description Name='space'
-++++group
-++++++link Name='E6. Eat'
-++++++description Name=' '
-++++++description Name='space'
-++++group
-++++++description Name='K1. Keep'
-++++++description Name=' '
-++++++description Name='space'
-++++group
-++++++description Name='K2. Keep'
-++++++description Name=' '
-++++++description Name='space'
-++++group
-++++++description Name='K3. Keep'
-++++++description Name=' '
-++++++description Name='space'
-++++group
-++++++description Name='K4. Keep '
-++++++description Name='space'
-++++group
-++++++description Name='K5. Keep'
-++++++description Name=' '
-++++++description Name='space'
-++++group
-++++++description Name='K6. Keep '
-++++++description Name='space'
-++++group
-++++++description Name='K7. Keep'
-++++++description Name=' space'
-++++group
-++++++description Name='K8. Keep'
-++++++description Name=' '
-++++++description Name='space'
+document
+++group
+++++description Name='This'
+++++description Name=' '
+++++description Name='paragraph has '
+++++description Name='text'
+++++description Name=' '
+++++description Name='in'
+++++description Name=' '
+++++description Name='spans'
+++++description Name='.'
+++group
+++++description Name='E1. Eat'
+++++description Name=' '
+++++link Name='space'
+++group
+++++description Name='E2. Eat'
+++++description Name=' '
+++++link Name='space'
+++group
+++++description Name='E3. Eat'
+++++description Name=' '
+++++link Name='space'
+++group
+++++link Name='E4. Eat'
+++++description Name=' '
+++++description Name='space'
+++group
+++++link Name='E5. Eat'
+++++description Name=' '
+++++description Name='space'
+++group
+++++link Name='E6. Eat'
+++++description Name=' '
+++++description Name='space'
+++group
+++++description Name='K1. Keep'
+++++description Name=' '
+++++description Name='space'
+++group
+++++description Name='K2. Keep'
+++++description Name=' '
+++++description Name='space'
+++group
+++++description Name='K3. Keep'
+++++description Name=' '
+++++description Name='space'
+++group
+++++description Name='K4. Keep '
+++++description Name='space'
+++group
+++++description Name='K5. Keep'
+++++description Name=' '
+++++description Name='space'
+++group
+++++description Name='K6. Keep '
+++++description Name='space'
+++group
+++++description Name='K7. Keep'
+++++description Name=' space'
+++group
+++++description Name='K8. Keep'
+++++description Name=' '
+++++description Name='space'
diff --git a/content/test/data/accessibility/html/sub-expected-uia-win.txt b/content/test/data/accessibility/html/sub-expected-uia-win.txt
index 50976776..9807886 100644
--- a/content/test/data/accessibility/html/sub-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/sub-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++description Name='This text contains '
-++++++description Name='subscript'
-++++++description Name=' text.'
+document
+++group
+++++description Name='This text contains '
+++++description Name='subscript'
+++++description Name=' text.'
diff --git a/content/test/data/accessibility/html/summary-expected-uia-win.txt b/content/test/data/accessibility/html/summary-expected-uia-win.txt
index b4b1305..05860971 100644
--- a/content/test/data/accessibility/html/summary-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/summary-expected-uia-win.txt
@@ -1,5 +1,4 @@
-region
-++document
-++++group
-++++++button Name='details tag' ExpandCollapse.ExpandCollapseState='Collapsed'
-++++++++description Name='details tag'
+document
+++group
+++++button Name='details tag' ExpandCollapse.ExpandCollapseState='Collapsed'
+++++++description Name='details tag'
diff --git a/content/test/data/accessibility/html/sup-expected-uia-win.txt b/content/test/data/accessibility/html/sup-expected-uia-win.txt
index 9e9c00bf..fdab9b2 100644
--- a/content/test/data/accessibility/html/sup-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/sup-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++description Name='This text contains'
-++++++description Name='superscript'
-++++++description Name='text.'
+document
+++group
+++++description Name='This text contains'
+++++description Name='superscript'
+++++description Name='text.'
diff --git a/content/test/data/accessibility/html/svg-expected-uia-win.txt b/content/test/data/accessibility/html/svg-expected-uia-win.txt
index a7838d9..26dc12b 100644
--- a/content/test/data/accessibility/html/svg-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/svg-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++img Name='svg' HelpText='SVG Title Tag'
-++++++++group
-++++++++++description Name='Test'
+document
+++group
+++++img Name='svg' HelpText='SVG Title Tag'
+++++++group
+++++++++description Name='Test'
diff --git a/content/test/data/accessibility/html/table-focusable-sections-expected-uia-win.txt b/content/test/data/accessibility/html/table-focusable-sections-expected-uia-win.txt
index d7d6f24b..8d9e6273 100644
--- a/content/test/data/accessibility/html/table-focusable-sections-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/table-focusable-sections-expected-uia-win.txt
@@ -1,18 +1,17 @@
-region
-++document Name='Table example - focusable thead, tbody, tfoot'
-++++grid Grid.ColumnCount=2 Grid.RowCount=4 Table.RowOrColumnMajor='RowMajor'
-++++++group
-++++++++row
-++++++++++columnheader Name='Sum' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++columnheader Name='Subtraction' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++group
-++++++++row
-++++++++++gridcell Name='10' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
-++++++++++gridcell Name='7' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
-++++++++row
-++++++++++gridcell Name='2' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=2 GridItem.RowSpan=1
-++++++++++gridcell Name='4' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=2 GridItem.RowSpan=1
-++++++group
-++++++++row
-++++++++++gridcell Name='12' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=3 GridItem.RowSpan=1
-++++++++++gridcell Name='3' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=3 GridItem.RowSpan=1
+document Name='Table example - focusable thead, tbody, tfoot'
+++grid Grid.ColumnCount=2 Grid.RowCount=4 Table.RowOrColumnMajor='RowMajor'
+++++group
+++++++row
+++++++++columnheader Name='Sum' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++columnheader Name='Subtraction' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++group
+++++++row
+++++++++gridcell Name='10' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
+++++++++gridcell Name='7' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
+++++++row
+++++++++gridcell Name='2' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=2 GridItem.RowSpan=1
+++++++++gridcell Name='4' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=2 GridItem.RowSpan=1
+++++group
+++++++row
+++++++++gridcell Name='12' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=3 GridItem.RowSpan=1
+++++++++gridcell Name='3' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=3 GridItem.RowSpan=1
diff --git a/content/test/data/accessibility/html/table-layout-expected-uia-win.txt b/content/test/data/accessibility/html/table-layout-expected-uia-win.txt
index 1f755d7..8e1396f 100644
--- a/content/test/data/accessibility/html/table-layout-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/table-layout-expected-uia-win.txt
@@ -1,24 +1,23 @@
-region
-++document Name='Table example #2'
-++++grid Grid.ColumnCount=0 Grid.RowCount=0 Table.RowOrColumnMajor='RowMajor'
-++++++row
-++++++++gridcell Name='1' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='1'
-++++++++gridcell Name='2' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='2'
-++++++++gridcell Name='3' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='3'
-++++++row
-++++++++gridcell Name='4' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='4'
-++++++++gridcell Name='5' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='5'
-++++++++gridcell Name='6' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='6'
-++++++row
-++++++++gridcell Name='7' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='7'
-++++++++gridcell Name='8' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='8'
-++++++++gridcell Name='9' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++++description Name='9'
+document Name='Table example #2'
+++grid Grid.ColumnCount=0 Grid.RowCount=0 Table.RowOrColumnMajor='RowMajor'
+++++row
+++++++gridcell Name='1' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='1'
+++++++gridcell Name='2' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='2'
+++++++gridcell Name='3' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='3'
+++++row
+++++++gridcell Name='4' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='4'
+++++++gridcell Name='5' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='5'
+++++++gridcell Name='6' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='6'
+++++row
+++++++gridcell Name='7' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='7'
+++++++gridcell Name='8' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='8'
+++++++gridcell Name='9' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++description Name='9'
diff --git a/content/test/data/accessibility/html/table-presentation-expected-uia-win.txt b/content/test/data/accessibility/html/table-presentation-expected-uia-win.txt
index a57c280..ea02498 100644
--- a/content/test/data/accessibility/html/table-presentation-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/table-presentation-expected-uia-win.txt
@@ -1,10 +1,9 @@
-region
-++document Name='Table with role=presentation'
-++++group
-++++++description Name='1'
-++++group
-++++++description Name='2'
-++++group
-++++++description Name='4'
-++++group
-++++++description Name='5'
+document Name='Table with role=presentation'
+++group
+++++description Name='1'
+++group
+++++description Name='2'
+++group
+++++description Name='4'
+++group
+++++description Name='5'
diff --git a/content/test/data/accessibility/html/table-th-colheader-expected-uia-win.txt b/content/test/data/accessibility/html/table-th-colheader-expected-uia-win.txt
index d26ea50..99c75b3 100644
--- a/content/test/data/accessibility/html/table-th-colheader-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/table-th-colheader-expected-uia-win.txt
@@ -1,9 +1,8 @@
-region
-++document
-++++grid Grid.ColumnCount=2 Grid.RowCount=2 Table.RowOrColumnMajor='RowMajor'
-++++++row
-++++++++columnheader Name='Firstname' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++columnheader Name='Lastname' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++row
-++++++++gridcell Name='Jill' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
-++++++++gridcell Name='Smith' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
+document
+++grid Grid.ColumnCount=2 Grid.RowCount=2 Table.RowOrColumnMajor='RowMajor'
+++++row
+++++++columnheader Name='Firstname' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++columnheader Name='Lastname' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++row
+++++++gridcell Name='Jill' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
+++++++gridcell Name='Smith' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
diff --git a/content/test/data/accessibility/html/table-th-rowheader-expected-uia-win.txt b/content/test/data/accessibility/html/table-th-rowheader-expected-uia-win.txt
index e3252e8..5e55b42f 100644
--- a/content/test/data/accessibility/html/table-th-rowheader-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/table-th-rowheader-expected-uia-win.txt
@@ -1,9 +1,8 @@
-region
-++document Name='Table example - th rowheader'
-++++grid Grid.ColumnCount=2 Grid.RowCount=2 Table.RowOrColumnMajor='RowMajor'
-++++++row
-++++++++rowheader Name='Firstname' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++++gridcell Name='Jill' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
-++++++row
-++++++++rowheader Name='Lastname' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
-++++++++gridcell Name='Smith' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
+document Name='Table example - th rowheader'
+++grid Grid.ColumnCount=2 Grid.RowCount=2 Table.RowOrColumnMajor='RowMajor'
+++++row
+++++++rowheader Name='Firstname' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++gridcell Name='Jill' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++row
+++++++rowheader Name='Lastname' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
+++++++gridcell Name='Smith' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=1 GridItem.RowSpan=1
diff --git a/content/test/data/accessibility/html/textarea-expected-uia-win.txt b/content/test/data/accessibility/html/textarea-expected-uia-win.txt
index b1c1fb316..a3c95cf 100644
--- a/content/test/data/accessibility/html/textarea-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/textarea-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='The <newline>textarea tag  defines a multi-line text input control.<newline>'
+document
+++group
+++++textbox Value.Value='The <newline>textarea tag  defines a multi-line text input control.<newline>'
diff --git a/content/test/data/accessibility/html/textarea-read-only-expected-uia-win.txt b/content/test/data/accessibility/html/textarea-read-only-expected-uia-win.txt
index 2f4e401..d968b8fa 100644
--- a/content/test/data/accessibility/html/textarea-read-only-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/textarea-read-only-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='The textarea tag defines a multi-line text input control.<newline>'
+document
+++group
+++++textbox Value.Value='The textarea tag defines a multi-line text input control.<newline>'
diff --git a/content/test/data/accessibility/html/textarea-with-selection-expected-uia-win.txt b/content/test/data/accessibility/html/textarea-with-selection-expected-uia-win.txt
index 2f4e401..d968b8fa 100644
--- a/content/test/data/accessibility/html/textarea-with-selection-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/textarea-with-selection-expected-uia-win.txt
@@ -1,4 +1,3 @@
-region
-++document
-++++group
-++++++textbox Value.Value='The textarea tag defines a multi-line text input control.<newline>'
+document
+++group
+++++textbox Value.Value='The textarea tag defines a multi-line text input control.<newline>'
diff --git a/content/test/data/accessibility/html/time-expected-uia-win.txt b/content/test/data/accessibility/html/time-expected-uia-win.txt
index a143c12c..a71df26 100644
--- a/content/test/data/accessibility/html/time-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/time-expected-uia-win.txt
@@ -1,8 +1,7 @@
-region
-++document
-++++group
-++++++description
-++++++++description Name='10:00'
-++++++description Name=' '
-++++++description
-++++++++description Name='Valentines day'
+document
+++group
+++++description
+++++++description Name='10:00'
+++++description Name=' '
+++++description
+++++++description Name='Valentines day'
diff --git a/content/test/data/accessibility/html/title-expected-uia-win.txt b/content/test/data/accessibility/html/title-expected-uia-win.txt
index 27d8d5c..520e0c2 100644
--- a/content/test/data/accessibility/html/title-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/title-expected-uia-win.txt
@@ -1,2 +1 @@
-region
-++document Name='Title of the document'
+document Name='Title of the document'
diff --git a/content/test/data/accessibility/html/ul-expected-uia-win.txt b/content/test/data/accessibility/html/ul-expected-uia-win.txt
index 8b08296..c538fb9 100644
--- a/content/test/data/accessibility/html/ul-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/ul-expected-uia-win.txt
@@ -1,9 +1,8 @@
-region
-++document
-++++list
-++++++listitem
-++++++++description Name='• '
-++++++listitem
-++++++++description Name='• '
-++++++listitem
-++++++++description Name='• '
+document
+++list
+++++listitem
+++++++description Name='• '
+++++listitem
+++++++description Name='• '
+++++listitem
+++++++description Name='• '
diff --git a/content/test/data/accessibility/html/wbr-expected-uia-win.txt b/content/test/data/accessibility/html/wbr-expected-uia-win.txt
index 81e4a900..712587b 100644
--- a/content/test/data/accessibility/html/wbr-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/wbr-expected-uia-win.txt
@@ -1,6 +1,5 @@
-region
-++document
-++++group
-++++++description Name='Supercali'
-++++++description Name='fragilistic'
-++++++description Name='expialidocious'
+document
+++group
+++++description Name='Supercali'
+++++description Name='fragilistic'
+++++description Name='expialidocious'
diff --git a/content/test/data/midi/request_midi_access.html b/content/test/data/midi/request_midi_access.html
new file mode 100644
index 0000000..8752f4d
--- /dev/null
+++ b/content/test/data/midi/request_midi_access.html
@@ -0,0 +1,14 @@
+<html>
+<script>
+// A smoke test to call requestMIDIAccess From ContentBrowserTest.
+// We cannot check result since it relies on the availability of system level
+// MIDI on the test runner.
+var error_message = "";
+navigator.requestMIDIAccess().then(
+  _ => { document.title = "pass" },
+  e => {
+    error_message = e.message;
+    document.title = "fail";
+  });
+</script>
+</html>
diff --git a/content/test/data/midi/subscribe_all.html b/content/test/data/midi/subscribe_all.html
new file mode 100644
index 0000000..03f0ad40
--- /dev/null
+++ b/content/test/data/midi/subscribe_all.html
@@ -0,0 +1,17 @@
+<html>
+<script>
+// A smoke test to call requestMIDIAccess From ContentBrowserTest.
+// We cannot check result since it relies on the availability of system level
+// MIDI on the test runner.
+var error_message = "";
+navigator.requestMIDIAccess().then(
+  a => {
+    a.inputs.forEach(i => i.onmidimessage = console.log);
+    document.title = "pass";
+  },
+  e => {
+    error_message = e.message;
+    document.title = "fail";
+  });
+</script>
+</html>
diff --git a/content/test/fake_mojo_message_dispatch_context.h b/content/test/fake_mojo_message_dispatch_context.h
new file mode 100644
index 0000000..9a342b53
--- /dev/null
+++ b/content/test/fake_mojo_message_dispatch_context.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_TEST_FAKE_MOJO_MESSAGE_DISPATCH_CONTEXT_H_
+#define CONTENT_TEST_FAKE_MOJO_MESSAGE_DISPATCH_CONTEXT_H_
+
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace content {
+
+class FakeMojoMessageDispatchContext {
+ public:
+  FakeMojoMessageDispatchContext()
+      : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) {}
+
+ private:
+  mojo::Message dummy_message_;
+  mojo::internal::MessageDispatchContext context_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeMojoMessageDispatchContext);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_TEST_FAKE_MOJO_MESSAGE_DISPATCH_CONTEXT_H_
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn
index 32652f0..d34ca44 100644
--- a/content/test/fuzzer/BUILD.gn
+++ b/content/test/fuzzer/BUILD.gn
@@ -149,16 +149,6 @@
   seed_corpus = "//components/cbor/reader_fuzzer_corpus/"
 }
 
-fuzzer_test("http_structured_header_fuzzer") {
-  sources = [
-    "http_structured_header_fuzzer.cc",
-  ]
-  deps = [
-    "//content/test:test_support",
-  ]
-  seed_corpus = "//content/test/data/fuzzer_corpus/http_structured_header_data/"
-}
-
 fuzzer_test("signed_exchange_certificate_chain_fuzzer") {
   sources = [
     "../../browser/web_package/signed_exchange_certificate_chain_fuzzer.cc",
diff --git a/content/test/gpu/gpu_tests/cloud_storage_integration_test_base.py b/content/test/gpu/gpu_tests/cloud_storage_integration_test_base.py
index 154479d..affdd6e 100644
--- a/content/test/gpu/gpu_tests/cloud_storage_integration_test_base.py
+++ b/content/test/gpu/gpu_tests/cloud_storage_integration_test_base.py
@@ -141,6 +141,12 @@
       help='For Skia Gold integration. Always report that the test passed even '
            'if the Skia Gold image comparison reported a failure, but '
            'otherwise perform the same steps as usual.')
+    parser.add_option(
+      '--stream-goldctl-output',
+      action='store_true', default=False,
+      help='When running goldctl commands, always show the stdout/stderr from '
+           'the subprocess instead of only on failure, and stream the output '
+           'instead of collecting it at the end.')
 
   def _CompareScreenshotSamples(self, tab, screenshot, expected_colors,
                                 tolerance, device_pixel_ratio,
@@ -453,9 +459,8 @@
       json.dump(gpu_keys, f)
     try:
       if not self.GetParsedCommandLineOptions().no_luci_auth:
-        subprocess.check_output([goldctl_bin, 'auth', '--luci',
-                                 '--work-dir', self._skia_gold_temp_dir],
-            stderr=subprocess.STDOUT)
+        self._RunGoldctlCommand([goldctl_bin, 'auth', '--luci',
+                                 '--work-dir', self._skia_gold_temp_dir])
       cmd = ([goldctl_bin, 'imgtest', 'add'] + mode +
                             ['--test-name', image_name,
                              '--instance', SKIA_GOLD_INSTANCE,
@@ -464,7 +469,7 @@
                              '--work-dir', self._skia_gold_temp_dir,
                              '--failure-file', failure_file] +
                             build_id_args)
-      subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+      self._RunGoldctlCommand(cmd)
     except CalledProcessError as e:
       contents = ''
       try:
@@ -476,6 +481,26 @@
       if not self.GetParsedCommandLineOptions().no_skia_gold_failure:
         raise Exception('goldctl command failed: ' + contents)
 
+  def _RunGoldctlCommand(self, command):
+    if self.GetParsedCommandLineOptions().stream_goldctl_output:
+      process = subprocess.Popen(
+          command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+      full_output = ""
+      while True:
+        line = process.stdout.readline()
+        if not line and process.poll() is not None:
+          break
+        if line:
+          full_output += line
+          logging.info(line.rstrip())
+      rc = process.returncode
+      # Emulate what subprocess.check_output would do so that callers don't
+      # have to care how the command is being run.
+      if rc != 0:
+        raise subprocess.CalledProcessError(rc, command, full_output)
+    else:
+      subprocess.check_output(command, stderr=subprocess.STDOUT)
+
   def _ValidateScreenshotSamplesWithSkiaGold(self, tab, page, screenshot,
                                              expectations, tolerance,
                                              device_pixel_ratio,
diff --git a/content/test/gpu/gpu_tests/test_expectations_unittest.py b/content/test/gpu/gpu_tests/test_expectations_unittest.py
index 9d2e5d9..bf9fcb4 100644
--- a/content/test/gpu/gpu_tests/test_expectations_unittest.py
+++ b/content/test/gpu/gpu_tests/test_expectations_unittest.py
@@ -94,6 +94,7 @@
     _trie = trie.setdefault(test[0], {})
     for l in test[1:]:
       _trie = _trie.setdefault(l, {})
+    _trie.setdefault('$', {})
   f = open(expectations_file, 'r')
   expectations_file = os.path.basename(expectations_file)
   expectations = f.read()
@@ -101,14 +102,18 @@
   parser = expectations_parser.TaggedTestListParser(expectations)
   for exp in parser.expectations:
     _trie = trie
+    is_glob = False
     for l in exp.test:
       if l == '*':
+        is_glob = True
         break
       assert l in _trie, (
-        "%s:%d: Glob '%s' does not match with any tests in the %s test suite" %
-        (expectations_file, exp.lineno, exp.test, test_class.Name()))
+        "%s:%d: Pattern '%s' does not match with any tests in the %s test suite"
+        % (expectations_file, exp.lineno, exp.test, test_class.Name()))
       _trie = _trie[l]
-
+    assert is_glob or '$' in _trie, (
+        "%s:%d: Pattern '%s' does not match with any tests in the %s test suite"
+        % (expectations_file, exp.lineno, exp.test, test_class.Name()))
 
 def CheckTestExpectationGlobsForCollision(expectations, file_name):
   """This function looks for collisions between test expectations with patterns
@@ -456,7 +461,14 @@
   def testExpectationPatternNotInGeneratedTests(self):
     with self.assertRaises(AssertionError) as context:
       _TestCheckTestExpectationsAreForExistingTests('a/b/d [ Failure ]')
-    self.assertIn(("1: Glob 'a/b/d' does not match with any"
+    self.assertIn(("1: Pattern 'a/b/d' does not match with any"
+                  " tests in the GpuIntegrationTest test suite"),
+                  str(context.exception))
+
+  def testExpectationPatternIsShorterThanAnyTestName(self):
+    with self.assertRaises(AssertionError) as context:
+      _TestCheckTestExpectationsAreForExistingTests('a/b [ Failure ]')
+    self.assertIn(("1: Pattern 'a/b' does not match with any"
                   " tests in the GpuIntegrationTest test suite"),
                   str(context.exception))
 
diff --git a/content/utility/BUILD.gn b/content/utility/BUILD.gn
index 992dde8..392c871 100644
--- a/content/utility/BUILD.gn
+++ b/content/utility/BUILD.gn
@@ -17,6 +17,8 @@
   sources = [
     "in_process_utility_thread.cc",
     "in_process_utility_thread.h",
+    "services.cc",
+    "services.h",
     "utility_blink_platform_with_sandbox_support_impl.cc",
     "utility_blink_platform_with_sandbox_support_impl.h",
     "utility_main.cc",
diff --git a/content/utility/services.cc b/content/utility/services.cc
new file mode 100644
index 0000000..44e7b19c
--- /dev/null
+++ b/content/utility/services.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/utility/services.h"
+
+#include <utility>
+
+#include "content/public/utility/content_utility_client.h"
+
+namespace content {
+
+void HandleServiceRequestOnIOThread(
+    mojo::GenericPendingReceiver receiver,
+    base::SequencedTaskRunner* main_thread_task_runner) {
+  // If the request was handled already, we should not reach this point.
+  DCHECK(receiver.is_valid());
+  GetContentClient()->utility()->RunIOThreadService(&receiver);
+
+  if (receiver.is_valid()) {
+    main_thread_task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(&HandleServiceRequestOnMainThread, std::move(receiver)));
+  }
+}
+
+void HandleServiceRequestOnMainThread(mojo::GenericPendingReceiver receiver) {
+  // If the request was handled already, we should not reach this point.
+  DCHECK(receiver.is_valid());
+  GetContentClient()->utility()->RunMainThreadService(std::move(receiver));
+}
+
+}  // namespace content
diff --git a/content/utility/services.h b/content/utility/services.h
new file mode 100644
index 0000000..54d4f56
--- /dev/null
+++ b/content/utility/services.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_UTILITY_SERVICES_H_
+#define CONTENT_UTILITY_SERVICES_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
+#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
+
+namespace content {
+
+void HandleServiceRequestOnIOThread(
+    mojo::GenericPendingReceiver receiver,
+    base::SequencedTaskRunner* main_thread_task_runner);
+
+void HandleServiceRequestOnMainThread(mojo::GenericPendingReceiver receiver);
+
+}  // namespace content
+
+#endif  // CONTENT_UTILITY_SERVICES_H_
diff --git a/content/utility/utility_thread_impl.cc b/content/utility/utility_thread_impl.cc
index adedf88..ae7dd99c 100644
--- a/content/utility/utility_thread_impl.cc
+++ b/content/utility/utility_thread_impl.cc
@@ -4,20 +4,29 @@
 
 #include "content/utility/utility_thread_impl.h"
 
+#include <set>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
+#include "base/containers/unique_ptr_adapters.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
 #include "build/build_config.h"
 #include "content/child/child_process.h"
+#include "content/common/service_control.mojom.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/simple_connection_filter.h"
 #include "content/public/utility/content_utility_client.h"
+#include "content/utility/services.h"
 #include "content/utility/utility_blink_platform_with_sandbox_support_impl.h"
 #include "content/utility/utility_service_factory.h"
 #include "ipc/ipc_sync_channel.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/sandbox/switches.h"
 
@@ -28,6 +37,71 @@
 
 namespace content {
 
+namespace {
+
+class ServiceControlImpl : public mojom::ServiceControl {
+ public:
+  explicit ServiceControlImpl(
+      scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner)
+      : main_thread_task_runner_(std::move(main_thread_task_runner)) {}
+  ~ServiceControlImpl() override = default;
+
+  // mojom::ServiceControl:
+  void BindServiceInterface(mojo::GenericPendingReceiver receiver) override {
+    // NOTE: The signals watcher are irrelevant. This watcher is never armed.
+    auto watcher = std::make_unique<mojo::SimpleWatcher>(
+        FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL);
+    watcher->Watch(receiver.pipe(), MOJO_HANDLE_SIGNAL_READABLE,
+                   MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+                   base::BindRepeating(&ServiceControlImpl::OnServicePipeClosed,
+                                       base::Unretained(this), watcher.get()));
+    service_pipe_watchers_.insert(std::move(watcher));
+    HandleServiceRequestOnIOThread(std::move(receiver),
+                                   main_thread_task_runner_.get());
+  }
+
+ private:
+  void OnServicePipeClosed(mojo::SimpleWatcher* which,
+                           MojoResult result,
+                           const mojo::HandleSignalsState& state) {
+    // This must be a cancellation notification (meaning the watched pipe has
+    // been closed locally) because we never arm the watcher to allow any other
+    // notifications.
+    DCHECK_EQ(MOJO_RESULT_CANCELLED, result);
+    auto it = service_pipe_watchers_.find(which);
+    DCHECK(it != service_pipe_watchers_.end());
+    service_pipe_watchers_.erase(it);
+
+    // No more services running in this process.
+    if (service_pipe_watchers_.empty()) {
+      main_thread_task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce([] { UtilityThread::Get()->ReleaseProcess(); }));
+    }
+  }
+
+  scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_;
+
+  // These trap signals on any (unowned) primordial service pipes. We don't
+  // actually care about the signals so these never get armed. We only watch for
+  // cancellation, because that means the service's primordial pipe handle was
+  // closed locally and we treat that as the service calling it quits.
+  std::set<std::unique_ptr<mojo::SimpleWatcher>, base::UniquePtrComparator>
+      service_pipe_watchers_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceControlImpl);
+};
+
+void BindServiceControl(
+    const scoped_refptr<base::SequencedTaskRunner>& main_thread_task_runner,
+    mojom::ServiceControlRequest request) {
+  mojo::MakeSelfOwnedReceiver<mojom::ServiceControl>(
+      std::make_unique<ServiceControlImpl>(main_thread_task_runner),
+      std::move(request));
+}
+
+}  // namespace
+
 #if !defined(OS_ANDROID)
 class ResourceUsageReporterImpl : public mojom::ResourceUsageReporter {
  public:
@@ -124,6 +198,8 @@
   ChildProcess::current()->AddRefProcess();
 
   auto registry = std::make_unique<service_manager::BinderRegistry>();
+  registry->AddInterface<mojom::ServiceControl>(base::BindRepeating(
+      &BindServiceControl, base::ThreadTaskRunnerHandle::Get()));
 #if !defined(OS_ANDROID)
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           service_manager::switches::kNoneSandboxAndElevatedPrivileges)) {
diff --git a/device/fido/bio/enrollment.cc b/device/fido/bio/enrollment.cc
index d749dce..3e13ac3 100644
--- a/device/fido/bio/enrollment.cc
+++ b/device/fido/bio/enrollment.cc
@@ -217,7 +217,7 @@
       return base::nullopt;
     }
 
-    std::vector<std::pair<std::vector<uint8_t>, std::string>> template_infos;
+    std::map<std::vector<uint8_t>, std::string> template_infos;
     for (const auto& bio_template : it->second.GetArray()) {
       if (!bio_template.is_map()) {
         return base::nullopt;
@@ -232,6 +232,10 @@
         return base::nullopt;
       }
       std::vector<uint8_t> id = template_it->second.GetBytestring();
+      if (template_infos.find(id) != template_infos.end()) {
+        // Found an existing ID, invalid response.
+        return base::nullopt;
+      }
 
       // name (optional)
       std::string name;
@@ -243,7 +247,7 @@
         }
         name = template_it->second.GetString();
       }
-      template_infos.push_back(std::make_pair(std::move(id), std::move(name)));
+      template_infos[std::move(id)] = std::move(name);
     }
     response.template_infos = std::move(template_infos);
   }
diff --git a/device/fido/bio/enrollment.h b/device/fido/bio/enrollment.h
index d9a18bc..92ff22a0 100644
--- a/device/fido/bio/enrollment.h
+++ b/device/fido/bio/enrollment.h
@@ -165,8 +165,7 @@
   base::Optional<std::vector<uint8_t>> template_id;
   base::Optional<BioEnrollmentSampleStatus> last_status;
   base::Optional<uint8_t> remaining_samples;
-  base::Optional<std::vector<std::pair<std::vector<uint8_t>, std::string>>>
-      template_infos;
+  base::Optional<std::map<std::vector<uint8_t>, std::string>> template_infos;
 };
 
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/bio/enrollment_handler.cc b/device/fido/bio/enrollment_handler.cc
index 46d4531..874db64 100644
--- a/device/fido/bio/enrollment_handler.cc
+++ b/device/fido/bio/enrollment_handler.cc
@@ -27,43 +27,38 @@
 }
 
 BioEnrollmentHandler::~BioEnrollmentHandler() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
 void BioEnrollmentHandler::GetModality(ResponseCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(authenticator_);
   authenticator_->GetModality(std::move(callback));
 }
 
 void BioEnrollmentHandler::GetSensorInfo(ResponseCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(authenticator_);
   authenticator_->GetSensorInfo(std::move(callback));
 }
 
 void BioEnrollmentHandler::EnrollTemplate(ResponseCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
-  DCHECK(!enroll_callback_);
-
-  enroll_callback_ = std::move(callback);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   authenticator_->BioEnrollFingerprint(
       *pin_token_response_,
-      base::BindRepeating(&BioEnrollmentHandler::OnEnrollTemplate,
-                          weak_factory_.GetWeakPtr()));
+      base::BindOnce(&BioEnrollmentHandler::OnEnrollTemplate,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void BioEnrollmentHandler::Cancel(StatusCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   authenticator_->BioEnrollCancel(
       base::BindOnce(&BioEnrollmentHandler::OnCancel,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void BioEnrollmentHandler::EnumerateTemplates(ResponseCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   authenticator_->BioEnrollEnumerate(
       *pin_token_response_,
       base::BindOnce(&BioEnrollmentHandler::OnEnumerateTemplates,
@@ -73,8 +68,7 @@
 void BioEnrollmentHandler::RenameTemplate(std::vector<uint8_t> id,
                                           std::string name,
                                           StatusCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   authenticator_->BioEnrollRename(
       *pin_token_response_, std::move(id), std::move(name),
       base::BindOnce(&BioEnrollmentHandler::OnRenameTemplate,
@@ -83,8 +77,7 @@
 
 void BioEnrollmentHandler::DeleteTemplate(std::vector<uint8_t> id,
                                           StatusCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   authenticator_->BioEnrollDelete(
       *pin_token_response_, std::move(id),
       base::BindOnce(&BioEnrollmentHandler::OnDeleteTemplate,
@@ -92,7 +85,7 @@
 }
 
 void BioEnrollmentHandler::DispatchRequest(FidoAuthenticator* authenticator) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   authenticator->GetTouch(base::BindOnce(&BioEnrollmentHandler::OnTouch,
                                          weak_factory_.GetWeakPtr(),
                                          authenticator));
@@ -101,7 +94,7 @@
 void BioEnrollmentHandler::AuthenticatorRemoved(
     FidoDiscoveryBase* discovery,
     FidoAuthenticator* authenticator) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator);
   if (authenticator_ != authenticator) {
     return;
@@ -138,7 +131,7 @@
 void BioEnrollmentHandler::OnRetriesResponse(
     CtapDeviceResponseCode code,
     base::Optional<pin::RetriesResponse> response) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!response || code != CtapDeviceResponseCode::kSuccess) {
     FIDO_LOG(DEBUG) << "OnRetriesResponse failed: " << static_cast<int>(code);
     std::move(error_callback_)
@@ -157,8 +150,7 @@
 }
 
 void BioEnrollmentHandler::OnHavePIN(std::string pin) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   authenticator_->GetEphemeralKey(
       base::BindOnce(&BioEnrollmentHandler::OnHaveEphemeralKey,
                      weak_factory_.GetWeakPtr(), std::move(pin)));
@@ -211,22 +203,22 @@
 }
 
 void BioEnrollmentHandler::OnEnrollTemplate(
+    ResponseCallback callback,
     CtapDeviceResponseCode code,
     base::Optional<BioEnrollmentResponse> response) {
   if (code != CtapDeviceResponseCode::kSuccess) {
     // Response is not valid if operation was not successful.
-    std::move(enroll_callback_).Run(code, base::nullopt);
+    std::move(callback).Run(code, base::nullopt);
     return;
   }
   if (!response || !response->last_status || !response->remaining_samples) {
-    std::move(enroll_callback_)
-        .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+    std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
+                            base::nullopt);
     return;
   }
   FIDO_LOG(DEBUG) << "Finished bio enrollment with code "
                   << static_cast<int>(code);
-  std::move(enroll_callback_).Run(code, std::move(response));
-  // TODO(noviv): Test calling OnEnrollTemplate multiple times.
+  std::move(callback).Run(code, std::move(response));
 }
 
 void BioEnrollmentHandler::OnCancel(StatusCallback callback,
diff --git a/device/fido/bio/enrollment_handler.h b/device/fido/bio/enrollment_handler.h
index cec194cf..9383c5a 100644
--- a/device/fido/bio/enrollment_handler.h
+++ b/device/fido/bio/enrollment_handler.h
@@ -65,7 +65,8 @@
                           base::Optional<pin::KeyAgreementResponse>);
   void OnHavePINToken(CtapDeviceResponseCode,
                       base::Optional<pin::TokenResponse>);
-  void OnEnrollTemplate(CtapDeviceResponseCode,
+  void OnEnrollTemplate(ResponseCallback,
+                        CtapDeviceResponseCode,
                         base::Optional<BioEnrollmentResponse>);
   void OnCancel(StatusCallback,
                 CtapDeviceResponseCode,
@@ -80,13 +81,12 @@
                         CtapDeviceResponseCode,
                         base::Optional<BioEnrollmentResponse>);
 
-  SEQUENCE_CHECKER(sequence_checker);
+  SEQUENCE_CHECKER(sequence_checker_);
 
   FidoAuthenticator* authenticator_ = nullptr;
   base::OnceClosure ready_callback_;
   ErrorCallback error_callback_;
   GetPINCallback get_pin_callback_;
-  ResponseCallback enroll_callback_;
   base::Optional<pin::TokenResponse> pin_token_response_;
   base::WeakPtrFactory<BioEnrollmentHandler> weak_factory_;
 
diff --git a/device/fido/bio/enrollment_handler_unittest.cc b/device/fido/bio/enrollment_handler_unittest.cc
index add0c03..c023627 100644
--- a/device/fido/bio/enrollment_handler_unittest.cc
+++ b/device/fido/bio/enrollment_handler_unittest.cc
@@ -188,11 +188,10 @@
 
   BioEnrollmentResponse expected;
   expected.template_infos =
-      std::vector<std::pair<std::vector<uint8_t>, std::string>>{
-          {{1}, "Template1"},
-          {{2}, "Template2"},
-          {{3}, "Template3"},
-          {{4}, "Template4"}};
+      std::map<std::vector<uint8_t>, std::string>{{{1}, "Template1"},
+                                                  {{2}, "Template2"},
+                                                  {{3}, "Template3"},
+                                                  {{4}, "Template4"}};
   EXPECT_EQ(cb.value(), expected);
 }
 
@@ -291,8 +290,7 @@
 
   BioEnrollmentResponse expected;
   expected.template_infos =
-      std::vector<std::pair<std::vector<uint8_t>, std::string>>{
-          {{1}, "Template1"}};
+      std::map<std::vector<uint8_t>, std::string>{{{1}, "Template1"}};
   EXPECT_EQ(cb1.value(), expected);
 }
 
@@ -341,8 +339,7 @@
 
   BioEnrollmentResponse expected;
   expected.template_infos =
-      std::vector<std::pair<std::vector<uint8_t>, std::string>>{
-          {{1}, "OtherFingerprint1"}};
+      std::map<std::vector<uint8_t>, std::string>{{{1}, "OtherFingerprint1"}};
   EXPECT_EQ(cb3.value(), expected);
 }
 
diff --git a/device/fido/cable/fido_cable_discovery.cc b/device/fido/cable/fido_cable_discovery.cc
index 8708e1d3..645850a 100644
--- a/device/fido/cable/fido_cable_discovery.cc
+++ b/device/fido/cable/fido_cable_discovery.cc
@@ -290,8 +290,13 @@
 void FidoCableDiscovery::CableDeviceFound(BluetoothAdapter* adapter,
                                           BluetoothDevice* device) {
   const auto* found_cable_device_data = GetCableDiscoveryData(device);
-  if (!found_cable_device_data)
+  const std::string device_address = device->GetAddress();
+  if (!found_cable_device_data ||
+      base::Contains(active_authenticator_eids_,
+                     found_cable_device_data->authenticator_eid) ||
+      base::Contains(active_devices_, device_address)) {
     return;
+  }
 
   FIDO_LOG(EVENT) << "Found new caBLE device.";
   // Nonce is embedded as first 8 bytes of client EID.
@@ -303,6 +308,8 @@
 
   auto cable_device =
       std::make_unique<FidoCableDevice>(adapter, device->GetAddress());
+  active_authenticator_eids_.insert(found_cable_device_data->authenticator_eid);
+  active_devices_.insert(device_address);
   StopAdvertisements(
       base::BindOnce(&FidoCableDiscovery::ConductEncryptionHandshake,
                      weak_factory_.GetWeakPtr(), std::move(cable_device),
@@ -313,17 +320,10 @@
     std::unique_ptr<FidoCableDevice> cable_device,
     base::span<const uint8_t, kSessionPreKeySize> session_pre_key,
     base::span<const uint8_t, 8> nonce) {
-  // At most one handshake messages should be exchanged for each Cable device.
-  if (base::Contains(cable_handshake_handlers_, cable_device->GetId())) {
-    FIDO_LOG(DEBUG) << "We've already exchanged a handshake with this device.";
-    return;
-  }
-
   auto handshake_handler =
       CreateHandshakeHandler(cable_device.get(), session_pre_key, nonce);
   auto* const handshake_handler_ptr = handshake_handler.get();
-  cable_handshake_handlers_.emplace(cable_device->GetId(),
-                                    std::move(handshake_handler));
+  cable_handshake_handlers_.emplace_back(std::move(handshake_handler));
 
   handshake_handler_ptr->InitiateCableHandshake(
       base::BindOnce(&FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage,
diff --git a/device/fido/cable/fido_cable_discovery.h b/device/fido/cable/fido_cable_discovery.h
index 40ba135..09dfc2c 100644
--- a/device/fido/cable/fido_cable_discovery.h
+++ b/device/fido/cable/fido_cable_discovery.h
@@ -93,10 +93,19 @@
       const BluetoothDevice* device) const;
 
   std::vector<CableDiscoveryData> discovery_data_;
+  // active_authenticator_eids_ contains authenticator EIDs for which a
+  // handshake is currently running. Further advertisements for the same EIDs
+  // will be ignored.
+  std::set<EidArray> active_authenticator_eids_;
+  // active_devices_ contains the BLE addresses of devices for which a handshake
+  // is already running. Further advertisements from these devices will be
+  // ignored. However, devices may rotate their BLE address at will so this is
+  // not completely effective.
+  std::set<std::string> active_devices_;
   size_t advertisement_success_counter_ = 0;
   size_t advertisement_failure_counter_ = 0;
   std::map<EidArray, scoped_refptr<BluetoothAdvertisement>> advertisements_;
-  std::map<std::string, std::unique_ptr<FidoCableHandshakeHandler>>
+  std::vector<std::unique_ptr<FidoCableHandshakeHandler>>
       cable_handshake_handlers_;
   base::WeakPtrFactory<FidoCableDiscovery> weak_factory_;
 
diff --git a/device/fido/ctap_get_assertion_request.h b/device/fido/ctap_get_assertion_request.h
index 0e2ee2e..c369b63 100644
--- a/device/fido/ctap_get_assertion_request.h
+++ b/device/fido/ctap_get_assertion_request.h
@@ -56,6 +56,7 @@
       alternative_application_parameter;
 
   bool is_incognito_mode = false;
+  bool is_u2f_only = false;
 };
 
 struct CtapGetNextAssertionRequest {
diff --git a/device/fido/win/type_conversions.cc b/device/fido/win/type_conversions.cc
index 62990b2..a0a1e9c 100644
--- a/device/fido/win/type_conversions.cc
+++ b/device/fido/win/type_conversions.cc
@@ -158,11 +158,7 @@
 }
 
 std::vector<WEBAUTHN_CREDENTIAL> ToWinCredentialVector(
-    const base::Optional<std::vector<PublicKeyCredentialDescriptor>>&
-        credentials) {
-  if (!credentials) {
-    return {};
-  }
+    const std::vector<PublicKeyCredentialDescriptor>* credentials) {
   std::vector<WEBAUTHN_CREDENTIAL> result;
   for (const auto& credential : *credentials) {
     if (credential.credential_type() != CredentialType::kPublicKey) {
@@ -179,11 +175,7 @@
 }
 
 std::vector<WEBAUTHN_CREDENTIAL_EX> ToWinCredentialExVector(
-    const base::Optional<std::vector<PublicKeyCredentialDescriptor>>&
-        credentials) {
-  if (!credentials) {
-    return {};
-  }
+    const std::vector<PublicKeyCredentialDescriptor>* credentials) {
   std::vector<WEBAUTHN_CREDENTIAL_EX> result;
   for (const auto& credential : *credentials) {
     if (credential.credential_type() != CredentialType::kPublicKey) {
diff --git a/device/fido/win/type_conversions.h b/device/fido/win/type_conversions.h
index 740979b..dc139a24 100644
--- a/device/fido/win/type_conversions.h
+++ b/device/fido/win/type_conversions.h
@@ -37,13 +37,11 @@
 
 COMPONENT_EXPORT(DEVICE_FIDO)
 std::vector<WEBAUTHN_CREDENTIAL> ToWinCredentialVector(
-    const base::Optional<std::vector<PublicKeyCredentialDescriptor>>&
-        credentials);
+    const std::vector<PublicKeyCredentialDescriptor>* credentials);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
 std::vector<WEBAUTHN_CREDENTIAL_EX> ToWinCredentialExVector(
-    const base::Optional<std::vector<PublicKeyCredentialDescriptor>>&
-        credentials);
+    const std::vector<PublicKeyCredentialDescriptor>* credentials);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
 CtapDeviceResponseCode WinErrorNameToCtapDeviceResponseCode(
diff --git a/device/fido/win/webauthn_api.cc b/device/fido/win/webauthn_api.cc
index 4ee1a90..5df439f 100644
--- a/device/fido/win/webauthn_api.cc
+++ b/device/fido/win/webauthn_api.cc
@@ -280,8 +280,10 @@
         ToWinAuthenticatorAttachment(request.authenticator_attachment);
   }
 
+  // Note that entries in |exclude_list_credentials| hold pointers
+  // into request.exclude_list.
   std::vector<WEBAUTHN_CREDENTIAL_EX> exclude_list_credentials =
-      ToWinCredentialExVector(request.exclude_list);
+      ToWinCredentialExVector(&request.exclude_list.value());
   std::vector<WEBAUTHN_CREDENTIAL_EX*> exclude_list_ptrs;
   std::transform(
       exclude_list_credentials.begin(), exclude_list_credentials.end(),
@@ -348,8 +350,10 @@
                           request.app_id->size()));
   }
 
+  // Note that entries in |allow_list_credentials| hold pointers into
+  // request.allow_list.
   std::vector<WEBAUTHN_CREDENTIAL_EX> allow_list_credentials =
-      ToWinCredentialExVector(request.allow_list);
+      ToWinCredentialExVector(&request.allow_list);
   std::vector<WEBAUTHN_CREDENTIAL_EX*> allow_list_ptrs;
   std::transform(allow_list_credentials.begin(), allow_list_credentials.end(),
                  std::back_inserter(allow_list_ptrs),
@@ -357,10 +361,12 @@
   WEBAUTHN_CREDENTIAL_LIST allow_credential_list{allow_list_ptrs.size(),
                                                  allow_list_ptrs.data()};
 
-  auto legacy_credentials = ToWinCredentialVector(request.allow_list);
+  // Note that entries in |legacy_credentials| hold pointers into
+  // request.allow_list.
+  auto legacy_credentials = ToWinCredentialVector(&request.allow_list);
 
   uint32_t authenticator_attachment;
-  if (opt_app_id16) {
+  if (request.is_u2f_only) {
     authenticator_attachment =
         WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
   } else if (request.is_incognito_mode) {
diff --git a/device/vr/android/gvr/gvr_delegate.cc b/device/vr/android/gvr/gvr_delegate.cc
index fd331cd..722593a 100644
--- a/device/vr/android/gvr/gvr_delegate.cc
+++ b/device/vr/android/gvr/gvr_delegate.cc
@@ -88,7 +88,7 @@
     const gfx::Transform& head_mat) {
   mojom::VRPosePtr pose = mojom::VRPose::New();
 
-  pose->orientation.emplace(4);
+  pose->orientation = gfx::Quaternion();
 
   gfx::Transform inv_transform(head_mat);
 
@@ -97,15 +97,11 @@
     gfx::DecomposedTransform decomposed_transform;
     gfx::DecomposeTransform(&decomposed_transform, transform);
 
-    pose->orientation.value()[0] = decomposed_transform.quaternion.x();
-    pose->orientation.value()[1] = decomposed_transform.quaternion.y();
-    pose->orientation.value()[2] = decomposed_transform.quaternion.z();
-    pose->orientation.value()[3] = decomposed_transform.quaternion.w();
+    pose->orientation = decomposed_transform.quaternion;
 
-    pose->position.emplace(3);
-    pose->position.value()[0] = decomposed_transform.translate[0];
-    pose->position.value()[1] = decomposed_transform.translate[1];
-    pose->position.value()[2] = decomposed_transform.translate[2];
+    pose->position = gfx::Point3F(decomposed_transform.translate[0],
+                                  decomposed_transform.translate[1],
+                                  decomposed_transform.translate[2]);
   }
 
   return pose;
@@ -157,13 +153,9 @@
   GvrMatToTransform(gvr_head_mat_2, &head_mat_2);
 
   // Add headset angular velocity to the pose.
-  pose->angularVelocity.emplace(3);
   double epsilon_seconds = kAngularVelocityEpsilonNanos * 1e-9;
-  gfx::Vector3dF angular_velocity =
+  pose->angularVelocity =
       GetAngularVelocityFromPoses(*head_mat_ptr, head_mat_2, epsilon_seconds);
-  pose->angularVelocity.value()[0] = angular_velocity.x();
-  pose->angularVelocity.value()[1] = angular_velocity.y();
-  pose->angularVelocity.value()[2] = angular_velocity.z();
 
   return pose;
 }
diff --git a/device/vr/android/gvr/gvr_device.cc b/device/vr/android/gvr/gvr_device.cc
index 84ac608..4eaf114 100644
--- a/device/vr/android/gvr/gvr_device.cc
+++ b/device/vr/android/gvr/gvr_device.cc
@@ -74,7 +74,6 @@
     const gfx::Size& maximum_size) {
   mojom::VREyeParametersPtr eye_params = mojom::VREyeParameters::New();
   eye_params->fieldOfView = mojom::VRFieldOfView::New();
-  eye_params->offset.resize(3);
   eye_params->renderWidth = maximum_size.width() / 2;
   eye_params->renderHeight = maximum_size.height();
 
@@ -87,9 +86,8 @@
   eye_params->fieldOfView->rightDegrees = eye_fov.right;
 
   gvr::Mat4f eye_mat = gvr_api->GetEyeFromHeadMatrix(eye);
-  eye_params->offset[0] = -eye_mat.m[0][3];
-  eye_params->offset[1] = -eye_mat.m[1][3];
-  eye_params->offset[2] = -eye_mat.m[2][3];
+  eye_params->offset =
+      gfx::Vector3dF(-eye_mat.m[0][3], -eye_mat.m[1][3], -eye_mat.m[2][3]);
   return eye_params;
 }
 
diff --git a/device/vr/isolated_gamepad_data_fetcher.cc b/device/vr/isolated_gamepad_data_fetcher.cc
index f766fe14c..d122caab 100644
--- a/device/vr/isolated_gamepad_data_fetcher.cc
+++ b/device/vr/isolated_gamepad_data_fetcher.cc
@@ -106,45 +106,45 @@
 
   if (pose->position) {
     ret.position.not_null = true;
-    ret.position.x = (*pose->position)[0];
-    ret.position.y = (*pose->position)[1];
-    ret.position.z = (*pose->position)[2];
+    ret.position.x = pose->position->x();
+    ret.position.y = pose->position->y();
+    ret.position.z = pose->position->z();
   }
 
   if (pose->orientation) {
     ret.orientation.not_null = true;
-    ret.orientation.x = (*pose->orientation)[0];
-    ret.orientation.y = (*pose->orientation)[1];
-    ret.orientation.z = (*pose->orientation)[2];
-    ret.orientation.w = (*pose->orientation)[3];
+    ret.orientation.x = pose->orientation->x();
+    ret.orientation.y = pose->orientation->y();
+    ret.orientation.z = pose->orientation->z();
+    ret.orientation.w = pose->orientation->w();
   }
 
   if (pose->angularVelocity) {
     ret.angular_velocity.not_null = true;
-    ret.angular_velocity.x = (*pose->angularVelocity)[0];
-    ret.angular_velocity.y = (*pose->angularVelocity)[1];
-    ret.angular_velocity.z = (*pose->angularVelocity)[2];
+    ret.angular_velocity.x = pose->angularVelocity->x();
+    ret.angular_velocity.y = pose->angularVelocity->y();
+    ret.angular_velocity.z = pose->angularVelocity->z();
   }
 
   if (pose->linearVelocity) {
     ret.linear_velocity.not_null = true;
-    ret.linear_velocity.x = (*pose->linearVelocity)[0];
-    ret.linear_velocity.y = (*pose->linearVelocity)[1];
-    ret.linear_velocity.z = (*pose->linearVelocity)[2];
+    ret.linear_velocity.x = pose->linearVelocity->x();
+    ret.linear_velocity.y = pose->linearVelocity->y();
+    ret.linear_velocity.z = pose->linearVelocity->z();
   }
 
   if (pose->angularAcceleration) {
     ret.angular_acceleration.not_null = true;
-    ret.angular_acceleration.x = (*pose->angularAcceleration)[0];
-    ret.angular_acceleration.y = (*pose->angularAcceleration)[1];
-    ret.angular_acceleration.z = (*pose->angularAcceleration)[2];
+    ret.angular_acceleration.x = pose->angularAcceleration->x();
+    ret.angular_acceleration.y = pose->angularAcceleration->y();
+    ret.angular_acceleration.z = pose->angularAcceleration->z();
   }
 
   if (pose->linearAcceleration) {
     ret.linear_acceleration.not_null = true;
-    ret.linear_acceleration.x = (*pose->linearAcceleration)[0];
-    ret.linear_acceleration.y = (*pose->linearAcceleration)[1];
-    ret.linear_acceleration.z = (*pose->linearAcceleration)[2];
+    ret.linear_acceleration.x = pose->linearAcceleration->x();
+    ret.linear_acceleration.y = pose->linearAcceleration->y();
+    ret.linear_acceleration.z = pose->linearAcceleration->z();
   }
 
   return ret;
diff --git a/device/vr/oculus/oculus_device.cc b/device/vr/oculus/oculus_device.cc
index 495af89..468fc2d 100644
--- a/device/vr/oculus/oculus_device.cc
+++ b/device/vr/oculus/oculus_device.cc
@@ -39,7 +39,7 @@
       gfx::RadToDeg(atanf(render_desc.Fov.RightTan));
 
   auto offset = render_desc.HmdToEyeOffset;
-  eye_parameters->offset = std::vector<float>({offset.x, offset.y, offset.z});
+  eye_parameters->offset = gfx::Vector3dF(offset.x, offset.y, offset.z);
 
   auto texture_size =
       ovr_GetFovTextureSize(session, eye, render_desc.Fov, 1.0f);
@@ -71,8 +71,9 @@
   float floor_height = ovr_state.HeadPose.ThePose.Position.y;
   ovr_SetTrackingOriginType(session, ovrTrackingOrigin_EyeLevel);
 
-  display_info->stageParameters->standingTransform = {
-      1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, floor_height, 0, 1};
+  gfx::Transform standing_transform;
+  standing_transform.Translate3d(0, floor_height, 0);
+  display_info->stageParameters->standingTransform = standing_transform;
 
   ovrVector3f boundary_size;
   ovr_GetBoundaryDimensions(session, ovrBoundary_PlayArea, &boundary_size);
diff --git a/device/vr/oculus/oculus_gamepad_helper.cc b/device/vr/oculus/oculus_gamepad_helper.cc
index 84ec28d2..fbbcb91 100644
--- a/device/vr/oculus/oculus_gamepad_helper.cc
+++ b/device/vr/oculus/oculus_gamepad_helper.cc
@@ -140,29 +140,29 @@
   auto dst_pose = mojom::VRPose::New();
   const ovrPoseStatef& src_pose = tracking.HandPoses[hand];
 
-  dst_pose->orientation = std::vector<float>(
-      {src_pose.ThePose.Orientation.x, src_pose.ThePose.Orientation.y,
-       src_pose.ThePose.Orientation.z, src_pose.ThePose.Orientation.w});
+  dst_pose->orientation = gfx::Quaternion(
+      src_pose.ThePose.Orientation.x, src_pose.ThePose.Orientation.y,
+      src_pose.ThePose.Orientation.z, src_pose.ThePose.Orientation.w);
 
-  dst_pose->position = std::vector<float>({src_pose.ThePose.Position.x,
-                                           src_pose.ThePose.Position.y,
-                                           src_pose.ThePose.Position.z});
+  dst_pose->position =
+      gfx::Point3F(src_pose.ThePose.Position.x, src_pose.ThePose.Position.y,
+                   src_pose.ThePose.Position.z);
 
-  dst_pose->angularVelocity = std::vector<float>({src_pose.AngularVelocity.x,
-                                                  src_pose.AngularVelocity.y,
-                                                  src_pose.AngularVelocity.z});
+  dst_pose->angularVelocity =
+      gfx::Vector3dF(src_pose.AngularVelocity.x, src_pose.AngularVelocity.y,
+                     src_pose.AngularVelocity.z);
 
   dst_pose->linearVelocity =
-      std::vector<float>({src_pose.LinearVelocity.x, src_pose.LinearVelocity.y,
-                          src_pose.LinearVelocity.z});
+      gfx::Vector3dF(src_pose.LinearVelocity.x, src_pose.LinearVelocity.y,
+                     src_pose.LinearVelocity.z);
 
-  dst_pose->angularAcceleration = std::vector<float>(
-      {src_pose.AngularAcceleration.x, src_pose.AngularAcceleration.y,
-       src_pose.AngularAcceleration.z});
+  dst_pose->angularAcceleration = gfx::Vector3dF(
+      src_pose.AngularAcceleration.x, src_pose.AngularAcceleration.y,
+      src_pose.AngularAcceleration.z);
 
-  dst_pose->linearAcceleration = std::vector<float>(
-      {src_pose.LinearAcceleration.x, src_pose.LinearAcceleration.y,
-       src_pose.LinearAcceleration.z});
+  dst_pose->linearAcceleration = gfx::Vector3dF(src_pose.LinearAcceleration.x,
+                                                src_pose.LinearAcceleration.y,
+                                                src_pose.LinearAcceleration.z);
 
   gamepad->pose = std::move(dst_pose);
   gamepad->can_provide_position = true;
diff --git a/device/vr/oculus/oculus_type_converters.cc b/device/vr/oculus/oculus_type_converters.cc
index 5f72dad..abd7329 100644
--- a/device/vr/oculus/oculus_type_converters.cc
+++ b/device/vr/oculus/oculus_type_converters.cc
@@ -18,10 +18,10 @@
     const ovrPosef& hmd_pose) {
   device::mojom::VRPosePtr pose = device::mojom::VRPose::New();
   pose->orientation =
-      std::vector<float>({hmd_pose.Orientation.x, hmd_pose.Orientation.y,
-                          hmd_pose.Orientation.z, hmd_pose.Orientation.w});
-  pose->position = std::vector<float>(
-      {hmd_pose.Position.x, hmd_pose.Position.y, hmd_pose.Position.z});
+      gfx::Quaternion(hmd_pose.Orientation.x, hmd_pose.Orientation.y,
+                      hmd_pose.Orientation.z, hmd_pose.Orientation.w);
+  pose->position = gfx::Point3F(hmd_pose.Position.x, hmd_pose.Position.y,
+                                hmd_pose.Position.z);
 
   // TODO: If we want linear/angular velocity, we need to convert a
   // ovrPoseStatef.
diff --git a/device/vr/openvr/openvr_device.cc b/device/vr/openvr/openvr_device.cc
index a1b02aee..d9efe02 100644
--- a/device/vr/openvr/openvr_device.cc
+++ b/device/vr/openvr/openvr_device.cc
@@ -40,27 +40,16 @@
   return out;
 }
 
-std::vector<float> HmdMatrix34ToWebVRTransformMatrix(
-    const vr::HmdMatrix34_t& mat) {
-  std::vector<float> transform;
-  transform.resize(16);
-  transform[0] = mat.m[0][0];
-  transform[1] = mat.m[1][0];
-  transform[2] = mat.m[2][0];
-  transform[3] = 0.0f;
-  transform[4] = mat.m[0][1];
-  transform[5] = mat.m[1][1];
-  transform[6] = mat.m[2][1];
-  transform[7] = 0.0f;
-  transform[8] = mat.m[0][2];
-  transform[9] = mat.m[1][2];
-  transform[10] = mat.m[2][2];
-  transform[11] = 0.0f;
-  transform[12] = mat.m[0][3];
-  transform[13] = mat.m[1][3];
-  transform[14] = mat.m[2][3];
-  transform[15] = 1.0f;
-  return transform;
+gfx::Transform HmdMatrix34ToTransform(const vr::HmdMatrix34_t& mat) {
+  // Disable formatting so that the 4x4 matrix is more readable
+  // clang-format off
+  return gfx::Transform(
+     mat.m[0][0], mat.m[0][1], mat.m[0][2], mat.m[0][3],
+     mat.m[1][0], mat.m[1][1], mat.m[1][2], mat.m[1][3],
+     mat.m[2][0], mat.m[2][1], mat.m[2][2], mat.m[2][3],
+     0.0f,        0.0f,        0.0f,        1.0f
+  );
+  // clang-format on
 }
 
 mojom::VRDisplayInfoPtr CreateVRDisplayInfo(vr::IVRSystem* vr_system,
@@ -92,14 +81,8 @@
   if (error != vr::TrackedProp_Success)
     ipd = kDefaultIPD;
 
-  left_eye->offset.resize(3);
-  left_eye->offset[0] = -ipd * 0.5;
-  left_eye->offset[1] = 0.0f;
-  left_eye->offset[2] = 0.0f;
-  right_eye->offset.resize(3);
-  right_eye->offset[0] = ipd * 0.5;
-  right_eye->offset[1] = 0.0;
-  right_eye->offset[2] = 0.0;
+  left_eye->offset = gfx::Vector3dF(-ipd * 0.5, 0.0, 0.0);
+  right_eye->offset = gfx::Vector3dF(ipd * 0.5, 0.0, 0.0);
 
   uint32_t width, height;
   vr_system->GetRecommendedRenderTargetSize(&width, &height);
@@ -112,7 +95,7 @@
   vr::HmdMatrix34_t mat =
       vr_system->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
   display_info->stageParameters->standingTransform =
-      HmdMatrix34ToWebVRTransformMatrix(mat);
+      HmdMatrix34ToTransform(mat);
 
   vr::IVRChaperone* chaperone = vr::VRChaperone();
   if (chaperone) {
diff --git a/device/vr/openvr/openvr_gamepad_helper.cc b/device/vr/openvr/openvr_gamepad_helper.cc
index db0114ae..fe26de2 100644
--- a/device/vr/openvr/openvr_gamepad_helper.cc
+++ b/device/vr/openvr/openvr_gamepad_helper.cc
@@ -229,17 +229,14 @@
       gfx::DecomposeTransform(&src_pose, transform);
       auto dst_pose = mojom::VRPose::New();
 
-      dst_pose->orientation = std::vector<float>(
-          {src_pose.quaternion.x(), src_pose.quaternion.y(),
-           src_pose.quaternion.z(), src_pose.quaternion.w()});
-      dst_pose->position =
-          std::vector<float>({src_pose.translate[0], src_pose.translate[1],
-                              src_pose.translate[2]});
-      dst_pose->angularVelocity = std::vector<float>(
-          {pose.vAngularVelocity.v[0], pose.vAngularVelocity.v[1],
-           pose.vAngularVelocity.v[2]});
-      dst_pose->linearVelocity = std::vector<float>(
-          {pose.vVelocity.v[0], pose.vVelocity.v[1], pose.vVelocity.v[2]});
+      dst_pose->orientation = src_pose.quaternion;
+      dst_pose->position = gfx::Point3F(
+          src_pose.translate[0], src_pose.translate[1], src_pose.translate[2]);
+      dst_pose->angularVelocity =
+          gfx::Vector3dF(pose.vAngularVelocity.v[0], pose.vAngularVelocity.v[1],
+                         pose.vAngularVelocity.v[2]);
+      dst_pose->linearVelocity = gfx::Vector3dF(
+          pose.vVelocity.v[0], pose.vVelocity.v[1], pose.vVelocity.v[2]);
 
       gamepad->pose = std::move(dst_pose);
     }
diff --git a/device/vr/openvr/openvr_type_converters.cc b/device/vr/openvr/openvr_type_converters.cc
index c4005e03..2cbc97e 100644
--- a/device/vr/openvr/openvr_type_converters.cc
+++ b/device/vr/openvr/openvr_type_converters.cc
@@ -18,8 +18,8 @@
 TypeConverter<device::mojom::VRPosePtr, vr::TrackedDevicePose_t>::Convert(
     const vr::TrackedDevicePose_t& hmd_pose) {
   device::mojom::VRPosePtr pose = device::mojom::VRPose::New();
-  pose->orientation = std::vector<float>({0.0f, 0.0f, 0.0f, 1.0f});
-  pose->position = std::vector<float>({0.0f, 0.0f, 0.0f});
+  pose->orientation = gfx::Quaternion();
+  pose->position = gfx::Point3F();
 
   if (hmd_pose.bPoseIsValid && hmd_pose.bDeviceIsConnected) {
     const float(&m)[3][4] = hmd_pose.mDeviceToAbsoluteTracking.m;
@@ -29,21 +29,17 @@
         m[2][0], m[2][1], m[2][2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
     gfx::DecomposedTransform decomposed;
     if (gfx::DecomposeTransform(&decomposed, transform)) {
-      pose->orientation.value()[0] = decomposed.quaternion.x();
-      pose->orientation.value()[1] = decomposed.quaternion.y();
-      pose->orientation.value()[2] = decomposed.quaternion.z();
-      pose->orientation.value()[3] = decomposed.quaternion.w();
+      pose->orientation = decomposed.quaternion;
     }
 
-    pose->position.value()[0] = m[0][3];
-    pose->position.value()[1] = m[1][3];
-    pose->position.value()[2] = m[2][3];
+    pose->position->SetPoint(m[0][3], m[1][3], m[2][3]);
 
-    pose->linearVelocity = std::vector<float>(std::begin(hmd_pose.vVelocity.v),
-                                              std::end(hmd_pose.vVelocity.v));
-    pose->angularVelocity =
-        std::vector<float>(std::begin(hmd_pose.vAngularVelocity.v),
-                           std::end(hmd_pose.vAngularVelocity.v));
+    pose->linearVelocity =
+        gfx::Vector3dF(hmd_pose.vVelocity.v[0], hmd_pose.vVelocity.v[1],
+                       hmd_pose.vVelocity.v[2]);
+    pose->angularVelocity = gfx::Vector3dF(hmd_pose.vAngularVelocity.v[0],
+                                           hmd_pose.vAngularVelocity.v[1],
+                                           hmd_pose.vAngularVelocity.v[2]);
   }
 
   return pose;
diff --git a/device/vr/orientation/orientation_device.cc b/device/vr/orientation/orientation_device.cc
index 71c3f465..e39547e 100644
--- a/device/vr/orientation/orientation_device.cc
+++ b/device/vr/orientation/orientation_device.cc
@@ -142,7 +142,6 @@
 void VROrientationDevice::OnGetInlineFrameData(
     mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
   mojom::VRPosePtr pose = mojom::VRPose::New();
-  pose->orientation.emplace(4);
 
   SensorReading latest_reading;
   // If the reading fails just return the last pose that we got.
@@ -156,10 +155,7 @@
         WorldSpaceToUserOrientedSpace(SensorSpaceToWorldSpace(latest_pose_));
   }
 
-  pose->orientation.value()[0] = latest_pose_.x();
-  pose->orientation.value()[1] = latest_pose_.y();
-  pose->orientation.value()[2] = latest_pose_.z();
-  pose->orientation.value()[3] = latest_pose_.w();
+  pose->orientation = latest_pose_;
 
   mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
   frame_data->pose = std::move(pose);
diff --git a/device/vr/orientation/orientation_device_unittest.cc b/device/vr/orientation/orientation_device_unittest.cc
index afeda8c..c5368f6 100644
--- a/device/vr/orientation/orientation_device_unittest.cc
+++ b/device/vr/orientation/orientation_device_unittest.cc
@@ -249,20 +249,20 @@
   // Set forward to 0 degrees
   DeviceReadPose(gfx::Quaternion(0, 0, 0, 1),
                  base::BindOnce([](mojom::VRPosePtr ptr) {
-                   EXPECT_NEAR(ptr->orientation->at(0), -0.707, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(3), 0.707, 0.001);
+                   EXPECT_NEAR(ptr->orientation->x(), -0.707, 0.001);
+                   EXPECT_NEAR(ptr->orientation->y(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->z(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->w(), 0.707, 0.001);
                  }));
 
   // Now a 90 degree rotation around x in device space should be default pose in
   // vr space.
   DeviceReadPose(gfx::Quaternion(0.707, 0, 0, 0.707),
                  base::BindOnce([](mojom::VRPosePtr ptr) {
-                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(3), 1, 0.001);
+                   EXPECT_NEAR(ptr->orientation->x(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->y(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->z(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->w(), 1, 0.001);
                  }));
 }
 
@@ -274,19 +274,19 @@
   // to be the default pose.
   DeviceReadPose(gfx::Quaternion(0.653, 0.271, 0.271, 0.653),
                  base::BindOnce([](mojom::VRPosePtr ptr) {
-                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(3), 1, 0.001);
+                   EXPECT_NEAR(ptr->orientation->x(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->y(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->z(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->w(), 1, 0.001);
                  }));
 
   // Now hold upright and straigt produces a 45 degree rotation to the right
   DeviceReadPose(gfx::Quaternion(0.707, 0, 0, 0.707),
                  base::BindOnce([](mojom::VRPosePtr ptr) {
-                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(1), -0.383, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(3), 0.924, 0.001);
+                   EXPECT_NEAR(ptr->orientation->x(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->y(), -0.383, 0.001);
+                   EXPECT_NEAR(ptr->orientation->z(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->w(), 0.924, 0.001);
                  }));
 }
 
@@ -299,20 +299,20 @@
   // landscape mode.
   DeviceReadPose(gfx::Quaternion(0.5, -0.5, 0.5, 0.5),
                  base::BindOnce([](mojom::VRPosePtr ptr) {
-                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(3), 1, 0.001);
+                   EXPECT_NEAR(ptr->orientation->x(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->y(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->z(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->w(), 1, 0.001);
                  }));
 
   // Rotating the device 45 left from base pose should cause 45 degree left
   // rotation around y in VR space.
   DeviceReadPose(gfx::Quaternion(0.653, -0.271, 0.653, 0.271),
                  base::BindOnce([](mojom::VRPosePtr ptr) {
-                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(1), 0.382, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(3), 0.924, 0.001);
+                   EXPECT_NEAR(ptr->orientation->x(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->y(), 0.382, 0.001);
+                   EXPECT_NEAR(ptr->orientation->z(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->w(), 0.924, 0.001);
                  }));
 }
 
@@ -325,20 +325,20 @@
   // landscape mode (twist the other way from what we'd need for ROTATE_90).
   DeviceReadPose(gfx::Quaternion(0.5, 0.5, -0.5, 0.5),
                  base::BindOnce([](mojom::VRPosePtr ptr) {
-                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(3), 1, 0.001);
+                   EXPECT_NEAR(ptr->orientation->x(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->y(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->z(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->w(), 1, 0.001);
                  }));
 
   // Rotating the device 45 left from base pose should cause 45 degree left
   // rotation around y in VR space
   DeviceReadPose(gfx::Quaternion(0.271, 0.653, -0.271, 0.653),
                  base::BindOnce([](mojom::VRPosePtr ptr) {
-                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(1), 0.382, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
-                   EXPECT_NEAR(ptr->orientation->at(3), 0.924, 0.001);
+                   EXPECT_NEAR(ptr->orientation->x(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->y(), 0.382, 0.001);
+                   EXPECT_NEAR(ptr->orientation->z(), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->w(), 0.924, 0.001);
                  }));
 }
 
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index dde8a4d..c4b9e60 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -73,6 +73,10 @@
   // info to more sensible places so that this doesn't need to be sent here.
   VRDisplayInfo display_info;
   XRPresentationConnection? submit_frame_sink;
+
+  // Indicates whether the device backing this session sends input events solely
+  // via eventing (as opposed to polling).
+  bool uses_input_eventing;
 };
 
 // This structure contains the infomation and interfaces needed to create a two
@@ -137,12 +141,14 @@
 // A display's position, orientation, velocity, and acceleration state at the
 // given timestamp.
 struct VRPose {
-  array<float, 4>? orientation;
-  array<float, 3>? position;
-  array<float, 3>? angularVelocity;
-  array<float, 3>? linearVelocity;
-  array<float, 3>? angularAcceleration;
-  array<float, 3>? linearAcceleration;
+  gfx.mojom.Quaternion? orientation;
+  gfx.mojom.Point3F? position;
+
+  // Velocity/Acceleration is in global coordinates, as rad/s.
+  gfx.mojom.Vector3dF? angularVelocity;
+  gfx.mojom.Vector3dF? linearVelocity;
+  gfx.mojom.Vector3dF? angularAcceleration;
+  gfx.mojom.Vector3dF? linearAcceleration;
 
   // For WebXR sessions only, reports the state of all active input devices
   // synced with the head pose.
@@ -160,10 +166,7 @@
 };
 
 struct XRHitResult {
-  // A 4x4 transformation matrix representing the position of the object hit
-  // and the orientation of the normal of the object at the hit location.
-  // TODO(https://crbug.com/845293): use gfx.mojom.Transform.
-  array<float, 16> hit_matrix;
+  gfx.mojom.Transform hit_matrix;
 };
 
 struct VRDisplayCapabilities {
@@ -183,13 +186,13 @@
 // Information about the optical properties for an eye in a VRDisplay.
 struct VREyeParameters {
   VRFieldOfView fieldOfView;
-  array<float, 3> offset;
+  gfx.mojom.Vector3dF offset;
   uint32 renderWidth;
   uint32 renderHeight;
 };
 
 struct VRStageParameters {
-  array<float, 16> standingTransform;
+  gfx.mojom.Transform standingTransform;
   float sizeX;
   float sizeZ;
 
@@ -386,6 +389,13 @@
   RequestHitTest(XRRay ray) => (array<XRHitResult>? results);
 };
 
+// Provides a mechanism for a channel to plumb up any button click events
+// separately from polling, to provide better latency/position tracking for
+// those events.
+interface XRInputSourceButtonListener {
+  OnButtonEvent(XRInputSourceState input_source);
+};
+
 struct XRFrameDataRequestOptions {
   // Controls whether XRFrameData.detected_planes should be populated by the
   // request to XRFrameDataProvider.GetFrameData().
@@ -403,6 +413,8 @@
   GetFrameData(XRFrameDataRequestOptions? options) => (XRFrameData? frame_data);
   GetEnvironmentIntegrationProvider(
       associated XREnvironmentIntegrationProvider& environment_provider);
+  SetInputSourceButtonListener(
+      associated XRInputSourceButtonListener? event_listener);
 };
 
 // Provides the necessary functionality for sending frames to a headset.
diff --git a/device/vr/test/fake_vr_device.cc b/device/vr/test/fake_vr_device.cc
index 693407f..ea72a21 100644
--- a/device/vr/test/fake_vr_device.cc
+++ b/device/vr/test/fake_vr_device.cc
@@ -39,10 +39,7 @@
   eye->fieldOfView->leftDegrees = fov;
   eye->fieldOfView->rightDegrees = fov;
 
-  eye->offset.resize(3);
-  eye->offset[0] = offset;
-  eye->offset[1] = 0.0f;
-  eye->offset[2] = 0.0f;
+  eye->offset = gfx::Vector3dF(offset, 0.0f, 0.0f);
 
   eye->renderWidth = size;
   eye->renderHeight = size;
diff --git a/device/vr/vr_display_impl.cc b/device/vr/vr_display_impl.cc
index ffe0096..98921904 100644
--- a/device/vr/vr_display_impl.cc
+++ b/device/vr/vr_display_impl.cc
@@ -46,6 +46,13 @@
   mojo::ReportBadMessage("Environment integration is not supported.");
 }
 
+void VRDisplayImpl::SetInputSourceButtonListener(
+    device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo) {
+  // Input eventing is not supported. This call should not
+  // be made on this device.
+  mojo::ReportBadMessage("Input eventing is not supported.");
+}
+
 // XRSessionController
 void VRDisplayImpl::SetFrameDataRestricted(bool frame_data_restricted) {
   restrict_frame_data_ = frame_data_restricted;
diff --git a/device/vr/vr_display_impl.h b/device/vr/vr_display_impl.h
index 5a064e6b..2494214 100644
--- a/device/vr/vr_display_impl.h
+++ b/device/vr/vr_display_impl.h
@@ -36,6 +36,9 @@
   void GetEnvironmentIntegrationProvider(
       mojom::XREnvironmentIntegrationProviderAssociatedRequest
           environment_provider) override;
+  void SetInputSourceButtonListener(
+      mojom::XRInputSourceButtonListenerAssociatedPtrInfo) override;
+
   gfx::Size sessionFrameSize() { return session_frame_size_; }
   display::Display::Rotation sessionRotation() { return session_rotation_; }
 
diff --git a/device/vr/windows/compositor_base.cc b/device/vr/windows/compositor_base.cc
index c20d508..b2eafaf 100644
--- a/device/vr/windows/compositor_base.cc
+++ b/device/vr/windows/compositor_base.cc
@@ -139,6 +139,7 @@
   frame_data_binding_.Close();
   gamepad_provider_.Close();
   overlay_binding_.Close();
+  input_event_listener_ = nullptr;
   StopRuntime();
 }
 
@@ -158,6 +159,12 @@
   SetOverlayAndWebXRVisibility(false, true);
 }
 
+bool XRCompositorCommon::UsesInputEventing() {
+  // By default we don't use input eventing.  Any subclass that does will need
+  // to override this.
+  return false;
+}
+
 void XRCompositorCommon::UpdateLayerBounds(int16_t frame_id,
                                            const gfx::RectF& left_bounds,
                                            const gfx::RectF& right_bounds,
@@ -226,6 +233,7 @@
   auto session = device::mojom::XRSession::New();
   session->data_provider = frame_data_provider.PassInterface();
   session->submit_frame_sink = std::move(submit_frame_sink);
+  session->uses_input_eventing = UsesInputEventing();
 
   main_thread_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), true, std::move(session)));
@@ -345,6 +353,12 @@
   }
 }
 
+void XRCompositorCommon::SetInputSourceButtonListener(
+    mojom::XRInputSourceButtonListenerAssociatedPtrInfo input_listener_info) {
+  DCHECK(UsesInputEventing());
+  input_event_listener_.Bind(std::move(input_listener_info));
+}
+
 void XRCompositorCommon::GetControllerDataAndSendFrameData(
     XRFrameDataProvider::GetFrameDataCallback callback,
     mojom::XRFrameDataPtr frame_data) {
diff --git a/device/vr/windows/compositor_base.h b/device/vr/windows/compositor_base.h
index fcc6d4a8..1b92c77 100644
--- a/device/vr/windows/compositor_base.h
+++ b/device/vr/windows/compositor_base.h
@@ -60,6 +60,9 @@
 
   void GetFrameData(mojom::XRFrameDataRequestOptionsPtr options,
                     XRFrameDataProvider::GetFrameDataCallback callback) final;
+  void SetInputSourceButtonListener(
+      mojom::XRInputSourceButtonListenerAssociatedPtrInfo input_listener_info)
+      override;
   void GetControllerDataAndSendFrameData(
       XRFrameDataProvider::GetFrameDataCallback callback,
       mojom::XRFrameDataPtr frame_data);
@@ -68,6 +71,7 @@
   void RequestOverlay(mojom::ImmersiveOverlayRequest request);
 
  protected:
+  virtual bool UsesInputEventing();
 #if defined(OS_WIN)
   D3D11TextureHelper texture_helper_;
 #endif
@@ -75,6 +79,7 @@
 
   // Allow derived classes to call methods on the main thread.
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+  mojom::XRInputSourceButtonListenerAssociatedPtr input_event_listener_;
 
  private:
   // base::Thread overrides:
diff --git a/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc b/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
index f8dfbec6..a4527c02 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
+++ b/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
@@ -14,9 +14,11 @@
 #include <vector>
 
 #include "base/strings/safe_sprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "device/gamepad/public/cpp/gamepads.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
 #include "device/vr/util/gamepad_builder.h"
+#include "device/vr/windows_mixed_reality/mixed_reality_renderloop.h"
 #include "device/vr/windows_mixed_reality/wrappers/wmr_input_location.h"
 #include "device/vr/windows_mixed_reality/wrappers/wmr_input_manager.h"
 #include "device/vr/windows_mixed_reality/wrappers/wmr_input_source.h"
@@ -85,43 +87,47 @@
   AddAxes(gamepad, data);
 }
 
-std::vector<float> ConvertToVector(GamepadVector vector) {
-  return {vector.x, vector.y, vector.z};
+gfx::Point3F ConvertToPoint3F(const GamepadVector& vector) {
+  return gfx::Point3F(vector.x, vector.y, vector.z);
 }
 
-std::vector<float> ConvertToVector(GamepadQuaternion quat) {
-  return {quat.x, quat.y, quat.z, quat.w};
+gfx::Vector3dF ConvertToVector3dF(const GamepadVector& vector) {
+  return gfx::Vector3dF(vector.x, vector.y, vector.z);
 }
 
-mojom::VRPosePtr ConvertToVRPose(GamepadPose gamepad_pose) {
+gfx::Quaternion ConvertToQuaternion(const GamepadQuaternion& quat) {
+  return gfx::Quaternion(quat.x, quat.y, quat.z, quat.w);
+}
+
+mojom::VRPosePtr ConvertToVRPose(const GamepadPose& gamepad_pose) {
   if (!gamepad_pose.not_null)
     return nullptr;
 
   auto pose = mojom::VRPose::New();
   if (gamepad_pose.orientation.not_null)
-    pose->orientation = ConvertToVector(gamepad_pose.orientation);
+    pose->orientation = ConvertToQuaternion(gamepad_pose.orientation);
 
   if (gamepad_pose.position.not_null)
-    pose->position = ConvertToVector(gamepad_pose.position);
+    pose->position = ConvertToPoint3F(gamepad_pose.position);
 
   if (gamepad_pose.angular_velocity.not_null)
-    pose->angularVelocity = ConvertToVector(gamepad_pose.angular_velocity);
+    pose->angularVelocity = ConvertToVector3dF(gamepad_pose.angular_velocity);
 
   if (gamepad_pose.linear_velocity.not_null)
-    pose->linearVelocity = ConvertToVector(gamepad_pose.linear_velocity);
+    pose->linearVelocity = ConvertToVector3dF(gamepad_pose.linear_velocity);
 
   if (gamepad_pose.angular_acceleration.not_null)
     pose->angularAcceleration =
-        ConvertToVector(gamepad_pose.angular_acceleration);
+        ConvertToVector3dF(gamepad_pose.angular_acceleration);
 
   if (gamepad_pose.linear_acceleration.not_null)
     pose->linearAcceleration =
-        ConvertToVector(gamepad_pose.linear_acceleration);
+        ConvertToVector3dF(gamepad_pose.linear_acceleration);
 
   return pose;
 }
 
-GamepadQuaternion ConvertToGamepadQuaternion(WFN::Quaternion quat) {
+GamepadQuaternion ConvertToGamepadQuaternion(const WFN::Quaternion& quat) {
   GamepadQuaternion gamepad_quaternion;
   gamepad_quaternion.not_null = true;
   gamepad_quaternion.x = quat.X;
@@ -131,7 +137,7 @@
   return gamepad_quaternion;
 }
 
-GamepadVector ConvertToGamepadVector(WFN::Vector3 vec3) {
+GamepadVector ConvertToGamepadVector(const WFN::Vector3& vec3) {
   GamepadVector gamepad_vector;
   gamepad_vector.not_null = true;
   gamepad_vector.x = vec3.X;
@@ -388,7 +394,13 @@
 }
 }  // namespace
 
-MixedRealityInputHelper::MixedRealityInputHelper(HWND hwnd) : hwnd_(hwnd) {}
+MixedRealityInputHelper::MixedRealityInputHelper(
+    HWND hwnd,
+    const base::WeakPtr<MixedRealityRenderLoop>& weak_render_loop)
+    : hwnd_(hwnd),
+      task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      weak_render_loop_(weak_render_loop),
+      weak_ptr_factory_(this) {}
 
 MixedRealityInputHelper::~MixedRealityInputHelper() {
   // Dispose must be called before destruction, which ensures that we're
@@ -427,31 +439,15 @@
 
   auto source_states =
       input_manager_->GetDetectedSourcesAtTimestamp(timestamp->GetRawPtr());
-  // This can't be acquired until after GetDetectedSourcesAtTimestamp() because
-  // otherwise the tests will deadlock when triggering pressed/released
-  // callbacks.
-  base::AutoLock scoped_lock(lock_);
+
   for (const auto& state : source_states) {
-    auto parsed_source_state =
-        LockedParseWindowsSourceState(state.get(), origin);
+    auto parsed_source_state = ParseWindowsSourceState(state.get(), origin);
 
     if (parsed_source_state.source_state) {
-      parsed_source_state.source_state->gamepad =
-          GetWebXRGamepad(parsed_source_state);
       input_states.push_back(std::move(parsed_source_state.source_state));
     }
   }
 
-  for (const auto& state : pending_voice_states_) {
-    auto parsed_source_state =
-        LockedParseWindowsSourceState(state.get(), origin);
-
-    if (parsed_source_state.source_state)
-      input_states.push_back(std::move(parsed_source_state.source_state));
-  }
-
-  pending_voice_states_.clear();
-
   return input_states;
 }
 
@@ -465,13 +461,9 @@
 
   auto source_states =
       input_manager_->GetDetectedSourcesAtTimestamp(timestamp->GetRawPtr());
-  // This can't be acquired until after GetDetectedSourcesAtTimestamp() because
-  // otherwise the tests will deadlock when triggering pressed/released
-  // callbacks.
-  base::AutoLock scoped_lock(lock_);
+
   for (const auto& state : source_states) {
-    auto parsed_source_state =
-        LockedParseWindowsSourceState(state.get(), origin);
+    auto parsed_source_state = ParseWindowsSourceState(state.get(), origin);
 
     // If we have a source_state, then we should have enough data.
     if (parsed_source_state.source_state)
@@ -481,7 +473,7 @@
   return ret;
 }
 
-ParsedInputState MixedRealityInputHelper::LockedParseWindowsSourceState(
+ParsedInputState MixedRealityInputHelper::ParseWindowsSourceState(
     const WMRInputSourceState* state,
     const WMRCoordinateSystem* origin) {
   ParsedInputState input_state;
@@ -550,7 +542,6 @@
   source_state->source_id = id;
   source_state->primary_input_pressed = controller_states_[id].pressed;
   source_state->primary_input_clicked = controller_states_[id].clicked;
-  controller_states_[id].clicked = false;
 
   // Grip position should *only* be specified if the controller is tracked.
   if (is_tracked)
@@ -577,32 +568,37 @@
 
   input_state.source_state = std::move(source_state);
 
+  input_state.source_state->gamepad = GetWebXRGamepad(input_state);
+
   return input_state;
 }
 
 void MixedRealityInputHelper::OnSourcePressed(
     const WMRInputSourceEventArgs& args) {
-  ProcessSourceEvent(args, true /* is_pressed */);
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&MixedRealityInputHelper::ProcessSourceEvent,
+                     weak_ptr_factory_.GetWeakPtr(), args.PressKind(),
+                     args.State(), true /* is_pressed */));
 }
 
 void MixedRealityInputHelper::OnSourceReleased(
     const WMRInputSourceEventArgs& args) {
-  ProcessSourceEvent(args, false /* is_pressed */);
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&MixedRealityInputHelper::ProcessSourceEvent,
+                     weak_ptr_factory_.GetWeakPtr(), args.PressKind(),
+                     args.State(), false /* is_pressed */));
 }
 
 void MixedRealityInputHelper::ProcessSourceEvent(
-    const WMRInputSourceEventArgs& args,
+    PressKind press_kind,
+    std::unique_ptr<WMRInputSourceState> state,
     bool is_pressed) {
-  base::AutoLock scoped_lock(lock_);
-
-  PressKind press_kind = args.PressKind();
-
   if (press_kind != PressKind::SpatialInteractionPressKind_Select)
     return;
 
-  std::unique_ptr<WMRInputSourceState> state = args.State();
   std::unique_ptr<WMRInputSource> source = state->GetSource();
-
   SourceKind source_kind = source->Kind();
 
   if (source_kind != SourceKind::SpatialInteractionSourceKind_Controller &&
@@ -612,15 +608,24 @@
   uint32_t id = GetSourceId(source.get());
 
   bool wasPressed = controller_states_[id].pressed;
-  bool wasClicked = controller_states_[id].clicked;
   controller_states_[id].pressed = is_pressed;
-  controller_states_[id].clicked = wasClicked || (wasPressed && !is_pressed);
+  controller_states_[id].clicked = (wasPressed && !is_pressed);
 
-  // Tracked controllers show up when we poll for DetectedSources, but voice
-  // does not.
-  if (source_kind == SourceKind::SpatialInteractionSourceKind_Voice &&
-      !is_pressed)
-    pending_voice_states_.push_back(std::move(state));
+  if (!weak_render_loop_)
+    return;
+
+  auto* origin = weak_render_loop_->GetOrigin();
+  if (!origin)
+    return;
+
+  auto parsed_source_state = ParseWindowsSourceState(state.get(), origin);
+  if (parsed_source_state.source_state) {
+    weak_render_loop_->OnInputSourceEvent(
+        std::move(parsed_source_state.source_state));
+  }
+
+  // We've sent up the click, so clear it.
+  controller_states_[id].clicked = false;
 }
 
 void MixedRealityInputHelper::SubscribeEvents() {
diff --git a/device/vr/windows_mixed_reality/mixed_reality_input_helper.h b/device/vr/windows_mixed_reality/mixed_reality_input_helper.h
index f85090d..c09b85df 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_input_helper.h
+++ b/device/vr/windows_mixed_reality/mixed_reality_input_helper.h
@@ -13,7 +13,6 @@
 #include <vector>
 
 #include "base/callback_list.h"
-#include "base/synchronization/lock.h"
 #include "device/gamepad/public/cpp/gamepads.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
@@ -44,9 +43,14 @@
 class WMRInputSourceState;
 class WMRInputSourceEventArgs;
 class WMRTimestamp;
+class MixedRealityRenderLoop;
 class MixedRealityInputHelper {
  public:
-  MixedRealityInputHelper(HWND hwnd);
+  // Note that the WeakPtr should be resolvable on the thread this is created
+  // on, (which should be the render loop thread).
+  MixedRealityInputHelper(
+      HWND hwnd,
+      const base::WeakPtr<MixedRealityRenderLoop>& weak_render_loop);
   virtual ~MixedRealityInputHelper();
   std::vector<mojom::XRInputSourceStatePtr> GetInputState(
       const WMRCoordinateSystem* origin,
@@ -60,13 +64,18 @@
  private:
   bool EnsureSpatialInteractionManager();
 
-  ParsedInputState LockedParseWindowsSourceState(
-      const WMRInputSourceState* state,
-      const WMRCoordinateSystem* origin);
+  ParsedInputState ParseWindowsSourceState(const WMRInputSourceState* state,
+                                           const WMRCoordinateSystem* origin);
 
+  // These event subscriptions can come back on a different thread, while
+  // everything else is expected to come back on the same thread.
   void OnSourcePressed(const WMRInputSourceEventArgs& args);
   void OnSourceReleased(const WMRInputSourceEventArgs& args);
-  void ProcessSourceEvent(const WMRInputSourceEventArgs& args, bool is_pressed);
+
+  void ProcessSourceEvent(
+      ABI::Windows::UI::Input::Spatial::SpatialInteractionPressKind press_kind,
+      std::unique_ptr<WMRInputSourceState> state,
+      bool is_pressed);
 
   void SubscribeEvents();
   void UnsubscribeEvents();
@@ -89,9 +98,13 @@
   std::unordered_map<uint32_t, ControllerState> controller_states_;
   HWND hwnd_;
 
-  std::vector<std::unique_ptr<WMRInputSourceState>> pending_voice_states_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
-  base::Lock lock_;
+  // Must be resolved on the task_runner_ thread, which is the thread we were
+  // created on (and should correspond to the render loop thread)
+  base::WeakPtr<MixedRealityRenderLoop> weak_render_loop_;
+
+  base::WeakPtrFactory<MixedRealityInputHelper> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(MixedRealityInputHelper);
 };
diff --git a/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc b/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc
index 20f449fb..ba6aba4 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc
+++ b/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc
@@ -79,19 +79,19 @@
 }
 
 namespace {
-std::vector<float> ConvertToStandingTransform(const Matrix4x4& transform) {
-  return {transform.M11, transform.M12, transform.M13, transform.M14,
-          transform.M21, transform.M22, transform.M23, transform.M24,
-          transform.M31, transform.M32, transform.M33, transform.M34,
-          transform.M41, transform.M42, transform.M43, transform.M44};
+gfx::Transform ConvertToGfxTransform(const Matrix4x4& matrix) {
+  // clang-format off
+  return gfx::Transform(
+      matrix.M11, matrix.M21, matrix.M31, matrix.M41,
+      matrix.M12, matrix.M22, matrix.M32, matrix.M42,
+      matrix.M13, matrix.M23, matrix.M33, matrix.M43,
+      matrix.M14, matrix.M24, matrix.M34, matrix.M44
+    );
+  // clang-format on
 }
 
 mojom::VRFieldOfViewPtr ParseProjection(const Matrix4x4& projection) {
-  gfx::Transform proj(
-      projection.M11, projection.M21, projection.M31, projection.M41,
-      projection.M12, projection.M22, projection.M32, projection.M42,
-      projection.M13, projection.M23, projection.M33, projection.M43,
-      projection.M14, projection.M24, projection.M34, projection.M44);
+  gfx::Transform proj = ConvertToGfxTransform(projection);
 
   gfx::Transform projInv;
   bool invertable = proj.GetInverse(&projInv);
@@ -144,6 +144,16 @@
   Stop();
 }
 
+const WMRCoordinateSystem* MixedRealityRenderLoop::GetOrigin() {
+  return anchor_origin_.get();
+}
+
+void MixedRealityRenderLoop::OnInputSourceEvent(
+    mojom::XRInputSourceStatePtr input_state) {
+  if (input_event_listener_)
+    input_event_listener_->OnButtonEvent(std::move(input_state));
+}
+
 bool MixedRealityRenderLoop::PreComposite() {
   if (rendering_params_) {
     ComPtr<ID3D11Texture2D> texture =
@@ -212,7 +222,8 @@
   if (!holographic_space_)
     return false;
 
-  input_helper_ = std::make_unique<MixedRealityInputHelper>(window_->hwnd());
+  input_helper_ = std::make_unique<MixedRealityInputHelper>(
+      window_->hwnd(), weak_ptr_factory_.GetWeakPtr());
 
   ABI::Windows::Graphics::Holographic::HolographicAdapterId id =
       holographic_space_->PrimaryAdapterId();
@@ -281,6 +292,10 @@
   }
 }
 
+bool MixedRealityRenderLoop::UsesInputEventing() {
+  return true;
+}
+
 void MixedRealityRenderLoop::InitializeOrigin() {
   TRACE_EVENT0("xr", "InitializeOrigin");
 
@@ -449,10 +464,7 @@
 
 EyeToWorldDecomposed DecomposeViewMatrix(
     const ABI::Windows::Foundation::Numerics::Matrix4x4& view) {
-  gfx::Transform world_to_view(view.M11, view.M21, view.M31, view.M41, view.M12,
-                               view.M22, view.M32, view.M42, view.M13, view.M23,
-                               view.M33, view.M43, view.M14, view.M24, view.M34,
-                               view.M44);
+  gfx::Transform world_to_view = ConvertToGfxTransform(view);
 
   gfx::Transform view_to_world;
   bool invertable = world_to_view.GetInverse(&view_to_world);
@@ -477,24 +489,20 @@
   auto pose = mojom::VRPose::New();
 
   // World to device orientation.
-  pose->orientation =
-      std::vector<float>{static_cast<float>(eye.world_to_eye_rotation.x()),
-                         static_cast<float>(eye.world_to_eye_rotation.y()),
-                         static_cast<float>(eye.world_to_eye_rotation.z()),
-                         static_cast<float>(eye.world_to_eye_rotation.w())};
+  pose->orientation = eye.world_to_eye_rotation;
 
   // Position in world space.
   pose->position =
-      std::vector<float>{eye.eye_in_world_space.x(), eye.eye_in_world_space.y(),
-                         eye.eye_in_world_space.z()};
+      gfx::Point3F(eye.eye_in_world_space.x(), eye.eye_in_world_space.y(),
+                   eye.eye_in_world_space.z());
 
   return pose;
 }
 
 struct PoseAndEyeTransform {
   mojom::VRPosePtr pose;
-  std::vector<float> left_offset;
-  std::vector<float> right_offset;
+  gfx::Vector3dF left_offset;
+  gfx::Vector3dF right_offset;
 };
 
 PoseAndEyeTransform GetStereoViewData(const HolographicStereoTransform& view) {
@@ -513,20 +521,15 @@
   world_to_view_rotation.Slerp(right_eye.world_to_eye_rotation, 0.5f);
 
   // Calculate new eye offsets.
-  gfx::Vector3dF left_offset = left_eye.eye_in_world_space - center;
-  gfx::Vector3dF right_offset = right_eye.eye_in_world_space - center;
+  PoseAndEyeTransform ret;
+  ret.left_offset = left_eye.eye_in_world_space - center;
+  ret.right_offset = right_eye.eye_in_world_space - center;
 
   gfx::Transform transform(world_to_view_rotation);  // World to view.
   transform.Transpose();                             // Now it is view to world.
 
-  transform.TransformVector(&left_offset);  // Offset is now in view space
-  transform.TransformVector(&right_offset);
-
-  PoseAndEyeTransform ret;
-  ret.right_offset =
-      std::vector<float>{right_offset.x(), right_offset.y(), right_offset.z()};
-  ret.left_offset =
-      std::vector<float>{left_offset.x(), left_offset.y(), left_offset.z()};
+  transform.TransformVector(&(ret.left_offset));  // Offset is now in view space
+  transform.TransformVector(&(ret.right_offset));
 
   // TODO(https://crbug.com/928433): We don't currently support per-eye rotation
   // in the mojo interface, but we should.
@@ -534,14 +537,10 @@
   ret.pose = mojom::VRPose::New();
 
   // World to device orientation.
-  ret.pose->orientation =
-      std::vector<float>{static_cast<float>(world_to_view_rotation.x()),
-                         static_cast<float>(world_to_view_rotation.y()),
-                         static_cast<float>(world_to_view_rotation.z()),
-                         static_cast<float>(world_to_view_rotation.w())};
+  ret.pose->orientation = world_to_view_rotation;
 
   // Position in world space.
-  ret.pose->position = std::vector<float>{center.x(), center.y(), center.z()};
+  ret.pose->position = gfx::Point3F(center.x(), center.y(), center.z());
 
   return ret;
 }
@@ -709,7 +708,7 @@
       }
 
       stage_parameters->standingTransform =
-          ConvertToStandingTransform(origin_to_stage);
+          ConvertToGfxTransform(origin_to_stage);
 
       current_display_info_->stageParameters = std::move(stage_parameters);
     }
@@ -758,14 +757,10 @@
     // TODO(http://crbug.com/931393): Send down emulated_position_, and report
     // reset events when this changes.
     emulated_position_ = false;
-    ABI::Windows::Foundation::Numerics::Matrix4x4 transform;
+    ABI::Windows::Foundation::Numerics::Matrix4x4 origin_from_attached;
     if (attached_coordinates->TryGetTransformTo(anchor_origin_.get(),
-                                                &transform)) {
-      last_origin_from_attached_ = gfx::Transform(
-          transform.M11, transform.M21, transform.M31, transform.M41,
-          transform.M12, transform.M22, transform.M32, transform.M42,
-          transform.M13, transform.M23, transform.M33, transform.M43,
-          transform.M14, transform.M24, transform.M34, transform.M44);
+                                                &origin_from_attached)) {
+      last_origin_from_attached_ = ConvertToGfxTransform(origin_from_attached);
     }
   } else {
     emulated_position_ = true;
@@ -806,7 +801,7 @@
     }
   } else {
     ret->pose = GetMonoViewData(view);
-    std::vector<float> offset = {0, 0, 0};
+    gfx::Vector3dF offset;
     if (current_display_info_->leftEye->offset != offset) {
       current_display_info_->leftEye->offset = offset;
       send_new_display_info = true;
@@ -838,12 +833,12 @@
 
   if (emulated_position_ && last_origin_from_attached_) {
     gfx::DecomposedTransform attached_from_view_decomp;
-    attached_from_view_decomp.quaternion = gfx::Quaternion(
-        (*ret->pose->orientation)[0], (*ret->pose->orientation)[1],
-        (*ret->pose->orientation)[2], (*ret->pose->orientation)[3]);
-    for (int i = 0; i < 3; ++i) {
-      attached_from_view_decomp.translate[i] = (*ret->pose->position)[i];
-    }
+    attached_from_view_decomp.quaternion = (*ret->pose->orientation);
+
+    attached_from_view_decomp.translate[0] = ret->pose->position->x();
+    attached_from_view_decomp.translate[1] = ret->pose->position->y();
+    attached_from_view_decomp.translate[2] = ret->pose->position->z();
+
     gfx::Transform attached_from_view =
         gfx::ComposeTransform(attached_from_view_decomp);
     gfx::Transform origin_from_view =
@@ -852,15 +847,11 @@
     bool success =
         gfx::DecomposeTransform(&origin_from_view_decomposed, origin_from_view);
     DCHECK(success);
-    ret->pose->orientation = std::vector<float>{
-        static_cast<float>(origin_from_view_decomposed.quaternion.x()),
-        static_cast<float>(origin_from_view_decomposed.quaternion.y()),
-        static_cast<float>(origin_from_view_decomposed.quaternion.z()),
-        static_cast<float>(origin_from_view_decomposed.quaternion.w())};
-    ret->pose->position = std::vector<float>{
+    ret->pose->orientation = origin_from_view_decomposed.quaternion;
+    ret->pose->position = gfx::Point3F(
         static_cast<float>(origin_from_view_decomposed.translate[0]),
         static_cast<float>(origin_from_view_decomposed.translate[1]),
-        static_cast<float>(origin_from_view_decomposed.translate[2])};
+        static_cast<float>(origin_from_view_decomposed.translate[2]));
   }
 
   return ret;
diff --git a/device/vr/windows_mixed_reality/mixed_reality_renderloop.h b/device/vr/windows_mixed_reality/mixed_reality_renderloop.h
index d0ebf0e2..8e72052 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_renderloop.h
+++ b/device/vr/windows_mixed_reality/mixed_reality_renderloop.h
@@ -45,11 +45,15 @@
           on_display_info_changed);
   ~MixedRealityRenderLoop() override;
 
+  const WMRCoordinateSystem* GetOrigin();
+  void OnInputSourceEvent(mojom::XRInputSourceStatePtr input_state);
+
  private:
   // XRCompositorCommon:
   bool StartRuntime() override;
   void StopRuntime() override;
   void OnSessionStart() override;
+  bool UsesInputEventing() override;
 
   // XRDeviceAbstraction:
   mojom::XRFrameDataPtr GetNextFrameData() override;
diff --git a/docs/accessibility/overview.md b/docs/accessibility/overview.md
index 1d45976..380f2a8 100644
--- a/docs/accessibility/overview.md
+++ b/docs/accessibility/overview.md
@@ -508,7 +508,7 @@
 [ax_enums.idl].
 
 [AccessibilityHostMsg_EventParams]: https://cs.chromium.org/chromium/src/content/common/accessibility_messages.h?sq=package:chromium&l=75
-[AutomationInternalCustomBindings]: https://cs.chromium.org/chromium/src/chrome/renderer/extensions/automation_internal_custom_bindings.h
+[AutomationInternalCustomBindings]: https://cs.chromium.org/chromium/src/extensions/renderer/api/automation/automation_internal_custom_bindings.h
 [AXContentNodeData]: https://cs.chromium.org/chromium/src/content/common/ax_content_node_data.h
 [AXLayoutObject]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
 [AXNodeObject]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -529,7 +529,7 @@
 [ui::AXNodeData]: https://cs.chromium.org/chromium/src/ui/accessibility/ax_node_data.h
 [WebAXObject]: https://cs.chromium.org/chromium/src/third_party/blink/public/web/web_ax_object.h
 [automation API]: https://cs.chromium.org/chromium/src/chrome/renderer/resources/extensions/automation
-[automation.idl]: https://cs.chromium.org/chromium/src/chrome/common/extensions/api/automation.idl
+[automation.idl]: https://cs.chromium.org/chromium/src/extensions/common/api/automation.idl
 [ax_enums.idl]: https://cs.chromium.org/chromium/src/ui/accessibility/ax_enums.idl
 [chrome.automation API]: https://developer.chrome.com/extensions/automation
 [webui-js]: https://cs.chromium.org/chromium/src/ui/webui/resources/js/cr/ui/
diff --git a/docs/linux_development.md b/docs/linux_development.md
index 4d010de..782c31f4 100644
--- a/docs/linux_development.md
+++ b/docs/linux_development.md
@@ -33,7 +33,7 @@
 
 ## Contributing code
 
-See [Contributing code](https://dev.chromium.org/developers/contributing-code).
+See [Contributing code](contributing.md).
 
 ## Debugging
 
diff --git a/docs/speed/benchmark/harnesses/power_perf.md b/docs/speed/benchmark/harnesses/power_perf.md
index 0437251..6eafcff 100644
--- a/docs/speed/benchmark/harnesses/power_perf.md
+++ b/docs/speed/benchmark/harnesses/power_perf.md
@@ -32,7 +32,6 @@
 - **`power.desktop`**: A desktop-only benchmark made up of two types of pages:
   - Pages focusing on a single, extremely simple behavior (e.g. a blinking cursor, a CSS blur animation)
   - Pages on which Chrome has exhibited pathological idle behavior in the past
-- **`power.typical_10_mobile`**: A mobile-only benchmark which visits ten popular sites and uses Android-specific APIs to measure approximately how much power is consumed. This benchmark is necessary to provide data to the Android System Health Council to assess whether Chrome Android is fit for release
 - **`media.desktop`**: A desktop-only benchmark in which each page tests a particular media-related scenario (e.g. playing a 1080p, H264 video with sound)
 - **`media.mobile`**: A mobile-only benchmark that parallels `media.desktop`
 
diff --git a/docs/testing/code_coverage.md b/docs/testing/code_coverage.md
index 08a2ae96..5fd5290 100644
--- a/docs/testing/code_coverage.md
+++ b/docs/testing/code_coverage.md
@@ -305,7 +305,7 @@
 
 The code used by the bots that generate the coverage data lives (among other
 places) in the
-[clang coverage recipe module](https://chromium.googlesource.com/chromium/tools/build/+/master/scripts/slave/recipe_modules/clang_coverage/).
+[code coverage recipe module](https://chromium.googlesource.com/chromium/tools/build/+/master/scripts/slave/recipe_modules/code_coverage/).
 
 ### Why is coverage for X not reported or unreasonably low, even though there is a test for X?
 
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index dd1eb2c0..774a509 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -686,6 +686,7 @@
     "//device/bluetooth:mocks",
     "//extensions:extensions_browser_resources",
     "//extensions:test_support",
+    "//extensions/browser/api/declarative_net_request/filter_list_converter:unit_tests",
     "//extensions/buildflags",
     "//extensions/common",
     "//extensions/common:test_support",
diff --git a/extensions/browser/api/app_window/app_window_apitest.cc b/extensions/browser/api/app_window/app_window_apitest.cc
index 2b46a3b..428393c 100644
--- a/extensions/browser/api/app_window/app_window_apitest.cc
+++ b/extensions/browser/api/app_window/app_window_apitest.cc
@@ -117,9 +117,7 @@
       "platform_apps/windows_api_always_on_top/no_permissions")) << message_;
 }
 
-// https://crbug.com/978272 - semi-consistently timing out on Linux CFI bots,
-// and flaking in some Windows/ChromeOS try runs.
-IN_PROC_BROWSER_TEST_F(AppWindowApiTest, DISABLED_Get) {
+IN_PROC_BROWSER_TEST_F(AppWindowApiTest, Get) {
   EXPECT_TRUE(RunPlatformAppTest("platform_apps/windows_api_get"))
       << message_;
 }
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/BUILD.gn b/extensions/browser/api/declarative_net_request/filter_list_converter/BUILD.gn
new file mode 100644
index 0000000..77037ea
--- /dev/null
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/BUILD.gn
@@ -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.
+
+import("//extensions/buildflags/buildflags.gni")
+
+assert(enable_extensions)
+
+source_set("support") {
+  testonly = true
+  sources = [
+    "converter.cc",
+    "converter.h",
+  ]
+  deps = [
+    "//base",
+    "//components/subresource_filter/tools/ruleset_converter:support",
+    "//extensions/browser",
+    "//extensions/common",
+    "//extensions/common:test_support",
+    "//url",
+  ]
+}
+
+executable("filter_list_converter") {
+  testonly = true
+  sources = [
+    "main.cc",
+  ]
+  deps = [
+    ":support",
+    "//base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "converter_unittest.cc",
+  ]
+  deps = [
+    ":support",
+    "//base",
+    "//testing/gtest",
+  ]
+}
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/DEPS b/extensions/browser/api/declarative_net_request/filter_list_converter/DEPS
new file mode 100644
index 0000000..c49e525
--- /dev/null
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/subresource_filter/tools/ruleset_converter",
+]
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/README b/extensions/browser/api/declarative_net_request/filter_list_converter/README
new file mode 100644
index 0000000..28509a94
--- /dev/null
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/README
@@ -0,0 +1,6 @@
+Filter List Converter is a tool to convert filter list files in the text format
+to a JSON file in a format supported by the Declarative Net Request API. It can
+either output the complete extension or just the JSON ruleset.
+
+This is based on the ruleset converter module in the subresource_filter
+component.
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/converter.cc b/extensions/browser/api/declarative_net_request/filter_list_converter/converter.cc
new file mode 100644
index 0000000..cae7606
--- /dev/null
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/converter.cc
@@ -0,0 +1,513 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/declarative_net_request/filter_list_converter/converter.h"
+
+#include <fstream>
+#include <string>
+#include <utility>
+
+#include "base/json/json_file_value_serializer.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "components/subresource_filter/tools/ruleset_converter/rule_stream.h"
+#include "extensions/browser/api/declarative_net_request/constants.h"
+#include "extensions/browser/api/declarative_net_request/indexed_rule.h"
+#include "extensions/common/api/declarative_net_request.h"
+#include "extensions/common/api/declarative_net_request/constants.h"
+#include "extensions/common/api/declarative_net_request/test_utils.h"
+#include "url/gurl.h"
+
+namespace extensions {
+namespace declarative_net_request {
+
+namespace {
+
+namespace proto = ::url_pattern_index::proto;
+namespace dnr_api = extensions::api::declarative_net_request;
+
+using ElementTypeMap =
+    base::flat_map<proto::ElementType, dnr_api::ResourceType>;
+
+constexpr char kJSONRulesFilename[] = "rules.json";
+const base::FilePath::CharType kJSONRulesetFilepath[] =
+    FILE_PATH_LITERAL("rules.json");
+
+// Utility class to convert the proto::UrlRule format to the JSON format
+// supported by Declarative Net Request.
+class ProtoToJSONRuleConverter {
+ public:
+  // Returns a dictionary value corresponding to a Declarative Net Request rule
+  // on success. On error, returns an empty/null value and populates |error|.
+  // |error| must be non-null.
+  static base::Value Convert(const proto::UrlRule& rule,
+                             int rule_id,
+                             std::string* error) {
+    CHECK(error);
+    ProtoToJSONRuleConverter json_rule(rule, rule_id);
+    return json_rule.Convert(error);
+  }
+
+ private:
+  ProtoToJSONRuleConverter(const proto::UrlRule& rule, int rule_id)
+      : input_rule_(rule),
+        rule_id_(rule_id),
+        json_rule_(base::Value::Type::DICTIONARY) {}
+
+  base::Value Convert(std::string* error) {
+    CHECK(error);
+
+    // Populate all the keys.
+    bool success = CheckActivationType() && PopulateID() &&
+                   PopulatePriorirty() && PopulateURLFilter() &&
+                   PopulateIsURLFilterCaseSensitive() && PopulateDomains() &&
+                   PopulateExcludedDomains() && PopulateResourceTypes() &&
+                   PopulateExcludedResourceTypes() && PopulateDomainType() &&
+                   PopulateRuleActionType() && PopulateRedirectURL() &&
+                   PopulateRemoveHeadersList();
+
+    if (!success) {
+      CHECK(!error_.empty());
+      *error = std::move(error_);
+      return base::Value();
+    }
+
+    // Sanity check that we can parse this rule.
+    base::string16 err;
+    dnr_api::Rule rule;
+    CHECK(dnr_api::Rule::Populate(json_rule_, &rule, &err) && err.empty())
+        << "Converted rule can't be parsed " << json_rule_;
+
+    IndexedRule indexed_rule;
+    ParseResult result = IndexedRule::CreateIndexedRule(
+        std::move(rule), GURL() /* base_url */, &indexed_rule);
+
+    auto get_non_ascii_error = [this](const std::string& context) {
+      return base::StringPrintf(
+          "Rule with filter '%s' ignored due to non ascii characters in %s.",
+          input_rule_.url_pattern().c_str(), context.c_str());
+    };
+
+    // Non-ascii characters in rules are not supported.
+    if (result == ParseResult::ERROR_NON_ASCII_URL_FILTER) {
+      *error = get_non_ascii_error("url filter");
+      return base::Value();
+    }
+    if (result == ParseResult::ERROR_NON_ASCII_DOMAIN) {
+      *error = get_non_ascii_error("domains");
+      return base::Value();
+    }
+    if (result == ParseResult::ERROR_NON_ASCII_EXCLUDED_DOMAIN) {
+      *error = get_non_ascii_error("excluded domains");
+      return base::Value();
+    }
+
+    CHECK_EQ(ParseResult::SUCCESS, result)
+        << "Unexpected parse error << " << static_cast<int>(result)
+        << " for rule " << json_rule_;
+
+    return std::move(json_rule_);
+  }
+
+  bool CheckActivationType() {
+    if (input_rule_.activation_types() == proto::ACTIVATION_TYPE_UNSPECIFIED)
+      return true;
+
+    std::vector<std::string> activation_types;
+    for (int activation_type = 1; activation_type <= proto::ACTIVATION_TYPE_MAX;
+         activation_type <<= 1) {
+      CHECK(proto::ActivationType_IsValid(activation_type));
+      if (!(input_rule_.activation_types() & activation_type))
+        continue;
+
+      switch (static_cast<proto::ActivationType>(activation_type)) {
+        case proto::ACTIVATION_TYPE_UNSPECIFIED:
+          CHECK(false);
+          break;
+        case proto::ACTIVATION_TYPE_DOCUMENT:
+          activation_types.emplace_back("document");
+          break;
+        case proto::ACTIVATION_TYPE_ELEMHIDE:
+          activation_types.emplace_back("elemhide");
+          break;
+        case proto::ACTIVATION_TYPE_GENERICHIDE:
+          activation_types.emplace_back("generichide");
+          break;
+        case proto::ACTIVATION_TYPE_GENERICBLOCK:
+          activation_types.emplace_back("genericblock");
+          break;
+        case proto::ACTIVATION_TYPE_ALL:
+          CHECK(false);
+          break;
+      }
+    }
+
+    // We don't support any activation types.
+    error_ = base::StringPrintf(
+        "Rule with filter '%s' ignored due to invalid activation types-[%s].",
+        input_rule_.url_pattern().c_str(),
+        base::JoinString(activation_types, "," /* separator */).c_str());
+    return false;
+  }
+
+  bool PopulateID() {
+    CHECK_GE(rule_id_, kMinValidID);
+    CHECK(json_rule_.SetKey(kIDKey, base::Value(rule_id_)));
+    return true;
+  }
+
+  bool PopulatePriorirty() {
+    // Do nothing. Priority is optional and only relevant for redirect rules.
+    return true;
+  }
+
+  bool PopulateURLFilter() {
+    // Pattern type validation.
+    CHECK_NE(proto::URL_PATTERN_TYPE_UNSPECIFIED,
+             input_rule_.url_pattern_type());
+
+    // TODO(karandeepb): It would be nice to print the actual filter-list string
+    // in cases where rule conversion fails.
+    if (input_rule_.url_pattern_type() == proto::URL_PATTERN_TYPE_REGEXP) {
+      error_ = base::StringPrintf(
+          "Rule with filter %s ignored since regex rules are not supported.",
+          input_rule_.url_pattern().c_str());
+      return false;
+    }
+
+    std::string result;
+    switch (input_rule_.anchor_left()) {
+      case proto::ANCHOR_TYPE_NONE:
+        break;
+      case proto::ANCHOR_TYPE_BOUNDARY:
+        result += '|';
+        break;
+      case proto::ANCHOR_TYPE_SUBDOMAIN:
+        result += "||";
+        break;
+      case proto::ANCHOR_TYPE_UNSPECIFIED:
+        CHECK(false);
+        break;
+    }
+
+    result += input_rule_.url_pattern();
+
+    switch (input_rule_.anchor_right()) {
+      case proto::ANCHOR_TYPE_NONE:
+        break;
+      case proto::ANCHOR_TYPE_BOUNDARY:
+        result += '|';
+        break;
+      case proto::ANCHOR_TYPE_SUBDOMAIN:
+      case proto::ANCHOR_TYPE_UNSPECIFIED:
+        CHECK(false);
+        break;
+    }
+
+    // If |result| is empty, omit persisting the url pattern. In that case, it
+    // will match all urls.
+    if (!result.empty()) {
+      CHECK(json_rule_.SetPath({kRuleConditionKey, kUrlFilterKey},
+                               base::Value(result)));
+    }
+
+    return true;
+  }
+
+  bool PopulateIsURLFilterCaseSensitive() {
+    // Omit if case sensitive, since it's the default.
+    const bool case_sensitive = input_rule_.match_case();
+    if (case_sensitive)
+      return true;
+
+    CHECK(json_rule_.SetPath({kRuleConditionKey, kIsUrlFilterCaseSensitiveKey},
+                             base::Value(false)));
+    return true;
+  }
+
+  bool PopulateDomains() {
+    return PopulateDomainsInternal(kDomainsKey, false /*exclude_value*/);
+  }
+
+  bool PopulateExcludedDomains() {
+    return PopulateDomainsInternal(kExcludedDomainsKey, true /*exclude_value*/);
+  }
+
+  bool PopulateDomainsInternal(base::StringPiece sub_key, bool exclude_value) {
+    base::Value domains(base::Value::Type::LIST);
+
+    for (const proto::DomainListItem& item : input_rule_.domains()) {
+      if (item.exclude() == exclude_value)
+        domains.GetList().emplace_back(item.domain());
+    }
+
+    // Omit empty domain list.
+    if (!domains.GetList().empty()) {
+      CHECK(
+          json_rule_.SetPath({kRuleConditionKey, sub_key}, std::move(domains)));
+    }
+
+    return true;
+  }
+
+  bool PopulateResourceTypes() {
+    // Ensure that |element_types()| is a subset of proto::ElementType_ALL.
+    CHECK_EQ(proto::ELEMENT_TYPE_ALL,
+             proto::ELEMENT_TYPE_ALL | input_rule_.element_types());
+
+    base::Value resource_types(base::Value::Type::LIST);
+
+    int kMaskUnsupported =
+        proto::ELEMENT_TYPE_POPUP | proto::ELEMENT_TYPE_OBJECT_SUBREQUEST;
+
+    int element_mask = input_rule_.element_types() & (~kMaskUnsupported);
+
+    // We don't support object-subrequest. Instead let these be treated as rules
+    // matching object requests.
+    if (input_rule_.element_types() & proto::ELEMENT_TYPE_OBJECT_SUBREQUEST)
+      element_mask |= proto::ELEMENT_TYPE_OBJECT;
+
+    // No supported element types.
+    if (!element_mask) {
+      std::vector<std::string> element_types_removed;
+      if (input_rule_.element_types() & proto::ELEMENT_TYPE_POPUP)
+        element_types_removed.emplace_back("popup");
+      error_ = base::StringPrintf(
+          "Rule with filter %s and resource types [%s] ignored, no applicable "
+          "resource types",
+          input_rule_.url_pattern().c_str(),
+          base::JoinString(element_types_removed, "," /*separator*/).c_str());
+      return false;
+    }
+
+    // Omit resource types to block all subresources by default.
+    if (element_mask == (proto::ELEMENT_TYPE_ALL & ~kMaskUnsupported))
+      return true;
+
+    for (int element_type = 1; element_type <= proto::ElementType_MAX;
+         element_type <<= 1) {
+      CHECK(proto::ElementType_IsValid(element_type));
+
+      if (!(element_type & element_mask))
+        continue;
+
+      dnr_api::ResourceType resource_type = dnr_api::RESOURCE_TYPE_NONE;
+      switch (static_cast<proto::ElementType>(element_type)) {
+        case proto::ELEMENT_TYPE_UNSPECIFIED:
+          CHECK(false);
+          break;
+        case proto::ELEMENT_TYPE_OTHER:
+          resource_type = dnr_api::RESOURCE_TYPE_OTHER;
+          break;
+        case proto::ELEMENT_TYPE_SCRIPT:
+          resource_type = dnr_api::RESOURCE_TYPE_SCRIPT;
+          break;
+        case proto::ELEMENT_TYPE_IMAGE:
+          resource_type = dnr_api::RESOURCE_TYPE_IMAGE;
+          break;
+        case proto::ELEMENT_TYPE_STYLESHEET:
+          resource_type = dnr_api::RESOURCE_TYPE_STYLESHEET;
+          break;
+        case proto::ELEMENT_TYPE_OBJECT:
+          resource_type = dnr_api::RESOURCE_TYPE_OBJECT;
+          break;
+        case proto::ELEMENT_TYPE_XMLHTTPREQUEST:
+          resource_type = dnr_api::RESOURCE_TYPE_XMLHTTPREQUEST;
+          break;
+        case proto::ELEMENT_TYPE_OBJECT_SUBREQUEST:
+          // This was removed above.
+          CHECK(false);
+          break;
+        case proto::ELEMENT_TYPE_SUBDOCUMENT:
+          resource_type = dnr_api::RESOURCE_TYPE_SUB_FRAME;
+          break;
+        case proto::ELEMENT_TYPE_PING:
+          resource_type = dnr_api::RESOURCE_TYPE_PING;
+          break;
+        case proto::ELEMENT_TYPE_MEDIA:
+          resource_type = dnr_api::RESOURCE_TYPE_MEDIA;
+          break;
+        case proto::ELEMENT_TYPE_FONT:
+          resource_type = dnr_api::RESOURCE_TYPE_FONT;
+          break;
+        case proto::ELEMENT_TYPE_POPUP:
+          CHECK(false);
+          break;
+        case proto::ELEMENT_TYPE_WEBSOCKET:
+          resource_type = dnr_api::RESOURCE_TYPE_WEBSOCKET;
+          break;
+        case proto::ELEMENT_TYPE_ALL:
+          CHECK(false);
+          break;
+      }
+
+      resource_types.GetList().emplace_back(dnr_api::ToString(resource_type));
+    }
+
+    CHECK(json_rule_.SetPath({kRuleConditionKey, kResourceTypesKey},
+                             std::move(resource_types)));
+    return true;
+  }
+
+  bool PopulateExcludedResourceTypes() {
+    // We don't populate the "excludedResourceTypes" since that information has
+    // been processed away by conversion to a proto::UrlRule.
+    return true;
+  }
+
+  bool PopulateDomainType() {
+    dnr_api::DomainType domain_type = dnr_api::DOMAIN_TYPE_NONE;
+
+    switch (input_rule_.source_type()) {
+      case proto::SOURCE_TYPE_ANY:
+        // This is the default domain type and can be omitted.
+        return true;
+      case proto::SOURCE_TYPE_FIRST_PARTY:
+        domain_type = dnr_api::DOMAIN_TYPE_FIRSTPARTY;
+        break;
+      case proto::SOURCE_TYPE_THIRD_PARTY:
+        domain_type = dnr_api::DOMAIN_TYPE_THIRDPARTY;
+        break;
+      case proto::SOURCE_TYPE_UNSPECIFIED:
+        CHECK(false);
+        break;
+    }
+
+    CHECK_NE(dnr_api::DOMAIN_TYPE_NONE, domain_type);
+    CHECK(json_rule_.SetPath({kRuleConditionKey, kDomainTypeKey},
+                             base::Value(dnr_api::ToString(domain_type))));
+    return true;
+  }
+
+  bool PopulateRuleActionType() {
+    dnr_api::RuleActionType action_type = dnr_api::RULE_ACTION_TYPE_NONE;
+
+    switch (input_rule_.semantics()) {
+      case proto::RULE_SEMANTICS_BLACKLIST:
+        action_type = dnr_api::RULE_ACTION_TYPE_BLOCK;
+        break;
+      case proto::RULE_SEMANTICS_WHITELIST:
+        action_type = dnr_api::RULE_ACTION_TYPE_ALLOW;
+        break;
+      case proto::RULE_SEMANTICS_UNSPECIFIED:
+        CHECK(false);
+        break;
+    }
+
+    CHECK_NE(dnr_api::RULE_ACTION_TYPE_NONE, action_type);
+    CHECK(json_rule_.SetPath({kRuleActionKey, kRuleActionTypeKey},
+                             base::Value(dnr_api::ToString(action_type))));
+    return true;
+  }
+
+  bool PopulateRedirectURL() {
+    // Do nothing. The tool only supports allow and block rules.
+    return true;
+  }
+
+  bool PopulateRemoveHeadersList() {
+    // Do nothing. The tool only supports allow and block rules.
+    return true;
+  }
+
+  proto::UrlRule input_rule_;
+  int rule_id_;
+  std::string error_;
+  base::Value json_rule_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProtoToJSONRuleConverter);
+};
+
+// Writes rules/extension to |output_path| in the format supported by
+// Declarative Net Request.
+class DNRJsonRuleOutputStream : public subresource_filter::RuleOutputStream {
+ public:
+  DNRJsonRuleOutputStream(const base::FilePath& output_path,
+                          filter_list_converter::WriteType type,
+                          bool noisy)
+      : rule_id_(kMinValidID),
+        output_rules_list_(base::Value::Type::LIST),
+        output_path_(output_path),
+        write_type_(type),
+        noisy_(noisy) {}
+
+  bool PutUrlRule(const proto::UrlRule& rule) override {
+    std::string error;
+    base::Value json_rule_value =
+        ProtoToJSONRuleConverter::Convert(rule, rule_id_, &error);
+
+    if (json_rule_value.is_none()) {
+      if (noisy_) {
+        LOG(ERROR) << base::StringPrintf("Error for id %d: %s", rule_id_,
+                                         error.c_str());
+      }
+      return false;
+    }
+
+    CHECK(error.empty());
+    CHECK(json_rule_value.is_dict());
+    output_rules_list_.GetList().push_back(std::move(json_rule_value));
+    ++rule_id_;
+    return true;
+  }
+
+  bool PutCssRule(const proto::CssRule& rule) override {
+    // Ignore CSS rules.
+    return true;
+  }
+
+  bool Finish() override {
+    switch (write_type_) {
+      case filter_list_converter::kExtension:
+        WriteManifestAndRuleset(output_path_, kJSONRulesetFilepath,
+                                kJSONRulesFilename, output_rules_list_,
+                                {} /* hosts */);
+        break;
+      case filter_list_converter::kJSONRuleset:
+        JSONFileValueSerializer(output_path_).Serialize(output_rules_list_);
+        break;
+    }
+
+    return true;
+  }
+
+ private:
+  int rule_id_ = kMinValidID;
+  base::Value output_rules_list_;
+  const base::FilePath output_path_;
+  const filter_list_converter::WriteType write_type_;
+  const bool noisy_;
+
+  DISALLOW_COPY_AND_ASSIGN(DNRJsonRuleOutputStream);
+};
+
+}  // namespace
+
+namespace filter_list_converter {
+
+bool ConvertRuleset(const std::vector<base::FilePath>& filter_list_inputs,
+                    const base::FilePath& output_path,
+                    WriteType type,
+                    bool noisy) {
+  DNRJsonRuleOutputStream rule_output_stream(output_path, type, noisy);
+
+  for (const auto& input_path : filter_list_inputs) {
+    auto rule_input_stream = subresource_filter::RuleInputStream::Create(
+        std::make_unique<std::ifstream>(input_path.AsUTF8Unsafe(),
+                                        std::ios::binary | std::ios::in),
+        subresource_filter::RulesetFormat::kFilterList);
+    CHECK(rule_input_stream);
+    CHECK(subresource_filter::TransferRules(rule_input_stream.get(),
+                                            &rule_output_stream,
+                                            nullptr /* css_rule_output */));
+  }
+
+  return rule_output_stream.Finish();
+}
+
+}  // namespace filter_list_converter
+}  // namespace declarative_net_request
+}  // namespace extensions
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/converter.h b/extensions/browser/api/declarative_net_request/filter_list_converter/converter.h
new file mode 100644
index 0000000..2e56540
--- /dev/null
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/converter.h
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium 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 EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_FILTER_LIST_CONVERTER_CONVERTER_H_
+#define EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_FILTER_LIST_CONVERTER_CONVERTER_H_
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+
+namespace extensions {
+namespace declarative_net_request {
+namespace filter_list_converter {
+
+enum WriteType {
+  kJSONRuleset,
+  kExtension,
+};
+
+// Utility function to convert filter list files in the text format to a JSON
+// file in a format supported by the Declarative Net Request API. If |type| is
+// kExtension, output_path is treated as the extension directory and the ruleset
+// is written to "rules.json". Else it is treated as the json ruleset location.
+// Returns false if the conversion fails.
+bool ConvertRuleset(const std::vector<base::FilePath>& filter_list_inputs,
+                    const base::FilePath& output_path,
+                    WriteType type,
+                    bool noisy = true);
+
+}  // namespace filter_list_converter
+}  // namespace declarative_net_request
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_FILTER_LIST_CONVERTER_CONVERTER_H_
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/converter_unittest.cc b/extensions/browser/api/declarative_net_request/filter_list_converter/converter_unittest.cc
new file mode 100644
index 0000000..f3deb94
--- /dev/null
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/converter_unittest.cc
@@ -0,0 +1,127 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/declarative_net_request/filter_list_converter/converter.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/optional.h"
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+namespace declarative_net_request {
+namespace filter_list_converter {
+namespace {
+
+void TestConversion(std::vector<std::string> filter_list_rules,
+                    std::string json_result,
+                    WriteType write_type) {
+  base::ScopedTempDir temp_dir;
+  CHECK(temp_dir.CreateUniqueTempDir());
+
+  base::FilePath input_path = temp_dir.GetPath().AppendASCII("filterlist.txt");
+  std::string filterlist = base::JoinString(filter_list_rules, "\n");
+  CHECK_EQ(filterlist.size(),
+           static_cast<size_t>(base::WriteFile(input_path, filterlist.c_str(),
+                                               filterlist.size())));
+
+  base::Optional<base::Value> expected_json =
+      base::JSONReader::Read(json_result);
+  CHECK(expected_json.has_value());
+
+  base::FilePath output_path = temp_dir.GetPath();
+  if (write_type == WriteType::kJSONRuleset)
+    output_path = output_path.AppendASCII("rules.json");
+
+  ConvertRuleset({input_path}, output_path, write_type, false /* noisy */);
+
+  base::FilePath output_json_path =
+      temp_dir.GetPath().AppendASCII("rules.json");
+  JSONFileValueDeserializer deserializer(output_json_path);
+  std::unique_ptr<base::Value> actual_json = deserializer.Deserialize(
+      nullptr /* error_code */, nullptr /* error_message */);
+  ASSERT_TRUE(actual_json.get());
+
+  EXPECT_EQ(*expected_json, *actual_json);
+
+  if (write_type == WriteType::kExtension) {
+    EXPECT_TRUE(
+        base::PathExists(temp_dir.GetPath().AppendASCII("manifest.json")));
+  }
+}
+
+class FilterListConverterTest : public ::testing::TestWithParam<WriteType> {};
+
+TEST_P(FilterListConverterTest, Convert) {
+  std::vector<std::string> filter_list_rules = {
+      "||example.com^|$script,image,font",
+      "@@allowed.com$domain=example.com|~sub.example.com",
+      "|https://*.abc.com|$match-case,~image,third-party",
+      "abc.com$~third-party"};
+
+  std::string expected_result = R"(
+    [ {
+       "action": {
+          "type": "block"
+       },
+       "condition": {
+          "isUrlFilterCaseSensitive": false,
+          "resourceTypes": [ "script", "image", "font" ],
+          "urlFilter": "||example.com^|"
+       },
+       "id": 1
+    }, {
+       "action": {
+          "type": "allow"
+       },
+       "condition": {
+          "domains": [ "example.com" ],
+          "excludedDomains": [ "sub.example.com" ],
+          "isUrlFilterCaseSensitive": false,
+          "urlFilter": "allowed.com"
+       },
+       "id": 2
+    }, {
+       "action": {
+          "type": "block"
+       },
+       "condition": {
+          "resourceTypes": [ "other", "script", "stylesheet", "object",
+              "xmlhttprequest", "sub_frame", "ping", "media", "font",
+              "websocket" ],
+          "urlFilter": "|https://*.abc.com|",
+          "domainType": "thirdParty"
+       },
+       "id": 3
+    }, {
+       "action": {
+          "type": "block"
+       },
+       "condition": {
+          "isUrlFilterCaseSensitive": false,
+          "urlFilter": "abc.com",
+          "domainType": "firstParty"
+       },
+       "id": 4
+    } ]
+
+)";
+
+  TestConversion(filter_list_rules, expected_result, GetParam());
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         FilterListConverterTest,
+                         ::testing::Values(WriteType::kExtension,
+                                           WriteType::kJSONRuleset));
+
+}  // namespace
+}  // namespace filter_list_converter
+}  // namespace declarative_net_request
+}  // namespace extensions
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/main.cc b/extensions/browser/api/declarative_net_request/filter_list_converter/main.cc
new file mode 100644
index 0000000..0a78a78
--- /dev/null
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/main.cc
@@ -0,0 +1,127 @@
+// Copyright 2019 The Chromium 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 <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "extensions/browser/api/declarative_net_request/filter_list_converter/converter.h"
+
+namespace {
+
+const char kSwitchInputFilterlistFiles[] = "input_filterlists";
+const char kSwitchOutputPath[] = "output_path";
+const char kSwitchOutputType[] = "output_type";
+const char kOutputTypeExtension[] = "extension";
+const char kOutputTypeJSON[] = "json";
+const base::FilePath::CharType kJSONExtension[] = FILE_PATH_LITERAL(".json");
+
+const char kHelpMsg[] = R"(
+  filter_list_converter --input_filterlists=[<path1>, <path2>]
+          --output_path=<path> --output_type=<extension,json>
+
+  Filter List Converter is a tool to convert filter list files in the text
+  format to a JSON file in a format supported by the Declarative Net Request
+  API. It can either output the complete extension or just the JSON ruleset.
+
+  --input_filterlists = List of input paths to text filter list files.
+  --output_path = The output path. The parent directory should exist.
+  --output_type = Optional switch. One of "extension" or "json". "json" is the
+                  default.
+)";
+
+namespace filter_list_converter =
+    extensions::declarative_net_request::filter_list_converter;
+
+void PrintHelp() {
+  LOG(ERROR) << kHelpMsg;
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
+
+  if (!command_line.HasSwitch(kSwitchInputFilterlistFiles) ||
+      !command_line.HasSwitch(kSwitchOutputPath)) {
+    PrintHelp();
+    return 1;
+  }
+
+  std::vector<base::FilePath> input_paths;
+  base::CommandLine::StringType comma_separated_paths =
+      command_line.GetSwitchValueNative(kSwitchInputFilterlistFiles);
+
+#if defined(OS_WIN)
+  base::CommandLine::StringType separator = base::ASCIIToUTF16(",");
+#else
+  base::CommandLine::StringType separator(",");
+#endif
+
+  for (const auto& piece : base::SplitStringPiece(
+           comma_separated_paths, separator, base::TRIM_WHITESPACE,
+           base::SPLIT_WANT_NONEMPTY)) {
+    base::FilePath path(piece);
+
+    if (!base::PathExists(path)) {
+      LOG(ERROR) << "Input path " << piece << " does not exist.";
+      return 1;
+    }
+
+    input_paths.push_back(path);
+  }
+  if (input_paths.empty()) {
+    LOG(ERROR) << base::StringPrintf(
+        "No valid input files specified using '%s'.",
+        kSwitchInputFilterlistFiles);
+    return 1;
+  }
+
+  filter_list_converter::WriteType write_type =
+      filter_list_converter::kJSONRuleset;
+  if (command_line.HasSwitch(kSwitchOutputType)) {
+    std::string output_type =
+        command_line.GetSwitchValueASCII(kSwitchOutputType);
+    if (output_type == kOutputTypeExtension) {
+      write_type = filter_list_converter::kExtension;
+    } else if (output_type == kOutputTypeJSON) {
+      write_type = filter_list_converter::kJSONRuleset;
+    } else {
+      LOG(ERROR) << base::StringPrintf("Invalid value for switch '%s'",
+                                       kSwitchOutputType);
+      return 1;
+    }
+  }
+
+  base::FilePath output_path =
+      command_line.GetSwitchValuePath(kSwitchOutputPath);
+  bool invalid_output_path = false;
+  switch (write_type) {
+    case filter_list_converter::kExtension:
+      invalid_output_path = !base::DirectoryExists(output_path);
+      break;
+    case filter_list_converter::kJSONRuleset:
+      invalid_output_path = output_path.Extension() != kJSONExtension;
+      invalid_output_path |= !base::DirectoryExists(output_path.DirName());
+      break;
+  }
+  if (invalid_output_path) {
+    LOG(ERROR) << "Invalid output path " << output_path.value();
+    return 1;
+  }
+
+  if (!filter_list_converter::ConvertRuleset(input_paths, output_path,
+                                             write_type)) {
+    LOG(ERROR) << "Conversion failed.";
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule.cc b/extensions/browser/api/declarative_net_request/indexed_rule.cc
index 18fc192..606ff4f6f 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule.cc
@@ -272,7 +272,6 @@
                                            const GURL& base_url,
                                            IndexedRule* indexed_rule) {
   DCHECK(indexed_rule);
-  DCHECK(IsAPIAvailable());
 
   if (parsed_rule.id < kMinValidID)
     return ParseResult::ERROR_INVALID_RULE_ID;
diff --git a/extensions/browser/api/declarative_net_request/ruleset_source.cc b/extensions/browser/api/declarative_net_request/ruleset_source.cc
index fb3d5180..a9db83fa 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_source.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_source.cc
@@ -367,6 +367,7 @@
                                               int* ruleset_checksum) const {
   DCHECK_LE(rules.size(), rule_count_limit_);
   DCHECK(ruleset_checksum);
+  DCHECK(IsAPIAvailable());
 
   FlatRulesetIndexer indexer;
 
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 9f6c9fef..efbe4ab 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -2508,8 +2508,6 @@
       relevant_registries.push_back(std::make_pair(it->second.get(), true));
   }
 
-  // The following block is experimentally enabled and its impact on load time
-  // logged with UMA Extensions.NetworkDelayRegistryLoad. crbug.com/175961
   for (auto it : relevant_registries) {
     WebRequestRulesRegistry* rules_registry = it.first;
     if (rules_registry->ready().is_signaled())
@@ -2573,10 +2571,6 @@
     return;
 
   BlockedRequest& blocked_request = it->second;
-  base::TimeDelta block_time =
-      base::Time::Now() - blocked_request.blocking_time;
-  UMA_HISTOGRAM_TIMES("Extensions.NetworkDelayRegistryLoad", block_time);
-
   ProcessDeclarativeRules(browser_context, blocked_request.extension_info_map,
                           event_name, blocked_request.request, request_stage,
                           blocked_request.filtered_response_headers.get());
diff --git a/extensions/browser/api/web_request/web_request_proxying_websocket.cc b/extensions/browser/api/web_request/web_request_proxying_websocket.cc
index f881dd2..a468635d 100644
--- a/extensions/browser/api/web_request/web_request_proxying_websocket.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_websocket.cc
@@ -157,13 +157,13 @@
   proxied_socket_->StartClosingHandshake(code, reason);
 }
 
-void WebRequestProxyingWebSocket::OnStartOpeningHandshake(
+void WebRequestProxyingWebSocket::OnOpeningHandshakeStarted(
     network::mojom::WebSocketHandshakeRequestPtr request) {
   DCHECK(forwarding_handshake_client_);
-  forwarding_handshake_client_->OnStartOpeningHandshake(std::move(request));
+  forwarding_handshake_client_->OnOpeningHandshakeStarted(std::move(request));
 }
 
-void WebRequestProxyingWebSocket::OnFinishOpeningHandshake(
+void WebRequestProxyingWebSocket::OnResponseReceived(
     network::mojom::WebSocketHandshakeResponsePtr response) {
   DCHECK(forwarding_handshake_client_);
 
@@ -181,11 +181,11 @@
 
   response_.remote_endpoint = response->remote_endpoint;
 
-  // TODO(yhirano): OnFinishOpeningHandshake is called with the original
+  // TODO(yhirano): OnResponseReceived is called with the original
   // response headers. That means if OnHeadersReceived modified them the
   // renderer won't see that modification. This is the opposite of http(s)
   // requests.
-  forwarding_handshake_client_->OnFinishOpeningHandshake(std::move(response));
+  forwarding_handshake_client_->OnResponseReceived(std::move(response));
 
   if (!binding_as_header_client_ || response_.headers) {
     ContinueToHeadersReceived();
@@ -215,7 +215,7 @@
   OnHeadersReceivedComplete(net::OK);
 }
 
-void WebRequestProxyingWebSocket::OnAddChannelResponse(
+void WebRequestProxyingWebSocket::OnConnectionEstablished(
     const std::string& selected_protocol,
     const std::string& extensions,
     uint64_t receive_quota_threshold) {
@@ -225,7 +225,7 @@
   ExtensionWebRequestEventRouter::GetInstance()->OnCompleted(
       browser_context_, info_map_, &info_.value(), net::ERR_WS_UPGRADE);
 
-  forwarding_handshake_client_->OnAddChannelResponse(
+  forwarding_handshake_client_->OnConnectionEstablished(
       selected_protocol, extensions, receive_quota_threshold);
 }
 
diff --git a/extensions/browser/api/web_request/web_request_proxying_websocket.h b/extensions/browser/api/web_request/web_request_proxying_websocket.h
index 852e6ff..a896caf8 100644
--- a/extensions/browser/api/web_request/web_request_proxying_websocket.h
+++ b/extensions/browser/api/web_request/web_request_proxying_websocket.h
@@ -70,13 +70,13 @@
   void StartClosingHandshake(uint16_t code, const std::string& reason) override;
 
   // mojom::WebSocketHandShakeClient methods:
-  void OnStartOpeningHandshake(
+  void OnOpeningHandshakeStarted(
       network::mojom::WebSocketHandshakeRequestPtr request) override;
-  void OnFinishOpeningHandshake(
+  void OnResponseReceived(
       network::mojom::WebSocketHandshakeResponsePtr response) override;
-  void OnAddChannelResponse(const std::string& selected_protocol,
-                            const std::string& extensions,
-                            uint64_t receive_quota_threshold) override;
+  void OnConnectionEstablished(const std::string& selected_protocol,
+                               const std::string& extensions,
+                               uint64_t receive_quota_threshold) override;
 
   // mojom::AuthenticationHandler method:
   void OnAuthRequired(const net::AuthChallengeInfo& auth_info,
diff --git a/extensions/browser/computed_hashes.cc b/extensions/browser/computed_hashes.cc
index 714b9f3b..c99e990f 100644
--- a/extensions/browser/computed_hashes.cc
+++ b/extensions/browser/computed_hashes.cc
@@ -79,57 +79,60 @@
   if (!base::ReadFileToString(path, &contents))
     return false;
 
-  base::DictionaryValue* top_dictionary = NULL;
-  std::unique_ptr<base::Value> value(
-      base::JSONReader::ReadDeprecated(contents));
-  if (!value.get() || !value->GetAsDictionary(&top_dictionary))
+  base::Optional<base::Value> top_dictionary = base::JSONReader::Read(contents);
+  if (!top_dictionary || !top_dictionary->is_dict())
     return false;
 
   // For now we don't support forwards or backwards compatability in the
   // format, so we return false on version mismatch.
-  int version = 0;
-  if (!top_dictionary->GetInteger(computed_hashes::kVersionKey, &version) ||
-      version != computed_hashes::kVersion)
+  base::Optional<int> version =
+      top_dictionary->FindIntKey(computed_hashes::kVersionKey);
+  if (!version || *version != computed_hashes::kVersion)
     return false;
 
-  base::ListValue* all_hashes = NULL;
-  if (!top_dictionary->GetList(computed_hashes::kFileHashesKey, &all_hashes))
+  const base::Value* all_hashes =
+      top_dictionary->FindListKey(computed_hashes::kFileHashesKey);
+  if (!all_hashes)
     return false;
 
-  for (size_t i = 0; i < all_hashes->GetSize(); i++) {
-    base::DictionaryValue* dictionary = NULL;
-    if (!all_hashes->GetDictionary(i, &dictionary))
+  for (const base::Value& file_hash : all_hashes->GetList()) {
+    if (!file_hash.is_dict())
       return false;
 
-    std::string relative_path_utf8;
-    if (!dictionary->GetString(computed_hashes::kPathKey, &relative_path_utf8))
+    const std::string* relative_path_utf8 =
+        file_hash.FindStringKey(computed_hashes::kPathKey);
+    if (!relative_path_utf8)
       return false;
 
-    int block_size;
-    if (!dictionary->GetInteger(computed_hashes::kBlockSizeKey, &block_size))
+    base::Optional<int> block_size =
+        file_hash.FindIntKey(computed_hashes::kBlockSizeKey);
+    if (!block_size)
       return false;
-    if (block_size <= 0 || ((block_size % 1024) != 0)) {
-      LOG(ERROR) << "Invalid block size: " << block_size;
+    if (*block_size <= 0 || ((*block_size % 1024) != 0)) {
+      LOG(ERROR) << "Invalid block size: " << *block_size;
       return false;
     }
 
-    base::ListValue* hashes_list = NULL;
-    if (!dictionary->GetList(computed_hashes::kBlockHashesKey, &hashes_list))
+    const base::Value* block_hashes =
+        file_hash.FindListKey(computed_hashes::kBlockHashesKey);
+    if (!block_hashes)
       return false;
 
+    const base::Value::ListStorage& hashes_list = block_hashes->GetList();
+
     base::FilePath relative_path =
-        base::FilePath::FromUTF8Unsafe(relative_path_utf8);
+        base::FilePath::FromUTF8Unsafe(*relative_path_utf8);
     relative_path = relative_path.NormalizePathSeparatorsTo('/');
 
-    data_[relative_path] = HashInfo(block_size, std::vector<std::string>());
+    data_[relative_path] = HashInfo(*block_size, std::vector<std::string>());
     std::vector<std::string>* hashes = &(data_[relative_path].second);
 
-    for (size_t j = 0; j < hashes_list->GetSize(); j++) {
-      std::string encoded;
-      if (!hashes_list->GetString(j, &encoded))
+    for (const base::Value& value : hashes_list) {
+      if (!value.is_string())
         return false;
 
       hashes->push_back(std::string());
+      const std::string& encoded = value.GetString();
       std::string* decoded = &hashes->back();
       if (!base::Base64Decode(encoded, decoded)) {
         hashes->clear();
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 398214af..5142bcb 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -165,8 +165,6 @@
       "features/feature_provider.h",
       "features/feature_session_type.cc",
       "features/feature_session_type.h",
-      "features/feature_util.cc",
-      "features/feature_util.h",
       "features/json_feature_provider_source.cc",
       "features/json_feature_provider_source.h",
       "features/manifest_feature.cc",
diff --git a/extensions/common/api/_behavior_features.json b/extensions/common/api/_behavior_features.json
index 028c94e3..5bff24f 100644
--- a/extensions/common/api/_behavior_features.json
+++ b/extensions/common/api/_behavior_features.json
@@ -88,7 +88,8 @@
     "platforms": ["chromeos"],
     "whitelist": [
       "FA84F98B32AFC3013F5711F8711F8F38DB210AB7", // Sign-in Screen Test Extension
-      "7FE4A999359A456C4B0FB7B7AD85CEA29CA50519"  // chrome.loginScreenUi Test Extension
+      "7FE4A999359A456C4B0FB7B7AD85CEA29CA50519", // chrome.loginScreenUi Test Extension
+      "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE"  // Imprivata (login screen)
     ]
   }],
   "allow_deprecated_audio_api": {
diff --git a/extensions/common/features/feature_provider.cc b/extensions/common/features/feature_provider.cc
index c652fab..bc017ec 100644
--- a/extensions/common/features/feature_provider.cc
+++ b/extensions/common/features/feature_provider.cc
@@ -8,7 +8,9 @@
 #include <memory>
 
 #include "base/command_line.h"
+#include "base/debug/alias.h"
 #include "base/lazy_instance.h"
+#include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -16,13 +18,30 @@
 #include "content/public/common/content_switches.h"
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/features/feature.h"
-#include "extensions/common/features/feature_util.h"
 #include "extensions/common/switches.h"
 
 namespace extensions {
 
 namespace {
 
+// Writes |message| to the stack so that it shows up in the minidump, then
+// crashes the current process.
+//
+// The prefix "e::" is used so that the crash can be quickly located.
+//
+// This is provided in feature_util because for some reason features are prone
+// to mysterious crashes in named map lookups. For example see crbug.com/365192
+// and crbug.com/461915.
+#define CRASH_WITH_MINIDUMP(message)                                  \
+  {                                                                   \
+    std::string message_copy(message);                                \
+    char minidump[BUFSIZ];                                            \
+    base::debug::Alias(&minidump);                                    \
+    base::snprintf(minidump, base::size(minidump), "e::%s:%d:\"%s\"", \
+                   __FILE__, __LINE__, message_copy.c_str());         \
+    LOG(FATAL) << message_copy;                                       \
+  }
+
 class FeatureProviderStatic {
  public:
   FeatureProviderStatic() {
diff --git a/extensions/common/features/feature_util.cc b/extensions/common/features/feature_util.cc
deleted file mode 100644
index 6a69149b..0000000
--- a/extensions/common/features/feature_util.cc
+++ /dev/null
@@ -1,15 +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 "extensions/common/features/feature_util.h"
-
-namespace extensions {
-namespace feature_util {
-
-bool ExtensionServiceWorkersEnabled() {
-  return true;
-}
-
-}  // namespace feature_util
-}  // namespace extensions
diff --git a/extensions/common/features/feature_util.h b/extensions/common/features/feature_util.h
deleted file mode 100644
index 621a109..0000000
--- a/extensions/common/features/feature_util.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef EXTENSIONS_COMMON_FEATURES_FEATURE_UTIL_H_
-#define EXTENSIONS_COMMON_FEATURES_FEATURE_UTIL_H_
-
-#include "base/debug/alias.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
-
-// Writes |message| to the stack so that it shows up in the minidump, then
-// crashes the current process.
-//
-// The prefix "e::" is used so that the crash can be quickly located.
-//
-// This is provided in feature_util because for some reason features are prone
-// to mysterious crashes in named map lookups. For example see crbug.com/365192
-// and crbug.com/461915.
-#define CRASH_WITH_MINIDUMP(message)                                  \
-  {                                                                   \
-    std::string message_copy(message);                                \
-    char minidump[BUFSIZ];                                            \
-    base::debug::Alias(&minidump);                                    \
-    base::snprintf(minidump, base::size(minidump), "e::%s:%d:\"%s\"", \
-                   __FILE__, __LINE__, message_copy.c_str());         \
-    LOG(FATAL) << message_copy;                                       \
-  }
-
-namespace extensions {
-namespace feature_util {
-
-// Returns true if service workers are enabled for extension schemes.
-// TODO(lazyboy): Remove this function once extension Service Workers
-// are enabled by default for a while.
-bool ExtensionServiceWorkersEnabled();
-
-}  // namespace feature_util
-}  // namespace extensions
-
-#endif  // EXTENSIONS_COMMON_FEATURES_FEATURE_UTIL_H_
diff --git a/extensions/common/features/simple_feature.cc b/extensions/common/features/simple_feature.cc
index ee03ee41..200db48 100644
--- a/extensions/common/features/simple_feature.cc
+++ b/extensions/common/features/simple_feature.cc
@@ -20,7 +20,6 @@
 #include "extensions/common/extension_api.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/features/feature_provider.h"
-#include "extensions/common/features/feature_util.h"
 #include "extensions/common/switches.h"
 
 using crx_file::id_util::HashedIdInHex;
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index fd19c1a..ae5c7d7 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -41,7 +41,6 @@
 #include "extensions/common/features/feature.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/features/feature_provider.h"
-#include "extensions/common/features/feature_util.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handlers/background_info.h"
@@ -283,11 +282,9 @@
   if (context->context_type() == Feature::CONTENT_SCRIPT_CONTEXT)
     InitOriginPermissions(context->extension());
 
-  {
-    std::unique_ptr<ModuleSystem> module_system(
-        new ModuleSystem(context, &source_map_));
-    context->SetModuleSystem(std::move(module_system));
-  }
+  context->SetModuleSystem(
+      std::make_unique<ModuleSystem>(context, &source_map_));
+
   ModuleSystem* module_system = context->module_system();
 
   // Enable natives in startup.
@@ -354,6 +351,8 @@
     const GURL& script_url) {
   const base::TimeTicks start_time = base::TimeTicks::Now();
 
+  // TODO(crbug/961821): We may want to give service workers not registered
+  // by extensions minimal bindings, the same as other webpage-like contexts.
   if (!script_url.SchemeIs(kExtensionScheme)) {
     // Early-out if this isn't a chrome-extension:// scheme, because looking up
     // the extension registry is unnecessary if it's not. Checking this will
@@ -389,6 +388,11 @@
     return;
   }
 
+  // Only the script specific in the manifest's background data gets bindings.
+  //
+  // TODO(crbug/961821): We may want to give other service workers registered
+  // by extensions minimal bindings, just as we might want to give them to
+  // service workers that aren't registered by extensions.
   ScriptContext* context = new ScriptContext(
       v8_context, nullptr, extension, Feature::SERVICE_WORKER_CONTEXT,
       extension, Feature::SERVICE_WORKER_CONTEXT);
@@ -396,7 +400,9 @@
   context->set_service_worker_scope(service_worker_scope);
   context->set_service_worker_version_id(service_worker_version_id);
 
-  if (ExtensionsClient::Get()->ExtensionAPIEnabledInExtensionServiceWorkers()) {
+  if (ExtensionsRendererClient::Get()
+          ->ExtensionAPIEnabledForServiceWorkerScript(service_worker_scope,
+                                                      script_url)) {
     WorkerThreadDispatcher* worker_dispatcher = WorkerThreadDispatcher::Get();
     std::unique_ptr<IPCMessageSender> ipc_sender =
         IPCMessageSender::CreateWorkerThreadIPCMessageSender(
@@ -494,11 +500,9 @@
     int64_t service_worker_version_id,
     const GURL& service_worker_scope,
     const GURL& script_url) {
-  if (!script_url.SchemeIs(kExtensionScheme)) {
-    // See comment in DidInitializeServiceWorkerContextOnWorkerThread.
-    return;
-  }
-  if (!ExtensionsClient::Get()->ExtensionAPIEnabledInExtensionServiceWorkers())
+  if (!ExtensionsRendererClient::Get()
+           ->ExtensionAPIEnabledForServiceWorkerScript(service_worker_scope,
+                                                       script_url))
     return;
 
   DCHECK(worker_thread_util::IsWorkerThread());
@@ -512,12 +516,13 @@
     int64_t service_worker_version_id,
     const GURL& service_worker_scope,
     const GURL& script_url) {
-  if (!script_url.SchemeIs(kExtensionScheme)) {
-    // See comment in DidInitializeServiceWorkerContextOnWorkerThread.
-    return;
-  }
-
-  if (ExtensionsClient::Get()->ExtensionAPIEnabledInExtensionServiceWorkers()) {
+  if (!ExtensionsRendererClient::Get()
+           ->ExtensionAPIEnabledForServiceWorkerScript(service_worker_scope,
+                                                       script_url)) {
+    // If extension APIs in service workers aren't enabled, we just need to
+    // remove the context.
+    g_worker_script_context_set.Get().Remove(v8_context, script_url);
+  } else {
     // TODO(lazyboy/devlin): Should this cleanup happen in a worker class, like
     // WorkerThreadDispatcher? If so, we should move the initialization as well.
     ScriptContext* script_context = WorkerThreadDispatcher::GetScriptContext();
@@ -531,10 +536,6 @@
     // the associated bindings system.
     g_worker_script_context_set.Get().Remove(v8_context, script_url);
     WorkerThreadDispatcher::Get()->RemoveWorkerData(service_worker_version_id);
-  } else {
-    // If extension APIs in service workers aren't enabled, we just need to
-    // remove the context.
-    g_worker_script_context_set.Get().Remove(v8_context, script_url);
   }
 }
 
@@ -1011,11 +1012,9 @@
   SetCurrentFeatureSessionType(session_type);
   script_context_set_->set_is_lock_screen_context(is_lock_screen_context);
 
-  if (feature_util::ExtensionServiceWorkersEnabled()) {
-    // chrome-extension: resources should be allowed to register ServiceWorkers.
-    blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers(
-        blink::WebString::FromUTF8(extensions::kExtensionScheme));
-  }
+  // chrome-extension: resources should be allowed to register ServiceWorkers.
+  blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers(
+      blink::WebString::FromUTF8(extensions::kExtensionScheme));
 
   blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingWasmEvalCSP(
       blink::WebString::FromUTF8(extensions::kExtensionScheme));
diff --git a/extensions/renderer/extensions_renderer_client.h b/extensions/renderer/extensions_renderer_client.h
index 2ef7246..74a10e2 100644
--- a/extensions/renderer/extensions_renderer_client.h
+++ b/extensions/renderer/extensions_renderer_client.h
@@ -7,6 +7,8 @@
 
 #include "extensions/common/extension_id.h"
 
+class GURL;
+
 namespace extensions {
 class Extension;
 class Dispatcher;
@@ -37,6 +39,14 @@
   virtual void OnExtensionLoaded(const Extension& extension) {}
   virtual void OnExtensionUnloaded(const ExtensionId& extension) {}
 
+  // Returns whether or not extension APIs are allowed for the specified
+  // script. The script must be specified in the extension's manifest
+  // background section and the scope must be the root scope of the
+  // extension.
+  virtual bool ExtensionAPIEnabledForServiceWorkerScript(
+      const GURL& scope,
+      const GURL& script_url) const = 0;
+
   // Returns the single instance of |this|.
   static ExtensionsRendererClient* Get();
 
diff --git a/extensions/renderer/test_extensions_renderer_client.cc b/extensions/renderer/test_extensions_renderer_client.cc
index 7f0a9c9d..a341ef55 100644
--- a/extensions/renderer/test_extensions_renderer_client.cc
+++ b/extensions/renderer/test_extensions_renderer_client.cc
@@ -27,4 +27,10 @@
   return nullptr;
 }
 
+bool TestExtensionsRendererClient::ExtensionAPIEnabledForServiceWorkerScript(
+    const GURL& scope,
+    const GURL& script_url) const {
+  return false;
+}
+
 }  // namespace extensions
diff --git a/extensions/renderer/test_extensions_renderer_client.h b/extensions/renderer/test_extensions_renderer_client.h
index 2ad51375..6493505 100644
--- a/extensions/renderer/test_extensions_renderer_client.h
+++ b/extensions/renderer/test_extensions_renderer_client.h
@@ -19,6 +19,9 @@
   bool IsIncognitoProcess() const override;
   int GetLowestIsolatedWorldId() const override;
   Dispatcher* GetDispatcher() override;
+  bool ExtensionAPIEnabledForServiceWorkerScript(
+      const GURL& scope,
+      const GURL& script_url) const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestExtensionsRendererClient);
diff --git a/extensions/shell/renderer/shell_extensions_renderer_client.cc b/extensions/shell/renderer/shell_extensions_renderer_client.cc
index 508a4cac..fb786c6 100644
--- a/extensions/shell/renderer/shell_extensions_renderer_client.cc
+++ b/extensions/shell/renderer/shell_extensions_renderer_client.cc
@@ -35,4 +35,10 @@
   return dispatcher_.get();
 }
 
+bool ShellExtensionsRendererClient::ExtensionAPIEnabledForServiceWorkerScript(
+    const GURL& scope,
+    const GURL& script_url) const {
+  return false;
+}
+
 }  // namespace extensions
diff --git a/extensions/shell/renderer/shell_extensions_renderer_client.h b/extensions/shell/renderer/shell_extensions_renderer_client.h
index c983625..8acf835 100644
--- a/extensions/shell/renderer/shell_extensions_renderer_client.h
+++ b/extensions/shell/renderer/shell_extensions_renderer_client.h
@@ -22,6 +22,9 @@
   bool IsIncognitoProcess() const override;
   int GetLowestIsolatedWorldId() const override;
   Dispatcher* GetDispatcher() override;
+  bool ExtensionAPIEnabledForServiceWorkerScript(
+      const GURL& scope,
+      const GURL& script_url) const override;
 
  private:
   std::unique_ptr<Dispatcher> dispatcher_;
diff --git a/fuchsia/BUILD.gn b/fuchsia/BUILD.gn
index f665f80..3082ec3 100644
--- a/fuchsia/BUILD.gn
+++ b/fuchsia/BUILD.gn
@@ -13,7 +13,6 @@
   sources = [
     "fidl/cast/api_bindings.fidl",
     "fidl/cast/application_config.fidl",
-    "fidl/cast/cast_channel.fidl",
     "fidl/cast/queryable_data.fidl",
   ]
 
@@ -103,7 +102,6 @@
     "http:http_service_tests",
     "runners:cast_runner",
     "runners:cast_runner_browsertests",
-    "runners:cast_runner_integration_tests",
     "runners:web_runner",
     "//chromecast/bindings:bindings_manager_fuchsia",
   ]
diff --git a/fuchsia/base/frame_test_util.cc b/fuchsia/base/frame_test_util.cc
index 463c19b..9bd182e72 100644
--- a/fuchsia/base/frame_test_util.cc
+++ b/fuchsia/base/frame_test_util.cc
@@ -4,8 +4,10 @@
 
 #include "fuchsia/base/frame_test_util.h"
 
+#include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "fuchsia/base/fit_adapter.h"
+#include "fuchsia/base/mem_buffer_util.h"
 #include "fuchsia/base/result_receiver.h"
 #include "fuchsia/base/test_navigation_listener.h"
 
@@ -14,16 +16,36 @@
 bool LoadUrlAndExpectResponse(
     fuchsia::web::NavigationController* navigation_controller,
     fuchsia::web::LoadUrlParams load_url_params,
-    std::string url) {
+    base::StringPiece url) {
   DCHECK(navigation_controller);
   base::RunLoop run_loop;
   ResultReceiver<fuchsia::web::NavigationController_LoadUrl_Result> result(
       run_loop.QuitClosure());
   navigation_controller->LoadUrl(
-      url, std::move(load_url_params),
+      url.as_string(), std::move(load_url_params),
       CallbackToFitFunction(result.GetReceiveCallback()));
   run_loop.Run();
   return result->is_response();
 }
 
+base::Optional<base::Value> ExecuteJavaScript(fuchsia::web::Frame* frame,
+                                              base::StringPiece script) {
+  base::RunLoop run_loop;
+  ResultReceiver<fuchsia::web::Frame_ExecuteJavaScript_Result> result(
+      run_loop.QuitClosure());
+  frame->ExecuteJavaScript({"*"}, MemBufferFromString(script),
+                           CallbackToFitFunction(result.GetReceiveCallback()));
+  run_loop.Run();
+
+  if (!result.has_value() || !result->is_response())
+    return {};
+
+  std::string result_json;
+  if (!StringFromMemBuffer(result->response().result, &result_json)) {
+    return {};
+  }
+
+  return base::JSONReader::Read(result_json);
+}
+
 }  // namespace cr_fuchsia
diff --git a/fuchsia/base/frame_test_util.h b/fuchsia/base/frame_test_util.h
index d4340f7..b4165bb9 100644
--- a/fuchsia/base/frame_test_util.h
+++ b/fuchsia/base/frame_test_util.h
@@ -7,6 +7,10 @@
 
 #include <fuchsia/web/cpp/fidl.h>
 
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+#include "base/values.h"
+
 namespace cr_fuchsia {
 
 // Uses |navigation_controller| to load |url| with |load_url_params|. Returns
@@ -15,7 +19,12 @@
 bool LoadUrlAndExpectResponse(
     fuchsia::web::NavigationController* navigation_controller,
     fuchsia::web::LoadUrlParams load_url_params,
-    std::string url);
+    base::StringPiece url);
+
+// Executes |script| in the context of |frame|'s top-level document.
+// Returns an un-set |base::Optional<>| on failure.
+base::Optional<base::Value> ExecuteJavaScript(fuchsia::web::Frame* frame,
+                                              base::StringPiece script);
 
 }  // namespace cr_fuchsia
 
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index e450b4f..165a179f 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -246,6 +246,7 @@
     "test_debug_listener.cc",
     "test_debug_listener.h",
     "web_engine_debug_integration_test.cc",
+    "web_engine_integration_test.cc",
   ]
   data = [
     "test/data",
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc
index 6a6d1e3..1782787 100644
--- a/fuchsia/engine/browser/frame_impl.cc
+++ b/fuchsia/engine/browser/frame_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind_helpers.h"
 #include "base/fuchsia/fuchsia_logging.h"
+#include "base/json/json_writer.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -227,6 +228,63 @@
   }
 }
 
+void FrameImpl::ExecuteJavaScriptInternal(std::vector<std::string> origins,
+                                          fuchsia::mem::Buffer script,
+                                          ExecuteJavaScriptCallback callback,
+                                          bool need_result) {
+  fuchsia::web::Frame_ExecuteJavaScript_Result result;
+  if (!context_->IsJavaScriptInjectionAllowed()) {
+    result.set_err(fuchsia::web::FrameError::INTERNAL_ERROR);
+    callback(std::move(result));
+    return;
+  }
+
+  if (!IsOriginWhitelisted(web_contents_->GetLastCommittedURL(), origins)) {
+    result.set_err(fuchsia::web::FrameError::INVALID_ORIGIN);
+    callback(std::move(result));
+    return;
+  }
+
+  base::string16 script_utf16;
+  if (!cr_fuchsia::ReadUTF8FromVMOAsUTF16(script, &script_utf16)) {
+    result.set_err(fuchsia::web::FrameError::BUFFER_NOT_UTF8);
+    callback(std::move(result));
+    return;
+  }
+
+  content::RenderFrameHost::JavaScriptResultCallback result_callback;
+  if (need_result) {
+    result_callback = base::BindOnce(
+        [](ExecuteJavaScriptCallback callback, base::Value result_value) {
+          fuchsia::web::Frame_ExecuteJavaScript_Result result;
+
+          std::string result_json;
+          if (!base::JSONWriter::Write(result_value, &result_json)) {
+            result.set_err(fuchsia::web::FrameError::INTERNAL_ERROR);
+            callback(std::move(result));
+            return;
+          }
+
+          fuchsia::web::Frame_ExecuteJavaScript_Response response;
+          response.result =
+              cr_fuchsia::MemBufferFromString(std::move(result_json));
+          result.set_response(std::move(response));
+          callback(std::move(result));
+        },
+        std::move(callback));
+  }
+
+  web_contents_->GetMainFrame()->ExecuteJavaScript(script_utf16,
+                                                   std::move(result_callback));
+
+  if (!need_result) {
+    // If no result is required then invoke callback() immediately.
+    fuchsia::web::Frame_ExecuteJavaScript_Result result;
+    result.set_response(fuchsia::web::Frame_ExecuteJavaScript_Response());
+    callback(std::move(result));
+  }
+}
+
 void FrameImpl::CreateView(fuchsia::ui::views::ViewToken view_token) {
   // If a View to this Frame is already active then disconnect it.
   TearDownView();
@@ -264,34 +322,31 @@
   navigation_controller_.AddBinding(std::move(controller));
 }
 
+void FrameImpl::ExecuteJavaScript(std::vector<std::string> origins,
+                                  fuchsia::mem::Buffer script,
+                                  ExecuteJavaScriptCallback callback) {
+  ExecuteJavaScriptInternal(std::move(origins), std::move(script),
+                            std::move(callback), true);
+}
+
 void FrameImpl::ExecuteJavaScriptNoResult(
     std::vector<std::string> origins,
     fuchsia::mem::Buffer script,
     ExecuteJavaScriptNoResultCallback callback) {
-  fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result;
-  if (!context_->IsJavaScriptInjectionAllowed()) {
-    result.set_err(fuchsia::web::FrameError::INTERNAL_ERROR);
-    callback(std::move(result));
-    return;
-  }
-
-  if (!IsOriginWhitelisted(web_contents_->GetLastCommittedURL(), origins)) {
-    result.set_err(fuchsia::web::FrameError::INVALID_ORIGIN);
-    callback(std::move(result));
-    return;
-  }
-
-  base::string16 script_utf16;
-  if (!cr_fuchsia::ReadUTF8FromVMOAsUTF16(script, &script_utf16)) {
-    result.set_err(fuchsia::web::FrameError::BUFFER_NOT_UTF8);
-    callback(std::move(result));
-    return;
-  }
-
-  web_contents_->GetMainFrame()->ExecuteJavaScript(script_utf16,
-                                                   base::NullCallback());
-  result.set_response(fuchsia::web::Frame_ExecuteJavaScriptNoResult_Response());
-  callback(std::move(result));
+  ExecuteJavaScriptInternal(
+      std::move(origins), std::move(script),
+      [callback = std::move(callback)](
+          fuchsia::web::Frame_ExecuteJavaScript_Result result_with_value) {
+        fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result;
+        if (result_with_value.is_err()) {
+          result.set_err(result_with_value.err());
+        } else {
+          result.set_response(
+              fuchsia::web::Frame_ExecuteJavaScriptNoResult_Response());
+        }
+        callback(std::move(result));
+      },
+      false);
 }
 
 void FrameImpl::AddBeforeLoadJavaScript(
diff --git a/fuchsia/engine/browser/frame_impl.h b/fuchsia/engine/browser/frame_impl.h
index 77e68787..74415513 100644
--- a/fuchsia/engine/browser/frame_impl.h
+++ b/fuchsia/engine/browser/frame_impl.h
@@ -86,11 +86,20 @@
   // Release the resources associated with the View, if one is active.
   void TearDownView();
 
+  // Shared implementation for the ExecuteJavaScript[NoResult]() APIs.
+  void ExecuteJavaScriptInternal(std::vector<std::string> origins,
+                                 fuchsia::mem::Buffer script,
+                                 ExecuteJavaScriptCallback callback,
+                                 bool need_result);
+
   // fuchsia::web::Frame implementation.
   void CreateView(fuchsia::ui::views::ViewToken view_token) override;
   void GetNavigationController(
       fidl::InterfaceRequest<fuchsia::web::NavigationController> controller)
       override;
+  void ExecuteJavaScript(std::vector<std::string> origins,
+                         fuchsia::mem::Buffer script,
+                         ExecuteJavaScriptCallback callback) override;
   void ExecuteJavaScriptNoResult(
       std::vector<std::string> origins,
       fuchsia::mem::Buffer script,
diff --git a/fuchsia/engine/browser/frame_impl_browsertest.cc b/fuchsia/engine/browser/frame_impl_browsertest.cc
index 9ce2b00f..67a73935 100644
--- a/fuchsia/engine/browser/frame_impl_browsertest.cc
+++ b/fuchsia/engine/browser/frame_impl_browsertest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/macros.h"
+#include "base/strings/stringprintf.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/test_timeouts.h"
 #include "content/public/browser/browser_context.h"
@@ -602,7 +603,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoad) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScript) {
   constexpr int64_t kBindingsId = 1234;
 
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -624,7 +625,7 @@
   navigation_listener_.RunUntilUrlEquals(url);
 }
 
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptUpdatedOnLoad) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptUpdated) {
   constexpr int64_t kBindingsId = 1234;
 
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -659,7 +660,7 @@
 
 // Verifies that bindings are injected in order by producing a cumulative,
 // non-commutative result.
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoadOrdered) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptOrdered) {
   constexpr int64_t kBindingsId1 = 1234;
   constexpr int64_t kBindingsId2 = 5678;
 
@@ -688,7 +689,7 @@
   navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello there");
 }
 
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoadRemoved) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptRemoved) {
   constexpr int64_t kBindingsId1 = 1234;
   constexpr int64_t kBindingsId2 = 5678;
 
@@ -722,7 +723,7 @@
   navigation_listener_.RunUntilUrlAndTitleEquals(url, "foo");
 }
 
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptRemoveInvalidId) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptRemoveInvalidId) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL(kPage1Path));
   fuchsia::web::FramePtr frame = CreateFrame();
@@ -737,33 +738,48 @@
   navigation_listener_.RunUntilUrlAndTitleEquals(url, kPage1Title);
 }
 
-// Test JS injection by using Javascript to trigger document navigation.
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptImmediate) {
+// Test JS injection using ExecuteJavaScriptNoResult() to set a value, and
+// ExecuteJavaScript() to retrieve that value.
+IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScript) {
+  constexpr char kJsonStringLiteral[] = "\"I am a literal, literally\"";
   fuchsia::web::FramePtr frame = CreateFrame();
 
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL title1(embedded_test_server()->GetURL(kPage1Path));
-  GURL title2(embedded_test_server()->GetURL(kPage2Path));
+  const GURL kUrl(embedded_test_server()->GetURL(kPage1Path));
 
   fuchsia::web::NavigationControllerPtr controller;
   frame->GetNavigationController(controller.NewRequest());
 
+  // Navigate to a page and wait for it to finish loading.
   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
-      controller.get(), fuchsia::web::LoadUrlParams(), title1.spec()));
-  navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
+      controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
+  navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, kPage1Title);
 
+  // Execute with no result to set the variable.
   frame->ExecuteJavaScriptNoResult(
-      {title1.GetOrigin().spec()},
-      cr_fuchsia::MemBufferFromString("window.location.href = \"" +
-                                      title2.spec() + "\";"),
+      {kUrl.GetOrigin().spec()},
+      cr_fuchsia::MemBufferFromString(
+          base::StringPrintf("my_variable = %s;", kJsonStringLiteral)),
       [](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) {
         EXPECT_TRUE(result.is_response());
       });
 
-  navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title);
+  // Execute a script snippet to return the variable's value.
+  base::RunLoop loop;
+  frame->ExecuteJavaScript(
+      {kUrl.GetOrigin().spec()},
+      cr_fuchsia::MemBufferFromString("my_variable;"),
+      [&](fuchsia::web::Frame_ExecuteJavaScript_Result result) {
+        ASSERT_TRUE(result.is_response());
+        std::string result_json =
+            StringFromMemBufferOrDie(result.response().result);
+        EXPECT_EQ(result_json, kJsonStringLiteral);
+        loop.Quit();
+      });
+  loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoadVmoDestroyed) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptVmoDestroyed) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
   fuchsia::web::FramePtr frame = CreateFrame();
@@ -783,7 +799,7 @@
   navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello");
 }
 
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavascriptOnLoadWrongOrigin) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptWrongOrigin) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
   fuchsia::web::FramePtr frame = CreateFrame();
@@ -806,7 +822,7 @@
       url, "Welcome to Stan the Offline Dino's Homepage");
 }
 
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoadWildcardOrigin) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptWildcardOrigin) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
   fuchsia::web::FramePtr frame = CreateFrame();
@@ -838,37 +854,9 @@
   navigation_listener_.RunUntilUrlAndTitleEquals(alt_url, "hello");
 }
 
-// Test that consecutive scripts are executed in order by computing a cumulative
-// result.
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteMultipleJavaScriptsOnLoad) {
-  constexpr int64_t kOnLoadScriptId2 = kOnLoadScriptId + 1;
-  ASSERT_TRUE(embedded_test_server()->Start());
-  GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
-  fuchsia::web::FramePtr frame = CreateFrame();
-
-  frame->AddBeforeLoadJavaScript(
-      kOnLoadScriptId, {url.GetOrigin().spec()},
-      cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
-      [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
-        EXPECT_TRUE(result.is_response());
-      });
-  frame->AddBeforeLoadJavaScript(
-      kOnLoadScriptId2, {url.GetOrigin().spec()},
-      cr_fuchsia::MemBufferFromString("stashed_title += ' there';"),
-      [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
-        EXPECT_TRUE(result.is_response());
-      });
-
-  fuchsia::web::NavigationControllerPtr controller;
-  frame->GetNavigationController(controller.NewRequest());
-
-  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
-      controller.get(), fuchsia::web::LoadUrlParams(), url.spec()));
-  navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello there");
-}
-
 // Test that we can inject scripts before and after RenderFrame creation.
-IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteOnLoadEarlyAndLateRegistrations) {
+IN_PROC_BROWSER_TEST_F(FrameImplTest,
+                       BeforeLoadScriptEarlyAndLateRegistrations) {
   constexpr int64_t kOnLoadScriptId2 = kOnLoadScriptId + 1;
 
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/fuchsia/engine/browser/web_engine_content_browser_client.cc b/fuchsia/engine/browser/web_engine_content_browser_client.cc
index 8fe5c17..f6621b5 100644
--- a/fuchsia/engine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia/engine/browser/web_engine_content_browser_client.cc
@@ -12,6 +12,7 @@
 #include "fuchsia/engine/browser/web_engine_browser_context.h"
 #include "fuchsia/engine/browser/web_engine_browser_main_parts.h"
 #include "fuchsia/engine/browser/web_engine_devtools_manager_delegate.h"
+#include "fuchsia/engine/common.h"
 
 WebEngineContentBrowserClient::WebEngineContentBrowserClient(
     fidl::InterfaceRequest<fuchsia::web::Context> request)
@@ -43,8 +44,14 @@
 }
 
 std::string WebEngineContentBrowserClient::GetUserAgent() {
-  return content::BuildUserAgentFromProduct(
-      version_info::GetProductNameAndVersionForUserAgent());
+  std::string user_agent = content::BuildUserAgentFromProduct(GetProduct());
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          kUserAgentProductAndVersion)) {
+    user_agent +=
+        " " + base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
+                  kUserAgentProductAndVersion);
+  }
+  return user_agent;
 }
 
 void WebEngineContentBrowserClient::OverrideWebkitPrefs(
diff --git a/fuchsia/engine/common.cc b/fuchsia/engine/common.cc
index 61927a9..22db116 100644
--- a/fuchsia/engine/common.cc
+++ b/fuchsia/engine/common.cc
@@ -6,3 +6,4 @@
 
 constexpr char kIncognitoSwitch[] = "incognito";
 constexpr char kRemoteDebuggerHandles[] = "remote-debugger-handles";
+constexpr char kUserAgentProductAndVersion[] = "user-agent-product";
diff --git a/fuchsia/engine/common.h b/fuchsia/engine/common.h
index b6f2672..f0664e0 100644
--- a/fuchsia/engine/common.h
+++ b/fuchsia/engine/common.h
@@ -20,6 +20,9 @@
 // a comma-separated list of remote debugger handle IDs as an argument.
 WEB_ENGINE_EXPORT extern const char kRemoteDebuggerHandles[];
 
+// Switch passed to Context process to customize the UserAgent string.
+WEB_ENGINE_EXPORT extern const char kUserAgentProductAndVersion[];
+
 // Handle ID for the Context interface request passed from ContextProvider to
 // Context process.
 constexpr uint32_t kContextRequestHandleId = PA_HND(PA_USER0, 0);
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index b55dd2e..5d50626 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -31,6 +31,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "fuchsia/engine/common.h"
+#include "net/http/http_util.h"
 #include "services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h"
 
 namespace {
@@ -183,6 +184,31 @@
     launch_command.AppendSwitchASCII("--use-gl", "stub");
   }
 
+  // Validate embedder-supplied product, and optional version, and pass it to
+  // the Context to include in the UserAgent.
+  if (params.has_user_agent_product()) {
+    if (!net::HttpUtil::IsToken(params.user_agent_product())) {
+      DLOG(ERROR) << "Invalid embedder product.";
+      context_request.Close(ZX_ERR_INVALID_ARGS);
+      return;
+    }
+    std::string product_tag(params.user_agent_product());
+    if (params.has_user_agent_version()) {
+      if (!net::HttpUtil::IsToken(params.user_agent_version())) {
+        DLOG(ERROR) << "Invalid embedder version.";
+        context_request.Close(ZX_ERR_INVALID_ARGS);
+        return;
+      }
+      product_tag += "/" + params.user_agent_version();
+    }
+    launch_command.AppendSwitchNative(kUserAgentProductAndVersion,
+                                      std::move(product_tag));
+  } else if (params.has_user_agent_version()) {
+    DLOG(ERROR) << "Embedder version without product.";
+    context_request.Close(ZX_ERR_INVALID_ARGS);
+    return;
+  }
+
   if (launch_for_test_)
     launch_for_test_.Run(launch_command, launch_options);
   else
diff --git a/fuchsia/engine/integration_tests_sandbox_policy b/fuchsia/engine/integration_tests_sandbox_policy
index d5649d3..7e786556 100644
--- a/fuchsia/engine/integration_tests_sandbox_policy
+++ b/fuchsia/engine/integration_tests_sandbox_policy
@@ -5,9 +5,11 @@
       "system-temp" ],
   "services": [
       "fuchsia.device.NameProvider",
+      "fuchsia.fonts.Provider",
       "fuchsia.logger.LogSink",
       "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
+      "fuchsia.posix.socket.Provider",
       "fuchsia.process.Launcher",
       "fuchsia.sysmem.Allocator",
       "fuchsia.web.ContextProvider"
diff --git a/fuchsia/engine/web_engine_integration_test.cc b/fuchsia/engine/web_engine_integration_test.cc
new file mode 100644
index 0000000..08b302d
--- /dev/null
+++ b/fuchsia/engine/web_engine_integration_test.cc
@@ -0,0 +1,189 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuchsia/web/cpp/fidl.h>
+#include <lib/fidl/cpp/binding.h>
+
+#include "base/fuchsia/file_utils.h"
+#include "base/fuchsia/service_directory_client.h"
+#include "base/macros.h"
+#include "base/test/scoped_task_environment.h"
+#include "fuchsia/base/frame_test_util.h"
+#include "fuchsia/base/test_navigation_listener.h"
+#include "net/http/http_request_headers.h"
+#include "net/test/embedded_test_server/default_handlers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char kValidUserAgentProduct[] = "TestProduct";
+constexpr char kValidUserAgentVersion[] = "dev.12345";
+constexpr char kValidUserAgentProductAndVersion[] = "TestProduct/dev.12345";
+constexpr char kInvalidUserAgentProduct[] = "Test/Product";
+constexpr char kInvalidUserAgentVersion[] = "dev/12345";
+
+}  // namespace
+
+class WebEngineIntegrationTest : public testing::Test {
+ public:
+  WebEngineIntegrationTest()
+      : task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
+  ~WebEngineIntegrationTest() override = default;
+
+  void SetUp() override {
+    web_context_provider_ =
+        base::fuchsia::ServiceDirectoryClient::ForCurrentProcess()
+            ->ConnectToService<fuchsia::web::ContextProvider>();
+    web_context_provider_.set_error_handler(
+        [](zx_status_t status) { ADD_FAILURE(); });
+
+    net::test_server::RegisterDefaultHandlers(&embedded_test_server_);
+    ASSERT_TRUE(embedded_test_server_.Start());
+  }
+
+  fuchsia::web::CreateContextParams DefaultContextParams() const {
+    fuchsia::web::CreateContextParams create_params;
+    auto directory = base::fuchsia::OpenDirectory(
+        base::FilePath(base::fuchsia::kServiceDirectoryPath));
+    EXPECT_TRUE(directory.is_valid());
+    create_params.set_service_directory(std::move(directory));
+    return create_params;
+  }
+
+  void CreateContextAndFrame(fuchsia::web::CreateContextParams params) {
+    web_context_provider_->Create(std::move(params), context_.NewRequest());
+    context_.set_error_handler([](zx_status_t status) { ADD_FAILURE(); });
+
+    context_->CreateFrame(frame_.NewRequest());
+    frame_.set_error_handler([](zx_status_t status) { ADD_FAILURE(); });
+
+    frame_->GetNavigationController(navigation_controller_.NewRequest());
+    navigation_controller_.set_error_handler(
+        [](zx_status_t status) { ADD_FAILURE(); });
+  }
+
+  void CreateContextAndExpectError(fuchsia::web::CreateContextParams params,
+                                   zx_status_t expected_error) {
+    web_context_provider_->Create(std::move(params), context_.NewRequest());
+    base::RunLoop run_loop;
+    context_.set_error_handler([&run_loop, expected_error](zx_status_t status) {
+      EXPECT_EQ(status, expected_error);
+      run_loop.Quit();
+    });
+    run_loop.Run();
+  }
+
+  void CreateContextAndFrameAndLoadUrl(fuchsia::web::CreateContextParams params,
+                                       const GURL& url) {
+    CreateContextAndFrame(std::move(params));
+
+    // Attach a navigation listener, to monitor the state of the Frame.
+    cr_fuchsia::TestNavigationListener listener;
+    fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
+        &listener);
+    frame_->SetNavigationEventListener(listener_binding.NewBinding());
+
+    // Navigate the Frame to |url| and wait for it to complete loading.
+    fuchsia::web::LoadUrlParams load_url_params;
+    ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
+        navigation_controller_.get(), std::move(load_url_params), url.spec()));
+
+    // Wait for the URL to finish loading.
+    listener.RunUntilUrlEquals(url);
+  }
+
+  std::string ExecuteJavaScriptWithStringResult(base::StringPiece script) {
+    base::Optional<base::Value> value =
+        cr_fuchsia::ExecuteJavaScript(frame_.get(), script);
+    return value ? value->GetString() : std::string();
+  }
+
+ protected:
+  const base::test::ScopedTaskEnvironment task_environment_;
+
+  fuchsia::web::ContextProviderPtr web_context_provider_;
+
+  net::EmbeddedTestServer embedded_test_server_;
+
+  fuchsia::web::ContextPtr context_;
+  fuchsia::web::FramePtr frame_;
+  fuchsia::web::NavigationControllerPtr navigation_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebEngineIntegrationTest);
+};
+
+TEST_F(WebEngineIntegrationTest, ValidUserAgent) {
+  const std::string kEchoHeaderPath =
+      std::string("/echoheader?") + net::HttpRequestHeaders::kUserAgent;
+  const GURL kEchoUserAgentUrl(embedded_test_server_.GetURL(kEchoHeaderPath));
+
+  {
+    // Create a Context with just an embedder product specified.
+    fuchsia::web::CreateContextParams create_params = DefaultContextParams();
+    create_params.set_user_agent_product(kValidUserAgentProduct);
+    CreateContextAndFrameAndLoadUrl(std::move(create_params),
+                                    kEchoUserAgentUrl);
+
+    // Query & verify that the header echoed into the document body contains
+    // the product tag.
+    std::string result =
+        ExecuteJavaScriptWithStringResult("document.body.innerText;");
+    EXPECT_TRUE(result.find(kValidUserAgentProduct) != std::string::npos);
+
+    // Query & verify that the navigator.userAgent contains the product tag.
+    result = ExecuteJavaScriptWithStringResult("navigator.userAgent;");
+    EXPECT_TRUE(result.find(kValidUserAgentProduct) != std::string::npos);
+  }
+
+  {
+    // Create a Context with both product and version specified.
+    fuchsia::web::CreateContextParams create_params = DefaultContextParams();
+    create_params.set_user_agent_product(kValidUserAgentProduct);
+    create_params.set_user_agent_version(kValidUserAgentVersion);
+    CreateContextAndFrameAndLoadUrl(std::move(create_params),
+                                    kEchoUserAgentUrl);
+
+    // Query & verify that the header echoed into the document body contains
+    // both product & version.
+    std::string result =
+        ExecuteJavaScriptWithStringResult("document.body.innerText;");
+    EXPECT_TRUE(result.find(kValidUserAgentProductAndVersion) !=
+                std::string::npos);
+
+    // Query & verify that the navigator.userAgent contains product & version.
+    result = ExecuteJavaScriptWithStringResult("navigator.userAgent;");
+    EXPECT_TRUE(result.find(kValidUserAgentProductAndVersion) !=
+                std::string::npos);
+  }
+}
+
+TEST_F(WebEngineIntegrationTest, InvalidUserAgent) {
+  const std::string kEchoHeaderPath =
+      std::string("/echoheader?") + net::HttpRequestHeaders::kUserAgent;
+  const GURL kEchoUserAgentUrl(embedded_test_server_.GetURL(kEchoHeaderPath));
+
+  {
+    // Try to create a Context with an invalid embedder product tag.
+    fuchsia::web::CreateContextParams create_params = DefaultContextParams();
+    create_params.set_user_agent_product(kInvalidUserAgentProduct);
+    CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS);
+  }
+
+  {
+    // Try to create a Context with an embedder version but no product.
+    fuchsia::web::CreateContextParams create_params = DefaultContextParams();
+    create_params.set_user_agent_version(kValidUserAgentVersion);
+    CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS);
+  }
+
+  {
+    // Try to create a Context with valid product tag, but invalid version.
+    fuchsia::web::CreateContextParams create_params = DefaultContextParams();
+    create_params.set_user_agent_product(kValidUserAgentProduct);
+    create_params.set_user_agent_version(kInvalidUserAgentVersion);
+    CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS);
+  }
+}
diff --git a/fuchsia/fidl/cast/cast_channel.fidl b/fuchsia/fidl/cast/cast_channel.fidl
deleted file mode 100644
index 6b18058..0000000
--- a/fuchsia/fidl/cast/cast_channel.fidl
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-library chromium.cast;
-
-using fuchsia.web;
-
-[Discoverable]
-protocol CastChannel {
-  /// Receives an opened Cast |channel| from the Cast application.
-  /// Open() must not be called again until the preceding call has returned.
-  Open(fuchsia.web.MessagePort channel) -> ();
-};
diff --git a/fuchsia/http/sandbox_policy b/fuchsia/http/sandbox_policy
index b174cb7..52ea938 100644
--- a/fuchsia/http/sandbox_policy
+++ b/fuchsia/http/sandbox_policy
@@ -4,6 +4,7 @@
       "fuchsia.device.NameProvider",
       "fuchsia.logger.LogSink",
       "fuchsia.net.SocketProvider",
-      "fuchsia.netstack.Netstack"
+      "fuchsia.netstack.Netstack",
+      "fuchsia.posix.socket.Provider"
   ]
 }
diff --git a/fuchsia/runners/BUILD.gn b/fuchsia/runners/BUILD.gn
index e7971b0..e0607d0 100644
--- a/fuchsia/runners/BUILD.gn
+++ b/fuchsia/runners/BUILD.gn
@@ -4,10 +4,23 @@
 
 assert(is_fuchsia)
 
+import("//build/buildflag_header.gni")
 import("//build/config/fuchsia/rules.gni")
 import("//build/config/fuchsia/symbol_archive.gni")
 import("//testing/test.gni")
 
+declare_args() {
+  # Enable remote debugging for WebContentRunner instances on this port.
+  # Set to 0 to disable.
+  enable_remote_debugging_on_port = 0
+}
+
+buildflag_header("buildflags") {
+  header = "buildflags.h"
+  flags = [ "ENABLE_REMOTE_DEBUGGING_ON_PORT=$enable_remote_debugging_on_port" ]
+  visibility = [ ":*" ]
+}
+
 # Files common to both cast_runner and web_runner targets.
 source_set("common") {
   sources = [
@@ -17,6 +30,7 @@
     "common/web_content_runner.h",
   ]
   deps = [
+    ":buildflags",
     "//base",
     "//fuchsia/base",
     "//fuchsia/base:modular",
@@ -34,8 +48,6 @@
   sources = [
     "cast/api_bindings_client.cc",
     "cast/api_bindings_client.h",
-    "cast/cast_channel_bindings.cc",
-    "cast/cast_channel_bindings.h",
     "cast/cast_component.cc",
     "cast/cast_component.h",
     "cast/cast_platform_bindings_ids.h",
@@ -43,15 +55,11 @@
     "cast/cast_runner.h",
     "cast/named_message_port_connector.cc",
     "cast/named_message_port_connector.h",
-    "cast/queryable_data_bindings.cc",
-    "cast/queryable_data_bindings.h",
     "cast/touch_input_bindings.cc",
     "cast/touch_input_bindings.h",
   ]
   data = [
-    "cast/cast_channel_bindings.js",
     "cast/not_implemented_api_bindings.js",
-    "cast/queryable_data_bindings.js",
     "cast/touch_input_bindings.js",
   ]
   data_deps = [
@@ -105,8 +113,6 @@
   sources = [
     "cast/fake_application_config_manager.cc",
     "cast/fake_application_config_manager.h",
-    "cast/fake_queryable_data.cc",
-    "cast/fake_queryable_data.h",
     "cast/test_api_bindings.cc",
     "cast/test_api_bindings.h",
   ]
@@ -147,10 +153,8 @@
 test("cast_runner_browsertests") {
   sources = [
     "cast/api_bindings_client_browsertest.cc",
-    "cast/cast_channel_bindings_browsertest.cc",
     "cast/named_message_port_connector_browsertest.cc",
     "cast/not_implemented_api_bindings_browsertest.cc",
-    "cast/queryable_data_bindings_browsertest.cc",
   ]
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
   data = [
diff --git a/fuchsia/runners/cast/api_bindings_client.cc b/fuchsia/runners/cast/api_bindings_client.cc
index 27e1cfd..43b8a63 100644
--- a/fuchsia/runners/cast/api_bindings_client.cc
+++ b/fuchsia/runners/cast/api_bindings_client.cc
@@ -27,22 +27,11 @@
   bindings_service_->GetAll(
       fit::bind_member(this, &ApiBindingsClient::OnBindingsReceived));
 
-  bindings_service_.set_error_handler([this](zx_status_t status) mutable {
-    ZX_LOG_IF(ERROR, status != ZX_OK, status)
-        << "ApiBindings disconnected before bindings were read.";
-
-    if (!bindings_) {
-      // The Agent disconnected before sending a bindings response,
-      // so it's possible that the Agent doesn't yet implement the ApiBindings
-      // service. Populate the bindings with an empty set so initialization may
-      // continue.
-      // TODO(crbug.com/953958): Remove this fallback once the Agent implements
-      // ApiBindings.
-      LOG(WARNING)
-          << "Couldn't receive bindings from Agent, proceeding anyway.";
-      OnBindingsReceived({});
-    }
-  });
+  bindings_service_.set_error_handler(
+      [](zx_status_t status) mutable {
+        ZX_LOG(FATAL, status)
+            << "ApiBindings service disconnected before entries were returned.";
+      });
 }
 
 void ApiBindingsClient::OnBindingsReceived(
diff --git a/fuchsia/runners/cast/cast_channel_bindings.cc b/fuchsia/runners/cast/cast_channel_bindings.cc
deleted file mode 100644
index cd36f9b..0000000
--- a/fuchsia/runners/cast/cast_channel_bindings.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fuchsia/runners/cast/cast_channel_bindings.h"
-
-#include <lib/fit/function.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/fuchsia/fuchsia_logging.h"
-#include "base/macros.h"
-#include "base/path_service.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "fuchsia/base/mem_buffer_util.h"
-#include "fuchsia/runners/cast/cast_platform_bindings_ids.h"
-#include "fuchsia/runners/cast/named_message_port_connector.h"
-
-// Unique identifier of the Cast Channel message port, used by the JavaScript
-// API to connect to the port.
-const char kMessagePortName[] = "cast.__platform__.channel";
-
-CastChannelBindings::CastChannelBindings(
-    fuchsia::web::Frame* frame,
-    NamedMessagePortConnector* connector,
-    chromium::cast::CastChannelPtr channel_consumer)
-    : frame_(frame),
-      connector_(connector),
-      channel_consumer_(std::move(channel_consumer)) {
-  DCHECK(connector_);
-  DCHECK(frame_);
-
-  channel_consumer_.set_error_handler([](zx_status_t status) mutable {
-    ZX_LOG(ERROR, status) << "Cast Channel FIDL client disconnected";
-  });
-
-  connector->Register(
-      kMessagePortName,
-      base::BindRepeating(&CastChannelBindings::OnMasterPortReceived,
-                          base::Unretained(this)));
-
-  base::FilePath assets_path;
-  CHECK(base::PathService::Get(base::DIR_ASSETS, &assets_path));
-  frame_->AddBeforeLoadJavaScript(
-      static_cast<uint64_t>(CastPlatformBindingsId::CAST_CHANNEL), {"*"},
-      cr_fuchsia::MemBufferFromFile(
-          base::File(assets_path.AppendASCII(
-                         "fuchsia/runners/cast/cast_channel_bindings.js"),
-                     base::File::FLAG_OPEN | base::File::FLAG_READ)),
-      [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
-        CHECK(result.is_response()) << "JavaScript injection error.";
-      });
-}
-
-CastChannelBindings::~CastChannelBindings() {
-  connector_->Unregister(kMessagePortName);
-}
-
-void CastChannelBindings::OnMasterPortError() {
-  master_port_.Unbind();
-}
-
-void CastChannelBindings::OnMasterPortReceived(
-    fidl::InterfaceHandle<fuchsia::web::MessagePort> port) {
-  DCHECK(port);
-
-  master_port_ = port.Bind();
-  master_port_.set_error_handler([this](zx_status_t status) {
-    ZX_LOG_IF(WARNING, status != ZX_ERR_PEER_CLOSED, status)
-        << "Cast Channel master port disconnected.";
-    OnMasterPortError();
-  });
-  master_port_->ReceiveMessage(fit::bind_member(
-      this, &CastChannelBindings::OnCastChannelMessageReceived));
-}
-
-void CastChannelBindings::OnCastChannelMessageReceived(
-    fuchsia::web::WebMessage message) {
-  if (!message.has_incoming_transfer() ||
-      !(message.incoming_transfer().size() == 1) ||
-      !message.incoming_transfer().at(0).is_message_port()) {
-    LOG(WARNING) << "Received a CastChannel without a message port.";
-    OnMasterPortError();
-    return;
-  }
-
-  SendChannelToConsumer(
-      message.mutable_incoming_transfer()->at(0).message_port().Bind());
-
-  master_port_->ReceiveMessage(fit::bind_member(
-      this, &CastChannelBindings::OnCastChannelMessageReceived));
-}
-
-void CastChannelBindings::SendChannelToConsumer(
-    fuchsia::web::MessagePortPtr channel) {
-  if (consumer_ready_for_port_) {
-    consumer_ready_for_port_ = false;
-    channel_consumer_->Open(
-        std::move(channel),
-        fit::bind_member(this, &CastChannelBindings::OnConsumerReadyForPort));
-  } else {
-    connected_channel_queue_.push_front(std::move(channel));
-  }
-}
-
-void CastChannelBindings::OnConsumerReadyForPort() {
-  DCHECK(!consumer_ready_for_port_);
-
-  consumer_ready_for_port_ = true;
-  if (!connected_channel_queue_.empty()) {
-    // Deliver the next enqueued channel.
-    fuchsia::web::MessagePortPtr next_port =
-        std::move(connected_channel_queue_.back());
-    SendChannelToConsumer(std::move(next_port));
-    connected_channel_queue_.pop_back();
-  }
-}
diff --git a/fuchsia/runners/cast/cast_channel_bindings.h b/fuchsia/runners/cast/cast_channel_bindings.h
deleted file mode 100644
index 79c4739..0000000
--- a/fuchsia/runners/cast/cast_channel_bindings.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FUCHSIA_RUNNERS_CAST_CAST_CHANNEL_BINDINGS_H_
-#define FUCHSIA_RUNNERS_CAST_CAST_CHANNEL_BINDINGS_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/containers/circular_deque.h"
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
-#include "fuchsia/runners/cast/named_message_port_connector.h"
-
-// Handles the injection of cast.__platform__.channel bindings into pages'
-// scripting context, and establishes a bidirectional message pipe over
-// which the two communicate.
-class CastChannelBindings {
- public:
-  // Attaches CastChannel bindings and port to a |frame|.
-  // |frame|: The frame to be provided with a CastChannel.
-  // |connector|: The NamedMessagePortConnector to use for establishing
-  // transport.
-  // |channel_consumer|: A FIDL service which receives opened Cast Channels.
-  // Both |frame| and |connector| must outlive |this|.
-  CastChannelBindings(fuchsia::web::Frame* frame,
-                      NamedMessagePortConnector* connector,
-                      chromium::cast::CastChannelPtr channel_consumer);
-  ~CastChannelBindings();
-
- private:
-  // Receives a port used for receiving new Cast Channel ports.
-  void OnMasterPortReceived(
-      fidl::InterfaceHandle<fuchsia::web::MessagePort> port);
-
-  // Receives a message containing a newly opened Cast Channel from
-  // |master_port_|.
-  void OnCastChannelMessageReceived(fuchsia::web::WebMessage message);
-
-  // Indicates that |channel_consumer_| is ready to take another port.
-  void OnConsumerReadyForPort();
-
-  // Sends or enqueues a Cast Channel for sending to |channel_consumer_|.
-  void SendChannelToConsumer(fuchsia::web::MessagePortPtr channel);
-
-  // Handles error conditions on |master_port_|.
-  void OnMasterPortError();
-
-  fuchsia::web::Frame* const frame_;
-  NamedMessagePortConnector* const connector_;
-
-  // A queue of channels waiting to be sent the Cast Channel FIDL service.
-  base::circular_deque<fuchsia::web::MessagePortPtr> connected_channel_queue_;
-
-  // A long-lived port, used to receive new Cast Channel ports when they are
-  // opened. Should be automatically  populated by the
-  // NamedMessagePortConnector whenever |frame| loads a new page.
-  fuchsia::web::MessagePortPtr master_port_;
-
-  fuchsia::mem::Buffer bindings_script_;
-
-  // The service which will receive connected Cast Channels.
-  chromium::cast::CastChannelPtr channel_consumer_;
-
-  // If set, indicates that |channel_consumer_| is ready to accept another Cast
-  // Channel.
-  bool consumer_ready_for_port_ = true;
-
-  DISALLOW_COPY_AND_ASSIGN(CastChannelBindings);
-};
-
-#endif  // FUCHSIA_RUNNERS_CAST_CAST_CHANNEL_BINDINGS_H_
diff --git a/fuchsia/runners/cast/cast_channel_bindings.js b/fuchsia/runners/cast/cast_channel_bindings.js
deleted file mode 100644
index de2e48db..0000000
--- a/fuchsia/runners/cast/cast_channel_bindings.js
+++ /dev/null
@@ -1,77 +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.
-
-'use strict';
-
-// Implementation of the cast.__platform__.channel API which uses MessagePort
-// IPC to communicate with an actual Cast Channel implementation provided by
-// the content embedder. There is at most one channel which may be opened (able
-// to send & receive messages) or closed.
-
-// Don't clobber the Cast Channel API if it was previously injected.
-if (!cast.__platform__.channel) {
-  cast.__platform__.channel = new class {
-    constructor() {
-      this.master_port_ = cast.__platform__.PortConnector.bind(
-          'cast.__platform__.channel');
-    }
-
-    // Signals to the peer that the Cast Channel is opened.
-    // |openHandler|: A callback function which is invoked on channel open with
-    //                a boolean indicating success.
-    // |messageHandler|: Invoked when a message arrives from the peer.
-    open(openHandler, messageHandler) {
-      if (this.current_port_) {
-        console.error('open() called on an open Cast Channel.');
-        openHandler(false);
-        return;
-      }
-
-      if (!messageHandler) {
-        console.error('Null messageHandler passed to open().');
-        openHandler(false);
-        return;
-      }
-
-      // Create the MessageChannel for Cast Channel and distribute its ports.
-      var channel = new MessageChannel();
-      this.master_port_.postMessage('', [channel.port1]);
-
-      this.current_port_ = channel.port2;
-      this.current_port_.onmessage = function(message) {
-        messageHandler(message.data);
-      };
-      this.current_port_.onerror = function() {
-        console.error('Cast Channel was closed unexpectedly by peer.');
-        return;
-      };
-
-      openHandler(true);
-    }
-
-    // Closes the Cast Channel.
-    close(closeHandler) {
-      if (this.current_port_) {
-        this.current_port_.close();
-        this.current_port_ = null;
-      }
-    }
-
-    // Sends a message to the Cast Channel's peer.
-    send(message) {
-      if (!this.current_port_) {
-        console.error('send() called on a closed Cast Channel.');
-        return;
-      }
-
-      this.current_port_.postMessage(message);
-    }
-
-    // Used to send newly opened Cast Channel ports to C++.
-    master_port_ = null;
-
-    // The current opened Cast Channel.
-    current_port_ = null;
-  };
-}
diff --git a/fuchsia/runners/cast/cast_channel_bindings_browsertest.cc b/fuchsia/runners/cast/cast_channel_bindings_browsertest.cc
deleted file mode 100644
index e3db04e1..0000000
--- a/fuchsia/runners/cast/cast_channel_bindings_browsertest.cc
+++ /dev/null
@@ -1,197 +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 <lib/fidl/cpp/binding.h>
-
-#include "base/barrier_closure.h"
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/path_service.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/thread_restrictions.h"
-#include "fuchsia/base/fit_adapter.h"
-#include "fuchsia/base/mem_buffer_util.h"
-#include "fuchsia/base/result_receiver.h"
-#include "fuchsia/base/test_navigation_listener.h"
-#include "fuchsia/engine/test/web_engine_browser_test.h"
-#include "fuchsia/runners/cast/cast_channel_bindings.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/url_constants.h"
-
-namespace {
-
-class CastChannelBindingsTest : public cr_fuchsia::WebEngineBrowserTest,
-                                public chromium::cast::CastChannel {
- public:
-  CastChannelBindingsTest()
-      : receiver_binding_(this),
-        run_timeout_(TestTimeouts::action_timeout(),
-                     base::MakeExpectedNotRunClosure(FROM_HERE)) {
-    set_test_server_root(base::FilePath("fuchsia/runners/cast/testdata"));
-  }
-
-  ~CastChannelBindingsTest() override = default;
-
- protected:
-  void SetUpOnMainThread() override {
-    cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread();
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    frame_ = WebEngineBrowserTest::CreateFrame(&navigation_listener_);
-    connector_ = std::make_unique<NamedMessagePortConnector>(frame_.get());
-  }
-
-  void Open(fidl::InterfaceHandle<fuchsia::web::MessagePort> channel,
-            OpenCallback receive_next_channel_cb) override {
-    connected_channel_ = channel.Bind();
-    receive_next_channel_cb_ = std::move(receive_next_channel_cb);
-
-    if (on_channel_connected_cb_)
-      std::move(on_channel_connected_cb_).Run();
-  }
-
-  void WaitUntilCastChannelOpened() {
-    if (!connected_channel_) {
-      base::RunLoop run_loop;
-      on_channel_connected_cb_ = run_loop.QuitClosure();
-      run_loop.Run();
-    }
-    receive_next_channel_cb_();
-  }
-
-  void WaitUntilCastChannelClosed() {
-    if (!connected_channel_)
-      return;
-
-    base::RunLoop run_loop;
-    connected_channel_.set_error_handler(
-        [quit_closure = run_loop.QuitClosure()](zx_status_t) {
-          quit_closure.Run();
-        });
-    run_loop.Run();
-  }
-
-  std::string ReadStringFromChannel() {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> message(
-        run_loop.QuitClosure());
-    connected_channel_->ReceiveMessage(
-        cr_fuchsia::CallbackToFitFunction(message.GetReceiveCallback()));
-    run_loop.Run();
-
-    std::string data;
-    CHECK(cr_fuchsia::StringFromMemBuffer(message->data(), &data));
-    return data;
-  }
-
-  void CheckLoadUrl(const GURL& url,
-                    fuchsia::web::NavigationController* controller) {
-    navigate_run_loop_ = std::make_unique<base::RunLoop>();
-    cr_fuchsia::ResultReceiver<
-        fuchsia::web::NavigationController_LoadUrl_Result>
-        result;
-    controller->LoadUrl(
-        url.spec(), fuchsia::web::LoadUrlParams(),
-        cr_fuchsia::CallbackToFitFunction(result.GetReceiveCallback()));
-    navigation_listener_.RunUntilUrlEquals(url);
-    EXPECT_TRUE(result->is_response());
-  }
-
-  std::unique_ptr<base::RunLoop> navigate_run_loop_;
-  fuchsia::web::FramePtr frame_;
-  std::unique_ptr<NamedMessagePortConnector> connector_;
-  fidl::Binding<chromium::cast::CastChannel> receiver_binding_;
-  cr_fuchsia::TestNavigationListener navigation_listener_;
-
-  // The connected Cast Channel.
-  fuchsia::web::MessagePortPtr connected_channel_;
-
-  // A pending on-connect callback, to be invoked once a Cast Channel is
-  // received.
-  base::OnceClosure on_channel_connected_cb_;
-
- private:
-  const base::RunLoop::ScopedRunTimeoutForTest run_timeout_;
-  OpenCallback receive_next_channel_cb_;
-
-  DISALLOW_COPY_AND_ASSIGN(CastChannelBindingsTest);
-};
-
-IN_PROC_BROWSER_TEST_F(CastChannelBindingsTest, CastChannelBufferedInput) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  ASSERT_TRUE(embedded_test_server()->Start());
-  GURL test_url(embedded_test_server()->GetURL("/cast_channel.html"));
-
-  frame_->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::INFO);
-  fuchsia::web::NavigationControllerPtr controller;
-  frame_->GetNavigationController(controller.NewRequest());
-
-  CastChannelBindings cast_channel_instance(
-      frame_.get(), connector_.get(), receiver_binding_.NewBinding().Bind());
-
-  // Verify that CastChannelBindings can properly handle message, connect,
-  // disconnect, and MessagePort disconnection events.
-  CheckLoadUrl(test_url, controller.get());
-  connector_->OnPageLoad();
-
-  WaitUntilCastChannelOpened();
-
-  auto expected_list = {"this", "is", "a", "test"};
-  for (const std::string& expected : expected_list) {
-    EXPECT_EQ(ReadStringFromChannel(), expected);
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(CastChannelBindingsTest, CastChannelReconnect) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  ASSERT_TRUE(embedded_test_server()->Start());
-  GURL test_url(embedded_test_server()->GetURL("/cast_channel_reconnect.html"));
-  GURL empty_url(embedded_test_server()->GetURL("/defaultresponse"));
-
-  frame_->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::INFO);
-  fuchsia::web::NavigationControllerPtr controller;
-  frame_->GetNavigationController(controller.NewRequest());
-
-  CastChannelBindings cast_channel_instance(
-      frame_.get(), connector_.get(), receiver_binding_.NewBinding().Bind());
-
-  // Verify that CastChannelBindings can properly handle message, connect,
-  // disconnect, and MessagePort disconnection events.
-  // Also verify that the cast channel is used across inter-page navigations.
-  for (int i = 0; i < 5; ++i) {
-    CheckLoadUrl(test_url, controller.get());
-    connector_->OnPageLoad();
-
-    WaitUntilCastChannelOpened();
-
-    WaitUntilCastChannelClosed();
-
-    WaitUntilCastChannelOpened();
-
-    EXPECT_EQ("reconnected", ReadStringFromChannel());
-
-    // Send a message to the channel.
-    {
-      fuchsia::web::WebMessage message;
-      message.set_data(cr_fuchsia::MemBufferFromString("hello"));
-
-      base::RunLoop run_loop;
-      cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result>
-          post_result(run_loop.QuitClosure());
-      connected_channel_->PostMessage(
-          std::move(message),
-          cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
-      run_loop.Run();
-      EXPECT_FALSE(post_result->is_err());
-    }
-
-    EXPECT_EQ("ack hello", ReadStringFromChannel());
-
-    // Navigate away.
-    CheckLoadUrl(empty_url, controller.get());
-  }
-}
-
-}  // namespace
diff --git a/fuchsia/runners/cast/cast_component.cc b/fuchsia/runners/cast/cast_component.cc
index 00eb1e4..03a0067 100644
--- a/fuchsia/runners/cast/cast_component.cc
+++ b/fuchsia/runners/cast/cast_component.cc
@@ -100,15 +100,6 @@
         CHECK(result.is_response()) << "Couldn't inject stub bindings.";
       });
 
-  cast_channel_ = std::make_unique<CastChannelBindings>(
-      frame(), &connector_,
-      agent_manager_->ConnectToAgentService<chromium::cast::CastChannel>(
-          CastRunner::kAgentComponentUrl));
-
-  queryable_data_ = std::make_unique<QueryableDataBindings>(
-      frame(),
-      agent_manager_->ConnectToAgentService<chromium::cast::QueryableData>(
-          CastRunner::kAgentComponentUrl));
   touch_input_ = std::make_unique<TouchInputBindings>(touch_input_policy_,
                                                       frame(), &connector_);
 }
diff --git a/fuchsia/runners/cast/cast_component.h b/fuchsia/runners/cast/cast_component.h
index 7ccf279..a65f3b8 100644
--- a/fuchsia/runners/cast/cast_component.h
+++ b/fuchsia/runners/cast/cast_component.h
@@ -11,9 +11,7 @@
 #include "base/fuchsia/service_directory.h"
 #include "fuchsia/base/agent_manager.h"
 #include "fuchsia/runners/cast/api_bindings_client.h"
-#include "fuchsia/runners/cast/cast_channel_bindings.h"
 #include "fuchsia/runners/cast/named_message_port_connector.h"
-#include "fuchsia/runners/cast/queryable_data_bindings.h"
 #include "fuchsia/runners/cast/touch_input_bindings.h"
 #include "fuchsia/runners/common/web_component.h"
 
@@ -52,8 +50,6 @@
   bool constructor_active_ = false;
   TouchInputPolicy touch_input_policy_;
   NamedMessagePortConnector connector_;
-  std::unique_ptr<CastChannelBindings> cast_channel_;
-  std::unique_ptr<QueryableDataBindings> queryable_data_;
   std::unique_ptr<TouchInputBindings> touch_input_;
   std::unique_ptr<ApiBindingsClient> api_bindings_client_;
 
diff --git a/fuchsia/runners/cast/cast_runner.cc b/fuchsia/runners/cast/cast_runner.cc
index 32c636b9d..6f53482 100644
--- a/fuchsia/runners/cast/cast_runner.cc
+++ b/fuchsia/runners/cast/cast_runner.cc
@@ -29,6 +29,8 @@
   std::unique_ptr<ApiBindingsClient> bindings_manager;
   fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request;
   chromium::cast::ApplicationConfig app_config;
+  fuchsia::web::AdditionalHeadersProviderPtr headers_provider;
+  base::Optional<std::vector<fuchsia::net::http::Header>> headers;
 };
 
 void CastRunner::StartComponent(
@@ -78,6 +80,28 @@
       base::BindOnce(&CastRunner::MaybeStartComponent, base::Unretained(this),
                      base::Unretained(pending_component.get())));
 
+  // Get AdditionalHeadersProvider from the Agent.
+  fidl::InterfaceHandle<fuchsia::web::AdditionalHeadersProvider>
+      additional_headers_provider;
+  pending_component->agent_manager->ConnectToAgentService(
+      kAgentComponentUrl, additional_headers_provider.NewRequest());
+  pending_component->headers_provider = additional_headers_provider.Bind();
+  pending_component->headers_provider.set_error_handler(
+      [this, pending_component = pending_component.get()](zx_status_t error) {
+        if (pending_component->headers.has_value())
+          return;
+        pending_component->headers = {};
+        MaybeStartComponent(pending_component);
+      });
+  pending_component->headers_provider->GetHeaders(
+      [this, pending_component = pending_component.get()](
+          std::vector<fuchsia::net::http::Header> headers, zx_time_t expiry) {
+        pending_component->headers =
+            base::Optional<std::vector<fuchsia::net::http::Header>>(
+                std::move(headers));
+        MaybeStartComponent(pending_component);
+      });
+
   const std::string cast_app_id(cast_url.GetContent());
   pending_component->app_config_manager->GetConfig(
       cast_app_id, [this, pending_component = pending_component.get()](
@@ -114,14 +138,12 @@
 }
 
 void CastRunner::MaybeStartComponent(PendingComponent* pending_component) {
-  // Called after the completion of GetConfigCallback() or
-  // ApiBindingsClient::OnBindingsReceived().
-  if (pending_component->app_config.IsEmpty() ||
-      !pending_component->bindings_manager->HasBindings()) {
-    // Don't proceed unless both the application config and API bindings are
-    // received.
+  if (pending_component->app_config.IsEmpty())
     return;
-  }
+  if (!pending_component->bindings_manager->HasBindings())
+    return;
+  if (!pending_component->headers.has_value())
+    return;
 
   // Create a component based on the returned configuration, and pass it the
   // fields stashed in PendingComponent.
@@ -132,8 +154,10 @@
       std::move(pending_component->startup_context),
       std::move(pending_component->controller_request),
       std::move(pending_component->agent_manager));
+  std::vector<fuchsia::net::http::Header> additional_headers =
+      pending_component->headers.value();
   pending_components_.erase(pending_component);
 
-  component->LoadUrl(std::move(cast_app_url));
+  component->LoadUrl(std::move(cast_app_url), std::move(additional_headers));
   RegisterComponent(std::move(component));
 }
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc
index 37415e3..ed8e8f2c 100644
--- a/fuchsia/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -14,17 +14,22 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/test_timeouts.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "fuchsia/base/agent_impl.h"
 #include "fuchsia/base/fake_component_context.h"
 #include "fuchsia/base/fit_adapter.h"
+#include "fuchsia/base/frame_test_util.h"
 #include "fuchsia/base/mem_buffer_util.h"
 #include "fuchsia/base/result_receiver.h"
+#include "fuchsia/base/test_navigation_listener.h"
 #include "fuchsia/runners/cast/cast_runner.h"
 #include "fuchsia/runners/cast/fake_application_config_manager.h"
 #include "fuchsia/runners/cast/test_api_bindings.h"
 #include "fuchsia/runners/common/web_component.h"
 #include "fuchsia/runners/common/web_content_runner.h"
 #include "net/test/embedded_test_server/default_handlers.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace castrunner {
@@ -39,40 +44,33 @@
   ADD_FAILURE();
 }
 
-class FakeCastChannel : public chromium::cast::CastChannel {
+std::vector<uint8_t> StringToUnsignedVector(base::StringPiece str) {
+  const uint8_t* raw_data = reinterpret_cast<const uint8_t*>(str.data());
+  return std::vector<uint8_t>(raw_data, raw_data + str.length());
+}
+
+class FakeAdditionalHeadersProvider
+    : public fuchsia::web::AdditionalHeadersProvider {
  public:
-  explicit FakeCastChannel(base::fuchsia::ServiceDirectory* directory)
+  FakeAdditionalHeadersProvider(base::fuchsia::ServiceDirectory* directory)
       : binding_(directory, this) {}
+  ~FakeAdditionalHeadersProvider() override = default;
 
-  // Returns null if the Cast channel is not open.
-  const fuchsia::web::MessagePortPtr& port() const { return port_; }
-
-  void set_on_opened(base::OnceClosure on_opened) {
-    on_opened_ = std::move(on_opened);
+ private:
+  void GetHeaders(GetHeadersCallback callback) override {
+    std::vector<fuchsia::net::http::Header> headers;
+    fuchsia::net::http::Header header;
+    header.name = StringToUnsignedVector("Test");
+    header.value = StringToUnsignedVector("Value");
+    headers.push_back(std::move(header));
+    callback(std::move(headers), 0);
   }
 
- protected:
-  // chromium::cast::CastChannel implementation.
-  void Open(fidl::InterfaceHandle<fuchsia::web::MessagePort> channel,
-            OpenCallback callback_ignored) override {
-    port_ = channel.Bind();
-
-    if (on_opened_)
-      std::move(on_opened_).Run();
-
-    callback_ignored();
-  }
-
-  const base::fuchsia::ScopedServiceBinding<chromium::cast::CastChannel>
+  const base::fuchsia::ScopedServiceBinding<
+      fuchsia::web::AdditionalHeadersProvider>
       binding_;
 
-  // Null until the Cast app connects to the Cast channel.
-  fuchsia::web::MessagePortPtr port_;
-
-  // Invoked when the contect opens a new Cast channel, if set.
-  base::OnceClosure on_opened_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeCastChannel);
+  DISALLOW_COPY_AND_ASSIGN(FakeAdditionalHeadersProvider);
 };
 
 class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase {
@@ -83,11 +81,14 @@
       chromium::cast::ApiBindings* bindings_manager)
       : ComponentStateBase(component_url),
         app_config_binding_(service_directory(), app_config_manager),
-        cast_channel_(std::make_unique<FakeCastChannel>(service_directory())) {
-    if (bindings_manager)
+        additional_headers_provider_(
+            std::make_unique<FakeAdditionalHeadersProvider>(
+                service_directory())) {
+    if (bindings_manager) {
       bindings_manager_binding_ = std::make_unique<
           base::fuchsia::ScopedServiceBinding<chromium::cast::ApiBindings>>(
           service_directory(), bindings_manager);
+    }
   }
   ~FakeComponentState() override {
     if (on_delete_)
@@ -98,9 +99,6 @@
     on_delete_ = std::move(on_delete);
   }
 
-  FakeCastChannel* cast_channel() { return cast_channel_.get(); }
-  void ClearCastChannel() { cast_channel_.reset(); }
-
  protected:
   const base::fuchsia::ScopedServiceBinding<
       chromium::cast::ApplicationConfigManager>
@@ -108,7 +106,7 @@
   std::unique_ptr<
       base::fuchsia::ScopedServiceBinding<chromium::cast::ApiBindings>>
       bindings_manager_binding_;
-  std::unique_ptr<FakeCastChannel> cast_channel_;
+  std::unique_ptr<FakeAdditionalHeadersProvider> additional_headers_provider_;
   base::OnceClosure on_delete_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeComponentState);
@@ -155,17 +153,6 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  void WaitUntilCastChannelOpened() {
-    if (component_state_->cast_channel()->port())
-      return;
-
-    base::RunLoop run_loop;
-    component_state_->cast_channel()->set_on_opened(run_loop.QuitClosure());
-    run_loop.Run();
-
-    ASSERT_TRUE(component_state_->cast_channel()->port());
-  }
-
   fuchsia::sys::ComponentControllerPtr StartCastComponent(
       base::StringPiece component_url) {
     DCHECK(!component_state_);
@@ -286,54 +273,46 @@
   run_loop.Run();
 }
 
-// Ensures that the runner will continue to work during the transitional period
-// when the Agent does not supply an ApiBindings.
-// TODO(crbug.com/953958): Remove this.
-TEST_F(CastRunnerIntegrationTest, NoApiBindings) {
-  provide_api_bindings_ = false;
+TEST_F(CastRunnerIntegrationTest, ApiBindings) {
+  provide_api_bindings_ = true;
   const char kBlankAppId[] = "00000000";
-  const char kBlankAppPath[] = "/defaultresponse";
+  const char kBlankAppPath[] = "/echo.html";
   app_config_manager_.AddAppMapping(kBlankAppId,
                                     test_server_.GetURL(kBlankAppPath));
 
+  std::vector<chromium::cast::ApiBinding> binding_list;
+  chromium::cast::ApiBinding echo_binding;
+  echo_binding.set_before_load_script(cr_fuchsia::MemBufferFromString(
+      "window.echo = cast.__platform__.PortConnector.bind('echoService');"));
+  binding_list.emplace_back(std::move(echo_binding));
+  api_bindings_.set_bindings(std::move(binding_list));
+
   // Launch the test-app component.
   fuchsia::sys::ComponentControllerPtr component_controller =
       StartCastComponent(base::StringPrintf("cast:%s", kBlankAppId));
   component_controller.set_error_handler(&ComponentErrorHandler);
 
-  // Access the NavigationController from the WebComponent. The test will hang
-  // here if no WebComponent was created.
-  fuchsia::web::NavigationControllerPtr nav_controller;
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> web_component(
-        run_loop.QuitClosure());
-    cast_runner_->GetWebComponentForTest(web_component.GetReceiveCallback());
-    run_loop.Run();
-    ASSERT_NE(*web_component, nullptr);
-    (*web_component)
-        ->frame()
-        ->GetNavigationController(nav_controller.NewRequest());
-  }
+  fuchsia::web::MessagePortPtr port =
+      api_bindings_.RunUntilMessagePortReceived("echoService").Bind();
 
-  // Ensure the NavigationState has the expected URL.
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> nav_entry(
-        run_loop.QuitClosure());
-    nav_controller->GetVisibleEntry(
-        cr_fuchsia::CallbackToFitFunction(nav_entry.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_TRUE(nav_entry->has_url());
-    EXPECT_EQ(nav_entry->url(), test_server_.GetURL(kBlankAppPath).spec());
-  }
+  fuchsia::web::WebMessage message;
+  message.set_data(cr_fuchsia::MemBufferFromString("ping"));
+  port->PostMessage(std::move(message),
+                    [](fuchsia::web::MessagePort_PostMessage_Result result) {
+                      EXPECT_TRUE(result.is_response());
+                    });
 
-  // Verify that the component is torn down when |component_controller| is
-  // unbound.
-  base::RunLoop run_loop;
-  component_state_->set_on_delete(run_loop.QuitClosure());
-  component_controller.Unbind();
-  run_loop.Run();
+  base::RunLoop response_loop;
+  cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> response(
+      response_loop.QuitClosure());
+  port->ReceiveMessage(
+      cr_fuchsia::CallbackToFitFunction(response.GetReceiveCallback()));
+  response_loop.Run();
+
+  std::string response_string;
+  EXPECT_TRUE(
+      cr_fuchsia::StringFromMemBuffer(response->data(), &response_string));
+  EXPECT_EQ("ack ping", response_string);
 }
 
 TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) {
@@ -356,89 +335,43 @@
   EXPECT_FALSE(web_component.has_value());
 }
 
-TEST_F(CastRunnerIntegrationTest, CastChannel) {
-  const char kCastChannelAppId[] = "00000001";
-  const char kCastChannelAppPath[] = "/cast_channel.html";
-  app_config_manager_.AddAppMapping(kCastChannelAppId,
-                                    test_server_.GetURL(kCastChannelAppPath));
+TEST_F(CastRunnerIntegrationTest, AdditionalHeadersProvider) {
+  const char kEchoAppId[] = "00000000";
+  const char kEchoAppPath[] = "/echoheader?Test";
+  const GURL echo_app_url = test_server_.GetURL(kEchoAppPath);
+  app_config_manager_.AddAppMapping(kEchoAppId, echo_app_url);
 
   // Launch the test-app component.
   fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId));
+      StartCastComponent(base::StringPrintf("cast:%s", kEchoAppId));
   component_controller.set_error_handler(&ComponentErrorHandler);
 
-  // Access the NavigationController from the WebComponent. The test will hang
-  // here if no WebComponent was created.
-  fuchsia::web::NavigationControllerPtr nav_controller;
+  WebComponent* web_component = nullptr;
   {
     base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<WebComponent*> web_component(
+    cr_fuchsia::ResultReceiver<WebComponent*> web_component_receiver(
         run_loop.QuitClosure());
-    cast_runner_->GetWebComponentForTest(web_component.GetReceiveCallback());
+    cast_runner_->GetWebComponentForTest(
+        web_component_receiver.GetReceiveCallback());
     run_loop.Run();
-    ASSERT_NE(*web_component, nullptr);
-    (*web_component)
-        ->frame()
-        ->GetNavigationController(nav_controller.NewRequest());
+    ASSERT_NE(*web_component_receiver, nullptr);
+    web_component = *web_component_receiver;
   }
 
-  // Ensure the NavigationState has the expected URL.
-  {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> nav_entry(
-        run_loop.QuitClosure());
-    nav_controller->GetVisibleEntry(
-        cr_fuchsia::CallbackToFitFunction(nav_entry.GetReceiveCallback()));
-    run_loop.Run();
-    ASSERT_TRUE(nav_entry->has_url());
-    EXPECT_EQ(nav_entry->url(),
-              test_server_.GetURL(kCastChannelAppPath).spec());
-  }
+  // Bind a TestNavigationListener to the Frame.
+  cr_fuchsia::TestNavigationListener navigation_listener;
+  fidl::Binding<fuchsia::web::NavigationEventListener>
+      navigation_listener_binding(&navigation_listener);
+  web_component->frame()->SetNavigationEventListener(
+      navigation_listener_binding.NewBinding());
+  navigation_listener.RunUntilUrlEquals(echo_app_url);
 
-  WaitUntilCastChannelOpened();
-
-  auto expected_list = {"this", "is", "a", "test"};
-  for (const std::string& expected : expected_list) {
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> message(
-        run_loop.QuitClosure());
-    component_state_->cast_channel()->port()->ReceiveMessage(
-        cr_fuchsia::CallbackToFitFunction(message.GetReceiveCallback()));
-    run_loop.Run();
-
-    std::string data;
-    ASSERT_TRUE(message->has_data());
-    ASSERT_TRUE(cr_fuchsia::StringFromMemBuffer(message->data(), &data));
-    EXPECT_EQ(data, expected);
-  }
-
-  // Shutdown the component and wait for the teardown of its state.
-  base::RunLoop run_loop;
-  component_state_->set_on_delete(run_loop.QuitClosure());
-  component_controller.Unbind();
-  run_loop.Run();
-}
-
-TEST_F(CastRunnerIntegrationTest, CastChannelComponentControllerDropped) {
-  const char kCastChannelAppId[] = "00000001";
-  const char kCastChannelAppPath[] = "/cast_channel.html";
-  app_config_manager_.AddAppMapping(kCastChannelAppId,
-                                    test_server_.GetURL(kCastChannelAppPath));
-
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller =
-      StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId));
-
-  // Spin the message loop to handle creation of the component state.
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(component_state_);
-
-  // Expect that disconnecting the ComponentController will destroy the Cast
-  // component.
-  base::RunLoop run_loop;
-  component_state_->set_on_delete(run_loop.QuitClosure());
-  component_controller.Unbind();
-  run_loop.Run();
+  // Check the header was properly set.
+  base::Optional<base::Value> result = cr_fuchsia::ExecuteJavaScript(
+      web_component->frame(), "document.body.innerText");
+  ASSERT_TRUE(result);
+  ASSERT_TRUE(result->is_string());
+  EXPECT_EQ(result->GetString(), "Value");
 }
 
 }  // namespace castrunner
diff --git a/fuchsia/runners/cast/not_implemented_api_bindings.js b/fuchsia/runners/cast/not_implemented_api_bindings.js
index 1c1f5efc..debe43a 100644
--- a/fuchsia/runners/cast/not_implemented_api_bindings.js
+++ b/fuchsia/runners/cast/not_implemented_api_bindings.js
@@ -154,30 +154,6 @@
   }
 
 
-  if (!cast.__platform__.metrics) {
-    cast.__platform__.metrics = {};
-
-    cast.__platform__.metrics.logBoolToUma =
-        cast.__platform__._notImplemented(
-            'metrics.logBoolToUma');
-
-    cast.__platform__.metrics.logIntToUma =
-        cast.__platform__._notImplemented(
-            'metrics.logIntToUma');
-
-    cast.__platform__.metrics.logEventToUma =
-        cast.__platform__._notImplemented(
-            'metrics.logEventToUma');
-
-    cast.__platform__.metrics.logHistogramValueToUma =
-        cast.__platform__._notImplemented(
-            'metrics.logHistogramValueToUma');
-
-    cast.__platform__.metrics.setMplVersion =
-        cast.__platform__._notImplemented('metrics.setMplVersion');
-  }
-
-
   if (!cast.__platform__.accessibility) {
     cast.__platform__.accessibility = {};
 
diff --git a/fuchsia/runners/cast/not_implemented_api_bindings_browsertest.cc b/fuchsia/runners/cast/not_implemented_api_bindings_browsertest.cc
index 9d7f2c85..6d019e0 100644
--- a/fuchsia/runners/cast/not_implemented_api_bindings_browsertest.cc
+++ b/fuchsia/runners/cast/not_implemented_api_bindings_browsertest.cc
@@ -153,12 +153,6 @@
       {"display.updateOutputMode", "promise {}"},
       {"display.getHdcpVersion", "promise \"0\""},
 
-      {"metrics.logBoolToUma"},
-      {"metrics.logIntToUma"},
-      {"metrics.logEventToUma"},
-      {"metrics.logHistogramValueToUma"},
-      {"metrics.setMplVersion"},
-
       {"accessibility.getAccessibilitySettings",
        "promise {\"isColorInversionEnabled\":false,"
        "\"isScreenReaderEnabled\":false}"},
diff --git a/fuchsia/runners/cast/queryable_data_bindings.cc b/fuchsia/runners/cast/queryable_data_bindings.cc
deleted file mode 100644
index d100f7df..0000000
--- a/fuchsia/runners/cast/queryable_data_bindings.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fuchsia/runners/cast/queryable_data_bindings.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/fuchsia/fuchsia_logging.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
-#include "base/logging.h"
-#include "base/path_service.h"
-#include "base/strings/stringprintf.h"
-#include "base/values.h"
-#include "fuchsia/base/mem_buffer_util.h"
-#include "fuchsia/runners/cast/cast_platform_bindings_ids.h"
-
-namespace {
-constexpr char kBindingsPath[] =
-    FILE_PATH_LITERAL("fuchsia/runners/cast/queryable_data_bindings.js");
-
-// Builds a script snippet which, when executed, will inserts entries into the
-// QueryableData JavaScript object. The scripts are inserted in the order
-// provided by the iterator.
-template <typename Iterator>
-base::Optional<fuchsia::mem::Buffer> BuildScriptFromEntries(Iterator begin,
-                                                            Iterator end) {
-  // Prepare values for serialization as a JavaScript object literal.
-  base::DictionaryValue output;
-  for (auto it = begin; it != end; ++it) {
-    const chromium::cast::QueryableDataEntry& current = *it;
-    base::Optional<base::Value> value_parsed =
-        base::JSONReader::Read(current.json_value);
-    if (!value_parsed) {
-      LOG(WARNING) << "Couldn't parse QueryableData item: " << current.key;
-      continue;
-    }
-
-    output.SetKey(current.key, std::move(*value_parsed));
-  }
-
-  std::string output_json;
-  if (!base::JSONWriter::Write(output, &output_json)) {
-    LOG(ERROR)
-        << "An unexpected error occurred while serializing QueryableData.";
-    return base::nullopt;
-  }
-
-  // Check for the |cast| namespace in case we are injecting into a fresh Frame
-  // which hasn't navigated to a page or received JS bindings.
-  std::string initialize_values_js = base::StringPrintf(
-      "if (window.cast)"
-      "  cast.__platform__.__queryPlatformValueStore__.mergeValues(%s);",
-      output_json.c_str());
-  output_json.clear();
-  output_json.shrink_to_fit();
-
-  fuchsia::mem::Buffer initialize_values_js_buffer =
-      cr_fuchsia::MemBufferFromString(initialize_values_js);
-  initialize_values_js.clear();
-  initialize_values_js.shrink_to_fit();
-
-  return initialize_values_js_buffer;
-}
-
-}  // namespace
-
-QueryableDataBindings::QueryableDataBindings(
-    fuchsia::web::Frame* frame,
-    fidl::InterfaceHandle<chromium::cast::QueryableData> service)
-    : frame_(frame), service_(service.Bind()) {
-  DCHECK(frame_);
-
-  base::FilePath assets_path;
-  CHECK(base::PathService::Get(base::DIR_ASSETS, &assets_path));
-  frame_->AddBeforeLoadJavaScript(
-      static_cast<uint64_t>(CastPlatformBindingsId::QUERYABLE_DATA), {"*"},
-      cr_fuchsia::MemBufferFromFile(
-          base::File(assets_path.AppendASCII(kBindingsPath),
-                     base::File::FLAG_OPEN | base::File::FLAG_READ)),
-      [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
-        CHECK(result.is_response()) << "JavaScript injection error.";
-      });
-
-  service_->GetChangedEntries(
-      fit::bind_member(this, &QueryableDataBindings::OnEntriesReceived));
-}
-
-QueryableDataBindings::~QueryableDataBindings() = default;
-
-bool QueryableDataBindings::QueryableDataEntryLess::operator()(
-    const chromium::cast::QueryableDataEntry& lhs,
-    const chromium::cast::QueryableDataEntry& rhs) const {
-  return lhs.key < rhs.key;
-}
-
-void QueryableDataBindings::OnEntriesReceived(
-    std::vector<chromium::cast::QueryableDataEntry> new_entries) {
-  // Push changes to the page immediately.
-  base::Optional<fuchsia::mem::Buffer> update_script =
-      BuildScriptFromEntries(new_entries.begin(), new_entries.end());
-
-  if (!update_script)
-    return;
-
-  frame_->ExecuteJavaScriptNoResult(
-      {"*"}, std::move(*update_script),
-      [](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) {
-        DCHECK(result.is_response()) << "JavaScript injection error.";
-      });
-
-  // Update the cached values by merging in the new entries.
-  cached_entries_.insert(new_entries.begin(), new_entries.end(),
-                         base::KEEP_LAST_OF_DUPES);
-
-  // Update the on-load script with the full list of values.
-  base::Optional<fuchsia::mem::Buffer> on_load_script =
-      BuildScriptFromEntries(cached_entries_.begin(), cached_entries_.end());
-  if (!on_load_script)
-    return;
-  frame_->AddBeforeLoadJavaScript(
-      static_cast<uint64_t>(CastPlatformBindingsId::QUERYABLE_DATA_VALUES),
-      {"*"}, std::move(*on_load_script),
-      [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
-        CHECK(result.is_response()) << "JavaScript injection error.";
-      });
-
-  // Request more changes from the FIDL service.
-  service_->GetChangedEntries(
-      fit::bind_member(this, &QueryableDataBindings::OnEntriesReceived));
-}
diff --git a/fuchsia/runners/cast/queryable_data_bindings.h b/fuchsia/runners/cast/queryable_data_bindings.h
deleted file mode 100644
index 33854a5e..0000000
--- a/fuchsia/runners/cast/queryable_data_bindings.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FUCHSIA_RUNNERS_CAST_QUERYABLE_DATA_BINDINGS_H_
-#define FUCHSIA_RUNNERS_CAST_QUERYABLE_DATA_BINDINGS_H_
-
-#include <fuchsia/web/cpp/fidl.h>
-#include <vector>
-
-#include "base/containers/flat_set.h"
-#include "base/macros.h"
-#include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
-
-// Adds JavaScript functions to a Frame for querying platform values from the
-// Agent.
-class QueryableDataBindings {
- public:
-  // |frame|: The Frame which will receive the bindings and data.
-  //          Must outlive |this|.
-  // |service|: The QueryableData service which will supply |this| with data.
-  //            Values from |service| will be read once and injected into
-  //            |frame|. Any changes to |service|'s values will not be
-  //            propagated to the Frame for the lifetime of |this|.
-  QueryableDataBindings(
-      fuchsia::web::Frame* frame,
-      fidl::InterfaceHandle<chromium::cast::QueryableData> service);
-  ~QueryableDataBindings();
-
- private:
-  // Allows QueryableDataEntry to be stored in a flat_set.
-  struct QueryableDataEntryLess {
-    bool operator()(const chromium::cast::QueryableDataEntry& lhs,
-                    const chromium::cast::QueryableDataEntry& rhs) const;
-  };
-
-  // Takes the initial list of QueryableData entries, or a list of updated
-  // entries, and propagates the information to |frame_|'s script context.
-  void OnEntriesReceived(
-      std::vector<chromium::cast::QueryableDataEntry> new_entries);
-
-  // The callbacks of any asynchronous calls made to |frame_| should ensure that
-  // |this| is valid before using it (e.g. via a WeakPtr).
-  fuchsia::web::Frame* const frame_;
-
-  chromium::cast::QueryableDataPtr service_;
-  base::flat_set<chromium::cast::QueryableDataEntry, QueryableDataEntryLess>
-      cached_entries_;
-
-  DISALLOW_COPY_AND_ASSIGN(QueryableDataBindings);
-};
-
-#endif  // FUCHSIA_RUNNERS_CAST_QUERYABLE_DATA_BINDINGS_H_
diff --git a/fuchsia/runners/cast/queryable_data_bindings.js b/fuchsia/runners/cast/queryable_data_bindings.js
deleted file mode 100644
index 6034264b..0000000
--- a/fuchsia/runners/cast/queryable_data_bindings.js
+++ /dev/null
@@ -1,33 +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.
-
-if (!cast)
-  var cast = new Object;
-
-if (!cast.__platform__)
-  cast.__platform__ = new Object;
-
-// Don't clobber the QueryableData API if it was previously injected.
-if (!cast.__platform__.queryPlatformValue) {
-  cast.__platform__.__queryPlatformValueStore__ = new class {
-    mergeValues(values) {
-      for (var key in values)
-        this.values_[key] = values[key];
-    }
-
-    getValue(key) {
-      if (!this.values_.hasOwnProperty(key)) {
-        console.error('Unknown platformValue: ' + key);
-        return null;
-      }
-      return this.values_[key];
-    }
-
-    values_ = {};
-  };
-
-  cast.__platform__.queryPlatformValue =
-      cast.__platform__.__queryPlatformValueStore__.getValue.bind(
-          cast.__platform__.__queryPlatformValueStore__);
-}
diff --git a/fuchsia/runners/cast/queryable_data_bindings_browsertest.cc b/fuchsia/runners/cast/queryable_data_bindings_browsertest.cc
deleted file mode 100644
index 3448628..0000000
--- a/fuchsia/runners/cast/queryable_data_bindings_browsertest.cc
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <fuchsia/web/cpp/fidl.h>
-
-#include "base/strings/stringprintf.h"
-#include "base/values.h"
-#include "fuchsia/base/fit_adapter.h"
-#include "fuchsia/base/frame_test_util.h"
-#include "fuchsia/base/mem_buffer_util.h"
-#include "fuchsia/base/result_receiver.h"
-#include "fuchsia/base/test_navigation_listener.h"
-#include "fuchsia/engine/test/web_engine_browser_test.h"
-#include "fuchsia/runners/cast/fake_queryable_data.h"
-#include "fuchsia/runners/cast/named_message_port_connector.h"
-#include "fuchsia/runners/cast/queryable_data_bindings.h"
-
-namespace {
-
-class QueryableDataBindingsTest : public cr_fuchsia::WebEngineBrowserTest {
- public:
-  QueryableDataBindingsTest()
-      : queryable_data_service_binding_(&queryable_data_service_) {
-    set_test_server_root(base::FilePath("fuchsia/runners/cast/testdata"));
-  }
-
-  ~QueryableDataBindingsTest() override { connector_->Unregister("testQuery"); }
-
-  void SetUpOnMainThread() override {
-    cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread();
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    frame_ = WebEngineBrowserTest::CreateFrame(&navigation_listener_);
-
-    ASSERT_TRUE(embedded_test_server()->Start());
-    test_url_ = embedded_test_server()->GetURL("/query_platform_value.html");
-
-    navigation_listener_.SetBeforeAckHook(base::BindRepeating(
-        &QueryableDataBindingsTest::OnBeforeAckHook, base::Unretained(this)));
-
-    connector_ = std::make_unique<NamedMessagePortConnector>(frame_.get());
-    connector_->Register(
-        "testQuery",
-        base::BindRepeating(&QueryableDataBindingsTest::ReceiveMessagePort,
-                            base::Unretained(this)));
-  }
-
-  // Blocks test execution until the page has indicated that it's processed the
-  // updates, which we achieve by setting the title to a new value and waiting
-  // for the resulting navigation event.
-  void SynchronizeWithPage() {
-    std::string unique_title =
-        base::StringPrintf("sync-%d", current_sync_id_++);
-    frame_->ExecuteJavaScriptNoResult(
-        {"*"},
-        cr_fuchsia::MemBufferFromString(
-            base::StringPrintf("document.title = '%s'", unique_title.c_str())),
-        [](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) {
-          ASSERT_TRUE(result.is_response());
-        });
-
-    navigation_listener_.RunUntilUrlAndTitleEquals(test_url_, unique_title);
-  }
-
-  // Communicates with the page to read an entry from its QueryableData store.
-  std::string CallQueryPlatformValue(base::StringPiece key) {
-    // Wait until the querying MessagePort is ready to use.
-    if (!query_port_) {
-      base::RunLoop run_loop;
-      on_query_port_received_cb_ = run_loop.QuitClosure();
-      run_loop.Run();
-      if (!query_port_)
-        return "";
-    }
-
-    // Send the request to the page.
-    fuchsia::web::WebMessage message;
-    message.set_data(cr_fuchsia::MemBufferFromString(key));
-    query_port_->PostMessage(
-        std::move(message),
-        [](fuchsia::web::MessagePort_PostMessage_Result result) {
-          ASSERT_TRUE(result.is_response());
-        });
-
-    // Return the response from the page.
-    base::RunLoop response_loop;
-    cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> response(
-        response_loop.QuitClosure());
-    query_port_->ReceiveMessage(
-        cr_fuchsia::CallbackToFitFunction(response.GetReceiveCallback()));
-    response_loop.Run();
-    if (!response->has_data())
-      return "";
-    std::string response_string;
-    if (!cr_fuchsia::StringFromMemBuffer(response->data(), &response_string))
-      return "";
-    return response_string;
-  }
-
-  void ReceiveMessagePort(
-      fidl::InterfaceHandle<fuchsia::web::MessagePort> port) {
-    query_port_ = port.Bind();
-    if (on_query_port_received_cb_)
-      std::move(on_query_port_received_cb_).Run();
-  }
-
- protected:
-  void OnBeforeAckHook(
-      const fuchsia::web::NavigationState& change,
-      fuchsia::web::NavigationEventListener::OnNavigationStateChangedCallback
-          callback) {
-    if (change.has_is_main_document_loaded() &&
-        change.is_main_document_loaded())
-      connector_->OnPageLoad();
-
-    callback();
-  }
-
-  fuchsia::web::FramePtr frame_;
-
-  GURL test_url_;
-  std::unique_ptr<NamedMessagePortConnector> connector_;
-  FakeQueryableData queryable_data_service_;
-  cr_fuchsia::TestNavigationListener navigation_listener_;
-  fidl::Binding<chromium::cast::QueryableData> queryable_data_service_binding_;
-  base::OnceClosure on_query_port_received_cb_;
-  base::OnceClosure on_navigate_cb_;
-  fuchsia::web::MessagePortPtr query_port_;
-  int current_sync_id_ = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(QueryableDataBindingsTest);
-};
-
-IN_PROC_BROWSER_TEST_F(QueryableDataBindingsTest, VariousTypes) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  base::DictionaryValue dict_value;
-  dict_value.SetString("key", "val");
-  queryable_data_service_.SendChanges({{"string", base::Value("foo")},
-                                       {"number", base::Value(123)},
-                                       {"null", base::Value()},
-                                       {"dict", std::move(dict_value)}});
-
-  QueryableDataBindings bindings(
-      frame_.get(), queryable_data_service_binding_.NewBinding().Bind());
-
-  fuchsia::web::NavigationControllerPtr controller;
-  frame_->GetNavigationController(controller.NewRequest());
-  frame_->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::INFO);
-  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
-      controller.get(), fuchsia::web::LoadUrlParams(), test_url_.spec()));
-  navigation_listener_.RunUntilUrlEquals(test_url_);
-
-  EXPECT_EQ(CallQueryPlatformValue("string"), "\"foo\"");
-  EXPECT_EQ(CallQueryPlatformValue("number"), "123");
-  EXPECT_EQ(CallQueryPlatformValue("null"), "null");
-  EXPECT_EQ(CallQueryPlatformValue("dict"), "{\"key\":\"val\"}");
-}
-
-IN_PROC_BROWSER_TEST_F(QueryableDataBindingsTest, NoValues) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  QueryableDataBindings bindings(
-      frame_.get(), queryable_data_service_binding_.NewBinding().Bind());
-
-  fuchsia::web::NavigationControllerPtr controller;
-  frame_->GetNavigationController(controller.NewRequest());
-  frame_->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::INFO);
-  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
-      controller.get(), fuchsia::web::LoadUrlParams(), test_url_.spec()));
-  navigation_listener_.RunUntilUrlEquals(test_url_);
-
-  EXPECT_EQ(CallQueryPlatformValue("string"), "null");
-}
-
-IN_PROC_BROWSER_TEST_F(QueryableDataBindingsTest, AtPageRuntime) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  queryable_data_service_.SendChanges({{"key1", base::Value(1)},
-                                       {"key2", base::Value(2)},
-                                       {"key3", base::Value(3)}});
-
-  QueryableDataBindings bindings(
-      frame_.get(), queryable_data_service_binding_.NewBinding().Bind());
-
-  fuchsia::web::NavigationControllerPtr controller;
-  frame_->GetNavigationController(controller.NewRequest());
-  frame_->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::INFO);
-  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
-      controller.get(), fuchsia::web::LoadUrlParams(), test_url_.spec()));
-  navigation_listener_.RunUntilUrlEquals(test_url_);
-
-  SynchronizeWithPage();
-
-  EXPECT_EQ(CallQueryPlatformValue("key1"), "1");
-  EXPECT_EQ(CallQueryPlatformValue("key2"), "2");
-  EXPECT_EQ(CallQueryPlatformValue("key3"), "3");
-
-  queryable_data_service_.SendChanges(
-      {{"key1", base::Value(10)}, {"key2", base::Value(20)}});
-
-  SynchronizeWithPage();
-
-  // Verify that the changes are immediately available.
-  EXPECT_EQ(CallQueryPlatformValue("key1"), "10");
-  EXPECT_EQ(CallQueryPlatformValue("key2"), "20");
-  EXPECT_EQ(CallQueryPlatformValue("key3"), "3");
-}
-
-// Sends updates to the Frame before the Frame has created a renderer.
-IN_PROC_BROWSER_TEST_F(QueryableDataBindingsTest, AtPageLoad) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  queryable_data_service_.SendChanges({{"key1", base::Value(1)},
-                                       {"key2", base::Value(2)},
-                                       {"key3", base::Value(3)}});
-
-  queryable_data_service_.SendChanges(
-      {{"key1", base::Value(10)}, {"key2", base::Value(20)}});
-
-  QueryableDataBindings bindings(
-      frame_.get(), queryable_data_service_binding_.NewBinding().Bind());
-
-  fuchsia::web::NavigationControllerPtr controller;
-  frame_->GetNavigationController(controller.NewRequest());
-  frame_->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::INFO);
-  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
-      controller.get(), fuchsia::web::LoadUrlParams(), test_url_.spec()));
-  navigation_listener_.RunUntilUrlEquals(test_url_);
-
-  SynchronizeWithPage();
-
-  EXPECT_EQ(CallQueryPlatformValue("key1"), "10");
-  EXPECT_EQ(CallQueryPlatformValue("key2"), "20");
-  EXPECT_EQ(CallQueryPlatformValue("key3"), "3");
-}
-
-}  // namespace
diff --git a/fuchsia/runners/cast/sandbox_policy b/fuchsia/runners/cast/sandbox_policy
index 80a1adbe..7cb7a6ec 100644
--- a/fuchsia/runners/cast/sandbox_policy
+++ b/fuchsia/runners/cast/sandbox_policy
@@ -10,6 +10,7 @@
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
+      "fuchsia.posix.socket.Provider",
       "fuchsia.process.Launcher",
       "fuchsia.sysmem.Allocator",
       "fuchsia.ui.input.ImeService",
diff --git a/fuchsia/runners/cast/testdata/cast_channel.html b/fuchsia/runners/cast/testdata/cast_channel.html
deleted file mode 100644
index ed9bd66..0000000
--- a/fuchsia/runners/cast/testdata/cast_channel.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<html>
-  <head><title>castChannel</title></head>
-  <body>
-    <script>
-      var reopened = false;
-
-      function openHandler() {
-        cast.__platform__.channel.send('this');
-        cast.__platform__.channel.send('is');
-        cast.__platform__.channel.send('a');
-        cast.__platform__.channel.send('test');
-      }
-
-      cast.__platform__.channel.open(openHandler, function(ignored) {});
-    </script>
-  </body>
-</html>
diff --git a/fuchsia/runners/cast/testdata/cast_channel_reconnect.html b/fuchsia/runners/cast/testdata/cast_channel_reconnect.html
deleted file mode 100644
index 936b2db..0000000
--- a/fuchsia/runners/cast/testdata/cast_channel_reconnect.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<html>
-  <head><title>castChannel</title></head>
-  <body>
-    <script>
-      var reopened = false;
-
-      // Executes state transitions: open, close, open, send, receive, send.
-      function openHandler() {
-        if (!reopened) {
-          reopened = true;
-          cast.__platform__.channel.close(function() {});
-          cast.__platform__.channel.open(openHandler, messageHandler);
-        } else {
-          cast.__platform__.channel.send('reconnected');
-        }
-      }
-
-      function messageHandler(data) {
-        cast.__platform__.channel.send('ack ' + data);
-      }
-
-      cast.__platform__.channel.open(openHandler, messageHandler);
-    </script>
-  </body>
-</html>
diff --git a/fuchsia/runners/cast/testdata/query_platform_value.html b/fuchsia/runners/cast/testdata/query_platform_value.html
deleted file mode 100644
index 5bbb5ac..0000000
--- a/fuchsia/runners/cast/testdata/query_platform_value.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<html>
-  <head><title>queryPlatformValue</title></head>
-  <body>
-    <script>
-      // Set up a message port so that native test code can query the live
-      // state of QueryableData.
-      var port = cast.__platform__.PortConnector.bind('testQuery');
-      port.onmessage = function(message) {
-          port.postMessage(
-              JSON.stringify(cast.__platform__.queryPlatformValue(message.data)));
-        }.bind(this);
-    </script>
-  </body>
-</html>
diff --git a/fuchsia/runners/common/web_component.cc b/fuchsia/runners/common/web_component.cc
index ceab7f38..b700f047 100644
--- a/fuchsia/runners/common/web_component.cc
+++ b/fuchsia/runners/common/web_component.cc
@@ -74,7 +74,9 @@
                                             termination_reason_);
 }
 
-void WebComponent::LoadUrl(const GURL& url) {
+void WebComponent::LoadUrl(
+    const GURL& url,
+    std::vector<fuchsia::net::http::Header> extra_headers) {
   DCHECK(url.is_valid());
   fuchsia::web::NavigationControllerPtr navigation_controller;
   frame()->GetNavigationController(navigation_controller.NewRequest());
@@ -84,6 +86,8 @@
   // content.
   fuchsia::web::LoadUrlParams params;
   params.set_was_user_activated(true);
+  if (!extra_headers.empty())
+    params.set_headers(std::move(extra_headers));
 
   navigation_controller->LoadUrl(
       url.spec(), std::move(params),
diff --git a/fuchsia/runners/common/web_component.h b/fuchsia/runners/common/web_component.h
index 7c9eb234..4a9aef8 100644
--- a/fuchsia/runners/common/web_component.h
+++ b/fuchsia/runners/common/web_component.h
@@ -45,8 +45,9 @@
 
   ~WebComponent() override;
 
-  // Navigates this component's Frame to |url|.
-  void LoadUrl(const GURL& url);
+  // Navigates this component's Frame to |url| and passes |extra_headers|.
+  void LoadUrl(const GURL& url,
+               std::vector<fuchsia::net::http::Header> extra_headers);
 
   fuchsia::web::Frame* frame() const { return frame_.get(); }
 
diff --git a/fuchsia/runners/common/web_content_runner.cc b/fuchsia/runners/common/web_content_runner.cc
index 0249eb5f..2ffe589 100644
--- a/fuchsia/runners/common/web_content_runner.cc
+++ b/fuchsia/runners/common/web_content_runner.cc
@@ -18,6 +18,7 @@
 #include "base/fuchsia/service_directory_client.h"
 #include "base/fuchsia/startup_context.h"
 #include "base/logging.h"
+#include "fuchsia/runners/buildflags.h"
 #include "fuchsia/runners/common/web_component.h"
 #include "url/gurl.h"
 
@@ -44,6 +45,12 @@
   if (data_directory)
     create_params.set_data_directory(std::move(data_directory));
 
+  // Set |remote_debugging_port| on the context, if set.
+  if (BUILDFLAG(ENABLE_REMOTE_DEBUGGING_ON_PORT) != 0) {
+    create_params.set_remote_debugging_port(
+        BUILDFLAG(ENABLE_REMOTE_DEBUGGING_ON_PORT));
+  }
+
   fuchsia::web::ContextPtr web_context;
   web_context_provider->Create(std::move(create_params),
                                web_context.NewRequest());
@@ -94,7 +101,7 @@
       this,
       std::make_unique<base::fuchsia::StartupContext>(std::move(startup_info)),
       std::move(controller_request));
-  component->LoadUrl(url);
+  component->LoadUrl(url, std::vector<fuchsia::net::http::Header>());
   RegisterComponent(std::move(component));
 }
 
diff --git a/fuchsia/runners/web/sandbox_policy b/fuchsia/runners/web/sandbox_policy
index 3179a5a..6e03334 100644
--- a/fuchsia/runners/web/sandbox_policy
+++ b/fuchsia/runners/web/sandbox_policy
@@ -11,6 +11,7 @@
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
+      "fuchsia.posix.socket.Provider",
       "fuchsia.process.Launcher",
       "fuchsia.sysmem.Allocator",
       "fuchsia.ui.input.ImeService",
diff --git a/google_apis/gaia/oauth2_access_token_manager.cc b/google_apis/gaia/oauth2_access_token_manager.cc
index 9c4ce3d..b3dcdad 100644
--- a/google_apis/gaia/oauth2_access_token_manager.cc
+++ b/google_apis/gaia/oauth2_access_token_manager.cc
@@ -17,6 +17,19 @@
 
 int OAuth2AccessTokenManager::max_fetch_retry_num_ = 5;
 
+OAuth2AccessTokenManager::Delegate::Delegate() = default;
+
+OAuth2AccessTokenManager::Delegate::~Delegate() = default;
+
+bool OAuth2AccessTokenManager::Delegate::FixRequestErrorIfPossible() {
+  return false;
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+OAuth2AccessTokenManager::Delegate::GetURLLoaderFactory() const {
+  return nullptr;
+}
+
 OAuth2AccessTokenManager::Request::Request() {}
 
 OAuth2AccessTokenManager::Request::~Request() {}
@@ -370,9 +383,13 @@
 
 OAuth2AccessTokenManager::OAuth2AccessTokenManager(
     OAuth2TokenService* token_service,
-    OAuth2TokenServiceDelegate* delegate)
-    : token_service_(token_service), delegate_(delegate) {
+    OAuth2TokenServiceDelegate* token_service_delegate,
+    OAuth2AccessTokenManager::Delegate* delegate)
+    : token_service_(token_service),
+      token_service_delegate_(token_service_delegate),
+      delegate_(delegate) {
   DCHECK(token_service_);
+  DCHECK(token_service_delegate_);
   DCHECK(delegate_);
 }
 
@@ -381,12 +398,12 @@
   pending_fetchers_.clear();
 }
 
-OAuth2TokenServiceDelegate* OAuth2AccessTokenManager::GetDelegate() {
+OAuth2AccessTokenManager::Delegate* OAuth2AccessTokenManager::GetDelegate() {
   return delegate_;
 }
 
-const OAuth2TokenServiceDelegate* OAuth2AccessTokenManager::GetDelegate()
-    const {
+const OAuth2AccessTokenManager::Delegate*
+OAuth2AccessTokenManager::GetDelegate() const {
   return delegate_;
 }
 
@@ -546,7 +563,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RemoveCachedTokenResponse(RequestParameters(client_id, account_id, scopes),
                             access_token);
-  delegate_->InvalidateAccessToken(account_id, client_id, scopes, access_token);
+  delegate_->OnAccessTokenInvalidated(account_id, client_id, scopes,
+                                      access_token);
 }
 
 void OAuth2AccessTokenManager::
@@ -589,7 +607,7 @@
   for (auto& observer : diagnostics_observer_list_)
     observer.OnAccessTokenRequested(account_id, consumer->id(), scopes);
 
-  if (!delegate_->RefreshTokenIsAvailable(account_id)) {
+  if (!token_service_delegate_->RefreshTokenIsAvailable(account_id)) {
     GoogleServiceAuthError error(GoogleServiceAuthError::USER_NOT_SIGNED_UP);
 
     for (auto& observer : diagnostics_observer_list_) {
diff --git a/google_apis/gaia/oauth2_access_token_manager.h b/google_apis/gaia/oauth2_access_token_manager.h
index afceb75..c5a0b18 100644
--- a/google_apis/gaia/oauth2_access_token_manager.h
+++ b/google_apis/gaia/oauth2_access_token_manager.h
@@ -26,6 +26,29 @@
   // A set of scopes in OAuth2 authentication.
   typedef std::set<std::string> ScopeSet;
 
+  class Delegate {
+   public:
+    Delegate();
+    virtual ~Delegate();
+
+    virtual std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher(
+        const CoreAccountId& account_id,
+        scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+        OAuth2AccessTokenConsumer* consumer) WARN_UNUSED_RESULT = 0;
+
+    // Attempts to fix the error if possible.  Returns true if the error was
+    // fixed and false otherwise.
+    virtual bool FixRequestErrorIfPossible();
+
+    virtual scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
+        const;
+
+    virtual void OnAccessTokenInvalidated(const CoreAccountId& account_id,
+                                          const std::string& client_id,
+                                          const std::set<std::string>& scopes,
+                                          const std::string& access_token) {}
+  };
+
   // Class representing a request that fetches an OAuth2 access token.
   class Request {
    public:
@@ -128,16 +151,17 @@
   typedef std::map<RequestParameters, OAuth2AccessTokenConsumer::TokenResponse>
       TokenCache;
 
-  // TODO(https://crbug.com/967598): Remove |token_service| parameter once
-  // OAuth2AccessTokenManager fully manages access tokens independently of
-  // OAuth2TokenService and replace |delegate| with
-  // OAuth2AccessTokenManagerDelegate.
-  explicit OAuth2AccessTokenManager(OAuth2TokenService* token_service,
-                                    OAuth2TokenServiceDelegate* delegate);
+  // TODO(https://crbug.com/967598): Remove |token_service| and
+  // |service_delegate| once OAuth2AccessTokenManager fully manages access
+  // tokens independently of OAuth2TokenService.
+  explicit OAuth2AccessTokenManager(
+      OAuth2TokenService* token_service,
+      OAuth2TokenServiceDelegate* service_delegate,
+      OAuth2AccessTokenManager::Delegate* delegate);
   virtual ~OAuth2AccessTokenManager();
 
-  OAuth2TokenServiceDelegate* GetDelegate();
-  const OAuth2TokenServiceDelegate* GetDelegate() const;
+  OAuth2AccessTokenManager::Delegate* GetDelegate();
+  const OAuth2AccessTokenManager::Delegate* GetDelegate() const;
 
   // Add or remove observers of this token manager.
   void AddDiagnosticsObserver(DiagnosticsObserver* observer);
@@ -285,9 +309,10 @@
   // TODO(https://crbug.com/967598): Remove this once OAuth2AccessTokenManager
   // fully manages access tokens independently of OAuth2TokenService.
   OAuth2TokenService* token_service_;
-  // TODO(https://crbug.com/967598): Replace it with
-  // OAuth2AccessTokenManagerDelegate.
-  OAuth2TokenServiceDelegate* delegate_;
+  // TODO(https://crbug.com/967598): Remove this once OAuth2AccessTokenManager
+  // is created without OAuth2TokenService.
+  OAuth2TokenServiceDelegate* token_service_delegate_;
+  Delegate* delegate_;
   // A map from fetch parameters to a fetcher that is fetching an OAuth2 access
   // token using these parameters.
   std::map<RequestParameters, std::unique_ptr<Fetcher>> pending_fetchers_;
diff --git a/google_apis/gaia/oauth2_token_service.cc b/google_apis/gaia/oauth2_token_service.cc
index 33b14fd..8805f9b 100644
--- a/google_apis/gaia/oauth2_token_service.cc
+++ b/google_apis/gaia/oauth2_token_service.cc
@@ -19,6 +19,7 @@
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_access_token_fetcher.h"
 #include "google_apis/gaia/oauth2_access_token_manager.h"
 #include "google_apis/gaia/oauth2_token_service_delegate.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -28,8 +29,9 @@
     : delegate_(std::move(delegate)), all_credentials_loaded_(false) {
   DCHECK(delegate_);
   AddObserver(this);
-  token_manager_ =
-      std::make_unique<OAuth2AccessTokenManager>(this, delegate_.get());
+  token_manager_ = std::make_unique<OAuth2AccessTokenManager>(
+      this /* OAuth2TokenService* */, delegate_.get(),
+      this /* OAuth2AccessTokenManager::Delegate* */);
 }
 
 OAuth2TokenService::~OAuth2TokenService() {
@@ -45,6 +47,10 @@
   return delegate_.get();
 }
 
+OAuth2AccessTokenManager* OAuth2TokenService::GetAccessTokenManager() {
+  return token_manager_.get();
+}
+
 const base::ObserverList<OAuth2AccessTokenManager::DiagnosticsObserver,
                          true>::Unchecked&
 OAuth2TokenService::GetAccessTokenDiagnosticsObservers() {
@@ -55,6 +61,33 @@
   return token_manager_->token_cache().size();
 }
 
+std::unique_ptr<OAuth2AccessTokenFetcher>
+OAuth2TokenService::CreateAccessTokenFetcher(
+    const CoreAccountId& account_id,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    OAuth2AccessTokenConsumer* consumer) {
+  return delegate_->CreateAccessTokenFetcher(account_id, url_loader_factory,
+                                             consumer);
+}
+
+bool OAuth2TokenService::FixRequestErrorIfPossible() {
+  return delegate_->FixRequestErrorIfPossible();
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+OAuth2TokenService::GetURLLoaderFactory() const {
+  return delegate_->GetURLLoaderFactory();
+}
+
+void OAuth2TokenService::OnAccessTokenInvalidated(
+    const CoreAccountId& account_id,
+    const std::string& client_id,
+    const std::set<std::string>& scopes,
+    const std::string& access_token) {
+  delegate_->OnAccessTokenInvalidated(account_id, client_id, scopes,
+                                      access_token);
+}
+
 void OAuth2TokenService::AddObserver(OAuth2TokenServiceObserver* observer) {
   delegate_->AddObserver(observer);
 }
diff --git a/google_apis/gaia/oauth2_token_service.h b/google_apis/gaia/oauth2_token_service.h
index e8bae544..5a2c231 100644
--- a/google_apis/gaia/oauth2_token_service.h
+++ b/google_apis/gaia/oauth2_token_service.h
@@ -56,13 +56,26 @@
 //
 // The caller of StartRequest() owns the returned request and is responsible to
 // delete the request even once the callback has been invoked.
-class OAuth2TokenService : public OAuth2TokenServiceObserver {
+class OAuth2TokenService : public OAuth2TokenServiceObserver,
+                           public OAuth2AccessTokenManager::Delegate {
  public:
-
   explicit OAuth2TokenService(
       std::unique_ptr<OAuth2TokenServiceDelegate> delegate);
   ~OAuth2TokenService() override;
 
+  // Overridden from OAuth2AccessTokenManager::Delegate.
+  std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher(
+      const CoreAccountId& account_id,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      OAuth2AccessTokenConsumer* consumer) override;
+  bool FixRequestErrorIfPossible() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
+      const override;
+  void OnAccessTokenInvalidated(const CoreAccountId& account_id,
+                                const std::string& client_id,
+                                const std::set<std::string>& scopes,
+                                const std::string& access_token) override;
+
   // Add or remove observers of this token service.
   void AddObserver(OAuth2TokenServiceObserver* observer);
   void RemoveObserver(OAuth2TokenServiceObserver* observer);
@@ -173,6 +186,8 @@
   OAuth2TokenServiceDelegate* GetDelegate();
   const OAuth2TokenServiceDelegate* GetDelegate() const;
 
+  OAuth2AccessTokenManager* GetAccessTokenManager();
+
   // TODO(https://crbug.com/967598): Remove this. It's opened only for
   // OAuth2TokenServiceTest.
   int GetTokenCacheCount();
diff --git a/google_apis/gaia/oauth2_token_service_delegate.h b/google_apis/gaia/oauth2_token_service_delegate.h
index 1b56e5dc..76fffa2 100644
--- a/google_apis/gaia/oauth2_token_service_delegate.h
+++ b/google_apis/gaia/oauth2_token_service_delegate.h
@@ -71,10 +71,10 @@
   virtual std::vector<CoreAccountId> GetAccounts() const;
   virtual void RevokeAllCredentials() {}
 
-  virtual void InvalidateAccessToken(const CoreAccountId& account_id,
-                                     const std::string& client_id,
-                                     const std::set<std::string>& scopes,
-                                     const std::string& access_token) {}
+  virtual void OnAccessTokenInvalidated(const CoreAccountId& account_id,
+                                        const std::string& client_id,
+                                        const std::set<std::string>& scopes,
+                                        const std::string& access_token) {}
 
   // If refresh token is accessible (on Desktop) sets error for it to
   // INVALID_GAIA_CREDENTIALS and notifies the observers. Otherwise
diff --git a/gpu/command_buffer/client/shared_image_interface.h b/gpu/command_buffer/client/shared_image_interface.h
index 24b3623..962506a 100644
--- a/gpu/command_buffer/client/shared_image_interface.h
+++ b/gpu/command_buffer/client/shared_image_interface.h
@@ -134,6 +134,9 @@
   // Generates a verified SyncToken that is released after all previous
   // commands on this interface have executed on the service side.
   virtual SyncToken GenVerifiedSyncToken() = 0;
+
+  // Flush the SharedImageInterface, issuing any deferred IPCs.
+  virtual void Flush() = 0;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/abstract_texture_impl_shared_context_state.cc b/gpu/command_buffer/service/abstract_texture_impl_shared_context_state.cc
index 0e4c62c..9b6f131 100644
--- a/gpu/command_buffer/service/abstract_texture_impl_shared_context_state.cc
+++ b/gpu/command_buffer/service/abstract_texture_impl_shared_context_state.cc
@@ -87,7 +87,11 @@
 void AbstractTextureImplOnSharedContext::BindStreamTextureImage(
     GLStreamTextureImage* image,
     GLuint service_id) {
-  NOTIMPLEMENTED();
+  const GLint level = 0;
+  const GLuint target = texture_->target();
+  texture_->SetLevelStreamTextureImage(
+      target, level, image, Texture::ImageState::UNBOUND, service_id);
+  texture_->SetLevelCleared(target, level, true);
 }
 
 void AbstractTextureImplOnSharedContext::BindImage(gl::GLImage* image,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 878d6d4..955e9e4 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -3530,6 +3530,9 @@
   if (workarounds().rely_on_implicit_sync_for_swap_buffers)
     surface_->SetRelyOnImplicitSync();
 
+  if (workarounds().force_gl_flush_on_swap_buffers)
+    surface_->SetForceGlFlushOnSwapBuffers();
+
   // Create GPU Tracer for timing values.
   gpu_tracer_.reset(new GPUTracer(this));
 
@@ -9783,9 +9786,12 @@
   if (!surface_->SetDrawRectangle(rect)) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSetDrawRectangleCHROMIUM",
                        "failed on surface");
+    // If SetDrawRectangle failed, we may not have a current context any
+    // more, make sure to report lost context.
     LOG(ERROR) << "Context lost because SetDrawRectangleCHROMIUM failed.";
     MarkContextLost(error::kUnknown);
     group_->LoseContexts(error::kUnknown);
+    return;
   }
   OnFboChanged();
 }
@@ -9805,6 +9811,11 @@
   if (!surface_->SetEnableDCLayers(!!enable)) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSetEnableDCLayersCHROMIUM",
                        "failed on surface");
+    // If SetEnableDCLayers failed, we may not have a current context any
+    // more, make sure to report lost context.
+    LOG(ERROR) << "Context lost because SetEnableDCLayers failed.";
+    MarkContextLost(error::kUnknown);
+    group_->LoseContexts(error::kUnknown);
   }
 }
 
@@ -10748,8 +10759,8 @@
             LOCAL_RENDER_WARNING(
                 std::string("texture bound to texture unit ") +
                 base::NumberToString(texture_unit_index) +
-                " is not renderable. It maybe non-power-of-2 and have"
-                " incompatible texture filtering.");
+                " is not renderable. It might be non-power-of-2 or have"
+                " incompatible texture filtering (maybe)?");
           }
           continue;
         } else if (!texture_ref->texture()->CompatibleWithSamplerUniformType(
@@ -16677,8 +16688,10 @@
 
 void GLES2DecoderImpl::FinishSwapBuffers(gfx::SwapResult result) {
   if (result == gfx::SwapResult::SWAP_FAILED) {
+    // If SwapBuffers/SwapBuffersWithBounds/PostSubBuffer failed, we may not
+    // have a current context any more.
     LOG(ERROR) << "Context lost because SwapBuffers failed.";
-    if (!CheckResetStatus()) {
+    if (!context_->IsCurrent(surface_.get()) || !CheckResetStatus()) {
       MarkContextLost(error::kUnknown);
       group_->LoseContexts(error::kUnknown);
     }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index d8d4b73f..e276178 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -2585,8 +2585,10 @@
     gfx::SwapResult result,
     const char* function_name) {
   if (result == gfx::SwapResult::SWAP_FAILED) {
+    // If SwapBuffers/SwapBuffersWithBounds/PostSubBuffer failed, we may not
+    // have a current context any more.
     LOG(ERROR) << "Context lost because " << function_name << " failed.";
-    if (!CheckResetStatus()) {
+    if (!context_->IsCurrent(surface_.get()) || !CheckResetStatus()) {
       MarkContextLost(error::kUnknown);
       group_->LoseContexts(error::kUnknown);
       return error::kLostContext;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 05c9bba..08edaeed 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -5015,7 +5015,11 @@
   gfx::Rect rect(x, y, width, height);
   if (!surface_->SetDrawRectangle(rect)) {
     InsertError(GL_INVALID_OPERATION, "SetDrawRectangle failed on surface");
-    return error::kNoError;
+    // If SetDrawRectangle failed, we may not have a current context any
+    // more, make sure to report lost context.
+    MarkContextLost(error::kUnknown);
+    group_->LoseContexts(error::kUnknown);
+    return error::kLostContext;
   }
 
   ApplySurfaceDrawOffset();
@@ -5040,7 +5044,11 @@
 
   if (!surface_->SetEnableDCLayers(!!enable)) {
     InsertError(GL_INVALID_OPERATION, "SetEnableDCLayers failed on surface.");
-    return error::kNoError;
+    // If SetEnableDCLayers failed, we may not have a current context any
+    // more, make sure to report lost context.
+    MarkContextLost(error::kUnknown);
+    group_->LoseContexts(error::kUnknown);
+    return error::kLostContext;
   }
 
   return error::kNoError;
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index 7aabcde..9d2e210 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -148,8 +148,7 @@
     options.fGlyphCacheTextureMaximumBytes = glyph_cache_max_texture_bytes_;
     options.fPersistentCache = cache;
     options.fAvoidStencilBuffers = workarounds.avoid_stencil_buffers;
-    // TODO(brianosman): turn this back on.  http://crbug.com/977938
-    options.fDisallowGLSLBinaryCaching = true;
+    options.fDisallowGLSLBinaryCaching = workarounds.disable_program_disk_cache;
     // TODO(csmartdalton): enable internal multisampling after the related Skia
     // rolls are in.
     options.fInternalMultisampleCount = 0;
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 619b9bbb..7c4753a8 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -1874,6 +1874,10 @@
   Texture::LevelInfo& info = face_infos_[face_index].level_infos[level];
   DCHECK_EQ(info.target, target);
   DCHECK_EQ(info.level, level);
+  // Workaround for StreamTexture which must be re-copied on each access.
+  // TODO(ericrk): Remove this once SharedImage transition is complete.
+  if (info.image && !info.image->HasMutableState())
+    return;
   info.image_state = state;
 }
 
diff --git a/gpu/config/BUILD.gn b/gpu/config/BUILD.gn
index 505ca73..a66d46c 100644
--- a/gpu/config/BUILD.gn
+++ b/gpu/config/BUILD.gn
@@ -110,6 +110,8 @@
     "gpu_driver_bug_workarounds.cc",
     "gpu_driver_bug_workarounds.h",
     "gpu_dx_diagnostics_win.cc",
+    "gpu_extra_info.cc",
+    "gpu_extra_info.h",
     "gpu_feature_info.cc",
     "gpu_feature_info.h",
     "gpu_feature_type.h",
diff --git a/gpu/config/gpu_extra_info.cc b/gpu/config/gpu_extra_info.cc
new file mode 100644
index 0000000..103f5de
--- /dev/null
+++ b/gpu/config/gpu_extra_info.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/config/gpu_extra_info.h"
+
+namespace gpu {
+
+ANGLEFeature::ANGLEFeature() = default;
+ANGLEFeature::ANGLEFeature(const ANGLEFeature& other) = default;
+ANGLEFeature::ANGLEFeature(ANGLEFeature&& other) = default;
+ANGLEFeature::~ANGLEFeature() = default;
+ANGLEFeature& ANGLEFeature::operator=(const ANGLEFeature& other) = default;
+ANGLEFeature& ANGLEFeature::operator=(ANGLEFeature&& other) = default;
+
+GpuExtraInfo::GpuExtraInfo() = default;
+GpuExtraInfo::GpuExtraInfo(const GpuExtraInfo&) = default;
+GpuExtraInfo::GpuExtraInfo(GpuExtraInfo&&) = default;
+GpuExtraInfo::~GpuExtraInfo() = default;
+GpuExtraInfo& GpuExtraInfo::operator=(const GpuExtraInfo&) = default;
+GpuExtraInfo& GpuExtraInfo::operator=(GpuExtraInfo&&) = default;
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_extra_info.h b/gpu/config/gpu_extra_info.h
new file mode 100644
index 0000000..7c0f143
--- /dev/null
+++ b/gpu/config/gpu_extra_info.h
@@ -0,0 +1,56 @@
+// 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 GPU_CONFIG_GPU_EXTRA_INFO_H_
+#define GPU_CONFIG_GPU_EXTRA_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+// Specification of a feature that can be enabled/disable in ANGLE
+struct GPU_EXPORT ANGLEFeature {
+  ANGLEFeature();
+  ANGLEFeature(const ANGLEFeature& other);
+  ANGLEFeature(ANGLEFeature&& other);
+  ~ANGLEFeature();
+  ANGLEFeature& operator=(const ANGLEFeature& other);
+  ANGLEFeature& operator=(ANGLEFeature&& other);
+
+  // Name of the feature in camel_case.
+  std::string name;
+
+  // Name of the category that the feature belongs to.
+  std::string category;
+
+  // One sentence description of the feature, why it's available.
+  std::string description;
+
+  // Full link to cr/angle bug if applicable.
+  std::string bug;
+
+  // Status, can be "enabled" or "disabled".
+  std::string status;
+};
+using ANGLEFeatures = std::vector<ANGLEFeature>;
+
+struct GPU_EXPORT GpuExtraInfo {
+  GpuExtraInfo();
+  GpuExtraInfo(const GpuExtraInfo&);
+  GpuExtraInfo(GpuExtraInfo&&);
+  ~GpuExtraInfo();
+  GpuExtraInfo& operator=(const GpuExtraInfo&);
+  GpuExtraInfo& operator=(GpuExtraInfo&&);
+
+  // List of the currently available ANGLE features. May be empty if not
+  // applicable.
+  ANGLEFeatures angle_features;
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_EXTRA_INFO_H_
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc
index 4fd34df..963884c 100644
--- a/gpu/config/gpu_info.cc
+++ b/gpu/config/gpu_info.cc
@@ -106,17 +106,6 @@
 }
 #endif
 
-void EnumerateANGLEFeature(const gpu::ANGLEFeature& feature,
-                           gpu::GPUInfo::Enumerator* enumerator) {
-  enumerator->BeginANGLEFeature();
-  enumerator->AddString("name", feature.name);
-  enumerator->AddString("category", feature.category);
-  enumerator->AddString("description", feature.description);
-  enumerator->AddString("bug", feature.bug);
-  enumerator->AddString("status", feature.status);
-  enumerator->EndANGLEFeature();
-}
-
 }  // namespace
 
 namespace gpu {
@@ -161,13 +150,6 @@
 ImageDecodeAcceleratorSupportedProfile& ImageDecodeAcceleratorSupportedProfile::
 operator=(ImageDecodeAcceleratorSupportedProfile&& other) = default;
 
-ANGLEFeature::ANGLEFeature() = default;
-ANGLEFeature::ANGLEFeature(const ANGLEFeature& other) = default;
-ANGLEFeature::ANGLEFeature(ANGLEFeature&& other) = default;
-ANGLEFeature::~ANGLEFeature() = default;
-ANGLEFeature& ANGLEFeature::operator=(const ANGLEFeature& other) = default;
-ANGLEFeature& ANGLEFeature::operator=(ANGLEFeature&& other) = default;
-
 GPUInfo::GPUDevice::GPUDevice()
     : vendor_id(0),
       device_id(0),
@@ -275,8 +257,6 @@
 #endif
 
     bool oop_rasterization_supported;
-
-    ANGLEFeatures angle_features;
   };
 
   // If this assert fails then most likely something below needs to be updated.
@@ -346,9 +326,6 @@
   enumerator->AddInt64("rgbaVisual", rgba_visual);
 #endif
   enumerator->AddBool("oopRasterizationSupported", oop_rasterization_supported);
-  for (const auto& angle_feature : angle_features)
-    EnumerateANGLEFeature(angle_feature, enumerator);
-  enumerator->EndAuxAttributes();
 }
 
 }  // namespace gpu
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h
index 46f3975..0e35bc60 100644
--- a/gpu/config/gpu_info.h
+++ b/gpu/config/gpu_info.h
@@ -177,32 +177,6 @@
 };
 #endif
 
-// Specification of a feature that can be enabled/disable in ANGLE
-struct GPU_EXPORT ANGLEFeature {
-  ANGLEFeature();
-  ANGLEFeature(const ANGLEFeature& other);
-  ANGLEFeature(ANGLEFeature&& other);
-  ~ANGLEFeature();
-  ANGLEFeature& operator=(const ANGLEFeature& other);
-  ANGLEFeature& operator=(ANGLEFeature&& other);
-
-  // Name of the feature in camel_case.
-  std::string name;
-
-  // Name of the category that the feature belongs to.
-  std::string category;
-
-  // One sentence description of the feature, why it's available.
-  std::string description;
-
-  // Full link to cr/angle bug if applicable.
-  std::string bug;
-
-  // Status, can be "enabled" or "disabled".
-  std::string status;
-};
-using ANGLEFeatures = std::vector<ANGLEFeature>;
-
 struct GPU_EXPORT GPUInfo {
   struct GPU_EXPORT GPUDevice {
     GPUDevice();
@@ -365,10 +339,6 @@
 
   bool oop_rasterization_supported;
 
-  // List of the currently available ANGLE features. May be empty if not
-  // applicable.
-  ANGLEFeatures angle_features;
-
   // Note: when adding new members, please remember to update EnumerateFields
   // in gpu_info.cc.
 
@@ -416,9 +386,6 @@
     virtual void BeginDx12VulkanVersionInfo() = 0;
     virtual void EndDx12VulkanVersionInfo() = 0;
 
-    virtual void BeginANGLEFeature() = 0;
-    virtual void EndANGLEFeature() = 0;
-
    protected:
     virtual ~Enumerator() = default;
   };
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc
index a4287b0..7164175 100644
--- a/gpu/config/gpu_info_collector.cc
+++ b/gpu/config/gpu_info_collector.cc
@@ -283,28 +283,6 @@
   gpu_info->pixel_shader_version = glsl_version;
   gpu_info->vertex_shader_version = glsl_version;
 
-  // Populate the list of ANGLE features by querying the functions exposed by
-  // EGL_ANGLE_feature_control if it's available.
-  if (gl::GLSurfaceEGL::IsANGLEFeatureControlSupported()) {
-    EGLDisplay display = gl::GLSurfaceEGL::GetHardwareDisplay();
-    EGLAttrib feature_count = 0;
-    eglQueryDisplayAttribANGLE(display, EGL_FEATURE_COUNT_ANGLE,
-                               &feature_count);
-    gpu_info->angle_features.resize(static_cast<size_t>(feature_count));
-    for (size_t i = 0; i < gpu_info->angle_features.size(); i++) {
-      gpu_info->angle_features[i].name =
-          QueryEGLStringi(display, EGL_FEATURE_NAME_ANGLE, i);
-      gpu_info->angle_features[i].category =
-          QueryEGLStringi(display, EGL_FEATURE_CATEGORY_ANGLE, i);
-      gpu_info->angle_features[i].description =
-          QueryEGLStringi(display, EGL_FEATURE_DESCRIPTION_ANGLE, i);
-      gpu_info->angle_features[i].bug =
-          QueryEGLStringi(display, EGL_FEATURE_BUG_ANGLE, i);
-      gpu_info->angle_features[i].status =
-          QueryEGLStringi(display, EGL_FEATURE_STATUS_ANGLE, i);
-    }
-  }
-
   IdentifyActiveGPU(gpu_info);
   return true;
 }
@@ -426,4 +404,30 @@
 #endif  // OS_ANDROID
 }
 
+bool CollectGpuExtraInfo(GpuExtraInfo* gpu_extra_info) {
+  // Populate the list of ANGLE features by querying the functions exposed by
+  // EGL_ANGLE_feature_control if it's available.
+  if (gl::GLSurfaceEGL::IsANGLEFeatureControlSupported()) {
+    EGLDisplay display = gl::GLSurfaceEGL::GetHardwareDisplay();
+    EGLAttrib feature_count = 0;
+    eglQueryDisplayAttribANGLE(display, EGL_FEATURE_COUNT_ANGLE,
+                               &feature_count);
+    gpu_extra_info->angle_features.resize(static_cast<size_t>(feature_count));
+    for (size_t i = 0; i < gpu_extra_info->angle_features.size(); i++) {
+      gpu_extra_info->angle_features[i].name =
+          QueryEGLStringi(display, EGL_FEATURE_NAME_ANGLE, i);
+      gpu_extra_info->angle_features[i].category =
+          QueryEGLStringi(display, EGL_FEATURE_CATEGORY_ANGLE, i);
+      gpu_extra_info->angle_features[i].description =
+          QueryEGLStringi(display, EGL_FEATURE_DESCRIPTION_ANGLE, i);
+      gpu_extra_info->angle_features[i].bug =
+          QueryEGLStringi(display, EGL_FEATURE_BUG_ANGLE, i);
+      gpu_extra_info->angle_features[i].status =
+          QueryEGLStringi(display, EGL_FEATURE_STATUS_ANGLE, i);
+    }
+  }
+
+  return true;
+}
+
 }  // namespace gpu
diff --git a/gpu/config/gpu_info_collector.h b/gpu/config/gpu_info_collector.h
index 10d3fef7..8edb8801 100644
--- a/gpu/config/gpu_info_collector.h
+++ b/gpu/config/gpu_info_collector.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include "build/build_config.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/gpu_export.h"
 
@@ -59,6 +60,9 @@
 // On other platforms, this calls CollectBasicGraphicsInfo().
 GPU_EXPORT void CollectGraphicsInfoForTesting(GPUInfo* gpu_info);
 
+// Collect Graphics info related to the current process
+GPU_EXPORT bool CollectGpuExtraInfo(GpuExtraInfo* gpu_extra_info);
+
 }  // namespace gpu
 
 #endif  // GPU_CONFIG_GPU_INFO_COLLECTOR_H_
diff --git a/gpu/config/software_rendering_list.json b/gpu/config/software_rendering_list.json
index 03e2515..9def82b 100644
--- a/gpu/config/software_rendering_list.json
+++ b/gpu/config/software_rendering_list.json
@@ -104,14 +104,14 @@
     },
     {
       "id": 12,
-      "description": "Drivers older than 2009-01 on Windows are possibly unreliable",
-      "cr_bugs": [72979, 89802, 315205],
+      "description": "Drivers older than 2010 on Windows are possibly unreliable",
+	"cr_bugs": [72979, 89802, 315205, 977432],
       "os": {
         "type": "win"
       },
       "driver_date": {
         "op": "<",
-        "value": "2009.1"
+        "value": "2010"
       },
       "exceptions": [
         {
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.cc b/gpu/ipc/client/command_buffer_proxy_impl.cc
index b753d1b..5a34972 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.cc
+++ b/gpu/ipc/client/command_buffer_proxy_impl.cc
@@ -475,23 +475,6 @@
   Send(new GpuCommandBufferMsg_DestroyImage(route_id_, id));
 }
 
-uint32_t CommandBufferProxyImpl::CreateStreamTexture(uint32_t texture_id) {
-  CheckLock();
-  base::AutoLock lock(last_state_lock_);
-  if (last_state_.error != gpu::error::kNoError)
-    return 0;
-
-  int32_t stream_id = channel_->GenerateRouteID();
-  bool succeeded = false;
-  Send(new GpuCommandBufferMsg_CreateStreamTexture(route_id_, texture_id,
-                                                   stream_id, &succeeded));
-  if (!succeeded) {
-    DLOG(ERROR) << "GpuCommandBufferMsg_CreateStreamTexture returned failure";
-    return 0;
-  }
-  return stream_id;
-}
-
 void CommandBufferProxyImpl::SetLock(base::Lock* lock) {
   lock_ = lock;
 }
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.h b/gpu/ipc/client/command_buffer_proxy_impl.h
index 48b4586..11e07b36 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.h
+++ b/gpu/ipc/client/command_buffer_proxy_impl.h
@@ -151,7 +151,6 @@
   const base::UnsafeSharedMemoryRegion& GetSharedStateRegion() const {
     return shared_state_shm_;
   }
-  uint32_t CreateStreamTexture(uint32_t texture_id);
 
  private:
   typedef std::map<int32_t, scoped_refptr<gpu::Buffer>> TransferBufferMap;
diff --git a/gpu/ipc/client/shared_image_interface_proxy.cc b/gpu/ipc/client/shared_image_interface_proxy.cc
index fa1e2c7..682b231 100644
--- a/gpu/ipc/client/shared_image_interface_proxy.cc
+++ b/gpu/ipc/client/shared_image_interface_proxy.cc
@@ -246,6 +246,11 @@
       next_release_id_);
 }
 
+void SharedImageInterfaceProxy::Flush() {
+  base::AutoLock lock(lock_);
+  host_->EnsureFlush(last_flush_id_);
+}
+
 bool SharedImageInterfaceProxy::GetSHMForPixelData(
     base::span<const uint8_t> pixel_data,
     size_t* shm_offset,
diff --git a/gpu/ipc/client/shared_image_interface_proxy.h b/gpu/ipc/client/shared_image_interface_proxy.h
index 8db9c29..2a40103 100644
--- a/gpu/ipc/client/shared_image_interface_proxy.h
+++ b/gpu/ipc/client/shared_image_interface_proxy.h
@@ -44,6 +44,7 @@
                           const Mailbox& mailbox) override;
   SyncToken GenVerifiedSyncToken() override;
   SyncToken GenUnverifiedSyncToken() override;
+  void Flush() override;
 
 #if defined(OS_WIN)
   SwapChainMailboxes CreateSwapChain(viz::ResourceFormat format,
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn
index 0e72647..6805489 100644
--- a/gpu/ipc/common/BUILD.gn
+++ b/gpu/ipc/common/BUILD.gn
@@ -193,6 +193,7 @@
     "capabilities.mojom",
     "context_result.mojom",
     "dx_diag_node.mojom",
+    "gpu_extra_info.mojom",
     "gpu_feature_info.mojom",
     "gpu_info.mojom",
     "mailbox.mojom",
diff --git a/gpu/ipc/common/gpu_extra_info.mojom b/gpu/ipc/common/gpu_extra_info.mojom
new file mode 100644
index 0000000..1144d653
--- /dev/null
+++ b/gpu/ipc/common/gpu_extra_info.mojom
@@ -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.
+
+// gpu/config/gpu_extra_info.h
+module gpu.mojom;
+
+// gpu::ANGLEFeature
+struct ANGLEFeature {
+  string name;
+  string category;
+  string description;
+  string bug;
+  string status;
+};
+
+// gpu:GpuExtraInfo
+struct GpuExtraInfo {
+  // List of features queried from ANGLE
+  array<ANGLEFeature> angle_features;
+};
diff --git a/gpu/ipc/common/gpu_extra_info.typemap b/gpu/ipc/common/gpu_extra_info.typemap
new file mode 100644
index 0000000..86d74c5
--- /dev/null
+++ b/gpu/ipc/common/gpu_extra_info.typemap
@@ -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.
+
+mojom = "//gpu/ipc/common/gpu_extra_info.mojom"
+public_headers = [ "//gpu/config/gpu_extra_info.h" ]
+traits_headers = [ "//gpu/ipc/common/gpu_extra_info_struct_traits.h" ]
+sources = [
+  "//gpu/ipc/common/gpu_extra_info_struct_traits.cc",
+]
+public_deps = [
+  "//gpu/config",
+  "//ui/gfx/geometry/mojo",
+]
+type_mappings = [
+  "gpu.mojom.GpuExtraInfo=gpu::GpuExtraInfo",
+  "gpu.mojom.ANGLEFeature=gpu::ANGLEFeature",
+]
diff --git a/gpu/ipc/common/gpu_extra_info_struct_traits.cc b/gpu/ipc/common/gpu_extra_info_struct_traits.cc
new file mode 100644
index 0000000..756c4946
--- /dev/null
+++ b/gpu/ipc/common/gpu_extra_info_struct_traits.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 "gpu/ipc/common/gpu_extra_info_struct_traits.h"
+#include "build/build_config.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<gpu::mojom::ANGLEFeatureDataView, gpu::ANGLEFeature>::Read(
+    gpu::mojom::ANGLEFeatureDataView data,
+    gpu::ANGLEFeature* out) {
+  return data.ReadName(&out->name) && data.ReadCategory(&out->category) &&
+         data.ReadDescription(&out->description) && data.ReadBug(&out->bug) &&
+         data.ReadStatus(&out->status);
+}
+
+// static
+bool StructTraits<gpu::mojom::GpuExtraInfoDataView, gpu::GpuExtraInfo>::Read(
+    gpu::mojom::GpuExtraInfoDataView data,
+    gpu::GpuExtraInfo* out) {
+  return data.ReadAngleFeatures(&out->angle_features);
+}
+
+}  // namespace mojo
diff --git a/gpu/ipc/common/gpu_extra_info_struct_traits.h b/gpu/ipc/common/gpu_extra_info_struct_traits.h
new file mode 100644
index 0000000..5c695d3
--- /dev/null
+++ b/gpu/ipc/common/gpu_extra_info_struct_traits.h
@@ -0,0 +1,53 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GPU_IPC_COMMON_GPU_EXTRA_INFO_STRUCT_TRAITS_H_
+#define GPU_IPC_COMMON_GPU_EXTRA_INFO_STRUCT_TRAITS_H_
+
+#include "gpu/config/gpu_extra_info.h"
+#include "gpu/ipc/common/gpu_extra_info.mojom.h"
+#include "ui/gfx/mojo/buffer_types_struct_traits.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<gpu::mojom::ANGLEFeatureDataView, gpu::ANGLEFeature> {
+  static bool Read(gpu::mojom::ANGLEFeatureDataView data,
+                   gpu::ANGLEFeature* out);
+
+  static const std::string& name(const gpu::ANGLEFeature& input) {
+    return input.name;
+  }
+
+  static const std::string& category(const gpu::ANGLEFeature& input) {
+    return input.category;
+  }
+
+  static const std::string& description(const gpu::ANGLEFeature& input) {
+    return input.description;
+  }
+
+  static const std::string& bug(const gpu::ANGLEFeature& input) {
+    return input.bug;
+  }
+
+  static const std::string& status(const gpu::ANGLEFeature& input) {
+    return input.status;
+  }
+};
+
+template <>
+struct StructTraits<gpu::mojom::GpuExtraInfoDataView, gpu::GpuExtraInfo> {
+  static bool Read(gpu::mojom::GpuExtraInfoDataView data,
+                   gpu::GpuExtraInfo* out);
+
+  static const std::vector<gpu::ANGLEFeature>& angle_features(
+      const gpu::GpuExtraInfo& input) {
+    return input.angle_features;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // GPU_IPC_COMMON_GPU_EXTRA_INFO_STRUCT_TRAITS_H_
diff --git a/gpu/ipc/common/gpu_info.mojom b/gpu/ipc/common/gpu_info.mojom
index a11e5844..9705ef1 100644
--- a/gpu/ipc/common/gpu_info.mojom
+++ b/gpu/ipc/common/gpu_info.mojom
@@ -116,15 +116,6 @@
   uint32 vulkan_version;
 };
 
-// gpu::ANGLEFeature
-struct ANGLEFeature {
-  string name;
-  string category;
-  string description;
-  string bug;
-  string status;
-};
-
 // Corresponds to |gpu::GPUInfo| in gpu/config/gpu_info.h
 struct GpuInfo {
   mojo_base.mojom.TimeDelta initialization_time;
@@ -176,6 +167,4 @@
   uint64 system_visual;
   uint64 rgba_visual;
   bool oop_rasterization_supported;
-
-  array<ANGLEFeature> angle_features;
 };
diff --git a/gpu/ipc/common/gpu_info.typemap b/gpu/ipc/common/gpu_info.typemap
index 227c76c..4ff5fe2 100644
--- a/gpu/ipc/common/gpu_info.typemap
+++ b/gpu/ipc/common/gpu_info.typemap
@@ -22,5 +22,4 @@
   "gpu.mojom.VideoDecodeAcceleratorCapabilities=gpu::VideoDecodeAcceleratorCapabilities",
   "gpu.mojom.VideoEncodeAcceleratorSupportedProfile=gpu::VideoEncodeAcceleratorSupportedProfile",
   "gpu.mojom.ImageDecodeAcceleratorSupportedProfile=gpu::ImageDecodeAcceleratorSupportedProfile",
-  "gpu.mojom.ANGLEFeature=gpu::ANGLEFeature",
 ]
diff --git a/gpu/ipc/common/gpu_info_struct_traits.cc b/gpu/ipc/common/gpu_info_struct_traits.cc
index 687a69a..28d5662 100644
--- a/gpu/ipc/common/gpu_info_struct_traits.cc
+++ b/gpu/ipc/common/gpu_info_struct_traits.cc
@@ -350,15 +350,6 @@
 }
 #endif
 
-// static
-bool StructTraits<gpu::mojom::ANGLEFeatureDataView, gpu::ANGLEFeature>::Read(
-    gpu::mojom::ANGLEFeatureDataView data,
-    gpu::ANGLEFeature* out) {
-  return data.ReadName(&out->name) && data.ReadCategory(&out->category) &&
-         data.ReadDescription(&out->description) && data.ReadBug(&out->bug) &&
-         data.ReadStatus(&out->status);
-}
-
 bool StructTraits<gpu::mojom::GpuInfoDataView, gpu::GPUInfo>::Read(
     gpu::mojom::GpuInfoDataView data,
     gpu::GPUInfo* out) {
@@ -412,8 +403,7 @@
          data.ReadVideoEncodeAcceleratorSupportedProfiles(
              &out->video_encode_accelerator_supported_profiles) &&
          data.ReadImageDecodeAcceleratorSupportedProfiles(
-             &out->image_decode_accelerator_supported_profiles) &&
-         data.ReadAngleFeatures(&out->angle_features);
+             &out->image_decode_accelerator_supported_profiles);
 }
 
 }  // namespace mojo
diff --git a/gpu/ipc/common/gpu_info_struct_traits.h b/gpu/ipc/common/gpu_info_struct_traits.h
index 93b0a635..9877fa47 100644
--- a/gpu/ipc/common/gpu_info_struct_traits.h
+++ b/gpu/ipc/common/gpu_info_struct_traits.h
@@ -220,32 +220,6 @@
 #endif
 
 template <>
-struct StructTraits<gpu::mojom::ANGLEFeatureDataView, gpu::ANGLEFeature> {
-  static bool Read(gpu::mojom::ANGLEFeatureDataView data,
-                   gpu::ANGLEFeature* out);
-
-  static const std::string& name(const gpu::ANGLEFeature& input) {
-    return input.name;
-  }
-
-  static const std::string& category(const gpu::ANGLEFeature& input) {
-    return input.category;
-  }
-
-  static const std::string& description(const gpu::ANGLEFeature& input) {
-    return input.description;
-  }
-
-  static const std::string& bug(const gpu::ANGLEFeature& input) {
-    return input.bug;
-  }
-
-  static const std::string& status(const gpu::ANGLEFeature& input) {
-    return input.status;
-  }
-};
-
-template <>
 struct StructTraits<gpu::mojom::GpuInfoDataView, gpu::GPUInfo> {
   static bool Read(gpu::mojom::GpuInfoDataView data, gpu::GPUInfo* out);
 
@@ -406,11 +380,6 @@
   static bool oop_rasterization_supported(const gpu::GPUInfo& input) {
     return input.oop_rasterization_supported;
   }
-
-  static std::vector<gpu::ANGLEFeature> angle_features(
-      const gpu::GPUInfo& input) {
-    return input.angle_features;
-  }
 };
 
 }  // namespace mojo
diff --git a/gpu/ipc/common/gpu_messages.h b/gpu/ipc/common/gpu_messages.h
index 332977b5..332c102 100644
--- a/gpu/ipc/common/gpu_messages.h
+++ b/gpu/ipc/common/gpu_messages.h
@@ -205,6 +205,11 @@
 // messages have been received.
 IPC_SYNC_MESSAGE_CONTROL0_0(GpuChannelMsg_Nop)
 
+// Creates a StreamTexture attached to the provided |stream_id|.
+IPC_SYNC_MESSAGE_CONTROL1_1(GpuChannelMsg_CreateStreamTexture,
+                            int32_t, /* stream_id */
+                            bool /* succeeded */)
+
 #if defined(OS_ANDROID)
 //------------------------------------------------------------------------------
 // Tells the StreamTexture to send its SurfaceTexture to the browser process,
@@ -212,16 +217,22 @@
 IPC_MESSAGE_ROUTED1(GpuStreamTextureMsg_ForwardForSurfaceRequest,
                     base::UnguessableToken)
 
-// Tells the GPU process to set the size of StreamTexture from the given
-// stream Id.
-IPC_MESSAGE_ROUTED1(GpuStreamTextureMsg_SetSize, gfx::Size /* size */)
-
 // Tells the service-side instance to start sending frame available
 // notifications.
 IPC_MESSAGE_ROUTED0(GpuStreamTextureMsg_StartListening)
 
 // Inform the renderer that a new frame is available.
 IPC_MESSAGE_ROUTED0(GpuStreamTextureMsg_FrameAvailable)
+
+// Create a SharedImage for the current StreamTexture at the provided |size|.
+IPC_MESSAGE_ROUTED3(GpuStreamTextureMsg_CreateSharedImage,
+                    gpu::Mailbox /* mailbox */,
+                    gfx::Size /* size */,
+                    uint32_t /* release_id */)
+
+// Destroys the StreamTexture attached to the provided |stream_id|.
+IPC_MESSAGE_ROUTED0(GpuStreamTextureMsg_Destroy)
+
 #endif
 
 //------------------------------------------------------------------------------
@@ -321,12 +332,6 @@
 // Destroy a previously created image.
 IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_DestroyImage, int32_t /* id */)
 
-// Attaches an external image stream to the client texture.
-IPC_SYNC_MESSAGE_ROUTED2_1(GpuCommandBufferMsg_CreateStreamTexture,
-                           uint32_t, /* client_texture_id */
-                           int32_t,  /* stream_id */
-                           bool /* succeeded */)
-
 // Send a GPU fence handle and store it for the specified gpu fence ID.
 IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_CreateGpuFenceFromHandle,
                     uint32_t /* gpu_fence_id */,
diff --git a/gpu/ipc/common/typemaps.gni b/gpu/ipc/common/typemaps.gni
index cf81e70..397b2b00 100644
--- a/gpu/ipc/common/typemaps.gni
+++ b/gpu/ipc/common/typemaps.gni
@@ -8,6 +8,7 @@
   "//gpu/ipc/common/gpu_feature_info.typemap",
   "//gpu/ipc/common/gpu_info.typemap",
   "//gpu/ipc/common/gpu_preferences.typemap",
+  "//gpu/ipc/common/gpu_extra_info.typemap",
   "//gpu/ipc/common/dx_diag_node.typemap",
   "//gpu/ipc/common/mailbox.typemap",
   "//gpu/ipc/common/mailbox_holder.typemap",
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 616b8d7..0487d74 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -283,6 +283,10 @@
     return sync_token;
   }
 
+  void Flush() override {
+    // No need to flush in this implementation.
+  }
+
   CommandBufferId command_buffer_id() const { return command_buffer_id_; }
 
  private:
diff --git a/gpu/ipc/service/DEPS b/gpu/ipc/service/DEPS
index c6f813963..b333190 100644
--- a/gpu/ipc/service/DEPS
+++ b/gpu/ipc/service/DEPS
@@ -5,6 +5,7 @@
   "+components/viz/common/gpu/gpu_vsync_callback.h",
   "+components/viz/common/gpu/vulkan_context_provider.h",
   "+components/viz/common/resources/resource_format.h",
+  "+components/viz/common/resources/resource_sizes.h",
   "+third_party/skia",
   "+ui/accelerated_widget_mac",
   "+ui/base",
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index 125fe87..9dcb6e3 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -49,10 +49,6 @@
 #include "base/win/win_util.h"
 #endif
 
-#if defined(OS_ANDROID)
-#include "gpu/ipc/service/stream_texture_android.h"
-#endif
-
 namespace gpu {
 struct WaitForCommandState {
   WaitForCommandState(int32_t start, int32_t end, IPC::Message* reply)
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index 28cc844..cf8b983 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -56,10 +56,6 @@
 #include "base/win/win_util.h"
 #endif
 
-#if defined(OS_ANDROID)
-#include "gpu/ipc/service/stream_texture_android.h"
-#endif
-
 namespace gpu {
 
 GLES2CommandBufferStub::GLES2CommandBufferStub(
@@ -440,8 +436,6 @@
                         OnReturnFrontBuffer);
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateImage, OnCreateImage);
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_DestroyImage, OnDestroyImage);
-    IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateStreamTexture,
-                        OnCreateStreamTexture)
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateGpuFenceFromHandle,
                         OnCreateGpuFenceFromHandle)
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_GetGpuFenceHandle,
@@ -556,16 +550,6 @@
   image_manager->RemoveImage(id);
 }
 
-void GLES2CommandBufferStub::OnCreateStreamTexture(uint32_t texture_id,
-                                                   int32_t stream_id,
-                                                   bool* succeeded) {
-#if defined(OS_ANDROID)
-  *succeeded = StreamTexture::Create(this, texture_id, stream_id);
-#else
-  *succeeded = false;
-#endif
-}
-
 void GLES2CommandBufferStub::OnSwapBuffers(uint64_t swap_id, uint32_t flags) {
   pending_swap_completed_params_.push_back({swap_id, flags});
   pending_presented_params_.push_back({swap_id, flags});
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.h b/gpu/ipc/service/gles2_command_buffer_stub.h
index 166ef98..5346b52d 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.h
+++ b/gpu/ipc/service/gles2_command_buffer_stub.h
@@ -60,9 +60,6 @@
   void OnGetGpuFenceHandle(uint32_t gpu_fence_id);
   void OnCreateImage(GpuCommandBufferMsg_CreateImage_Params params);
   void OnDestroyImage(int32_t id);
-  void OnCreateStreamTexture(uint32_t texture_id,
-                             int32_t stream_id,
-                             bool* succeeded);
 
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
 
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index 71446ad..3e3dc5a 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -33,6 +33,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/service/abstract_texture_impl_shared_context_state.h"
 #include "gpu/command_buffer/service/image_factory.h"
 #include "gpu/command_buffer/service/image_manager.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
@@ -55,6 +56,10 @@
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_utils.h"
 
+#if defined(OS_ANDROID)
+#include "gpu/ipc/service/stream_texture_android.h"
+#endif  // defined(OS_ANDROID)
+
 namespace gpu {
 
 struct GpuChannelMessage {
@@ -400,6 +405,14 @@
   // Clear stubs first because of dependencies.
   stubs_.clear();
 
+#if defined(OS_ANDROID)
+  // Release any references to this channel held by StreamTexture.
+  for (auto& stream_texture : stream_textures_) {
+    stream_texture.second->ReleaseChannel();
+  }
+  stream_textures_.clear();
+#endif  // OS_ANDROID
+
   // Destroy filter first to stop posting tasks to scheduler.
   filter_->Destroy();
 
@@ -548,6 +561,8 @@
                         OnCreateCommandBuffer)
     IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer,
                         OnDestroyCommandBuffer)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateStreamTexture,
+                        OnCreateStreamTexture)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -631,6 +646,16 @@
   }
   return nullptr;
 }
+
+void GpuChannel::DestroyStreamTexture(int32_t stream_id) {
+  auto found = stream_textures_.find(stream_id);
+  if (found == stream_textures_.end()) {
+    LOG(ERROR) << "Trying to destroy a non-existent stream texture.";
+    return;
+  }
+  found->second->ReleaseChannel();
+  stream_textures_.erase(stream_id);
+}
 #endif
 
 void GpuChannel::OnCreateCommandBuffer(
@@ -757,6 +782,28 @@
   RemoveRoute(route_id);
 }
 
+void GpuChannel::OnCreateStreamTexture(int32_t stream_id, bool* succeeded) {
+#if defined(OS_ANDROID)
+  auto found = stream_textures_.find(stream_id);
+  if (found != stream_textures_.end()) {
+    LOG(ERROR)
+        << "Trying to create a StreamTexture with an existing stream_id.";
+    *succeeded = false;
+    return;
+  }
+  scoped_refptr<StreamTexture> stream_texture =
+      StreamTexture::Create(this, stream_id);
+  if (!stream_texture) {
+    *succeeded = false;
+    return;
+  }
+  stream_textures_.emplace(stream_id, std::move(stream_texture));
+  *succeeded = true;
+#else
+  *succeeded = false;
+#endif
+}
+
 void GpuChannel::CacheShader(const std::string& key,
                              const std::string& shader) {
   gpu_channel_manager_->delegate()->StoreShaderToDisk(client_id_, key, shader);
diff --git a/gpu/ipc/service/gpu_channel.h b/gpu/ipc/service/gpu_channel.h
index 8c10927..c784ac6 100644
--- a/gpu/ipc/service/gpu_channel.h
+++ b/gpu/ipc/service/gpu_channel.h
@@ -41,13 +41,13 @@
 }
 
 namespace gpu {
-
 class GpuChannelManager;
 class GpuChannelMessageFilter;
 class ImageDecodeAcceleratorStub;
 class ImageDecodeAcceleratorWorker;
 class Scheduler;
 class SharedImageStub;
+class StreamTexture;
 class SyncPointManager;
 
 // Encapsulates an IPC channel between the GPU process and one renderer
@@ -159,6 +159,10 @@
 
 #if defined(OS_ANDROID)
   const CommandBufferStub* GetOneStub() const;
+
+  // Called by StreamTexture to remove the GpuChannel's reference to the
+  // StreamTexture.
+  void DestroyStreamTexture(int32_t stream_id);
 #endif
 
   SharedImageStub* shared_image_stub() const {
@@ -189,6 +193,7 @@
                              gpu::ContextResult* result,
                              gpu::Capabilities* capabilities);
   void OnDestroyCommandBuffer(int32_t route_id);
+  void OnCreateStreamTexture(int32_t stream_id, bool* succeeded);
   bool CreateSharedImageStub();
 
   std::unique_ptr<IPC::SyncChannel> sync_channel_;  // nullptr in tests.
@@ -240,6 +245,11 @@
 
   const bool is_gpu_host_;
 
+#if defined(OS_ANDROID)
+  // Set of active StreamTextures.
+  base::flat_map<int32_t, scoped_refptr<StreamTexture>> stream_textures_;
+#endif
+
   // Member variables should appear before the WeakPtrFactory, to ensure that
   // any WeakPtrs to Controller are invalidated before its members variable's
   // destructors are executed, rendering them invalid.
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index f604ee7..a945e818 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -320,6 +320,12 @@
     }
   }
 
+  // Collect GPU process info
+  if (!gl_disabled) {
+    if (!CollectGpuExtraInfo(&gpu_extra_info_))
+      return false;
+  }
+
   if (!gl_disabled) {
     if (!gpu_feature_info_.disabled_extensions.empty()) {
       gl::init::SetDisabledExtensionsPlatform(
@@ -413,6 +419,11 @@
   UMA_HISTOGRAM_BOOLEAN("GPU.Sandbox.InitializedSuccessfully",
                         gpu_info_.sandboxed);
 
+  // Notify the gpu watchdog that the gpu init has completed So the watchdog
+  // can be disarmed.
+  if (watchdog_thread_)
+    watchdog_thread_->OnInitComplete();
+
   init_successful_ = true;
 #if defined(USE_OZONE)
   ui::OzonePlatform::GetInstance()->AfterSandboxEntry();
diff --git a/gpu/ipc/service/gpu_init.h b/gpu/ipc/service/gpu_init.h
index 0889c59d..0545b9d 100644
--- a/gpu/ipc/service/gpu_init.h
+++ b/gpu/ipc/service/gpu_init.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "gpu/config/gpu_extra_info.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/config/gpu_preferences.h"
@@ -55,6 +56,7 @@
 
   const GPUInfo& gpu_info() const { return gpu_info_; }
   const GpuFeatureInfo& gpu_feature_info() const { return gpu_feature_info_; }
+  const GpuExtraInfo& gpu_extra_info() const { return gpu_extra_info_; }
   const base::Optional<GPUInfo>& gpu_info_for_hardware_gpu() const {
     return gpu_info_for_hardware_gpu_;
   }
@@ -90,6 +92,8 @@
   base::Optional<GPUInfo> gpu_info_for_hardware_gpu_;
   base::Optional<GpuFeatureInfo> gpu_feature_info_for_hardware_gpu_;
 
+  GpuExtraInfo gpu_extra_info_;
+
 #if BUILDFLAG(ENABLE_VULKAN)
   std::unique_ptr<VulkanImplementation> vulkan_implementation_;
 #endif
diff --git a/gpu/ipc/service/gpu_watchdog_thread.h b/gpu/ipc/service/gpu_watchdog_thread.h
index 9083776..deac19a 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.h
+++ b/gpu/ipc/service/gpu_watchdog_thread.h
@@ -44,12 +44,13 @@
   virtual void OnBackgrounded() = 0;
   virtual void OnForegrounded() = 0;
 
+  // The watchdog starts armed to catch startup hangs, and needs to be disarmed
+  // once init is complete, before executing tasks.
+  virtual void OnInitComplete() = 0;
+
  protected:
   GpuWatchdogThread();
 
-  // Do not change this name. This is used for [GPU HANG] carsh reports
-  virtual void DeliberatelyTerminateToRecoverFromHang() = 0;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(GpuWatchdogThread);
 };
@@ -62,14 +63,15 @@
   static std::unique_ptr<GpuWatchdogThreadImplV1> Create(
       bool start_backgrounded);
 
+  // Implements GpuWatchdogThread.
   void AddPowerObserver() override;
+  void OnBackgrounded() override;
+  void OnForegrounded() override;
+  void OnInitComplete() override {}
 
   // gl::ProgressReporter implementation:
   void ReportProgress() override;
 
-  void OnBackgrounded() override;
-  void OnForegrounded() override;
-
  protected:
   void Init() override;
   void CleanUp() override;
@@ -131,7 +133,8 @@
   void OnAcknowledge();
   void OnCheck(bool after_suspend);
   void OnCheckTimeout();
-  void DeliberatelyTerminateToRecoverFromHang() override;
+  // Do not change the function name. It is used for [GPU HANG] carsh reports.
+  void DeliberatelyTerminateToRecoverFromHang();
 #if defined(USE_X11)
   void SetupXServer();
   void SetupXChangeProp();
diff --git a/gpu/ipc/service/gpu_watchdog_thread_v2.cc b/gpu/ipc/service/gpu_watchdog_thread_v2.cc
index 8cccd38..ccaf6aa 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_v2.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread_v2.cc
@@ -4,32 +4,36 @@
 
 #include "gpu/ipc/service/gpu_watchdog_thread_v2.h"
 
+#include "base/atomicops.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/debug/alias.h"
 #include "base/message_loop/message_loop_current.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 
 namespace gpu {
 
 namespace {
 #if defined(CYGPROFILE_INSTRUMENTATION)
-const int kGpuTimeoutInMs = 30000;
+const int kGpuTimeoutInSec = 30;
 #elif defined(OS_WIN) || defined(OS_MACOSX)
-const int kGpuTimeoutInMs = 15000;
+const int kGpuTimeoutInSec = 15;
 #else
-const int kGpuTimeoutInMs = 10000;
+const int kGpuTimeoutInSec = 10;
 #endif
 }  // namespace
 
 GpuWatchdogThreadImplV2::GpuWatchdogThreadImplV2()
-    : timeout_(base::TimeDelta::FromMilliseconds(kGpuTimeoutInMs)),
+    : watchdog_timeout_(base::TimeDelta::FromSeconds(kGpuTimeoutInSec)),
       watched_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       weak_factory_(this) {
-  Disarm();
-
   base::MessageLoopCurrent::Get()->AddTaskObserver(this);
+  weak_ptr_ = weak_factory_.GetWeakPtr();
+  watchdog_start_time_ = base::TimeTicks::Now();
+  Arm();
 }
 
 GpuWatchdogThreadImplV2::~GpuWatchdogThreadImplV2() {
@@ -43,8 +47,6 @@
 // static
 std::unique_ptr<GpuWatchdogThreadImplV2> GpuWatchdogThreadImplV2::Create(
     bool start_backgrounded) {
-  NOTREACHED();  // Not ready yet
-
   auto watchdog_thread = base::WrapUnique(new GpuWatchdogThreadImplV2);
   base::Thread::Options options;
   options.timer_slack = base::TIMER_SLACK_MAXIMUM;
@@ -57,22 +59,47 @@
 // Do not add power observer during watchdog init, PowerMonitor might not be up
 // running yet.
 void GpuWatchdogThreadImplV2::AddPowerObserver() {
-  DCHECK(base::PowerMonitor::IsInitialized());
-  base::PowerMonitor::AddObserver(this);
+  task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&GpuWatchdogThreadImplV2::OnAddPowerObserver,
+                                base::Unretained(this)));
 }
 
-void GpuWatchdogThreadImplV2::OnBackgrounded() {}
+// Called from the gpu thread.
+void GpuWatchdogThreadImplV2::OnBackgrounded() {
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&GpuWatchdogThreadImplV2::OnWatchdogBackgrounded,
+                     base::Unretained(this)));
+}
 
-void GpuWatchdogThreadImplV2::OnForegrounded() {}
+// Called from the gpu thread.
+void GpuWatchdogThreadImplV2::OnForegrounded() {
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&GpuWatchdogThreadImplV2::OnWatchdogForegrounded,
+                     base::Unretained(this)));
+}
 
-void GpuWatchdogThreadImplV2::ReportProgress() {}
+// Called from the gpu thread when gpu init has completed
+void GpuWatchdogThreadImplV2::OnInitComplete() {
+  Disarm();
+}
 
-void GpuWatchdogThreadImplV2::Init() {}
+void GpuWatchdogThreadImplV2::Init() {
+  task_runner()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&GpuWatchdogThreadImplV2::OnWatchdogTimeout, weak_ptr_),
+      watchdog_timeout_);
+}
 
 void GpuWatchdogThreadImplV2::CleanUp() {
   weak_factory_.InvalidateWeakPtrs();
 }
 
+void GpuWatchdogThreadImplV2::ReportProgress() {
+  InProgress();
+}
+
 void GpuWatchdogThreadImplV2::WillProcessTask(
     const base::PendingTask& pending_task) {
   Arm();
@@ -83,17 +110,93 @@
   Disarm();
 }
 
-void GpuWatchdogThreadImplV2::Arm() {}
+void GpuWatchdogThreadImplV2::OnSuspend() {
+  in_power_suspension_ = true;
+  suspend_time_ = base::TimeTicks::Now();
+}
 
-void GpuWatchdogThreadImplV2::Disarm() {}
+void GpuWatchdogThreadImplV2::OnResume() {
+  in_power_suspension_ = false;
+  resume_time_ = base::TimeTicks::Now();
+}
 
-void GpuWatchdogThreadImplV2::OnSuspend() {}
+// Running on the watchdog thread.
+void GpuWatchdogThreadImplV2::OnAddPowerObserver() {
+  DCHECK(base::PowerMonitor::IsInitialized());
+  base::PowerMonitor::AddObserver(this);
+}
 
-void GpuWatchdogThreadImplV2::OnResume() {}
+// Running on the watchdog thread.
+void GpuWatchdogThreadImplV2::OnWatchdogBackgrounded() {
+  is_backgrounded_ = true;
+  backgrounded_time_ = base::TimeTicks::Now();
+}
+
+// Running on the watchdog thread.
+void GpuWatchdogThreadImplV2::OnWatchdogForegrounded() {
+  is_backgrounded_ = false;
+  foregrounded_time_ = base::TimeTicks::Now();
+}
+
+void GpuWatchdogThreadImplV2::Arm() {
+  base::subtle::NoBarrier_AtomicIncrement(&arm_disarm_counter_, 1);
+
+  // Arm/Disarm are always called in sequence. Now it's an odd number.
+  DCHECK(base::subtle::NoBarrier_Load(&arm_disarm_counter_) & 1);
+}
+
+void GpuWatchdogThreadImplV2::Disarm() {
+  base::subtle::NoBarrier_AtomicIncrement(&arm_disarm_counter_, 1);
+
+  // Arm/Disarm are always called in sequence. Now it's an even number.
+  DCHECK(base::subtle::NoBarrier_Load(&arm_disarm_counter_) % 2 == 0);
+}
+
+void GpuWatchdogThreadImplV2::InProgress() {
+  // This is equivalent to Disarm() + Arm().
+  base::subtle::NoBarrier_AtomicIncrement(&arm_disarm_counter_, 2);
+
+  // Now it's an odd number.
+  DCHECK(base::subtle::NoBarrier_Load(&arm_disarm_counter_) & 1);
+}
+
+void GpuWatchdogThreadImplV2::OnWatchdogTimeout() {
+  base::subtle::Atomic32 arm_disarm_counter =
+      base::subtle::NoBarrier_Load(&arm_disarm_counter_);
+
+  // disarmed is true if it's an even number.
+  bool disarmed = arm_disarm_counter % 2 == 0;
+  bool gpu_makes_progress = arm_disarm_counter != last_arm_disarm_counter_;
+  last_arm_disarm_counter_ = arm_disarm_counter;
+
+  // No gpu hang is detected. Continue with another OnWatchdogTimeout
+  if (disarmed || gpu_makes_progress) {
+    task_runner()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&GpuWatchdogThreadImplV2::OnWatchdogTimeout, weak_ptr_),
+        watchdog_timeout_);
+    return;
+  }
+
+  // Still armed without any progress. GPU possibly hangs.
+  DeliberatelyTerminateToRecoverFromHang();
+}
 
 void GpuWatchdogThreadImplV2::DeliberatelyTerminateToRecoverFromHang() {
+#if defined(OS_WIN)
+  if (IsDebuggerPresent())
+    return;
+#endif
+
   // Store variables so they're available in crash dumps to help determine the
   // cause of any hang.
+  base::TimeTicks current_time = base::TimeTicks::Now();
+  base::debug::Alias(&current_time);
+  base::debug::Alias(&watchdog_start_time_);
+  base::debug::Alias(&suspend_time_);
+  base::debug::Alias(&resume_time_);
+  base::debug::Alias(&backgrounded_time_);
+  base::debug::Alias(&foregrounded_time_);
 
   // Deliberately crash the process to create a crash dump.
   *((volatile int*)0) = 0xdeadface;
diff --git a/gpu/ipc/service/gpu_watchdog_thread_v2.h b/gpu/ipc/service/gpu_watchdog_thread_v2.h
index 52d38fb..398f880 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_v2.h
+++ b/gpu/ipc/service/gpu_watchdog_thread_v2.h
@@ -17,38 +17,71 @@
       bool start_backgrounded);
   ~GpuWatchdogThreadImplV2() override;
 
-  // Implements GpuWatchdogThread
+  // Implements GpuWatchdogThread.
   void AddPowerObserver() override;
   void OnBackgrounded() override;
   void OnForegrounded() override;
+  void OnInitComplete() override;
 
-  // Implements gl::ProgressReporter
-  void ReportProgress() override;
-
- protected:
-  // Implements base::Thread
+  // Implements base::Thread.
   void Init() override;
   void CleanUp() override;
 
- private:
-  GpuWatchdogThreadImplV2();
-  void Arm();
-  void Disarm();
-
-  // Implements base::PowerObserver
-  void OnSuspend() override;
-  void OnResume() override;
+  // Implements gl::ProgressReporter.
+  void ReportProgress() override;
 
   // Implements MessageLoopCurrent::TaskObserver.
   void WillProcessTask(const base::PendingTask& pending_task) override;
   void DidProcessTask(const base::PendingTask& pending_task) override;
 
-  // Implements GpuWatchdogThread
-  void DeliberatelyTerminateToRecoverFromHang() override;
+  // Implements base::PowerObserver.
+  void OnSuspend() override;
+  void OnResume() override;
 
-  base::TimeDelta timeout_;
+ private:
+  GpuWatchdogThreadImplV2();
+  void OnAddPowerObserver();
+  void OnWatchdogBackgrounded();
+  void OnWatchdogForegrounded();
+  void Arm();
+  void Disarm();
+  void InProgress();
+  void OnWatchdogTimeout();
+
+  // Do not change the function name. It is used for [GPU HANG] carsh reports.
+  void DeliberatelyTerminateToRecoverFromHang();
+
+  // This counter is only written on the gpu thread, and read on the watchdog
+  // thread.
+  base::subtle::Atomic32 arm_disarm_counter_ = 0;
+  // The counter number read in the last OnWatchdogTimeout() on the watchdog
+  // thread.
+  int32_t last_arm_disarm_counter_ = 0;
+
+  // Timeout on the watchdog thread to check if gpu hangs
+  base::TimeDelta watchdog_timeout_;
+
+  // The time the gpu watchdog was created
+  base::TimeTicks watchdog_start_time_;
+
+  // The time the last OnSuspend and OnResume was called.
+  base::TimeTicks suspend_time_;
+  base::TimeTicks resume_time_;
+
+  // The time the last OnBackgrounded and OnForegrounded was called.
+  base::TimeTicks backgrounded_time_;
+  base::TimeTicks foregrounded_time_;
+
+  // The system has entered the power suspension mode.
+  bool in_power_suspension_ = false;
+
+  // Chrome is running on the background on Android. Gpu is probably very slow
+  // or stalled.
+  bool is_backgrounded_ = false;
+
   scoped_refptr<base::SingleThreadTaskRunner> watched_task_runner_;
 
+  base::WeakPtr<GpuWatchdogThreadImplV2> weak_ptr_;
   base::WeakPtrFactory<GpuWatchdogThreadImplV2> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuWatchdogThreadImplV2);
diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc
index 8d6d9f6..921c3c71 100644
--- a/gpu/ipc/service/stream_texture_android.cc
+++ b/gpu/ipc/service/stream_texture_android.cc
@@ -7,77 +7,146 @@
 #include <string.h>
 
 #include "base/bind.h"
-#include "gpu/command_buffer/service/context_group.h"
+#include "components/viz/common/resources/resource_sizes.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
+#include "gpu/command_buffer/service/abstract_texture_impl_shared_context_state.h"
 #include "gpu/command_buffer/service/context_state.h"
-#include "gpu/command_buffer/service/decoder_context.h"
-#include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/scheduler.h"
+#include "gpu/command_buffer/service/shared_context_state.h"
+#include "gpu/command_buffer/service/shared_image_backing.h"
+#include "gpu/command_buffer/service/shared_image_factory.h"
 #include "gpu/ipc/common/android/scoped_surface_request_conduit.h"
+#include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/service/gpu_channel.h"
+#include "gpu/ipc/service/gpu_channel_manager.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gl/gl_context.h"
+#include "ui/gl/scoped_binders.h"
 #include "ui/gl/scoped_make_current.h"
 
 namespace gpu {
-
-using gles2::ContextGroup;
-using gles2::TextureManager;
-using gles2::TextureRef;
-
-// static
-bool StreamTexture::Create(CommandBufferStub* owner_stub,
-                           uint32_t client_texture_id,
-                           int stream_id) {
-  gles2::ContextGroup* context_group =
-      owner_stub->decoder_context()->GetContextGroup();
-  DCHECK(context_group);
-  TextureManager* texture_manager = context_group->texture_manager();
-  TextureRef* texture = texture_manager->GetTexture(client_texture_id);
-
-  if (texture && (!texture->texture()->target() ||
-                  texture->texture()->target() == GL_TEXTURE_EXTERNAL_OES)) {
-
-    // TODO: Ideally a valid image id was returned to the client so that
-    // it could then call glBindTexImage2D() for doing the following.
-    scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image(
-        new StreamTexture(owner_stub, stream_id, texture->service_id()));
-    gfx::Size size = gl_image->GetSize();
-    texture_manager->SetTarget(texture, GL_TEXTURE_EXTERNAL_OES);
-    texture_manager->SetLevelInfo(texture, GL_TEXTURE_EXTERNAL_OES, 0, GL_RGBA,
-                                  size.width(), size.height(), 1, 0, GL_RGBA,
-                                  GL_UNSIGNED_BYTE, gfx::Rect(size));
-    texture_manager->SetLevelStreamTextureImage(
-        texture, GL_TEXTURE_EXTERNAL_OES, 0, gl_image.get(),
-        gles2::Texture::UNBOUND, 0);
-    return true;
+namespace {
+std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrent(
+    SharedContextState* context_state) {
+  std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current;
+  bool needs_make_current = !context_state->IsCurrent(nullptr);
+  if (needs_make_current) {
+    scoped_make_current = std::make_unique<ui::ScopedMakeCurrent>(
+        context_state->context(), context_state->surface());
   }
-
-  return false;
+  return scoped_make_current;
 }
 
-StreamTexture::StreamTexture(CommandBufferStub* owner_stub,
-                             int32_t route_id,
-                             uint32_t texture_id)
-    : surface_owner_(SurfaceOwner::Create(texture_id)),
+class SharedImageBackingStreamTexture : public gpu::SharedImageBacking {
+ public:
+  SharedImageBackingStreamTexture(
+      const gpu::Mailbox& mailbox,
+      const gfx::Size& size,
+      std::unique_ptr<gpu::gles2::AbstractTexture> abstract_texture)
+      : SharedImageBacking(
+            mailbox,
+            viz::RGBA_8888,
+            size,
+            gfx::ColorSpace(),
+            gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_GLES2,
+            viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size,
+                                                             viz::RGBA_8888),
+            false /* is_thread_safe */),
+        abstract_texture_(std::move(abstract_texture)) {}
+
+  ~SharedImageBackingStreamTexture() override {}
+
+  // SharedImageBacking implementation.
+  bool IsCleared() const override { return true; }
+  void SetCleared() override {}
+  void Update(std::unique_ptr<gfx::GpuFence> in_fence) override {
+    NOTREACHED();
+  }
+  bool ProduceLegacyMailbox(gpu::MailboxManager* mailbox_manager) override {
+    mailbox_manager->ProduceTexture(mailbox(),
+                                    abstract_texture_->GetTextureBase());
+    return true;
+  }
+  void Destroy() override {}
+
+ private:
+  // |abstract_texture_| is only used for legacy mailbox.
+  std::unique_ptr<gpu::gles2::AbstractTexture> abstract_texture_;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedImageBackingStreamTexture);
+};
+
+}  // namespace
+
+// static
+scoped_refptr<StreamTexture> StreamTexture::Create(GpuChannel* channel,
+                                                   int stream_id) {
+  ContextResult result;
+  auto context_state =
+      channel->gpu_channel_manager()->GetSharedContextState(&result);
+  if (result != ContextResult::kSuccess)
+    return nullptr;
+  auto scoped_make_current = MakeCurrent(context_state.get());
+  auto texture = std::make_unique<gles2::AbstractTextureImplOnSharedContext>(
+      GL_TEXTURE_EXTERNAL_OES, GL_RGBA, 0, 0, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+      context_state.get());
+  return new StreamTexture(channel, stream_id, std::move(texture),
+                           std::move(context_state));
+}
+
+StreamTexture::StreamTexture(
+    GpuChannel* channel,
+    int32_t route_id,
+    std::unique_ptr<gles2::AbstractTexture> surface_owner_texture,
+    scoped_refptr<SharedContextState> context_state)
+    : surface_owner_texture_(std::move(surface_owner_texture)),
+      surface_owner_texture_id_(
+          surface_owner_texture_->GetTextureBase()->service_id()),
+      surface_owner_(SurfaceOwner::Create(surface_owner_texture_id_)),
       size_(0, 0),
       has_pending_frame_(false),
-      owner_stub_(owner_stub),
+      channel_(channel),
       route_id_(route_id),
       has_listener_(false),
-      texture_id_(texture_id),
+      context_state_(std::move(context_state)),
+      sequence_(
+          channel_->scheduler()->CreateSequence(SchedulingPriority::kLow)),
+      sync_point_client_state_(
+          channel_->sync_point_manager()->CreateSyncPointClientState(
+              CommandBufferNamespace::GPU_IO,
+              CommandBufferIdFromChannelAndRoute(channel_->client_id(),
+                                                 route_id),
+              sequence_)),
       weak_factory_(this) {
-  owner_stub->AddDestructionObserver(this);
+  context_state_->AddContextLostObserver(this);
   memset(current_matrix_, 0, sizeof(current_matrix_));
-  owner_stub->channel()->AddRoute(route_id, owner_stub->sequence_id(), this);
+  channel->AddRoute(route_id, sequence_, this);
   surface_owner_->SetFrameAvailableCallback(base::BindRepeating(
       &StreamTexture::OnFrameAvailable, weak_factory_.GetWeakPtr()));
 }
 
 StreamTexture::~StreamTexture() {
-  if (owner_stub_) {
-    owner_stub_->RemoveDestructionObserver(this);
-    owner_stub_->channel()->RemoveRoute(route_id_);
-  }
+  // |channel_| is always released before GpuChannel releases its reference to
+  // this class.
+  DCHECK(!channel_);
+  context_state_->RemoveContextLostObserver(this);
+}
+
+void StreamTexture::ReleaseChannel() {
+  DCHECK(channel_);
+  channel_->RemoveRoute(route_id_);
+  channel_->scheduler()->DestroySequence(sequence_);
+  sequence_ = SequenceId();
+  sync_point_client_state_->Destroy();
+  sync_point_client_state_ = nullptr;
+  channel_ = nullptr;
+
+  // If the channel goes away, there is no need to keep the SurfaceTexture
+  // around. The GL texture will keep working regardless with the currently
+  // bound frame.
+  surface_owner_ = nullptr;
 }
 
 // gpu::gles2::GLStreamTextureMatrix implementation
@@ -90,62 +159,27 @@
   YInvertMatrix(xform);
 }
 
-void StreamTexture::OnWillDestroyStub(bool have_context) {
-  owner_stub_->RemoveDestructionObserver(this);
-  owner_stub_->channel()->RemoveRoute(route_id_);
-
-  owner_stub_ = nullptr;
-
-  // If the owner goes away, there is no need to keep the SurfaceTexture around.
-  // The GL texture will keep working regardless with the currently bound frame.
+void StreamTexture::OnContextLost() {
   surface_owner_ = nullptr;
 }
 
-std::unique_ptr<ui::ScopedMakeCurrent> StreamTexture::MakeStubCurrent() {
-  std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current;
-  bool needs_make_current =
-      !owner_stub_->decoder_context()->GetGLContext()->IsCurrent(nullptr);
-  if (needs_make_current) {
-    scoped_make_current.reset(new ui::ScopedMakeCurrent(
-        owner_stub_->decoder_context()->GetGLContext(),
-        owner_stub_->surface()));
-  }
-  return scoped_make_current;
-}
-
 void StreamTexture::UpdateTexImage() {
   DCHECK(surface_owner_.get());
-  DCHECK(owner_stub_);
 
   if (!has_pending_frame_) return;
 
-  std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current(MakeStubCurrent());
-
+  auto scoped_make_current = MakeCurrent(context_state_.get());
+  gl::ScopedTextureBinder scoped_bind_texture(GL_TEXTURE_EXTERNAL_OES,
+                                              surface_owner_texture_id_);
   surface_owner_->UpdateTexImage();
-
   has_pending_frame_ = false;
-
-  if (scoped_make_current.get()) {
-    // UpdateTexImage() implies glBindTexture().
-    // The cmd decoder takes care of restoring the binding for this GLImage as
-    // far as the current context is concerned, but if we temporarily change
-    // it, we have to keep the state intact in *that* context also.
-    const gles2::ContextState* state =
-        owner_stub_->decoder_context()->GetContextState();
-    const gles2::TextureUnit& active_unit =
-        state->texture_units[state->active_texture_unit];
-    glBindTexture(GL_TEXTURE_EXTERNAL_OES,
-                  active_unit.bound_texture_external_oes.get()
-                      ? active_unit.bound_texture_external_oes->service_id()
-                      : 0);
-  }
 }
 
 bool StreamTexture::CopyTexImage(unsigned target) {
   if (target != GL_TEXTURE_EXTERNAL_OES)
     return false;
 
-  if (!owner_stub_ || !surface_owner_.get())
+  if (!surface_owner_.get())
     return false;
 
   GLint texture_id;
@@ -156,33 +190,19 @@
   // On some devices GL_TEXTURE_BINDING_EXTERNAL_OES is not supported as
   // glGetIntegerv() parameter. In this case the value of |texture_id| will be
   // zero and we assume that it is properly bound to |texture_id_|.
-  if (texture_id > 0 && static_cast<unsigned>(texture_id) != texture_id_)
+  if (texture_id > 0 &&
+      static_cast<unsigned>(texture_id) != surface_owner_texture_id_)
     return false;
 
   UpdateTexImage();
 
-  gles2::ContextGroup* context_group =
-      owner_stub_->decoder_context()->GetContextGroup();
-  DCHECK(context_group);
-  TextureManager* texture_manager = context_group->texture_manager();
-  gles2::Texture* texture =
-      texture_manager->GetTextureForServiceId(texture_id_);
-  if (texture) {
-    // By setting image state to UNBOUND instead of COPIED we ensure that
-    // CopyTexImage() is called each time the surface texture is used for
-    // drawing.
-    texture->SetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, 0, this,
-                                        gles2::Texture::UNBOUND, 0);
-  }
-
   return true;
 }
 
 void StreamTexture::OnFrameAvailable() {
   has_pending_frame_ = true;
-  if (has_listener_ && owner_stub_) {
-    owner_stub_->channel()->Send(
-        new GpuStreamTextureMsg_FrameAvailable(route_id_));
+  if (has_listener_ && channel_) {
+    channel_->Send(new GpuStreamTextureMsg_FrameAvailable(route_id_));
   }
 }
 
@@ -200,7 +220,9 @@
     IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_StartListening, OnStartListening)
     IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_ForwardForSurfaceRequest,
                         OnForwardForSurfaceRequest)
-    IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_SetSize, OnSetSize)
+    IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_CreateSharedImage,
+                        OnCreateSharedImage)
+    IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_Destroy, OnDestroy)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -215,7 +237,7 @@
 
 void StreamTexture::OnForwardForSurfaceRequest(
     const base::UnguessableToken& request_token) {
-  if (!owner_stub_)
+  if (!channel_)
     return;
 
   ScopedSurfaceRequestConduit::GetInstance()
@@ -223,28 +245,49 @@
                                              surface_owner_.get());
 }
 
-void StreamTexture::OnSetSize(const gfx::Size& size) {
+void StreamTexture::OnCreateSharedImage(const gpu::Mailbox& mailbox,
+                                        const gfx::Size& size,
+                                        uint32_t release_id) {
+  DCHECK(channel_);
   size_ = size;
-  if (!owner_stub_ || !surface_owner_.get())
+
+  if (!surface_owner_)
     return;
 
-  gles2::ContextGroup* context_group =
-      owner_stub_->decoder_context()->GetContextGroup();
-  DCHECK(context_group);
-  TextureManager* texture_manager = context_group->texture_manager();
-  gles2::Texture* texture =
-      texture_manager->GetTextureForServiceId(texture_id_);
-  if (texture) {
-    // SetLevelInfo will reset the image / stream texture image, which may be
-    // the last reference to |this|, so keep a reference around, and make sure
-    // to reset the stream texture image.
-    scoped_refptr<StreamTexture> self(this);
-    texture->SetLevelInfo(GL_TEXTURE_EXTERNAL_OES, 0, GL_RGBA, size.width(),
-                          size.height(), 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
-                          gfx::Rect(size));
-    texture->SetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, 0, this,
-                                        gles2::Texture::UNBOUND, 0);
-  }
+  // We do not update |surface_owner_texture_|'s internal gles2::Texture's
+  // size. This is because the gles2::Texture is never used directly, the
+  // associated |surface_owner_texture_id_| being the only part of that object
+  // we interact with.
+  // If we ever use |surface_owner_texture_|, we need to ensure that it gets
+  // updated here.
+
+  auto scoped_make_current = MakeCurrent(context_state_.get());
+  auto legacy_mailbox_texture =
+      std::make_unique<gles2::AbstractTextureImplOnSharedContext>(
+          GL_TEXTURE_EXTERNAL_OES, GL_RGBA, size.width(), size.height(), 1, 0,
+          GL_RGBA, GL_UNSIGNED_BYTE, context_state_.get());
+  legacy_mailbox_texture->BindStreamTextureImage(this,
+                                                 surface_owner_texture_id_);
+
+  auto shared_image = std::make_unique<SharedImageBackingStreamTexture>(
+      mailbox, size_, std::move(legacy_mailbox_texture));
+  channel_->shared_image_stub()->factory()->RegisterBacking(
+      std::move(shared_image), true /* allow_legacy_mailbox */);
+
+  SyncToken sync_token(sync_point_client_state_->namespace_id(),
+                       sync_point_client_state_->command_buffer_id(),
+                       release_id);
+  auto* mailbox_manager = channel_->gpu_channel_manager()->mailbox_manager();
+  mailbox_manager->PushTextureUpdates(sync_token);
+  sync_point_client_state_->ReleaseFenceSync(release_id);
+}
+
+void StreamTexture::OnDestroy() {
+  DCHECK(channel_);
+
+  // The following call may delete the StreamTexture, so we must ensure that no
+  // access to |this| occurs after the call.
+  channel_->DestroyStreamTexture(route_id_);
 }
 
 StreamTexture::BindOrCopy StreamTexture::ShouldBindOrCopy() {
@@ -283,4 +326,8 @@
   // TODO(ericrk): Add OnMemoryDump for GLImages. crbug.com/514914
 }
 
+bool StreamTexture::HasMutableState() const {
+  return false;
+}
+
 }  // namespace gpu
diff --git a/gpu/ipc/service/stream_texture_android.h b/gpu/ipc/service/stream_texture_android.h
index aabd38f9..918e936 100644
--- a/gpu/ipc/service/stream_texture_android.h
+++ b/gpu/ipc/service/stream_texture_android.h
@@ -10,37 +10,41 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
 #include "gpu/command_buffer/service/gl_stream_texture_image.h"
+#include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/ipc/common/android/surface_owner_android.h"
 #include "gpu/ipc/service/command_buffer_stub.h"
 #include "ipc/ipc_listener.h"
 #include "ui/gl/android/surface_texture.h"
 #include "ui/gl/gl_image.h"
 
-namespace ui {
-class ScopedMakeCurrent;
-}
-
 namespace gfx {
 class Size;
 }
 
 namespace gpu {
+class GpuChannel;
+struct Mailbox;
 
 class StreamTexture : public gpu::gles2::GLStreamTextureImage,
                       public IPC::Listener,
-                      public CommandBufferStub::DestructionObserver {
+                      public SharedContextState::ContextLostObserver {
  public:
-  static bool Create(CommandBufferStub* owner_stub,
-                     uint32_t client_texture_id,
-                     int stream_id);
+  static scoped_refptr<StreamTexture> Create(GpuChannel* channel,
+                                             int stream_id);
+
+  // Cleans up related data and nulls |channel_|. Called when the channel
+  // releases its ref on this class.
+  void ReleaseChannel();
 
  private:
-  StreamTexture(CommandBufferStub* owner_stub,
+  StreamTexture(GpuChannel* channel,
                 int32_t route_id,
-                uint32_t texture_id);
+                std::unique_ptr<gles2::AbstractTexture> surface_owner_texture,
+                scoped_refptr<SharedContextState> context_state);
   ~StreamTexture() override;
 
   // gl::GLImage implementation:
@@ -65,6 +69,7 @@
   void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
                     uint64_t process_tracing_id,
                     const std::string& dump_name) override;
+  bool HasMutableState() const override;
 
   // gpu::gles2::GLStreamTextureMatrix implementation
   void GetTextureMatrix(float xform[16]) override;
@@ -74,10 +79,8 @@
                            int display_width,
                            int display_height) override {}
 
-  // CommandBufferStub::DestructionObserver implementation.
-  void OnWillDestroyStub(bool have_context) override;
-
-  std::unique_ptr<ui::ScopedMakeCurrent> MakeStubCurrent();
+  // SharedContextState::ContextLostObserver implementation.
+  void OnContextLost() override;
 
   void UpdateTexImage();
 
@@ -90,8 +93,17 @@
   // IPC message handlers:
   void OnStartListening();
   void OnForwardForSurfaceRequest(const base::UnguessableToken& request_token);
-  void OnSetSize(const gfx::Size& size);
+  void OnCreateSharedImage(const gpu::Mailbox& mailbox,
+                           const gfx::Size& size,
+                           uint32_t release_id);
+  void OnDestroy();
 
+  // An AbstractTexture which owns |surface_owner_texture_id_|, which is used
+  // by |surface_owner_|.
+  std::unique_ptr<gles2::AbstractTexture> surface_owner_texture_;
+  uint32_t surface_owner_texture_id_;
+
+  // The SurfaceOwner which receives frames.
   std::unique_ptr<SurfaceOwner> surface_owner_;
 
   // Current transform matrix of the surface owner.
@@ -103,10 +115,12 @@
   // Whether a new frame is available that we should update to.
   bool has_pending_frame_;
 
-  CommandBufferStub* owner_stub_;
+  GpuChannel* channel_;
   int32_t route_id_;
   bool has_listener_;
-  uint32_t texture_id_;
+  scoped_refptr<SharedContextState> context_state_;
+  SequenceId sequence_;
+  scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
 
   base::WeakPtrFactory<StreamTexture> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(StreamTexture);
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index b4a2ef3..bbe9e09f 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1151,7 +1151,10 @@
       name: "android-code-coverage"
       mixins: "code-coverage"
       mixins: "linux"
+      mixins: "linux-xenial"
+      mixins: "builderless"
       dimensions: "cores:32"
+      dimensions: "ssd:1"
     }
 
     builders {
@@ -1371,6 +1374,8 @@
       name: "chromeos-amd64-generic-rel-vm-tests"
       mixins: "fyi-ci"
       mixins: "linux"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
@@ -1384,7 +1389,10 @@
       mixins: "code-coverage"
       mixins: "fyi-ci"
       mixins: "linux"
+      mixins: "linux-xenial"
+      mixins: "builderless"
       dimensions: "cores:32"
+      dimensions: "ssd:1"
     }
 
     builders {
@@ -1407,7 +1415,10 @@
       mixins: "code-coverage"
       mixins: "fyi-ci"
       mixins: "linux"
+      mixins: "linux-xenial"
+      mixins: "builderless"
       dimensions: "cores:32"
+      dimensions: "ssd:1"
     }
 
     builders {
@@ -1422,7 +1433,10 @@
       mixins: "code-coverage"
       mixins: "fyi-ci"
       mixins: "linux"
+      mixins: "linux-xenial"
+      mixins: "builderless"
       dimensions: "cores:32"
+      dimensions: "ssd:1"
       recipe {
         properties: "exclude_sources:all_test_files"
       }
@@ -1437,6 +1451,8 @@
     builders {
       name: "Linux ChromiumOS Full"
       mixins: "chromeos-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     # Fuchsia bots.
@@ -1450,6 +1466,8 @@
     builders {
       name: "fuchsia-arm64-cast"
       mixins: "linux-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
@@ -1462,24 +1480,29 @@
     builders {
       name: "fuchsia-x64-cast"
       mixins: "linux-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
       name: "fuchsia-fyi-arm64-rel"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
       name: "fuchsia-fyi-x64-dbg"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     builders {
       name: "fuchsia-fyi-x64-rel"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
 
     # Linux bots.
@@ -2526,6 +2549,7 @@
       dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "TSAN Debug"
@@ -2628,10 +2652,11 @@
     }
     builders {
       name: "android-archive-dbg"
-      dimensions: "os:Ubuntu-14.04"
       # Bump to 32 if needed.
       dimensions: "cores:8"
       mixins: "chromium-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "android-archive-rel"
@@ -2660,8 +2685,9 @@
     }
     builders {
       name: "Linux remote_run Tester"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "WebKit Mac10.13 (retina)"
@@ -2671,9 +2697,10 @@
     }
     builders {
       name: "Afl Upload Linux ASan"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
       recipe {
         name: "chromium_afl"
       }
@@ -2714,9 +2741,10 @@
     }
     builders {
       name: "Site Isolation Android"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "MSAN Release (no origins)"
@@ -2731,8 +2759,9 @@
     }
     builders {
       name: "linux-annotator-rel"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "win-annotator-rel"
@@ -2753,12 +2782,14 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "VR Linux"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "linux-tcmalloc-rel"
@@ -2786,6 +2817,7 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Libfuzzer Upload Linux V8-ARM64 ASan Debug"
@@ -2793,12 +2825,14 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Closure Compilation Linux"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
       recipe {
         name: "closure_compilation"
       }
@@ -2828,6 +2862,7 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Libfuzzer Upload Linux V8-ARM64 ASan"
@@ -2835,6 +2870,7 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Libfuzzer Upload Linux32 ASan Debug"
@@ -2842,6 +2878,7 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Libfuzzer Upload Linux32 ASan"
@@ -2849,6 +2886,7 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Libfuzzer Upload Linux32 V8-ARM ASan Debug"
@@ -2856,6 +2894,7 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Libfuzzer Upload Linux32 V8-ARM ASan"
@@ -2863,6 +2902,7 @@
       mixins: "libfuzzer"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Libfuzzer Upload Chrome OS ASan"
@@ -2907,7 +2947,8 @@
       mixins: "fyi-ci"
       # This is launching & collecting entirely isolated tests.
       # OS shouldn't matter.
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Mojo ChromiumOS"
@@ -2917,9 +2958,10 @@
     }
     builders {
       name: "Linux remote_run Builder"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Win ASan Release Media"
@@ -2946,9 +2988,10 @@
     }
     builders {
       name: "Mojo Linux"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Linux Chromium OS ASan LSan Tests (1)"
@@ -2958,8 +3001,9 @@
     }
     builders {
       name: "Memory Infra Tester"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "android-fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "ios-device-goma-canary-clobber"
@@ -2994,27 +3038,31 @@
     }
     builders {
       name: "Linux Viz"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Linux ARM"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod-ats"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Jumbo Linux x64"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Mojo Android"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
       mixins: "goma-rbe-prod"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     # Goma RBE ToT/Staging/FYI
     builders {
diff --git a/infra/config/lint-luci-milo.py b/infra/config/lint-luci-milo.py
index 5ca1484..7196423 100755
--- a/infra/config/lint-luci-milo.py
+++ b/infra/config/lint-luci-milo.py
@@ -72,8 +72,8 @@
   # Check that every referenced subwaterfall has its own console, unless it's
   # explicitly excluded below.
   excluded_names = [
-      # This is the chrome/chromium.chrome console in src-internal.
-      'chromium.chrome',
+      # This is the chrome console in src-internal.
+      'chrome',
   ]
   all_console_names = [console.id for console in project.consoles]
   referenced_names = set(subwaterfalls.keys())
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index b81138a2..b2e097c 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -236,7 +236,7 @@
     console_ids: "chromium/chromium.mac"
     console_ids: "chromium/chromium.linux"
     console_ids: "chromium/chromium.chromiumos"
-    console_ids: "chrome/chromium.chrome"
+    console_ids: "chrome/chrome"
     console_ids: "chromium/chromium.memory"
     console_ids: "chromium/chromium.gpu"
   }
@@ -632,22 +632,22 @@
   }
   builders {
     name: "buildbucket/luci.chrome.ci/linux-chromeos-google-rel"
-    category: "chromium.chrome"
+    category: "chrome"
     short_name: "cro"
   }
   builders {
     name: "buildbucket/luci.chrome.ci/linux-google-rel"
-    category: "chromium.chrome"
+    category: "chrome"
     short_name: "lnx"
   }
   builders {
     name: "buildbucket/luci.chrome.ci/mac-google-rel"
-    category: "chromium.chrome"
+    category: "chrome"
     short_name: "mac"
   }
   builders {
     name: "buildbucket/luci.chrome.ci/win-google-rel"
-    category: "chromium.chrome"
+    category: "chrome"
     short_name: "win"
   }
   builders {
@@ -2977,6 +2977,56 @@
     category: "week2.5|linux"
     short_name: "gpu"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux ASan"
+    category: "week4|linux"
+    short_name: "asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux ASan Debug"
+    category: "week4|linux"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux MSan"
+    category: "week4|linux"
+    short_name: "msan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux UBSan"
+    category: "week4|linux"
+    short_name: "ubsan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux V8-ARM64 ASan"
+    category: "week4|linux|v8arm"
+    short_name: "asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux V8-ARM64 ASan Debug"
+    category: "week4|linux|v8arm"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux32 ASan"
+    category: "week4|linux32"
+    short_name: "asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux32 ASan Debug"
+    category: "week4|linux32"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux32 V8-ARM ASan"
+    category: "week4|linux32|v8arm"
+    short_name: "asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux32 V8-ARM ASan Debug"
+    category: "week4|linux32|v8arm"
+    short_name: "dbg"
+  }
 }
 
 consoles {
diff --git a/ios/build/bots/scripts/xcode_log_parser.py b/ios/build/bots/scripts/xcode_log_parser.py
new file mode 100644
index 0000000..d87a8262
--- /dev/null
+++ b/ios/build/bots/scripts/xcode_log_parser.py
@@ -0,0 +1,350 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""XCode test log parser."""
+
+import json
+import logging
+import os
+import plistlib
+import re
+import shutil
+import subprocess
+
+import test_runner
+
+
+LOGGER = logging.getLogger(__name__)
+
+
+def format_test_case(test_case):
+  """Format test case from `-[TestClass TestMethod]` to `TestClass_TestMethod`.
+
+  Args:
+    test_case: (str) Test case id in format `-[TestClass TestMethod]` or
+               `[TestClass/TestMethod]`
+
+  Returns:
+    Test case id in format TestClass_TestMethod.
+  """
+  return test_case.replace('[', '').replace(']', '').replace(
+      '-', '').replace(' ', '/')
+
+
+def copy_screenshots_for_failed_test(failure_message, test_case_folder):
+  screenshot_regex = re.compile(r'Screenshots:\s({(\n.*)+?\n})')
+  screenshots = screenshot_regex.search(failure_message)
+  if not os.path.exists(test_case_folder):
+    os.makedirs(test_case_folder)
+  if screenshots:
+    screenshots_files = screenshots.group(1).strip()
+    # For some failures xcodebuild attaches screenshots in the `Attachments`
+    # folder and in plist no paths to them, only references e.g.
+    # "Screenshot At Failure" : <UIImage: 0x6000032ab410>, {768, 1024}
+    if 'UIImage:' in screenshots_files:
+      return
+    LOGGER.info('Screenshots for failure "%s" in "%s"' % (
+        os.path.basename(test_case_folder), test_case_folder))
+    d = json.loads(screenshots_files)
+    for f in d.values():
+      if not os.path.exists(f):
+        LOGGER.warning('File %s does not exist!' % f)
+        continue
+      screenshot = os.path.join(test_case_folder, os.path.basename(f))
+      shutil.copyfile(f, screenshot)
+
+
+class Xcode11LogParser(object):
+  """Xcode 11 log parser. Parse Xcode result types v3."""
+
+  @staticmethod
+  def _xcresulttool_get(xcresult_path, ref_id=None):
+    """Runs `xcresulttool get` command and returns JSON output.
+
+    Xcresult folder contains test result in Xcode Result Types v. 3.19.
+    Documentation of xcresulttool usage is in
+    https://help.apple.com/xcode/mac/current/#/devc38fc7392?sub=dev0fe9c3ea3
+
+    Args:
+      xcresult_path: A full path to xcresult folder that must have Info.plist.
+      ref_id: A reference id used in a command and can be used to get test data.
+          If id is from ['timelineRef', 'logRef', 'testsRef', 'diagnosticsRef']
+          method will run xcresulttool 2 times:
+          1. to get specific id value running command without id parameter.
+            xcresulttool get --path %xcresul%
+          2. to get data based on id
+            xcresulttool get --path %xcresul% --id %id%
+
+    Returns:
+      An output of a command in JSON format.
+    """
+    xcode_info = test_runner.get_current_xcode_info()
+    folder = os.path.join(xcode_info['path'], 'usr', 'bin')
+    # By default xcresulttool is %Xcode%/usr/bin,
+    # that is not in directories from $PATH
+    # Need to check whether %Xcode%/usr/bin is in a $PATH
+    # and then call xcresulttool
+    if folder not in os.environ['PATH']:
+      os.environ['PATH'] += ':%s' % folder
+    reference_types = ['timelineRef', 'logRef', 'testsRef', 'diagnosticsRef']
+    if ref_id in reference_types:
+      data = json.loads(Xcode11LogParser._xcresulttool_get(xcresult_path))
+      # Redefine ref_id to get only the reference data
+      ref_id = data['actions']['_values'][0]['actionResult'][
+          ref_id]['id']['_value']
+    # If no ref_id then xcresulttool will use default(root) id.
+    id_params = ['--id', ref_id] if ref_id else []
+    xcresult_command = ['xcresulttool', 'get', '--format', 'json',
+                        '--path', xcresult_path] + id_params
+    return subprocess.check_output(xcresult_command).strip()
+
+  @staticmethod
+  def _list_of_failed_tests(actions_invocation_record):
+    """Gets failed tests from xcresult root data.
+
+    ActionsInvocationRecord is an object that contains properties:
+      + metadataRef: id of the record that can be get as
+        `xcresult get --path xcresult --id metadataRef`
+      + metrics: number of run and failed tests.
+      + issues: contains TestFailureIssueSummary in case of failure otherwise
+        it contains just declaration of `issues` node.
+      + actions: a list of ActionRecord.
+
+    Args:
+      actions_invocation_record: An output of `xcresult get --path xcresult`.
+
+    Returns:
+      Failed tests as a map:
+      {
+          'failed_test': ['StackTrace']
+      }
+    """
+    failed = {}
+    if 'testFailureSummaries' not in actions_invocation_record['issues']:
+      return failed
+    for failure_summary in actions_invocation_record['issues'][
+        'testFailureSummaries']['_values']:
+      error_line = failure_summary['documentLocationInCreatingWorkspace'][
+          'url']['_value']
+      fail_message = [error_line] + failure_summary['message'][
+          '_value'].splitlines()
+      test_case_id = format_test_case(failure_summary['testCaseName']['_value'])
+      failed[test_case_id] = fail_message
+    return failed
+
+  @staticmethod
+  def _list_of_passed_tests(xcresult):
+    """Gets list of passed tests from xcresult.
+
+    Args:
+      xcresult: (str) A path to xcresult.
+
+    Returns:
+      A list of passed tests.
+    """
+    root = json.loads(Xcode11LogParser._xcresulttool_get(xcresult, 'testsRef'))
+    passed_tests = []
+    for summary in root['summaries']['_values'][0][
+        'testableSummaries']['_values']:
+      if not summary['tests']:
+        continue
+      for test_suite in summary['tests']['_values'][0]['subtests'][
+          '_values'][0]['subtests']['_values']:
+        if 'subtests' not in test_suite:
+          # Sometimes(if crash occurs) `subtests` node does not upload.
+          # It happens only for failed tests that and a list of failures
+          # can be parsed from root.
+          continue
+        for test in test_suite['subtests']['_values']:
+          if test['testStatus']['_value'] == 'Success':
+            passed_tests.append(test['identifier']['_value'])
+    return passed_tests
+
+  @staticmethod
+  def collect_test_results(xcresult):
+    """Gets test result data from xcresult.
+
+    Args:
+      xcresult: (str) A path to xcresult.
+
+    Returns:
+      Test result as a map:
+        {
+          'passed': [passed_tests],
+          'failed': {
+              'failed_test': ['StackTrace']
+          }
+        }
+    """
+    LOGGER.info('Reading %s' % xcresult)
+    test_results = {
+        'passed': [],
+        'failed': {}
+    }
+    if not os.path.exists(xcresult):
+      test_results['failed']['TESTS_DID_NOT_START'] = [
+          '%s with test results does not exist.' % xcresult]
+      return test_results
+
+    plist_path = os.path.join(xcresult + '.xcresult', 'Info.plist')
+    if not os.path.exists(plist_path):
+      test_results['failed']['BUILD_INTERRUPTED'] = [
+          '%s with test results does not exist.' % plist_path]
+      return test_results
+
+    root = json.loads(Xcode11LogParser._xcresulttool_get(xcresult))
+    metrics = root['metrics']
+    # In case of test crash both numbers of run and failed tests are equal to 0.
+    if (metrics.get('testsCount', {}).get('_value', 0) == 0 and
+        metrics.get('testsFailedCount', {}).get('_value', 0) == 0):
+      test_results['failed']['TESTS_DID_NOT_START'] = ['0 tests executed!']
+    else:
+      test_results['failed'] = Xcode11LogParser._list_of_failed_tests(root)
+      test_results['passed'] = Xcode11LogParser._list_of_passed_tests(xcresult)
+    return test_results
+
+  @staticmethod
+  def copy_screenshots(output_folder):
+    """Copy screenshots of failed tests to output folder.
+
+    Args:
+      output_folder: (str) A full path to folder where
+    """
+    plist_path = os.path.join(output_folder + '.xcresult', 'Info.plist')
+    if not os.path.exists(plist_path):
+      LOGGER.info('%s does not exist.' % plist_path)
+      return
+
+    root = json.loads(Xcode11LogParser._xcresulttool_get(output_folder))
+    if 'testFailureSummaries' not in root['issues']:
+      LOGGER.info('No failures in %s' % output_folder)
+      return
+
+    for failure_summary in root['issues']['testFailureSummaries']['_values']:
+      test_case = failure_summary['testCaseName']['_value']
+      test_case_folder = os.path.join(output_folder, 'failures',
+                                      format_test_case(test_case))
+      copy_screenshots_for_failed_test(failure_summary['message']['_value'],
+                                       test_case_folder)
+
+
+class XcodeLogParser(object):
+  """Xcode log parser. Parses logs for Xcode until version 11."""
+
+  @staticmethod
+  def _test_status_summary(summary_plist):
+    """Gets status summary from TestSummaries.plist.
+
+    Args:
+      summary_plist: (str) A path to plist-file.
+
+    Returns:
+      A dict that contains all passed and failed tests from the egtests.app.
+      e.g.
+      {
+          'passed': [passed_tests],
+          'failed': {
+              'failed_test': ['StackTrace']
+          }
+      }
+    """
+    root_summary = plistlib.readPlist(summary_plist)
+    status_summary = {
+        'passed': [],
+        'failed': {}
+    }
+    for summary in root_summary['TestableSummaries']:
+      failed_egtests = {}  # Contains test identifier and message
+      passed_egtests = []
+      if not summary['Tests']:
+        continue
+      for test_suite in summary['Tests'][0]['Subtests'][0]['Subtests']:
+        for test in test_suite['Subtests']:
+          if test['TestStatus'] == 'Success':
+            passed_egtests.append(test['TestIdentifier'])
+          else:
+            message = []
+            for failure_summary in test['FailureSummaries']:
+              failure_message = failure_summary['FileName']
+              if failure_summary['LineNumber']:
+                failure_message = '%s: line %s' % (
+                    failure_message, failure_summary['LineNumber'])
+              message.append(failure_message)
+              message.extend(failure_summary['Message'].splitlines())
+            failed_egtests[test['TestIdentifier']] = message
+      if failed_egtests:
+        status_summary['failed'] = failed_egtests
+      if passed_egtests:
+        status_summary['passed'] = passed_egtests
+    return status_summary
+
+  @staticmethod
+  def collect_test_results(output_folder):
+    """Gets test result data from Info.plist.
+
+    Args:
+      output_folder: (str) A path to output folder.
+    Returns:
+      Test result as a map:
+        {
+          'passed': [passed_tests],
+          'failed': {
+              'failed_test': ['StackTrace']
+          }
+      }
+    """
+    test_results = {
+        'passed': [],
+        'failed': {}
+    }
+    plist_path = os.path.join(output_folder, 'Info.plist')
+    if not os.path.exists(plist_path):
+      test_results['failed']['BUILD_INTERRUPTED'] = [
+          '%s with test results does not exist.' % plist_path]
+      return test_results
+
+    root = plistlib.readPlist(plist_path)
+
+    for action in root['Actions']:
+      action_result = action['ActionResult']
+      if ((root['TestsCount'] == 0 and
+           root['TestsFailedCount'] == 0)
+          or 'TestSummaryPath' not in action_result):
+        test_results['failed']['TESTS_DID_NOT_START'] = []
+        if ('ErrorSummaries' in action_result
+            and action_result['ErrorSummaries']):
+          test_results['failed']['TESTS_DID_NOT_START'].append('\n'.join(
+              error_summary['Message']
+              for error_summary in action_result['ErrorSummaries']))
+      else:
+        summary_plist = os.path.join(os.path.dirname(plist_path),
+                                     action_result['TestSummaryPath'])
+        summary = XcodeLogParser._test_status_summary(summary_plist)
+        test_results['failed'] = summary['failed']
+        test_results['passed'] = summary['passed']
+    return test_results
+
+  @staticmethod
+  def copy_screenshots(output_folder):
+    """Copy screenshots of failed tests to output folder.
+
+    Args:
+      output_folder: (str) A full path to folder where
+    """
+    info_plist_path = os.path.join(output_folder, 'Info.plist')
+    if not os.path.exists(info_plist_path):
+      LOGGER.info('%s does not exist.' % info_plist_path)
+      return
+
+    plist = plistlib.readPlist(info_plist_path)
+    if 'TestFailureSummaries' not in plist or not plist['TestFailureSummaries']:
+      LOGGER.info('No failures in %s' % info_plist_path)
+      return
+
+    for failure_summary in plist['TestFailureSummaries']:
+      # Screenshot folder has format 'TestClass_test_method'
+      test_case_id = format_test_case(failure_summary['TestCase'])
+      test_case_folder = os.path.join(output_folder, 'failures', test_case_id)
+      copy_screenshots_for_failed_test(failure_summary['Message'],
+                                       test_case_folder)
diff --git a/ios/build/bots/scripts/xcode_log_parser_test.py b/ios/build/bots/scripts/xcode_log_parser_test.py
new file mode 100644
index 0000000..33473ce0
--- /dev/null
+++ b/ios/build/bots/scripts/xcode_log_parser_test.py
@@ -0,0 +1,252 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unittests for xcode_log_parser.py."""
+
+import json
+import mock
+import os
+
+import test_runner
+import test_runner_test
+import xcode_log_parser
+
+
+_XTEST_RESULT = '/tmp/temp_file.xcresult'
+XCODE11_DICT = {
+    'path': '/Users/user1/Xcode.app',
+    'version': '11.0',
+    'build': '11M336w',
+}
+REF_ID = """
+  {
+    "actions": {
+      "_values": [{
+        "actionResult": {
+          "testsRef": {
+            "id": {
+              "_value": "REF_ID"
+            }
+          }
+        }
+      }]
+    }
+  }"""
+
+ACTIONS_RECORD_FAILED_TEST = """
+  {
+    "issues": {
+      "testFailureSummaries": {
+        "_values": [{
+          "documentLocationInCreatingWorkspace": {
+            "url": {
+              "_value": "file://<unknown>#CharacterRangeLen=0"
+            }
+          },
+          "message": {
+            "_value": "Fail. Screenshots: {\\n\\"Failure\\": \\"path.png\\"\\n}"
+          },
+          "testCaseName": {
+            "_value": "-[WebUITestCase testBackForwardFromWebURL]"
+          }
+        }]
+      }
+    }
+  }"""
+
+PASSED_TESTS = """
+  {
+    "summaries": {
+      "_values": [{
+        "testableSummaries": {
+          "_type": {
+            "_name": "Array"
+          },
+          "_values": [{
+            "tests": {
+              "_type": {
+                "_name": "Array"
+              },
+              "_values": [{
+                "subtests": {
+                  "_values": [{
+                    "subtests": {
+                      "_values": [{
+                        "subtests": {
+                          "_values": [{
+                            "testStatus": {
+                              "_value": "Success"
+                            },
+                            "identifier": {
+                              "_value": "TestCase1/testMethod1"
+                            },
+                            "name": {
+                              "_value": "testMethod1"
+                            }
+                          },
+                          {
+                            "testStatus": {
+                              "_value": "Failure"
+                            },
+                            "identifier": {
+                              "_value": "TestCase1/testFailed1"
+                            },
+                            "name": {
+                              "_value": "testFailed1"
+                            }
+                          },
+                          {
+                            "testStatus": {
+                              "_value": "Success"
+                            },
+                            "identifier": {
+                              "_value": "TestCase2/testMethod1"
+                            },
+                            "name": {
+                              "_value": "testMethod1"
+                            }
+                          }]
+                        }
+                      }]
+                    }
+                  }]
+                }
+              }]
+            }
+          }]
+        }
+      }]
+    }
+  }
+"""
+
+
+class XCode11LogParserTest(test_runner_test.TestCase):
+  """Test case to test Xcode11LogParser."""
+
+  def setUp(self):
+    super(XCode11LogParserTest, self).setUp()
+    self.mock(test_runner, 'get_current_xcode_info', lambda: XCODE11_DICT)
+
+  @mock.patch('subprocess.check_output', autospec=True)
+  def testXcresulttoolGetRoot(self, mock_process):
+    mock_process.return_value = '%JSON%'
+    xcode_log_parser.Xcode11LogParser()._xcresulttool_get('xcresult_path')
+    self.assertTrue(
+        os.path.join(XCODE11_DICT['path'], 'usr', 'bin') in os.environ['PATH'])
+    self.assertEqual(
+        ['xcresulttool', 'get', '--format', 'json', '--path', 'xcresult_path'],
+        mock_process.mock_calls[0][1][0])
+
+  @mock.patch('subprocess.check_output', autospec=True)
+  def testXcresulttoolGetRef(self, mock_process):
+    mock_process.side_effect = [REF_ID, 'JSON']
+    xcode_log_parser.Xcode11LogParser()._xcresulttool_get('xcresult_path',
+                                                          'testsRef')
+    self.assertEqual(
+        ['xcresulttool', 'get', '--format', 'json', '--path', 'xcresult_path'],
+        mock_process.mock_calls[0][1][0])
+    self.assertEqual([
+        'xcresulttool', 'get', '--format', 'json', '--path', 'xcresult_path',
+        '--id', 'REF_ID'], mock_process.mock_calls[1][1][0])
+
+  def testXcresulttoolListFailedTests(self):
+    failure_message = [
+        'file://<unknown>#CharacterRangeLen=0'
+    ] + 'Fail. Screenshots: {\n\"Failure\": \"path.png\"\n}'.splitlines()
+    expected = {
+        'WebUITestCase/testBackForwardFromWebURL': failure_message
+    }
+    self.assertEqual(expected,
+                     xcode_log_parser.Xcode11LogParser()._list_of_failed_tests(
+                         json.loads(ACTIONS_RECORD_FAILED_TEST)))
+
+  @mock.patch('xcode_log_parser.Xcode11LogParser._xcresulttool_get')
+  def testXcresulttoolListPassedTests(self, mock_xcresult):
+    mock_xcresult.return_value = PASSED_TESTS
+    expected = ['TestCase1/testMethod1', 'TestCase2/testMethod1']
+    self.assertEqual(expected,
+                     xcode_log_parser.Xcode11LogParser()._list_of_passed_tests(
+                         _XTEST_RESULT))
+
+  @mock.patch('os.path.exists', autospec=True)
+  @mock.patch('xcode_log_parser.Xcode11LogParser._xcresulttool_get')
+  @mock.patch('xcode_log_parser.Xcode11LogParser._list_of_failed_tests')
+  @mock.patch('xcode_log_parser.Xcode11LogParser._list_of_passed_tests')
+  def testCollectTestTesults(self, mock_get_passed_tests, mock_get_failed_tests,
+                             mock_root, mock_exist_file):
+    metrics_json = """
+    {
+      "metrics": {
+        "testsCount": {
+          "_value": "7"
+        },
+        "testsFailedCount": {
+          "_value": "14"
+        }
+      }
+    }"""
+    expected_test_results = {
+        'passed': [
+            'TestCase1/testMethod1', 'TestCase2/testMethod1'],
+        'failed': {
+            'WebUITestCase/testBackForwardFromWebURL': [
+                'file://<unknown>#CharacterRangeLen=0',
+                'Test crashed in <external symbol>'
+            ]
+        }
+    }
+    mock_get_passed_tests.return_value = expected_test_results['passed']
+    mock_get_failed_tests.return_value = expected_test_results['failed']
+    mock_root.return_value = metrics_json
+    mock_exist_file.return_value = True
+    self.assertEqual(expected_test_results,
+                     xcode_log_parser.Xcode11LogParser().collect_test_results(
+                         _XTEST_RESULT))
+
+  @mock.patch('os.path.exists', autospec=True)
+  @mock.patch('xcode_log_parser.Xcode11LogParser._xcresulttool_get')
+  def testCollectTestsRanZeroTests(self, mock_root, mock_exist_file):
+    metrics_json = '{"metrics": {}}'
+    expected_test_results = {
+        'passed': [],
+        'failed': {'TESTS_DID_NOT_START': ['0 tests executed!']}}
+    mock_root.return_value = metrics_json
+    mock_exist_file.return_value = True
+    self.assertEqual(expected_test_results,
+                     xcode_log_parser.Xcode11LogParser().collect_test_results(
+                         _XTEST_RESULT))
+
+  @mock.patch('os.path.exists', autospec=True)
+  def testCollectTestsDidNotRun(self, mock_exist_file):
+    mock_exist_file.return_value = False
+    expected_test_results = {
+        'passed': [],
+        'failed': {'TESTS_DID_NOT_START': [
+            '%s with test results does not exist.' % _XTEST_RESULT]}}
+    self.assertEqual(expected_test_results,
+                     xcode_log_parser.Xcode11LogParser().collect_test_results(
+                         _XTEST_RESULT))
+
+  @mock.patch('os.path.exists', autospec=True)
+  def testCollectTestsInterruptedRun(self, mock_exist_file):
+    mock_exist_file.side_effect = [True, False]
+    expected_test_results = {
+        'passed': [],
+        'failed': {'BUILD_INTERRUPTED': [
+            '%s with test results does not exist.' % os.path.join(
+                _XTEST_RESULT + '.xcresult', 'Info.plist')]}}
+    self.assertEqual(expected_test_results,
+                     xcode_log_parser.Xcode11LogParser().collect_test_results(
+                         _XTEST_RESULT))
+
+  @mock.patch('os.path.exists', autospec=True)
+  @mock.patch('xcode_log_parser.Xcode11LogParser._xcresulttool_get')
+  @mock.patch('shutil.copyfile', autospec=True)
+  def testCopyScreenshots(self, mock_copy, mock_xcresulttool_get,
+                          mock_exist_file):
+    mock_exist_file.return_value = True
+    mock_xcresulttool_get.return_value = ACTIONS_RECORD_FAILED_TEST
+    xcode_log_parser.Xcode11LogParser().copy_screenshots(_XTEST_RESULT)
+    self.assertEqual(1, mock_copy.call_count)
diff --git a/ios/build/bots/scripts/xcodebuild_runner.py b/ios/build/bots/scripts/xcodebuild_runner.py
index d805a74..b39f19a4 100644
--- a/ios/build/bots/scripts/xcodebuild_runner.py
+++ b/ios/build/bots/scripts/xcodebuild_runner.py
@@ -7,18 +7,17 @@
 import sys
 
 import collections
-import json
+import distutils.version
 import logging
 import multiprocessing
 import os
 import plistlib
-import re
-import shutil
 import subprocess
 import threading
 import time
 
 import test_runner
+import xcode_log_parser
 
 LOGGER = logging.getLogger(__name__)
 
@@ -51,89 +50,6 @@
     LOGGER.info('Error while killing a process: %s' % ex)
 
 
-def test_status_summary(summary_plist):
-  """Gets status summary from TestSummaries.plist.
-
-  Args:
-    summary_plist: (str) A path to plist-file.
-
-  Returns:
-    A dict that contains all passed and failed tests from the egtests.app.
-    e.g.
-    {
-        'passed': [passed_tests],
-        'failed': {
-            'failed_test': ['StackTrace']
-        }
-    }
-  """
-  root_summary = plistlib.readPlist(summary_plist)
-  status_summary = {
-      'passed': [],
-      'failed': {}
-  }
-  for summary in root_summary['TestableSummaries']:
-    failed_egtests = {}  # Contains test identifier and message
-    passed_egtests = []
-    if not summary['Tests']:
-      continue
-    for test_suite in summary['Tests'][0]['Subtests'][0]['Subtests']:
-      for test in test_suite['Subtests']:
-        if test['TestStatus'] == 'Success':
-          passed_egtests.append(test['TestIdentifier'])
-        else:
-          message = []
-          for failure_summary in test['FailureSummaries']:
-            message.append('%s: line %s' % (failure_summary['FileName'],
-                                            failure_summary['LineNumber']))
-            message.extend(failure_summary['Message'].splitlines())
-          failed_egtests[test['TestIdentifier']] = message
-    if failed_egtests:
-      status_summary['failed'] = failed_egtests
-    if passed_egtests:
-      status_summary['passed'] = passed_egtests
-  return status_summary
-
-
-def collect_test_results(plist_path):
-  """Gets test result data from Info.plist.
-
-  Args:
-    plist_path: (str) A path to plist-file.
-  Returns:
-    Test result as a map:
-      {
-        'passed': [passed_tests],
-        'failed': {
-            'failed_test': ['StackTrace']
-        }
-    }
-  """
-  test_results = {
-      'passed': [],
-      'failed': {}
-  }
-  root = plistlib.readPlist(plist_path)
-
-  for action in root['Actions']:
-    action_result = action['ActionResult']
-    if ((root['TestsCount'] == 0 and
-         root['TestsFailedCount'] == 0)
-        or 'TestSummaryPath' not in action_result):
-      test_results['failed']['TESTS_DID_NOT_START'] = []
-      if 'ErrorSummaries' in action_result and action_result['ErrorSummaries']:
-        test_results['failed']['TESTS_DID_NOT_START'].append('\n'.join(
-            error_summary['Message']
-            for error_summary in action_result['ErrorSummaries']))
-    else:
-      summary_plist = os.path.join(os.path.dirname(plist_path),
-                                   action_result['TestSummaryPath'])
-      summary = test_status_summary(summary_plist)
-      test_results['failed'] = summary['failed']
-      test_results['passed'] = summary['passed']
-  return test_results
-
-
 class EgtestsApp(object):
   """Egtests to run.
 
@@ -285,6 +201,11 @@
     self.logs = collections.OrderedDict()
     self.test_results = collections.OrderedDict()
     self.env = env
+    if distutils.version.LooseVersion('11.0') <= distutils.version.LooseVersion(
+        test_runner.get_current_xcode_info()['version']):
+      self._log_parser = xcode_log_parser.Xcode11LogParser()
+    else:
+      self._log_parser = xcode_log_parser.XcodeLogParser()
 
   def _make_cmd_list_for_failed_tests(self, failed_results, out_dir,
                                       test_args=None, env_vars=None):
@@ -314,39 +235,6 @@
     # Regenerates xctest run and gets a command.
     return self.command(eg_app, out_dir, self.destination, shards=1)
 
-  def _copy_screenshots(self, info_plist_path, output_folder):
-    """Copy screenshots of failed tests to output folder.
-
-    Args:
-      info_plist_path: (str) A full path to Info.plist
-      output_folder: (str) A full path to folder where
-    """
-    plist = plistlib.readPlist(info_plist_path)
-    if 'TestFailureSummaries' not in plist or not plist['TestFailureSummaries']:
-      LOGGER.info('No failures in %s' % info_plist_path)
-      return
-
-    screenshot_regex = re.compile(r'Screenshots:\s\{(\n.*)+?\n}')
-    for failure_summary in plist['TestFailureSummaries']:
-      screenshots = screenshot_regex.search(failure_summary['Message'])
-      test_case_folder = os.path.join(
-          output_folder,
-          'failures',
-          failure_summary['TestCase'].replace('[', '').replace(']', '').replace(
-              ' ', '_').replace('-', ''))
-      if not os.path.exists(test_case_folder):
-        os.makedirs(test_case_folder)
-      if screenshots:
-        LOGGER.info('Screenshots for failure "%s" in "%s"' % (
-            failure_summary['TestCase'], test_case_folder))
-        d = json.loads(screenshots.group().replace('Screenshots:', '').strip())
-        for f in d.values():
-          if not os.path.exists(f):
-            LOGGER.warning('File %s does not exist!' % f)
-            continue
-          screenshot = os.path.join(test_case_folder, os.path.basename(f))
-          shutil.copyfile(f, screenshot)
-
   def summary_log(self):
     """Calculates test summary - how many passed, failed and error tests.
 
@@ -420,8 +308,9 @@
       # Create a command for the 1st run or if tests did not start,
       # re-run the same command but with different output folder.
       # (http://crbug.com/916620) If tests did not start, repeat the command.
-      if (not self.test_results['attempts'] or 'TESTS_DID_NOT_START'
-          in self.test_results['attempts'][-1]['failed']):
+      if (not self.test_results['attempts'] or
+          {'TESTS_DID_NOT_START', 'BUILD_INTERRUPTED'}.intersection(
+              self.test_results['attempts'][-1]['failed'].keys())):
         cmd_list = self.command(self.egtests_app,
                                 outdir_attempt,
                                 self.destination,
@@ -439,12 +328,11 @@
           attempt, ' '.join(cmd_list)))
       self.launch_attempt(cmd_list, outdir_attempt)
       self.test_results['attempts'].append(
-          collect_test_results(os.path.join(outdir_attempt, 'Info.plist')))
+          self._log_parser.collect_test_results(outdir_attempt))
       if self.retries == attempt or not self.test_results[
           'attempts'][-1]['failed']:
         break
-      self._copy_screenshots(os.path.join(outdir_attempt, 'Info.plist'),
-                             outdir_attempt)
+      self._log_parser.copy_screenshots(outdir_attempt)
 
     self.test_results['end_run'] = int(time.time())
     self.summary_log()
diff --git a/ios/build/bots/scripts/xcodebuild_runner_test.py b/ios/build/bots/scripts/xcodebuild_runner_test.py
index 4fe768d2..2962183f 100644
--- a/ios/build/bots/scripts/xcodebuild_runner_test.py
+++ b/ios/build/bots/scripts/xcodebuild_runner_test.py
@@ -12,6 +12,7 @@
 import tempfile
 import test_runner
 import test_runner_test
+import xcode_log_parser
 import xcodebuild_runner
 
 
@@ -29,9 +30,8 @@
   def setUp(self):
     super(XCodebuildRunnerTest, self).setUp()
     self.mock(os.path, 'exists', lambda _: True)
-    self.mock(xcodebuild_runner.LaunchCommand, '_copy_screenshots',
-              lambda _1, _2, _3: None)
-    self.mock(xcodebuild_runner.LaunchCommand, 'summary_log', lambda _: None)
+    self.mock(xcode_log_parser.XcodeLogParser, 'copy_screenshots',
+              lambda _1, _2: None)
     self.tmpdir = tempfile.mkdtemp()
 
   def tearDown(self):
@@ -121,7 +121,7 @@
 
   @mock.patch('os.listdir', autospec=True)
   def testEgtests_not_found_xctest(self, mock_listdir):
-    mock_listdir.return_value = ['some_egtests.xctest']
+    mock_listdir.return_value = ['random_file']
     egtest = xcodebuild_runner.EgtestsApp(_EGTESTS_APP_PATH)
     with self.assertRaises(test_runner.XCTestPlugInNotFoundError):
       egtest._xctest_path()
@@ -180,10 +180,11 @@
 
   @mock.patch('plistlib.writePlist', autospec=True)
   @mock.patch('os.path.join', autospec=True)
-  def testFill_xctest_run(self, mock_path_join, _):
-    self._mocks[xcodebuild_runner.LaunchCommand].pop('fill_xctest_run', None)
+  @mock.patch('test_runner.get_current_xcode_info', autospec=True)
+  def testFill_xctest_run(self, xcode_version, mock_path_join, _):
     mock_path_join.return_value = _XTEST_RUN
     mock_egtest = mock.MagicMock(spec=xcodebuild_runner.EgtestsApp)
+    xcode_version.return_value = {'version': '10.2.1'}
     launch_command = xcodebuild_runner.LaunchCommand(
         mock_egtest, _DESTINATION, shards=1, retries=1, out_dir=_OUT_DIR)
     self.assertEqual(_XTEST_RUN, launch_command.fill_xctest_run(mock_egtest))
@@ -202,6 +203,7 @@
     ]
     egtest_app = 'module_1_egtests.app'
     egtest_app_path = '%s/%s' % (_ROOT_FOLDER_PATH, egtest_app)
+    host_app_path = '%s/%s' % (_ROOT_FOLDER_PATH, egtest_app)
     failed_tests = {
         egtest_app: [
             'TestCase1_1/TestMethod1',
@@ -214,6 +216,8 @@
     mock_egtest = mock.MagicMock(spec=xcodebuild_runner.EgtestsApp)
     type(mock_egtest).egtests_path = mock.PropertyMock(
         return_value=egtest_app_path)
+    type(mock_egtest).host_app_path = mock.PropertyMock(
+        return_value=host_app_path)
     cmd = xcodebuild_runner.LaunchCommand(
         egtests_app=mock_egtest,
         destination=_DESTINATION,
@@ -228,9 +232,17 @@
                           fill_xctest_run_mock.mock_calls[0][1][1].__dict__)
 
   @mock.patch('os.listdir', autospec=True)
-  def testLaunchCommand_restartFailed1stAttempt(self, mock_listdir):
+  @mock.patch('test_runner.get_current_xcode_info', autospec=True)
+  @mock.patch('xcode_log_parser.XcodeLogParser.collect_test_results')
+  def testLaunchCommand_restartFailed1stAttempt(self, mock_collect_results,
+                                                xcode_version, mock_listdir):
     mock_listdir.return_value = ['any_egtests.xctest']
     egtests = xcodebuild_runner.EgtestsApp(_EGTESTS_APP_PATH)
+    xcode_version.return_value = {'version': '10.2.1'}
+    mock_collect_results.side_effect = [
+        {'failed': {'TESTS_DID_NOT_START': ['not started']}},
+        {'failed': {}, 'passed': ['passedTest1']}
+    ]
     launch_command = xcodebuild_runner.LaunchCommand(egtests,
                                                      _DESTINATION,
                                                      shards=1,
diff --git a/ios/chrome/app/startup/setup_debugging.mm b/ios/chrome/app/startup/setup_debugging.mm
index ffc02208..3c2fba1 100644
--- a/ios/chrome/app/startup/setup_debugging.mm
+++ b/ios/chrome/app/startup/setup_debugging.mm
@@ -18,9 +18,34 @@
 
 #if !defined(NDEBUG)
 
+// Swizzles [UIColor colorNamed:] to trigger a DCHECK if an invalid color is
+// attempted to be loaded.
+void SwizzleUIColorColorNamed() {
+  // The original implementation of [UIColor colorNamed:].
+  // Called by the new implementation.
+  static IMP originalImp;
+  IMP* originalImpPtr = &originalImp;
+
+  id swizzleBlock = ^(id self, NSString* colorName) {
+    // Call the original [UIColor colorNamed:] method.
+    UIColor* (*imp)(id, SEL, id) =
+        (UIColor * (*)(id, SEL, id)) * originalImpPtr;
+    Class aClass = objc_getClass("UIColor");
+    UIColor* color = imp(aClass, @selector(colorNamed:), colorName);
+    DCHECK(color) << "Missing color: " << base::SysNSStringToUTF8(colorName);
+    return color;
+  };
+
+  Method method = class_getClassMethod([UIColor class], @selector(colorNamed:));
+  DCHECK(method);
+
+  IMP blockImp = imp_implementationWithBlock(swizzleBlock);
+  originalImp = method_setImplementation(method, blockImp);
+}
+
 // Swizzles [UIImage imageNamed:] to trigger a DCHECK if an invalid image is
 // attempted to be loaded.
-void swizzleUIImageImageNamed() {
+void SwizzleUIImageImageNamed() {
   // Retained by the swizzle block.
   NSMutableSet* whiteList = [NSMutableSet set];
 
@@ -61,7 +86,7 @@
   originalImp = method_setImplementation(method, blockImp);
 }
 
-#endif  // !defined(NDEBUG) && TARGET_IPHONE_SIMULATOR
+#endif  // !defined(NDEBUG)
 
 }  // namespace
 
@@ -75,8 +100,9 @@
 #endif
 
 #if !defined(NDEBUG)
-  // Enable the detection of missing image assets.
-  swizzleUIImageImageNamed();
+  // Enable the detection of missing assets.
+  SwizzleUIColorColorNamed();
+  SwizzleUIImageImageNamed();
 #endif
 }
 
diff --git a/ios/chrome/app/strings/resources/ios_strings_nl.xtb b/ios/chrome/app/strings/resources/ios_strings_nl.xtb
index c51ebcf..83213d9 100644
--- a/ios/chrome/app/strings/resources/ios_strings_nl.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_nl.xtb
@@ -527,7 +527,7 @@
 <translation id="8904976895050290827">Chrome-synchronisatie</translation>
 <translation id="895541991026785598">Een probleem melden</translation>
 <translation id="8971089644512329999">OK, begrepen</translation>
-<translation id="8976382372951310360">Help</translation>
+<translation id="8976382372951310360">Hulp</translation>
 <translation id="8981454092730389528">Google-activiteitsopties</translation>
 <translation id="8985320356172329008">Ingelogd bij Google als</translation>
 <translation id="9016406938567631235">Verzenden…</translation>
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index d606b43..4bd9d92 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -25,8 +25,6 @@
 source_set("browser") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "about_flags.h",
-    "about_flags.mm",
     "app_startup_parameters.h",
     "app_startup_parameters.mm",
     "application_context.cc",
@@ -70,10 +68,6 @@
     "procedural_block_types.h",
     "system_flags.h",
     "system_flags.mm",
-    "tab_parenting_global_observer.cc",
-    "tab_parenting_global_observer.h",
-    "web_data_service_factory.cc",
-    "web_data_service_factory.h",
   ]
 
   deps = [
@@ -205,6 +199,7 @@
     "//ios/chrome/browser/browser_state:browser_state_impl",
     "//ios/chrome/browser/component_updater",
     "//ios/chrome/browser/first_run",
+    "//ios/chrome/browser/flags",
     "//ios/chrome/browser/gcm",
     "//ios/chrome/browser/history",
     "//ios/chrome/browser/metrics",
diff --git a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
index b327017..d191af2d 100644
--- a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
+++ b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
@@ -15,9 +15,9 @@
 #include "ios/chrome/browser/autofill/autofill_profile_validator_factory.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 
 namespace autofill {
 
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index ae4e2cd..ad8df79 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -32,6 +32,7 @@
 #import "ios/chrome/browser/autofill/form_suggestion_controller.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h"
@@ -39,7 +40,6 @@
 #include "ios/chrome/browser/ui/settings/personal_data_manager_finished_profile_tasks_waiter.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
 #include "ios/web/public/js_messaging/web_frame.h"
 #include "ios/web/public/js_messaging/web_frame_util.h"
diff --git a/ios/chrome/browser/autofill/personal_data_manager_factory.cc b/ios/chrome/browser/autofill/personal_data_manager_factory.cc
index 87ec039..41ad023 100644
--- a/ios/chrome/browser/autofill/personal_data_manager_factory.cc
+++ b/ios/chrome/browser/autofill/personal_data_manager_factory.cc
@@ -15,9 +15,9 @@
 #include "ios/chrome/browser/autofill/autofill_profile_validator_factory.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 
 namespace autofill {
 
diff --git a/ios/chrome/browser/browser_state/BUILD.gn b/ios/chrome/browser/browser_state/BUILD.gn
index 39a6910..b811fe821 100644
--- a/ios/chrome/browser/browser_state/BUILD.gn
+++ b/ios/chrome/browser/browser_state/BUILD.gn
@@ -9,6 +9,8 @@
     "chrome_browser_state.h",
     "chrome_browser_state.mm",
     "chrome_browser_state_manager.h",
+    "web_data_service_factory.cc",
+    "web_data_service_factory.h",
   ]
 
   public_deps = [
@@ -18,9 +20,16 @@
 
   deps = [
     "//base",
+    "//components/autofill/core/browser",
+    "//components/keyed_service/core",
+    "//components/keyed_service/ios",
     "//components/prefs",
+    "//components/search_engines",
+    "//components/signin/core/browser",
+    "//components/signin/core/browser/webdata",
     "//components/sync_preferences",
     "//components/variations/net",
+    "//components/webdata_services",
     "//ios/chrome/browser/net:net_types",
     "//ios/web/public/webui",
   ]
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index 2c46f40..450a9a6 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -11,6 +11,7 @@
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_remover_factory.h"
 #include "ios/chrome/browser/content_settings/cookie_settings_factory.h"
 #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
@@ -57,7 +58,6 @@
 #import "ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
 #include "ios/chrome/browser/unified_consent/unified_consent_service_factory.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
index 3d246d4..2dc3350 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
@@ -121,7 +121,7 @@
   if (!IsOffTheRecord()) {
     google_services_user_account_id_.Init(prefs::kGoogleServicesUserAccountId,
                                           pref_service);
-    google_services_user_account_id_.MoveToThread(io_task_runner);
+    google_services_user_account_id_.MoveToSequence(io_task_runner);
   }
 }
 
@@ -297,7 +297,7 @@
   // read from there.
   enable_metrics_.Init(metrics::prefs::kMetricsReportingEnabled,
                        GetApplicationContext()->GetLocalState());
-  enable_metrics_.MoveToThread(
+  enable_metrics_.MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({web::WebThread::IO}));
 }
 
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
index 152a185..541a3fb 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
@@ -38,6 +38,7 @@
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h"
 #include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/history/history_client_impl.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/history/top_sites_factory.h"
@@ -45,7 +46,6 @@
 #include "ios/chrome/browser/prefs/browser_prefs.h"
 #include "ios/chrome/browser/prefs/ios_chrome_pref_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
diff --git a/ios/chrome/browser/web_data_service_factory.cc b/ios/chrome/browser/browser_state/web_data_service_factory.cc
similarity index 95%
rename from ios/chrome/browser/web_data_service_factory.cc
rename to ios/chrome/browser/browser_state/web_data_service_factory.cc
index 99a1fb3..e3288863 100644
--- a/ios/chrome/browser/web_data_service_factory.cc
+++ b/ios/chrome/browser/browser_state/web_data_service_factory.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 "ios/chrome/browser/web_data_service_factory.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -93,11 +93,9 @@
 WebDataServiceFactory::WebDataServiceFactory()
     : BrowserStateKeyedServiceFactory(
           "WebDataService",
-          BrowserStateDependencyManager::GetInstance()) {
-}
+          BrowserStateDependencyManager::GetInstance()) {}
 
-WebDataServiceFactory::~WebDataServiceFactory() {
-}
+WebDataServiceFactory::~WebDataServiceFactory() {}
 
 std::unique_ptr<KeyedService> WebDataServiceFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
diff --git a/ios/chrome/browser/web_data_service_factory.h b/ios/chrome/browser/browser_state/web_data_service_factory.h
similarity index 92%
rename from ios/chrome/browser/web_data_service_factory.h
rename to ios/chrome/browser/browser_state/web_data_service_factory.h
index 2621128..79ed343 100644
--- a/ios/chrome/browser/web_data_service_factory.h
+++ b/ios/chrome/browser/browser_state/web_data_service_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_WEB_DATA_SERVICE_FACTORY_H_
-#define IOS_CHROME_BROWSER_WEB_DATA_SERVICE_FACTORY_H_
+#ifndef IOS_CHROME_BROWSER_BROWSER_STATE_WEB_DATA_SERVICE_FACTORY_H_
+#define IOS_CHROME_BROWSER_BROWSER_STATE_WEB_DATA_SERVICE_FACTORY_H_
 
 #include <memory>
 
@@ -78,4 +78,4 @@
 
 }  // namespace ios
 
-#endif  // IOS_CHROME_BROWSER_WEB_DATA_SERVICE_FACTORY_H_
+#endif  // IOS_CHROME_BROWSER_BROWSER_STATE_WEB_DATA_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc b/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
index ae1d0ecb3..c5c490a7 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
+++ b/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
@@ -16,13 +16,13 @@
 #include "components/prefs/pref_service.h"
 #include "components/sync/driver/sync_service.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_features.h"
 #include "ios/chrome/browser/browsing_data/cache_counter.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/history/web_history_service_factory.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 
 namespace {
 
diff --git a/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm b/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
index 3ffc0265d..0bb5cd1 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
+++ b/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
@@ -42,6 +42,7 @@
 #include "ios/chrome/browser/autofill/strike_database_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_remover_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_features.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_remove_mask.h"
 #include "ios/chrome/browser/external_files/external_file_remover.h"
@@ -57,7 +58,6 @@
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
 #include "ios/chrome/browser/snapshots/snapshots_util.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #include "ios/net/http_cache_helper.h"
 #import "ios/web/public/browsing_data/browsing_data_removing_util.h"
 #include "ios/web/public/thread/web_task_traits.h"
diff --git a/ios/chrome/browser/context_menu/context_menu_egtest.mm b/ios/chrome/browser/context_menu/context_menu_egtest.mm
index df8e0304..35c0aef 100644
--- a/ios/chrome/browser/context_menu/context_menu_egtest.mm
+++ b/ios/chrome/browser/context_menu/context_menu_egtest.mm
@@ -37,7 +37,6 @@
 using chrome_test_util::ContextMenuCopyButton;
 using chrome_test_util::OmniboxText;
 using chrome_test_util::OpenLinkInNewTabButton;
-using chrome_test_util::SystemSelectionCallout;
 using chrome_test_util::SystemSelectionCalloutCopyButton;
 
 namespace {
@@ -310,8 +309,8 @@
       assertWithMatcher:grey_nil()];
 
   // Verify that system text selection callout is displayed.
-  [[[EarlGrey selectElementWithMatcher:SystemSelectionCalloutCopyButton()]
-      inRoot:SystemSelectionCallout()] assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:SystemSelectionCalloutCopyButton()]
+      assertWithMatcher:grey_notNil()];
 
   // Verify that system touches were not cancelled.
   histogramTester.ExpectTotalCount("ContextMenu.CancelSystemTouches", 0,
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
new file mode 100644
index 0000000..576cbab
--- /dev/null
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -0,0 +1,59 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("flags") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "about_flags.h",
+    "about_flags.mm",
+  ]
+
+  deps = [
+    "//base",
+    "//components/autofill/core/common",
+    "//components/autofill/ios/browser",
+    "//components/dom_distiller/core",
+    "//components/feature_engagement/public",
+    "//components/flags_ui",
+    "//components/flags_ui:switches",
+    "//components/invalidation/impl:feature_list",
+    "//components/ntp_tiles",
+    "//components/omnibox/browser",
+    "//components/omnibox/common",
+    "//components/password_manager/core/common",
+    "//components/payments/core",
+    "//components/search_provider_logos",
+    "//components/security_state/core",
+    "//components/send_tab_to_self",
+    "//components/signin/core/browser",
+    "//components/signin/core/browser:shared",
+    "//components/strings:components_strings",
+    "//components/sync/driver",
+    "//components/translate/core/browser",
+    "//components/ukm/ios:features",
+    "//components/unified_consent",
+    "//components/variations",
+    "//ios/chrome/app/strings:ios_strings",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/app_launcher:feature_flags",
+    "//ios/chrome/browser/browsing_data:feature_flags",
+    "//ios/chrome/browser/crash_report:flags",
+    "//ios/chrome/browser/drag_and_drop",
+    "//ios/chrome/browser/find_in_page:feature_flags",
+    "//ios/chrome/browser/passwords:feature_flags",
+    "//ios/chrome/browser/reading_list:features",
+    "//ios/chrome/browser/signin:feature_flags",
+    "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/dialogs:feature_flags",
+    "//ios/chrome/browser/ui/fullscreen:feature_flags",
+    "//ios/chrome/browser/ui/infobars:feature_flags",
+    "//ios/chrome/browser/ui/toolbar/public:feature_flags",
+    "//ios/chrome/browser/ui/toolbar_container:feature_flags",
+    "//ios/chrome/browser/web:feature_flags",
+    "//ios/public/provider/chrome/browser",
+    "//ios/web/common",
+    "//ios/web/public",
+    "//ios/web/public:user_agent",
+  ]
+}
diff --git a/ios/chrome/browser/about_flags.h b/ios/chrome/browser/flags/about_flags.h
similarity index 92%
rename from ios/chrome/browser/about_flags.h
rename to ios/chrome/browser/flags/about_flags.h
index a9b894b..65021447 100644
--- a/ios/chrome/browser/about_flags.h
+++ b/ios/chrome/browser/flags/about_flags.h
@@ -5,8 +5,8 @@
 // Implementation of about_flags for iOS that sets flags based on experimental
 // settings.
 
-#ifndef IOS_CHROME_BROWSER_ABOUT_FLAGS_H_
-#define IOS_CHROME_BROWSER_ABOUT_FLAGS_H_
+#ifndef IOS_CHROME_BROWSER_FLAGS_ABOUT_FLAGS_H_
+#define IOS_CHROME_BROWSER_FLAGS_ABOUT_FLAGS_H_
 
 #include <stddef.h>
 #include <string>
@@ -17,7 +17,7 @@
 namespace base {
 class CommandLine;
 class ListValue;
-}
+}  // namespace base
 
 namespace flags_ui {
 class FlagsStorage;
@@ -62,4 +62,4 @@
 
 }  // namespace testing
 
-#endif  // IOS_CHROME_BROWSER_ABOUT_FLAGS_H_
+#endif  // IOS_CHROME_BROWSER_FLAGS_ABOUT_FLAGS_H_
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
similarity index 98%
rename from ios/chrome/browser/about_flags.mm
rename to ios/chrome/browser/flags/about_flags.mm
index 99b5095..cdc9ef4 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -5,11 +5,11 @@
 // Implementation of about_flags for iOS that sets flags based on experimental
 // settings.
 
-#include "ios/chrome/browser/about_flags.h"
+#include "ios/chrome/browser/flags/about_flags.h"
 
+#import <UIKit/UIKit.h>
 #include <stddef.h>
 #include <stdint.h>
-#import <UIKit/UIKit.h>
 
 #include "base/base_switches.h"
 #include "base/bind.h"
@@ -584,6 +584,10 @@
     {"toolbar-new-tab-button", flag_descriptions::kToolbarNewTabButtonName,
      flag_descriptions::kToolbarNewTabButtonDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kToolbarNewTabButton)},
+    {"optional-article-thumbnail",
+     flag_descriptions::kOptionalArticleThumbnailName,
+     flag_descriptions::kOptionalArticleThumbnailDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kOptionalArticleThumbnail)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
index 2bf93d3c..1fffe2de 100644
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
+++ b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
@@ -80,14 +80,6 @@
 const char* const kGeolocationHeaderSentOrNotHistogram =
     "Geolocation.HeaderSentOrNot";
 
-// Name of the histogram recording location acquisition time.
-const char* const kOmniboxQueryGeolocationAcquisitionTimeHistogram =
-    "Omnibox.QueryGeolocationAcquisitionTime";
-
-// Name of the histogram recording estimated location accuracy.
-const char* const kOmniboxQueryGeolocationHorizontalAccuracyHistogram =
-    "Omnibox.QueryGeolocationHorizontalAccuracy";
-
 // Name of the histogram recording AuthorizationAction for an existing user.
 const char* const kGeolocationAuthorizationActionExistingUser =
     "Geolocation.AuthorizationActionExistingUser";
@@ -301,18 +293,6 @@
               @{ @"X-Geo" : [currentLocation cr_xGeoString] };
           item->AddHttpRequestHeaders(locationHTTPHeaders);
           headerState = kHeaderStateSent;
-
-          NSTimeInterval acquisitionInterval =
-              currentLocation.cr_acquisitionInterval;
-          base::TimeDelta acquisitionTime = base::TimeDelta::FromMilliseconds(
-              acquisitionInterval * base::Time::kMillisecondsPerSecond);
-          UMA_HISTOGRAM_TIMES(kOmniboxQueryGeolocationAcquisitionTimeHistogram,
-                              acquisitionTime);
-
-          double horizontalAccuracy = currentLocation.horizontalAccuracy;
-          UMA_HISTOGRAM_COUNTS_10000(
-              kOmniboxQueryGeolocationHorizontalAccuracyHistogram,
-              horizontalAccuracy);
         }
         break;
       }
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index efa0aea..086562f3 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -337,6 +337,12 @@
 const char kOnlyNewPasswordFormParsingDescription[] =
     "The old password form parsing is disabled";
 
+const char kOptionalArticleThumbnailName[] =
+    "Enable optional thumbnails for NTP articles";
+const char kOptionalArticleThumbnailDescription[] =
+    "Make thumbnails of NTP articles optional due to European copyright "
+    "directive(EUCD). Also change the layout of article cells";
+
 const char kPasswordGenerationName[] = "Password generation suggestion";
 const char kPasswordGenerationDescription[] =
     "Add 'Suggest Password' in suggestion list for form completion.";
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index 6379bbc..449d730 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -283,6 +283,11 @@
 extern const char kOnlyNewPasswordFormParsingName[];
 extern const char kOnlyNewPasswordFormParsingDescription[];
 
+// Title and description for the flag to enable optional thumbnail for NTP
+// articles according to European copyright directive(EUCD).
+extern const char kOptionalArticleThumbnailName[];
+extern const char kOptionalArticleThumbnailDescription[];
+
 // Title and description for the flag to enable password generation.
 extern const char kPasswordGenerationName[];
 extern const char kPasswordGenerationDescription[];
diff --git a/ios/chrome/browser/ios_chrome_main_parts.mm b/ios/chrome/browser/ios_chrome_main_parts.mm
index d41dcda7..96c5e58 100644
--- a/ios/chrome/browser/ios_chrome_main_parts.mm
+++ b/ios/chrome/browser/ios_chrome_main_parts.mm
@@ -33,13 +33,13 @@
 #include "components/variations/synthetic_trials_active_group_id_provider.h"
 #include "components/variations/variations_crash_keys.h"
 #include "components/variations/variations_http_header_provider.h"
-#include "ios/chrome/browser/about_flags.h"
 #include "ios/chrome/browser/application_context_impl.h"
 #include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
 #include "ios/chrome/browser/chrome_paths.h"
 #import "ios/chrome/browser/first_run/first_run.h"
+#include "ios/chrome/browser/flags/about_flags.h"
 #include "ios/chrome/browser/install_time_util.h"
 #include "ios/chrome/browser/metrics/ios_expired_histograms_array.h"
 #include "ios/chrome/browser/open_from_clipboard/create_clipboard_recent_content.h"
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 9299937a..2be410e 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
@@ -58,7 +58,7 @@
 #include "ios/chrome/browser/signin/ios_chrome_signin_status_metrics_provider_delegate.h"
 #include "ios/chrome/browser/sync/device_info_sync_service_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
-#include "ios/chrome/browser/tab_parenting_global_observer.h"
+#include "ios/chrome/browser/tabs/tab_parenting_global_observer.h"
 #include "ios/chrome/browser/translate/translate_ranker_metrics_provider.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/thread/web_thread.h"
diff --git a/ios/chrome/browser/net/ios_chrome_http_user_agent_settings.mm b/ios/chrome/browser/net/ios_chrome_http_user_agent_settings.mm
index a6bae224..baba01a2 100644
--- a/ios/chrome/browser/net/ios_chrome_http_user_agent_settings.mm
+++ b/ios/chrome/browser/net/ios_chrome_http_user_agent_settings.mm
@@ -23,7 +23,7 @@
   last_pref_accept_language_ = *pref_accept_language_;
   last_http_accept_language_ =
       net::HttpUtil::GenerateAcceptLanguageHeader(last_pref_accept_language_);
-  pref_accept_language_.MoveToThread(
+  pref_accept_language_.MoveToSequence(
       base::CreateSingleThreadTaskRunnerWithTraits({web::WebThread::IO}));
 }
 
diff --git a/ios/chrome/browser/net/ios_chrome_network_delegate.cc b/ios/chrome/browser/net/ios_chrome_network_delegate.cc
index d55b59d..f9b81e6 100644
--- a/ios/chrome/browser/net/ios_chrome_network_delegate.cc
+++ b/ios/chrome/browser/net/ios_chrome_network_delegate.cc
@@ -70,7 +70,7 @@
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   if (enable_do_not_track) {
     enable_do_not_track->Init(prefs::kEnableDoNotTrack, pref_service);
-    enable_do_not_track->MoveToThread(
+    enable_do_not_track->MoveToSequence(
         base::CreateSingleThreadTaskRunnerWithTraits({web::WebThread::IO}));
   }
 }
diff --git a/ios/chrome/browser/overlays/BUILD.gn b/ios/chrome/browser/overlays/BUILD.gn
index 51c035c..0985ace 100644
--- a/ios/chrome/browser/overlays/BUILD.gn
+++ b/ios/chrome/browser/overlays/BUILD.gn
@@ -6,7 +6,10 @@
   public = [
     "public/overlay_dismissal_callback.h",
     "public/overlay_modality.h",
+    "public/overlay_presentation_context.h",
+    "public/overlay_presentation_context_observer.h",
     "public/overlay_presenter.h",
+    "public/overlay_presenter_observer.h",
     "public/overlay_request.h",
     "public/overlay_request_queue.h",
     "public/overlay_response.h",
diff --git a/ios/chrome/browser/overlays/README.md b/ios/chrome/browser/overlays/README.md
index 9618609..e967994 100644
--- a/ios/chrome/browser/overlays/README.md
+++ b/ios/chrome/browser/overlays/README.md
@@ -32,19 +32,20 @@
 OverlayPresenter drives the presentation of the UI for OverlayRequests added to
 queues for WebStates in a Browser.
 
-#### OverlayPresenter::UIDelegate
+#### OverlayPresentationContext
 
-Clients must provide a UI delegate to a Browser's OverlayPresenter that handles
-the presentation of overlay UI for that presenter's modality and Browser.
+Clients must provide a presentation context to a Browser's OverlayPresenter that
+handles the presentation of overlay UI for that presenter's modality and
+Browser.
 
-#### OverlayPresenter::Observer
+#### OverlayPresenterObserver
 
 Objects that care about the presentation and dismissal of overlay UI by the
 presenter should add themselves as observers to the presenter.  This can be used
 to respond to update UI for UI presentation, for example to update the location
 bar text while a dialog is displayed.
 
-## Setting up OverlayPresenterr:
+## Setting up OverlayPresenter:
 
 Multiple OverlayPresenters may be active for a single Browser to manage overlay
 UI at different levels of modality (i.e. modal over WebState content area, modal
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl.h b/ios/chrome/browser/overlays/overlay_presenter_impl.h
index 8f542ec..7f3110c 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl.h
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl.h
@@ -8,7 +8,9 @@
 #include "base/memory/weak_ptr.h"
 #import "ios/chrome/browser/main/browser_observer.h"
 #import "ios/chrome/browser/overlays/overlay_request_queue_impl.h"
+#import "ios/chrome/browser/overlays/public/overlay_dismissal_callback.h"
 #import "ios/chrome/browser/overlays/public/overlay_modality.h"
+#import "ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h"
 #import "ios/chrome/browser/overlays/public/overlay_presenter.h"
 #import "ios/chrome/browser/overlays/public/overlay_user_data.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
@@ -19,6 +21,7 @@
 // - manages hiding and showing overlays for active WebState changes.
 class OverlayPresenterImpl : public BrowserObserver,
                              public OverlayPresenter,
+                             public OverlayPresentationContextObserver,
                              public OverlayRequestQueueImpl::Observer,
                              public WebStateListObserver {
  public:
@@ -45,9 +48,10 @@
   };
 
   // OverlayPresenter:
-  void SetUIDelegate(UIDelegate* delegate) override;
-  void AddObserver(OverlayPresenter::Observer* observer) override;
-  void RemoveObserver(OverlayPresenter::Observer* observer) override;
+  void SetPresentationContext(
+      OverlayPresentationContext* presentation_context) override;
+  void AddObserver(OverlayPresenterObserver* observer) override;
+  void RemoveObserver(OverlayPresenterObserver* observer) override;
 
  private:
   // Private constructor used by the container.
@@ -75,11 +79,11 @@
   // only be called when |presenting_| is false.
   void PresentOverlayForActiveRequest();
 
-  // Notifies this object that |ui_delegate| has finished dismissing the
-  // overlay UI corresponding with |request| in |queue| for |reason|.  This
-  // function is called when the OverlayDismissalCallback provided to the UI
-  // delegate is executed.
-  void OverlayWasDismissed(UIDelegate* ui_delegate,
+  // Notifies this object that the UI for |request| has finished being dismissed
+  // in |presentation_context| in for |reason|.  |queue| is |request|'s queue.
+  // This function is called when the OverlayDismissalCallback provided to
+  // |presentation_context| is executed.
+  void OverlayWasDismissed(OverlayPresentationContext* presentation_context,
                            OverlayRequest* request,
                            base::WeakPtr<OverlayRequestQueueImpl> queue,
                            OverlayDismissalReason reason);
@@ -99,6 +103,13 @@
   void QueuedRequestCancelled(OverlayRequestQueueImpl* queue,
                               OverlayRequest* request) override;
 
+  // OverlayPresentationContextObserver:
+  void OverlayPresentationContextWillChangeActivationState(
+      OverlayPresentationContext* presentation_context,
+      bool activating) override;
+  void OverlayPresentationContextDidChangeActivationState(
+      OverlayPresentationContext* presentation_context) override;
+
   // WebStateListObserver:
   void WebStateInsertedAt(WebStateList* web_state_list,
                           web::WebState* web_state,
@@ -127,8 +138,8 @@
   OverlayModality modality_;
   WebStateList* web_state_list_ = nullptr;
   web::WebState* active_web_state_ = nullptr;
-  UIDelegate* ui_delegate_ = nullptr;
-  base::ObserverList<OverlayPresenter::Observer,
+  OverlayPresentationContext* presentation_context_ = nullptr;
+  base::ObserverList<OverlayPresenterObserver,
                      /* check_empty= */ true>
       observers_;
   base::WeakPtrFactory<OverlayPresenterImpl> weak_factory_;
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl.mm b/ios/chrome/browser/overlays/overlay_presenter_impl.mm
index 78934a51..9fdfe2d 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl.mm
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl.mm
@@ -6,6 +6,8 @@
 
 #include "base/logging.h"
 #import "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/overlays/public/overlay_presentation_context.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter_observer.h"
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 
@@ -62,7 +64,7 @@
 OverlayPresenterImpl::~OverlayPresenterImpl() {
   // The presenter should be disconnected from WebStateList changes before
   // destruction.
-  DCHECK(!ui_delegate_);
+  DCHECK(!presentation_context_);
   DCHECK(!web_state_list_);
 
   for (auto& observer : observers_) {
@@ -74,29 +76,34 @@
 
 #pragma mark OverlayPresenter
 
-void OverlayPresenterImpl::SetUIDelegate(UIDelegate* ui_delegate) {
-  // When the UI delegate is reset, the presenter will begin showing overlays in
-  // the new delegate's presentation context.  Cancel overlay state from the
-  // previous delegate since this Browser's overlays will no longer be presented
+void OverlayPresenterImpl::SetPresentationContext(
+    OverlayPresentationContext* presentation_context) {
+  // When the presentation context is reset, the presenter will begin showing
+  // overlays in the new presentation context.  Cancel overlay state from the
+  // previous context since this Browser's overlays will no longer be presented
   // there.
-  if (ui_delegate_)
+  if (presentation_context_) {
     CancelAllOverlayUI();
+    presentation_context_->RemoveObserver(this);
+  }
 
-  ui_delegate_ = ui_delegate;
+  presentation_context_ = presentation_context;
 
   // Reset |presenting| since it was tracking the status for the previous
   // delegate's presentation context.
   presenting_ = false;
-  if (ui_delegate_)
+
+  if (presentation_context_) {
+    presentation_context_->AddObserver(this);
     PresentOverlayForActiveRequest();
+  }
 }
 
-void OverlayPresenterImpl::AddObserver(OverlayPresenter::Observer* observer) {
+void OverlayPresenterImpl::AddObserver(OverlayPresenterObserver* observer) {
   observers_.AddObserver(observer);
 }
 
-void OverlayPresenterImpl::RemoveObserver(
-    OverlayPresenter::Observer* observer) {
+void OverlayPresenterImpl::RemoveObserver(OverlayPresenterObserver* observer) {
   observers_.RemoveObserver(observer);
 }
 
@@ -124,7 +131,7 @@
   detaching_active_web_state_ = false;
 
   // Early return if there's no UI delegate, since presentation cannot occur.
-  if (!ui_delegate_)
+  if (!presentation_context_)
     return;
 
   // If not already presenting, immediately show the next overlay.
@@ -143,7 +150,7 @@
   } else {
     // For WebState activations, the overlay UI for the previously active
     // WebState should be hidden, as it may be shown again upon reactivating.
-    ui_delegate_->HideOverlayUI(this, previously_active_request);
+    presentation_context_->HideOverlayUI(this, previously_active_request);
   }
 }
 
@@ -176,8 +183,8 @@
   // Overlays cannot be presented if one is already presented.
   DCHECK(!presenting_);
 
-  // Overlays cannot be shown without a UI delegate.
-  if (!ui_delegate_)
+  // Overlays cannot be shown without an active presentation context.
+  if (!presentation_context_ || !presentation_context_->IsActive())
     return;
 
   // No presentation is necessary if there is no active reqeust.
@@ -195,12 +202,13 @@
   // Present the overlay UI via the UI delegate.
   OverlayDismissalCallback dismissal_callback = base::BindOnce(
       &OverlayPresenterImpl::OverlayWasDismissed, weak_factory_.GetWeakPtr(),
-      ui_delegate_, request, GetActiveQueue()->GetWeakPtr());
-  ui_delegate_->ShowOverlayUI(this, request, std::move(dismissal_callback));
+      presentation_context_, request, GetActiveQueue()->GetWeakPtr());
+  presentation_context_->ShowOverlayUI(this, request,
+                                       std::move(dismissal_callback));
 }
 
 void OverlayPresenterImpl::OverlayWasDismissed(
-    UIDelegate* ui_delegate,
+    OverlayPresentationContext* presentation_context,
     OverlayRequest* request,
     base::WeakPtr<OverlayRequestQueueImpl> queue,
     OverlayDismissalReason reason) {
@@ -208,7 +216,7 @@
   // be cancelled and dismissed.  The presenter is now using the new UI
   // delegate's presentation context, so this dismissal should not trigger
   // presentation logic.
-  if (ui_delegate_ != ui_delegate)
+  if (presentation_context_ != presentation_context)
     return;
 
   // Pop the request for overlays dismissed by the user.  The check against the
@@ -239,9 +247,9 @@
 #pragma mark Cancellation helpers
 
 void OverlayPresenterImpl::CancelOverlayUIForRequest(OverlayRequest* request) {
-  if (!ui_delegate_ || !request)
+  if (!presentation_context_ || !request)
     return;
-  ui_delegate_->CancelOverlayUI(this, request);
+  presentation_context_->CancelOverlayUI(this, request);
 }
 
 void OverlayPresenterImpl::CancelAllOverlayUI() {
@@ -255,7 +263,7 @@
 #pragma mark BrowserObserver
 
 void OverlayPresenterImpl::BrowserDestroyed(Browser* browser) {
-  SetUIDelegate(nullptr);
+  SetPresentationContext(nullptr);
   SetActiveWebState(nullptr, CHANGE_REASON_NONE);
 
   for (int i = 0; i < web_state_list_->count(); ++i) {
@@ -283,6 +291,26 @@
   CancelOverlayUIForRequest(request);
 }
 
+#pragma mark - OverlayPresentationContextObserver
+
+void OverlayPresenterImpl::OverlayPresentationContextWillChangeActivationState(
+    OverlayPresentationContext* presentation_context,
+    bool activating) {
+  DCHECK_EQ(presentation_context_, presentation_context);
+  // Hide the presented overlay UI if the presentation context is deactivating.
+  if (!activating && presenting_)
+    presentation_context_->HideOverlayUI(this, GetActiveRequest());
+}
+
+void OverlayPresenterImpl::OverlayPresentationContextDidChangeActivationState(
+    OverlayPresentationContext* presentation_context) {
+  DCHECK_EQ(presentation_context_, presentation_context);
+  // Attempt to present the active request's overlay UI if the context is being
+  // activated.
+  if (presentation_context_->IsActive())
+    PresentOverlayForActiveRequest();
+}
+
 #pragma mark - WebStateListObserver
 
 void OverlayPresenterImpl::WebStateInsertedAt(WebStateList* web_state_list,
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl_unittest.mm b/ios/chrome/browser/overlays/overlay_presenter_impl_unittest.mm
index 247ee0b..e783381 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl_unittest.mm
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl_unittest.mm
@@ -8,8 +8,9 @@
 #import "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/overlays/overlay_request_queue_impl.h"
 #include "ios/chrome/browser/overlays/public/overlay_modality.h"
+#include "ios/chrome/browser/overlays/public/overlay_presenter_observer.h"
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
-#include "ios/chrome/browser/overlays/test/fake_overlay_presenter_ui_delegate.h"
+#include "ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h"
 #include "ios/chrome/browser/overlays/test/fake_overlay_user_data.h"
 #import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -25,7 +26,7 @@
 
 namespace {
 // Mock queue observer.
-class MockOverlayPresenterObserver : public OverlayPresenter::Observer {
+class MockOverlayPresenterObserver : public OverlayPresenterObserver {
  public:
   MockOverlayPresenterObserver() {}
   ~MockOverlayPresenterObserver() {}
@@ -71,7 +72,9 @@
     return *OverlayPresenterImpl::Container::FromUserData(browser_.get())
                 ->PresenterForModality(OverlayModality::kWebContentArea);
   }
-  FakeOverlayPresenterUIDelegate& ui_delegate() { return ui_delegate_; }
+  FakeOverlayPresentationContext& presentation_context() {
+    return presentation_context_;
+  }
   MockOverlayPresenterObserver& observer() { return observer_; }
 
   OverlayRequestQueueImpl* GetQueueForWebState(web::WebState* web_state) {
@@ -102,48 +105,85 @@
   web::TestWebThreadBundle thread_bundle_;
   FakeWebStateListDelegate web_state_list_delegate_;
   WebStateList web_state_list_;
-  FakeOverlayPresenterUIDelegate ui_delegate_;
+  FakeOverlayPresentationContext presentation_context_;
   MockOverlayPresenterObserver observer_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<Browser> browser_;
 };
 
-// Tests that setting the UI delegate will present overlays requested before the
-// delegate is provided.
-TEST_F(OverlayPresenterImplTest, PresentAfterSettingUIDelegate) {
+// Tests that setting the presentation context will present overlays requested
+// before the delegate is provided.
+TEST_F(OverlayPresenterImplTest, PresentAfterSettingPresentationContext) {
   // Add a WebState to the list and add a request to that WebState's queue.
   web_state_list().InsertWebState(
       /*index=*/0, std::make_unique<web::TestWebState>(),
       WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
   OverlayRequest* request = AddRequest(active_web_state());
-  ASSERT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kNotPresented,
-            ui_delegate().GetPresentationState(request));
+  ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kNotPresented,
+            presentation_context().GetPresentationState(request));
 
   // Set the UI delegate and verify that the request has been presented.
-  presenter().SetUIDelegate(&ui_delegate());
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(request));
+  presenter().SetPresentationContext(&presentation_context());
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(request));
 }
 
 // Tests that requested overlays are presented when added to the active queue
-// after the UI delegate has been provided.
+// after the presentation context has been provided.
 TEST_F(OverlayPresenterImplTest, PresentAfterRequestAddedToActiveQueue) {
   // Add a WebState to the list and add a request to that WebState's queue.
-  presenter().SetUIDelegate(&ui_delegate());
+  presenter().SetPresentationContext(&presentation_context());
   web_state_list().InsertWebState(
       /*index=*/0, std::make_unique<web::TestWebState>(),
       WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
   OverlayRequest* request = AddRequest(active_web_state());
   // Verify that the requested overlay has been presented.
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(request));
 }
 
-// Tests resetting the UI delegate.  The UI should be cancelled in the previous
-// UI delegate and presented in the new delegate.
-TEST_F(OverlayPresenterImplTest, ResetUIDelegate) {
+// Tests that requested overlays are presented when the presentation context is
+// activated.
+TEST_F(OverlayPresenterImplTest, PresentAfterContextActivation) {
   // Add a WebState to the list and add a request to that WebState's queue.
-  presenter().SetUIDelegate(&ui_delegate());
+  presentation_context().SetIsActive(false);
+  presenter().SetPresentationContext(&presentation_context());
+  web_state_list().InsertWebState(
+      /*index=*/0, std::make_unique<web::TestWebState>(),
+      WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
+  OverlayRequest* request = AddRequest(active_web_state());
+  ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kNotPresented,
+            presentation_context().GetPresentationState(request));
+
+  // Activate the presentation context and verify that the UI is presented.
+  presentation_context().SetIsActive(true);
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(request));
+}
+
+// Tests that presented overlay UI is hidden when the presentation context is
+// deactivated.
+TEST_F(OverlayPresenterImplTest, HideAfterContextDeactivation) {
+  // Add a WebState to the list and add a request to that WebState's queue.
+  presenter().SetPresentationContext(&presentation_context());
+  web_state_list().InsertWebState(
+      /*index=*/0, std::make_unique<web::TestWebState>(),
+      WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
+  OverlayRequest* request = AddRequest(active_web_state());
+  ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(request));
+
+  // Deactivate the presentation context and verify that the UI is hidden.
+  presentation_context().SetIsActive(false);
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kHidden,
+            presentation_context().GetPresentationState(request));
+}
+
+// Tests resetting the presentation context.  The UI should be cancelled in the
+// previous context and presented in the new context.
+TEST_F(OverlayPresenterImplTest, ResetPresentationContext) {
+  // Add a WebState to the list and add a request to that WebState's queue.
+  presenter().SetPresentationContext(&presentation_context());
   web_state_list().InsertWebState(
       /*index=*/0, std::make_unique<web::TestWebState>(),
       WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
@@ -151,27 +191,27 @@
   OverlayRequest* request = AddRequest(web_state);
   OverlayRequestQueueImpl* queue = GetQueueForWebState(web_state);
 
-  ASSERT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(request));
+  ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(request));
 
   // Reset the UI delegate and verify that the overlay UI is cancelled in the
   // previous delegate's context and presented in the new delegate's context.
-  FakeOverlayPresenterUIDelegate new_ui_delegate;
+  FakeOverlayPresentationContext new_presentation_context;
   EXPECT_CALL(observer(), DidHideOverlay(&presenter(), request));
   EXPECT_CALL(observer(), WillShowOverlay(&presenter(), request));
-  presenter().SetUIDelegate(&new_ui_delegate);
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kCancelled,
-            ui_delegate().GetPresentationState(request));
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            new_ui_delegate.GetPresentationState(request));
+  presenter().SetPresentationContext(&new_presentation_context);
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kCancelled,
+            presentation_context().GetPresentationState(request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            new_presentation_context.GetPresentationState(request));
   EXPECT_EQ(request, queue->front_request());
 
   // Reset the UI delegate to nullptr and verify that the overlay UI is
-  // cancelled in |new_ui_delegate|'s context.
+  // cancelled in |new_presentation_context|'s context.
   EXPECT_CALL(observer(), DidHideOverlay(&presenter(), request));
-  presenter().SetUIDelegate(nullptr);
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kCancelled,
-            new_ui_delegate.GetPresentationState(request));
+  presenter().SetPresentationContext(nullptr);
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kCancelled,
+            new_presentation_context.GetPresentationState(request));
   EXPECT_EQ(request, queue->front_request());
 }
 
@@ -179,7 +219,7 @@
 // current active WebState.
 TEST_F(OverlayPresenterImplTest, ChangeActiveWebStateWhileNotPresenting) {
   // Add a WebState to the list and activate it.
-  presenter().SetUIDelegate(&ui_delegate());
+  presenter().SetPresentationContext(&presentation_context());
   web_state_list().InsertWebState(
       /*index=*/0, std::make_unique<web::TestWebState>(),
       WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
@@ -195,21 +235,21 @@
                                   WebStateOpener());
 
   // Verify that the new active WebState's overlay is being presented.
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(request));
 }
 
 // Tests changing the active WebState while is it presenting an overlay.
 TEST_F(OverlayPresenterImplTest, ChangeActiveWebStateWhilePresenting) {
   // Add a WebState to the list and add a request to that WebState's queue.
-  presenter().SetUIDelegate(&ui_delegate());
+  presenter().SetPresentationContext(&presentation_context());
   web_state_list().InsertWebState(
       /*index=*/0, std::make_unique<web::TestWebState>(),
       WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
   web::WebState* first_web_state = active_web_state();
   OverlayRequest* first_request = AddRequest(first_web_state);
-  ASSERT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(first_request));
+  ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(first_request));
 
   // Create a new WebState with a queued request and add it as the new active
   // WebState.
@@ -224,33 +264,33 @@
 
   // Verify that the previously shown overlay is hidden and that the overlay for
   // the new active WebState is presented.
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kHidden,
-            ui_delegate().GetPresentationState(first_request));
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(second_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kHidden,
+            presentation_context().GetPresentationState(first_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(second_request));
 
   // Reactivate the first WebState and verify that its overlay is presented
   // while the second WebState's overlay is hidden.
   EXPECT_CALL(observer(), DidHideOverlay(&presenter(), second_request));
   EXPECT_CALL(observer(), WillShowOverlay(&presenter(), first_request));
   web_state_list().ActivateWebStateAt(0);
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(first_request));
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kHidden,
-            ui_delegate().GetPresentationState(second_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(first_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kHidden,
+            presentation_context().GetPresentationState(second_request));
 }
 
 // Tests replacing the active WebState while it is presenting an overlay.
 TEST_F(OverlayPresenterImplTest, ReplaceActiveWebState) {
   // Add a WebState to the list and add a request to that WebState's queue.
-  presenter().SetUIDelegate(&ui_delegate());
+  presenter().SetPresentationContext(&presentation_context());
   web_state_list().InsertWebState(
       /*index=*/0, std::make_unique<web::TestWebState>(),
       WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
   web::WebState* first_web_state = active_web_state();
   OverlayRequest* first_request = AddRequest(first_web_state);
-  ASSERT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(first_request));
+  ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(first_request));
 
   // Replace |first_web_state| with a new active WebState with a queued request.
   std::unique_ptr<web::WebState> passed_web_state =
@@ -262,35 +302,35 @@
 
   // Verify that the previously shown overlay is canceled and that the overlay
   // for the replacement WebState is presented.
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kCancelled,
-            ui_delegate().GetPresentationState(first_request));
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(replacement_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kCancelled,
+            presentation_context().GetPresentationState(first_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(replacement_request));
 }
 
 // Tests removing the active WebState while it is presenting an overlay.
 TEST_F(OverlayPresenterImplTest, RemoveActiveWebState) {
   // Add a WebState to the list and add a request to that WebState's queue.
-  presenter().SetUIDelegate(&ui_delegate());
+  presenter().SetPresentationContext(&presentation_context());
   web_state_list().InsertWebState(
       /*index=*/0, std::make_unique<web::TestWebState>(),
       WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
   web::WebState* web_state = active_web_state();
   OverlayRequest* request = AddRequest(web_state);
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(request));
 
   // Remove the WebState and verify that its overlay was cancelled.
   EXPECT_CALL(observer(), DidHideOverlay(&presenter(), request));
   web_state_list().CloseWebStateAt(/*index=*/0, /* close_flags= */ 0);
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kCancelled,
-            ui_delegate().GetPresentationState(request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kCancelled,
+            presentation_context().GetPresentationState(request));
 }
 
 // Tests dismissing an overlay for user interaction.
 TEST_F(OverlayPresenterImplTest, DismissForUserInteraction) {
   // Add a WebState to the list and add two request to that WebState's queue.
-  presenter().SetUIDelegate(&ui_delegate());
+  presenter().SetPresentationContext(&presentation_context());
   web_state_list().InsertWebState(
       /*index=*/0, std::make_unique<web::TestWebState>(),
       WebStateList::InsertionFlags::INSERT_ACTIVATE, WebStateOpener());
@@ -299,21 +339,21 @@
   OverlayRequest* first_request = AddRequest(web_state);
   OverlayRequest* second_request = AddRequest(web_state);
 
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(first_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(first_request));
   EXPECT_EQ(first_request, queue->front_request());
   EXPECT_EQ(2U, queue->size());
 
   // Dismiss the overlay and check that the second request's overlay is
   // presented.
   EXPECT_CALL(observer(), DidHideOverlay(&presenter(), first_request));
-  ui_delegate().SimulateDismissalForRequest(
+  presentation_context().SimulateDismissalForRequest(
       first_request, OverlayDismissalReason::kUserInteraction);
 
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kUserDismissed,
-            ui_delegate().GetPresentationState(first_request));
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(second_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kUserDismissed,
+            presentation_context().GetPresentationState(first_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(second_request));
   EXPECT_EQ(second_request, queue->front_request());
   EXPECT_EQ(1U, queue->size());
 }
@@ -321,7 +361,7 @@
 // Tests cancelling the requests.
 TEST_F(OverlayPresenterImplTest, CancelRequests) {
   // Add a WebState to the list and a request to that WebState's queue.
-  presenter().SetUIDelegate(&ui_delegate());
+  presenter().SetPresentationContext(&presentation_context());
   web_state_list().InsertWebState(0, std::make_unique<web::TestWebState>(),
                                   WebStateList::InsertionFlags::INSERT_ACTIVATE,
                                   WebStateOpener());
@@ -330,16 +370,16 @@
   OverlayRequest* queued_request =
       AddRequest(active_web_state(), /*expect_presentation=*/false);
 
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kPresented,
-            ui_delegate().GetPresentationState(active_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
+            presentation_context().GetPresentationState(active_request));
 
   // Cancel the queue's requests and verify that the UI is also cancelled.
   EXPECT_CALL(observer(), DidHideOverlay(&presenter(), active_request));
   queue->CancelAllRequests();
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kCancelled,
-            ui_delegate().GetPresentationState(active_request));
-  EXPECT_EQ(FakeOverlayPresenterUIDelegate::PresentationState::kCancelled,
-            ui_delegate().GetPresentationState(queued_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kCancelled,
+            presentation_context().GetPresentationState(active_request));
+  EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kCancelled,
+            presentation_context().GetPresentationState(queued_request));
 }
 
 // Tests that deleting the presenter
diff --git a/ios/chrome/browser/overlays/public/overlay_presentation_context.h b/ios/chrome/browser/overlays/public/overlay_presentation_context.h
new file mode 100644
index 0000000..4ac8c4b
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/overlay_presentation_context.h
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_H_
+
+#include "ios/chrome/browser/overlays/public/overlay_dismissal_callback.h"
+
+class OverlayPresenter;
+class OverlayRequest;
+class OverlayPresentationContextObserver;
+
+// Object that handles presenting the overlay UI for OverlayPresenter.
+class OverlayPresentationContext {
+ public:
+  OverlayPresentationContext() = default;
+  virtual ~OverlayPresentationContext() = default;
+
+  // Adds and removes |observer|.
+  virtual void AddObserver(OverlayPresentationContextObserver* observer) = 0;
+  virtual void RemoveObserver(OverlayPresentationContextObserver* observer) = 0;
+
+  // Whether the presentation context is active.  Overlay UI will only be
+  // presented for active contexts.
+  virtual bool IsActive() const = 0;
+
+  // Called by |presenter| to show the overlay UI for |request|.
+  // |dismissal_callback| must be stored and called whenever the UI is finished
+  // being dismissed for user interaction, hiding, or cancellation.
+  virtual void ShowOverlayUI(OverlayPresenter* presenter,
+                             OverlayRequest* request,
+                             OverlayDismissalCallback dismissal_callback) = 0;
+
+  // Called by |presenter| to hide the overlay UI for |request|.  Hidden
+  // overlays may be shown again, so they should be kept in memory or
+  // serialized so that the state can be restored if shown again.  When hiding
+  // an overlay, the presented UI must be dismissed, and the overlay's
+  // dismissal callback must must be executed upon the dismissal's completion.
+  virtual void HideOverlayUI(OverlayPresenter* presenter,
+                             OverlayRequest* request) = 0;
+
+  // Called by |presenter| to cancel the overlay UI for |request|.  If the UI
+  // is presented, it should be dismissed and the dismissal callback should be
+  // executed upon the dismissal's completion.  Otherwise, any state
+  // corresponding to any hidden overlays should be cleaned up.
+  virtual void CancelOverlayUI(OverlayPresenter* presenter,
+                               OverlayRequest* request) = 0;
+};
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_H_
diff --git a/ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h b/ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h
new file mode 100644
index 0000000..facfa0a
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_OBSERVER_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+
+class OverlayPresentationContext;
+
+// Observer class for the ObserverPresentationContext.
+class OverlayPresentationContextObserver : public base::CheckedObserver {
+ public:
+  OverlayPresentationContextObserver() = default;
+
+  // Called before |presentation_context|'s activation state changes to
+  // |activating|.
+  virtual void OverlayPresentationContextWillChangeActivationState(
+      OverlayPresentationContext* presentation_context,
+      bool activating) {}
+
+  // Called after |presentation_context|'s activation state changes.
+  virtual void OverlayPresentationContextDidChangeActivationState(
+      OverlayPresentationContext* presentation_context) {}
+};
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_OBSERVER_H_
diff --git a/ios/chrome/browser/overlays/public/overlay_presenter.h b/ios/chrome/browser/overlays/public/overlay_presenter.h
index 6e4a2de0..fa8d4bc3a 100644
--- a/ios/chrome/browser/overlays/public/overlay_presenter.h
+++ b/ios/chrome/browser/overlays/public/overlay_presenter.h
@@ -7,12 +7,11 @@
 
 #include <memory>
 
-#include "base/observer_list_types.h"
-#include "ios/chrome/browser/overlays/public/overlay_dismissal_callback.h"
 #include "ios/chrome/browser/overlays/public/overlay_modality.h"
 
 class Browser;
-class OverlayRequest;
+class OverlayPresenterObserver;
+class OverlayPresentationContext;
 
 // OverlayPresenter handles the presentation of overlay UI for OverlayRequests
 // added to the OverlayRequestQueues for WebStates in a Browser.
@@ -25,62 +24,15 @@
   static OverlayPresenter* FromBrowser(Browser* browser,
                                        OverlayModality modality);
 
-  // Delegate that handles presenting the overlay UI for OverlayPresenter
-  class UIDelegate : public base::CheckedObserver {
-   public:
-    UIDelegate() = default;
-
-    // Called by |presenter| to show the overlay UI for |request|.
-    // |dismissal_callback| must be stored by the delegate and called whenever
-    // the UI is finished being dismissed for user interaction, hiding, or
-    // cancellation.
-    virtual void ShowOverlayUI(OverlayPresenter* presenter,
-                               OverlayRequest* request,
-                               OverlayDismissalCallback dismissal_callback) = 0;
-
-    // Called by |presenter| to hide the overlay UI for |request|.  Hidden
-    // overlays may be shown again, so they should be kept in memory or
-    // serialized so that the state can be restored if shown again.  When hiding
-    // an overlay, the presented UI must be dismissed, and the overlay's
-    // dismissal callback must must be executed upon the dismissal's completion.
-    virtual void HideOverlayUI(OverlayPresenter* presenter,
-                               OverlayRequest* request) = 0;
-
-    // Called by |presenter| to cancel the overlay UI for |request|.  If the UI
-    // is presented, it should be dismissed and the dismissal callback should be
-    // executed upon the dismissal's completion.  Otherwise, any state
-    // corresponding to any hidden overlays should be cleaned up.
-    virtual void CancelOverlayUI(OverlayPresenter* presenter,
-                                 OverlayRequest* request) = 0;
-  };
-
-  // Sets the UI delegate for the presenter.  Upon being set, the presenter will
-  // attempt to begin presenting overlay UI for the active WebState in its
-  // Browser.
-  virtual void SetUIDelegate(UIDelegate* delegate) = 0;
-
-  // Observer interface for objects interested in overlay presentation events
-  // triggered by OverlayPresenter.
-  class Observer : public base::CheckedObserver {
-   public:
-    Observer() = default;
-
-    // Called when |presenter| is about to show the overlay UI for |request|.
-    virtual void WillShowOverlay(OverlayPresenter* presenter,
-                                 OverlayRequest* request) {}
-
-    // Called when |presenter| is finished dismissing the overlay UI for
-    // |request|.
-    virtual void DidHideOverlay(OverlayPresenter* presenter,
-                                OverlayRequest* request) {}
-
-    // Called when |presenter| is destroyed.
-    virtual void OverlayPresenterDestroyed(OverlayPresenter* presenter) {}
-  };
+  // Sets the presentation context in which to show overlay UI.  Upon being set,
+  // the presenter will attempt to begin presenting overlay UI for the active
+  // WebState in its Browser.
+  virtual void SetPresentationContext(
+      OverlayPresentationContext* presentation_context) = 0;
 
   // Adds and removes observers.
-  virtual void AddObserver(Observer* observer) = 0;
-  virtual void RemoveObserver(Observer* observer) = 0;
+  virtual void AddObserver(OverlayPresenterObserver* observer) = 0;
+  virtual void RemoveObserver(OverlayPresenterObserver* observer) = 0;
 };
 
 #endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTER_H_
diff --git a/ios/chrome/browser/overlays/public/overlay_presenter_observer.h b/ios/chrome/browser/overlays/public/overlay_presenter_observer.h
new file mode 100644
index 0000000..e4bcf81
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/overlay_presenter_observer.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTER_OBSERVER_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTER_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+
+class OverlayPresenter;
+class OverlayRequest;
+
+// Observer interface for objects interested in overlay presentation events
+// triggered by OverlayPresenter.
+class OverlayPresenterObserver : public base::CheckedObserver {
+ public:
+  OverlayPresenterObserver() = default;
+
+  // Called when |presenter| is about to show the overlay UI for |request|.
+  virtual void WillShowOverlay(OverlayPresenter* presenter,
+                               OverlayRequest* request) {}
+
+  // Called when |presenter| is finished dismissing its overlay UI.
+  virtual void DidHideOverlay(OverlayPresenter* presenter,
+                              OverlayRequest* request) {}
+
+  // Called when |presenter| is destroyed.
+  virtual void OverlayPresenterDestroyed(OverlayPresenter* presenter) {}
+};
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTER_OBSERVER_H_
diff --git a/ios/chrome/browser/overlays/test/BUILD.gn b/ios/chrome/browser/overlays/test/BUILD.gn
index 4c7a3ed9..ab2ca63 100644
--- a/ios/chrome/browser/overlays/test/BUILD.gn
+++ b/ios/chrome/browser/overlays/test/BUILD.gn
@@ -5,8 +5,8 @@
 source_set("test") {
   testonly = true
   sources = [
-    "fake_overlay_presenter_ui_delegate.cc",
-    "fake_overlay_presenter_ui_delegate.h",
+    "fake_overlay_presentation_context.cc",
+    "fake_overlay_presentation_context.h",
     "fake_overlay_user_data.cc",
     "fake_overlay_user_data.h",
   ]
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc
new file mode 100644
index 0000000..52ede06
--- /dev/null
+++ b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc
@@ -0,0 +1,87 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h"
+#include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+
+FakeOverlayPresentationContext::FakeOverlayPresentationContext() = default;
+FakeOverlayPresentationContext::~FakeOverlayPresentationContext() = default;
+
+FakeOverlayPresentationContext::PresentationState
+FakeOverlayPresentationContext::GetPresentationState(OverlayRequest* request) {
+  return presentation_states_[request];
+}
+
+void FakeOverlayPresentationContext::SimulateDismissalForRequest(
+    OverlayRequest* request,
+    OverlayDismissalReason reason) {
+  DCHECK_EQ(PresentationState::kPresented, presentation_states_[request]);
+  switch (reason) {
+    case OverlayDismissalReason::kUserInteraction:
+      presentation_states_[request] = PresentationState::kUserDismissed;
+      break;
+    case OverlayDismissalReason::kHiding:
+      presentation_states_[request] = PresentationState::kHidden;
+      break;
+    case OverlayDismissalReason::kCancellation:
+      presentation_states_[request] = PresentationState::kCancelled;
+      break;
+  }
+  std::move(overlay_callbacks_[request]).Run(reason);
+}
+
+void FakeOverlayPresentationContext::SetIsActive(bool active) {
+  if (active_ == active)
+    return;
+
+  for (auto& observer : observers_) {
+    observer.OverlayPresentationContextWillChangeActivationState(this, active);
+  }
+  active_ = active;
+  for (auto& observer : observers_) {
+    observer.OverlayPresentationContextDidChangeActivationState(this);
+  }
+}
+
+void FakeOverlayPresentationContext::AddObserver(
+    OverlayPresentationContextObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void FakeOverlayPresentationContext::RemoveObserver(
+    OverlayPresentationContextObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool FakeOverlayPresentationContext::IsActive() const {
+  return active_;
+}
+
+void FakeOverlayPresentationContext::ShowOverlayUI(
+    OverlayPresenter* presenter,
+    OverlayRequest* request,
+    OverlayDismissalCallback dismissal_callback) {
+  presentation_states_[request] = PresentationState::kPresented;
+  overlay_callbacks_[request] = std::move(dismissal_callback);
+}
+
+void FakeOverlayPresentationContext::HideOverlayUI(OverlayPresenter* presenter,
+                                                   OverlayRequest* request) {
+  SimulateDismissalForRequest(request, OverlayDismissalReason::kHiding);
+}
+
+void FakeOverlayPresentationContext::CancelOverlayUI(
+    OverlayPresenter* presenter,
+    OverlayRequest* request) {
+  PresentationState& state = presentation_states_[request];
+  if (state == PresentationState::kPresented) {
+    SimulateDismissalForRequest(request, OverlayDismissalReason::kCancellation);
+  } else {
+    state = PresentationState::kCancelled;
+  }
+}
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_presenter_ui_delegate.h b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
similarity index 66%
rename from ios/chrome/browser/overlays/test/fake_overlay_presenter_ui_delegate.h
rename to ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
index 8ece1716..b159253 100644
--- a/ios/chrome/browser/overlays/test/fake_overlay_presenter_ui_delegate.h
+++ b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
@@ -2,19 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_OVERLAYS_TEST_FAKE_OVERLAY_PRESENTER_UI_DELEGATE_H_
-#define IOS_CHROME_BROWSER_OVERLAYS_TEST_FAKE_OVERLAY_PRESENTER_UI_DELEGATE_H_
+#ifndef IOS_CHROME_BROWSER_OVERLAYS_TEST_FAKE_OVERLAY_PRESENTATION_CONTEXT_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_TEST_FAKE_OVERLAY_PRESENTATION_CONTEXT_H_
 
 #include <map>
 
-#include "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#include "base/observer_list.h"
+#include "ios/chrome/browser/overlays/public/overlay_presentation_context.h"
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 
 // Fake implementation of OverlayUIDelegate used for testing.
-class FakeOverlayPresenterUIDelegate : public OverlayPresenter::UIDelegate {
+class FakeOverlayPresentationContext : public OverlayPresentationContext {
  public:
-  FakeOverlayPresenterUIDelegate();
-  ~FakeOverlayPresenterUIDelegate() override;
+  FakeOverlayPresentationContext();
+  ~FakeOverlayPresentationContext() override;
 
   // Enum describing the state of the overlay UI.
   enum class PresentationState {
@@ -36,7 +37,13 @@
   void SimulateDismissalForRequest(OverlayRequest* request,
                                    OverlayDismissalReason reason);
 
+  // Setter for whether the context is active.
+  void SetIsActive(bool active);
+
   // OverlayUIDelegate:
+  void AddObserver(OverlayPresentationContextObserver* observer) override;
+  void RemoveObserver(OverlayPresentationContextObserver* observer) override;
+  bool IsActive() const override;
   void ShowOverlayUI(OverlayPresenter* presenter,
                      OverlayRequest* request,
                      OverlayDismissalCallback dismissal_callback) override;
@@ -50,6 +57,12 @@
   std::map<OverlayRequest*, PresentationState> presentation_states_;
   // The callbacks for each OverlayRequest.
   std::map<OverlayRequest*, OverlayDismissalCallback> overlay_callbacks_;
+  // Whether the context is active.
+  bool active_ = true;
+
+  base::ObserverList<OverlayPresentationContextObserver,
+                     /* check_empty= */ true>
+      observers_;
 };
 
-#endif  // IOS_CHROME_BROWSER_OVERLAYS_TEST_FAKE_OVERLAY_PRESENTER_UI_DELEGATE_H_
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_TEST_FAKE_OVERLAY_PRESENTATION_CONTEXT_H_
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_presenter_ui_delegate.cc b/ios/chrome/browser/overlays/test/fake_overlay_presenter_ui_delegate.cc
deleted file mode 100644
index f2e7ead..0000000
--- a/ios/chrome/browser/overlays/test/fake_overlay_presenter_ui_delegate.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/overlays/test/fake_overlay_presenter_ui_delegate.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-
-FakeOverlayPresenterUIDelegate::FakeOverlayPresenterUIDelegate() = default;
-FakeOverlayPresenterUIDelegate::~FakeOverlayPresenterUIDelegate() = default;
-
-FakeOverlayPresenterUIDelegate::PresentationState
-FakeOverlayPresenterUIDelegate::GetPresentationState(OverlayRequest* request) {
-  return presentation_states_[request];
-}
-
-void FakeOverlayPresenterUIDelegate::SimulateDismissalForRequest(
-    OverlayRequest* request,
-    OverlayDismissalReason reason) {
-  DCHECK_EQ(PresentationState::kPresented, presentation_states_[request]);
-  switch (reason) {
-    case OverlayDismissalReason::kUserInteraction:
-      presentation_states_[request] = PresentationState::kUserDismissed;
-      break;
-    case OverlayDismissalReason::kHiding:
-      presentation_states_[request] = PresentationState::kHidden;
-      break;
-    case OverlayDismissalReason::kCancellation:
-      presentation_states_[request] = PresentationState::kCancelled;
-      break;
-  }
-  std::move(overlay_callbacks_[request]).Run(reason);
-}
-
-void FakeOverlayPresenterUIDelegate::ShowOverlayUI(
-    OverlayPresenter* presenter,
-    OverlayRequest* request,
-    OverlayDismissalCallback dismissal_callback) {
-  presentation_states_[request] = PresentationState::kPresented;
-  overlay_callbacks_[request] = std::move(dismissal_callback);
-}
-
-void FakeOverlayPresenterUIDelegate::HideOverlayUI(OverlayPresenter* presenter,
-                                                   OverlayRequest* request) {
-  SimulateDismissalForRequest(request, OverlayDismissalReason::kHiding);
-}
-
-void FakeOverlayPresenterUIDelegate::CancelOverlayUI(
-    OverlayPresenter* presenter,
-    OverlayRequest* request) {
-  PresentationState& state = presentation_states_[request];
-  if (state == PresentationState::kPresented) {
-    SimulateDismissalForRequest(request, OverlayDismissalReason::kCancellation);
-  } else {
-    state = PresentationState::kCancelled;
-  }
-}
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc b/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
index 24f734c..e3e5bca 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
+++ b/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
@@ -26,9 +26,9 @@
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/sync/glue/sync_start_util.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 // static
diff --git a/ios/chrome/browser/payments/BUILD.gn b/ios/chrome/browser/payments/BUILD.gn
index 9b329be..865ef2c 100644
--- a/ios/chrome/browser/payments/BUILD.gn
+++ b/ios/chrome/browser/payments/BUILD.gn
@@ -36,6 +36,7 @@
     "//components/image_fetcher/ios",
     "//components/keyed_service/ios",
     "//components/payments/core",
+    "//components/payments/core:error_strings",
     "//components/payments/mojom",
     "//components/prefs",
     "//components/security_state/core",
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
index 89531496..0e6c5632 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
@@ -13,6 +13,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "components/payments/core/error_strings.h"
 #include "components/payments/core/payment_details.h"
 #include "components/payments/core/payment_instrument.h"
 #import "ios/chrome/browser/payments/payment_request_constants.h"
@@ -239,10 +240,14 @@
 void IOSPaymentInstrumentLauncher::CompleteLaunchRequest(
     const std::string& method_name,
     const std::string& details) {
-  if (!method_name.empty() && !details.empty())
+  if (method_name.empty()) {
+    delegate_->OnInstrumentDetailsError(
+        errors::kMissingMethodNameFromPaymentApp);
+  } else if (details.empty()) {
+    delegate_->OnInstrumentDetailsError(errors::kMissingDetailsFromPaymentApp);
+  } else {
     delegate_->OnInstrumentDetailsReady(method_name, details);
-  else
-    delegate_->OnInstrumentDetailsError();
+  }
   delegate_ = nullptr;
   payment_request_id_ = "";
 }
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
index 82b22a18..d343ea6 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
@@ -43,7 +43,7 @@
     on_instrument_details_ready_called_ = true;
   }
 
-  void OnInstrumentDetailsError() override {
+  void OnInstrumentDetailsError(const std::string& error_message) override {
     if (run_loop_)
       run_loop_->Quit();
     on_instrument_details_error_called_ = true;
diff --git a/ios/chrome/browser/payments/payment_response_helper.h b/ios/chrome/browser/payments/payment_response_helper.h
index 9398ecb9..09c3fa2 100644
--- a/ios/chrome/browser/payments/payment_response_helper.h
+++ b/ios/chrome/browser/payments/payment_response_helper.h
@@ -48,7 +48,7 @@
   void OnInstrumentDetailsReady(
       const std::string& method_name,
       const std::string& stringified_details) override;
-  void OnInstrumentDetailsError() override;
+  void OnInstrumentDetailsError(const std::string& error_message) override;
 
  private:
   // Called when the AddressNormalizationManager is done, whether any autofill
diff --git a/ios/chrome/browser/payments/payment_response_helper.mm b/ios/chrome/browser/payments/payment_response_helper.mm
index 26ab1ed..d85f780 100644
--- a/ios/chrome/browser/payments/payment_response_helper.mm
+++ b/ios/chrome/browser/payments/payment_response_helper.mm
@@ -62,7 +62,8 @@
           &PaymentResponseHelper::AddressNormalizationCompleted, AsWeakPtr()));
 }
 
-void PaymentResponseHelper::OnInstrumentDetailsError() {
+void PaymentResponseHelper::OnInstrumentDetailsError(
+    const std::string& error_message) {
   [consumer_ paymentResponseHelperDidFailToReceivePaymentMethodDetails];
 }
 
diff --git a/ios/chrome/browser/prerender/BUILD.gn b/ios/chrome/browser/prerender/BUILD.gn
index 9cb0c20f..27db3fd 100644
--- a/ios/chrome/browser/prerender/BUILD.gn
+++ b/ios/chrome/browser/prerender/BUILD.gn
@@ -36,6 +36,7 @@
     "//ios/chrome/browser/web",
     "//ios/chrome/browser/web_state_list",
     "//ios/web/public/deprecated",
+    "//ios/web/public/deprecated:deprecated_web_util",
     "//ui/base",
     "//url",
   ]
diff --git a/ios/chrome/browser/prerender/preload_controller.mm b/ios/chrome/browser/prerender/preload_controller.mm
index 04f5eb0..60a8ecc 100644
--- a/ios/chrome/browser/prerender/preload_controller.mm
+++ b/ios/chrome/browser/prerender/preload_controller.mm
@@ -26,9 +26,9 @@
 #import "ios/chrome/browser/tabs/legacy_tab_helper.h"
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_helper_util.h"
-#import "ios/chrome/browser/tabs/tab_private.h"
 #import "ios/web/public/deprecated/crw_native_content.h"
 #import "ios/web/public/deprecated/crw_native_content_holder.h"
+#import "ios/web/public/deprecated/crw_web_controller_util.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/thread/web_thread.h"
@@ -277,9 +277,7 @@
   std::unique_ptr<web::WebState> webState = std::move(webState_);
   DCHECK(![self isWebStatePrerendered:webState.get()]);
 
-  Tab* tab = LegacyTabHelper::GetTabForWebState(webState.get());
-  [tab.webController nativeContentHolder].nativeProvider = nil;
-
+  web_deprecated::SetNativeProvider(webState.get(), nil);
   webState->RemoveObserver(webStateObserver_.get());
   breakpad::StopMonitoringURLsForWebState(webState.get());
   webState->SetDelegate(nullptr);
@@ -401,10 +399,7 @@
       std::make_unique<web::WebStatePolicyDeciderBridge>(webState_.get(), self);
   AttachTabHelpers(webState_.get(), /*for_prerender=*/true);
 
-  Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get());
-  DCHECK(tab);
-
-  [tab.webController nativeContentHolder].nativeProvider = nil;
+  web_deprecated::SetNativeProvider(webState_.get(), nil);
 
   webState_->SetDelegate(webStateDelegate_.get());
   webState_->AddObserver(webStateObserver_.get());
@@ -448,8 +443,7 @@
   UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName, reason,
                             PRERENDER_FINAL_STATUS_MAX);
 
-  Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get());
-  [tab.webController nativeContentHolder].nativeProvider = nil;
+  web_deprecated::SetNativeProvider(webState_.get(), nil);
   webState_->RemoveObserver(webStateObserver_.get());
   breakpad::StopMonitoringURLsForWebState(webState_.get());
   webState_->SetDelegate(nullptr);
diff --git a/ios/chrome/browser/search_engines/template_url_service_factory.cc b/ios/chrome/browser/search_engines/template_url_service_factory.cc
index a87287d..24b178a5 100644
--- a/ios/chrome/browser/search_engines/template_url_service_factory.cc
+++ b/ios/chrome/browser/search_engines/template_url_service_factory.cc
@@ -15,11 +15,11 @@
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/google/google_url_tracker_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/search_engines/template_url_service_client_impl.h"
 #include "ios/chrome/browser/search_engines/ui_thread_search_terms_data.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #include "rlz/buildflags/buildflags.h"
 
 #if BUILDFLAG(ENABLE_RLZ)
diff --git a/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.cc b/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.cc
index df233e6f..744f4e37 100644
--- a/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.cc
+++ b/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.cc
@@ -12,6 +12,7 @@
 #include "components/send_tab_to_self/send_tab_to_self_metrics.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/window_open_disposition.h"
 #include "ui/strings/grit/ui_strings.h"
 
 namespace send_tab_to_self {
@@ -37,7 +38,12 @@
 }
 
 int IOSSendTabToSelfInfoBarDelegate::GetButtons() const {
-  return BUTTON_NONE;
+  return BUTTON_OK;
+}
+
+base::string16 IOSSendTabToSelfInfoBarDelegate::GetButtonLabel(
+    InfoBarButton button) const {
+  return l10n_util::GetStringUTF16(IDS_SEND_TAB_TO_SELF_INFOBAR_MESSAGE_URL);
 }
 
 int IOSSendTabToSelfInfoBarDelegate::GetIconId() const {
@@ -49,25 +55,13 @@
 }
 
 base::string16 IOSSendTabToSelfInfoBarDelegate::GetMessageText() const {
-  // The iOS confirm infobar controller requires the message to also include the
-  // link text.
-  return l10n_util::GetStringUTF16(IDS_SEND_TAB_TO_SELF_INFOBAR_MESSAGE) +
-         base::UTF8ToUTF16(" ") +
-         l10n_util::GetStringUTF16(IDS_SEND_TAB_TO_SELF_INFOBAR_MESSAGE_URL);
+  return l10n_util::GetStringUTF16(IDS_SEND_TAB_TO_SELF_INFOBAR_MESSAGE);
 }
 
-base::string16 IOSSendTabToSelfInfoBarDelegate::GetLinkText() const {
-  return l10n_util::GetStringUTF16(IDS_SEND_TAB_TO_SELF_INFOBAR_MESSAGE_URL);
-}
-
-GURL IOSSendTabToSelfInfoBarDelegate::GetLinkURL() const {
-  return entry_->GetURL();
-}
-
-bool IOSSendTabToSelfInfoBarDelegate::LinkClicked(
-    WindowOpenDisposition disposition) {
+bool IOSSendTabToSelfInfoBarDelegate::Accept() {
   RecordNotificationHistogram(SendTabToSelfNotification::kOpened);
-  infobar()->owner()->OpenURL(GetLinkURL(), disposition);
+  infobar()->owner()->OpenURL(entry_->GetURL(),
+                              WindowOpenDisposition::CURRENT_TAB);
   return true;
 }
 
diff --git a/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.h b/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.h
index d739f9e..f4a32b8 100644
--- a/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.h
+++ b/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.h
@@ -29,12 +29,11 @@
   // ConfirmInfoBarDelegate:
   infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
   int GetButtons() const override;
+  base::string16 GetButtonLabel(InfoBarButton button) const override;
   int GetIconId() const override;
   void InfoBarDismissed() override;
   base::string16 GetMessageText() const override;
-  base::string16 GetLinkText() const override;
-  GURL GetLinkURL() const override;
-  bool LinkClicked(WindowOpenDisposition disposition) override;
+  bool Accept() override;
   bool Cancel() override;
 
   // The entry that was share to this device. Must outlive this instance.
diff --git a/ios/chrome/browser/sessions/session_service_ios.mm b/ios/chrome/browser/sessions/session_service_ios.mm
index d35842ae..3f1ee2e9 100644
--- a/ios/chrome/browser/sessions/session_service_ios.mm
+++ b/ios/chrome/browser/sessions/session_service_ios.mm
@@ -138,8 +138,17 @@
     if (!data)
       return nil;
 
+    NSError* error = nil;
     NSKeyedUnarchiver* unarchiver =
-        [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
+        [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error];
+    if (!unarchiver || error) {
+      DLOG(WARNING) << "Error creating unarchiver, session file: "
+                    << base::SysNSStringToUTF8(sessionPath) << ": "
+                    << base::SysNSStringToUTF8([error description]);
+      return nil;
+    }
+
+    unarchiver.requiresSecureCoding = NO;
 
     // Register compatibility aliases to support legacy saved sessions.
     [unarchiver cr_registerCompatibilityAliases];
@@ -176,10 +185,11 @@
           return;
 
         NSError* error = nil;
-        if (![fileManager removeItemAtPath:sessionPath error:nil])
+        if (![fileManager removeItemAtPath:sessionPath error:&error] || error) {
           CHECK(false) << "Unable to delete session file: "
                        << base::SysNSStringToUTF8(sessionPath) << ": "
                        << base::SysNSStringToUTF8([error description]);
+        }
       }),
       std::move(callback));
 }
@@ -202,7 +212,17 @@
   SessionIOS* session = factory();
 
   @try {
-    NSData* sessionData = [NSKeyedArchiver archivedDataWithRootObject:session];
+    NSError* error = nil;
+    NSData* sessionData = [NSKeyedArchiver archivedDataWithRootObject:session
+                                                requiringSecureCoding:NO
+                                                                error:&error];
+    if (!sessionData || error) {
+      DLOG(WARNING) << "Error serializing session for path: "
+                    << base::SysNSStringToUTF8(sessionPath) << ": "
+                    << base::SysNSStringToUTF8([error description]);
+      return;
+    }
+
     _taskRunner->PostTask(FROM_HERE, base::BindOnce(^{
                             [self performSaveSessionData:sessionData
                                              sessionPath:sessionPath];
diff --git a/ios/chrome/browser/sessions/session_window_ios_unittest.mm b/ios/chrome/browser/sessions/session_window_ios_unittest.mm
index a76f128..2fcba9e 100644
--- a/ios/chrome/browser/sessions/session_window_ios_unittest.mm
+++ b/ios/chrome/browser/sessions/session_window_ios_unittest.mm
@@ -49,12 +49,19 @@
 TEST_F(SessionWindowIOSTest, CodingEncoding) {
   SessionWindowIOS* original_session_window = CreateSessionWindowForTest(1u);
 
+  NSError* error = nil;
   NSData* data =
-      [NSKeyedArchiver archivedDataWithRootObject:original_session_window];
+      [NSKeyedArchiver archivedDataWithRootObject:original_session_window
+                            requiringSecureCoding:NO
+                                            error:&error];
   ASSERT_TRUE(data != nil);
+  ASSERT_TRUE(error == nil);
 
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
+  unarchiver.requiresSecureCoding = NO;
   SessionWindowIOS* unarchived_session_window =
-      [NSKeyedUnarchiver unarchiveObjectWithData:data];
+      [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
   ASSERT_TRUE(unarchived_session_window != nil);
 
   EXPECT_EQ(1u, unarchived_session_window.selectedIndex);
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h
index 2f202ac..b0fbeb6 100644
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h
+++ b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h
@@ -39,6 +39,7 @@
   sessions::LiveTab* GetLiveTabAt(int index) const override;
   sessions::LiveTab* GetActiveLiveTab() const override;
   bool IsTabPinned(int index) const override;
+  base::Optional<base::Token> GetTabGroupForTab(int index) const override;
   const gfx::Rect GetRestoredBounds() const override;
   ui::WindowShowState GetRestoredState() const override;
   std::string GetWorkspace() const override;
@@ -47,6 +48,7 @@
       int tab_index,
       int selected_navigation,
       const std::string& extension_app_id,
+      base::Optional<base::Token> group,
       bool select,
       bool pin,
       bool from_last_session,
@@ -54,6 +56,7 @@
       const std::string& user_agent_override) override;
   sessions::LiveTab* ReplaceRestoredTab(
       const std::vector<sessions::SerializedNavigationEntry>& navigations,
+      base::Optional<base::Token> group,
       int selected_navigation,
       bool from_last_session,
       const std::string& extension_app_id,
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.mm b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.mm
index 4b9044ab..714ce92 100644
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.mm
+++ b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.mm
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/optional.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/sessions/core/session_types.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -70,6 +71,12 @@
   return false;
 }
 
+base::Optional<base::Token> TabRestoreServiceDelegateImplIOS::GetTabGroupForTab(
+    int index) const {
+  // Not supported by iOS.
+  return base::nullopt;
+}
+
 const gfx::Rect TabRestoreServiceDelegateImplIOS::GetRestoredBounds() const {
   // Not supported by iOS.
   return gfx::Rect();
@@ -90,6 +97,7 @@
     int tab_index,
     int selected_navigation,
     const std::string& extension_app_id,
+    base::Optional<base::Token> group,
     bool select,
     bool pin,
     bool from_last_session,
@@ -108,6 +116,7 @@
 
 sessions::LiveTab* TabRestoreServiceDelegateImplIOS::ReplaceRestoredTab(
     const std::vector<sessions::SerializedNavigationEntry>& navigations,
+    base::Optional<base::Token> group,
     int selected_navigation,
     bool from_last_session,
     const std::string& extension_app_id,
diff --git a/ios/chrome/browser/sessions/test_session_service.mm b/ios/chrome/browser/sessions/test_session_service.mm
index dd9098d3c..ec1f5de 100644
--- a/ios/chrome/browser/sessions/test_session_service.mm
+++ b/ios/chrome/browser/sessions/test_session_service.mm
@@ -23,9 +23,12 @@
           directory:(NSString*)directory
         immediately:(BOOL)immediately {
   NSString* sessionPath = [[self class] sessionPathForDirectory:directory];
-  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:factory()];
-  if (self.performIO)
+  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:factory()
+                                       requiringSecureCoding:NO
+                                                       error:nil];
+  if (self.performIO) {
     [self performSaveSessionData:data sessionPath:sessionPath];
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/share_extension/share_extension_item_receiver.mm b/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
index 2aeb855..c7a1f2b 100644
--- a/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
+++ b/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
@@ -195,7 +195,18 @@
 }
 
 - (BOOL)receivedData:(NSData*)data withCompletion:(ProceduralBlock)completion {
-  id entryID = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+  NSError* error = nil;
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error];
+  if (!unarchiver || error) {
+    DLOG(WARNING) << "Error creating share extension item unarchiver: "
+                  << base::SysNSStringToUTF8([error description]);
+    return NO;
+  }
+
+  unarchiver.requiresSecureCoding = NO;
+
+  id entryID = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
   NSDictionary* entry = base::mac::ObjCCast<NSDictionary>(entryID);
   if (!entry) {
     if (completion) {
diff --git a/ios/chrome/browser/signin/ios_chrome_signin_client.mm b/ios/chrome/browser/signin/ios_chrome_signin_client.mm
index 1fa899f..00a2785 100644
--- a/ios/chrome/browser/signin/ios_chrome_signin_client.mm
+++ b/ios/chrome/browser/signin/ios_chrome_signin_client.mm
@@ -12,9 +12,9 @@
 #include "ios/chrome/browser/browser_state/browser_state_info_cache.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
 #include "ios/chrome/browser/signin/gaia_auth_fetcher_ios.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
diff --git a/ios/chrome/browser/ssl/ios_ssl_blocking_page.h b/ios/chrome/browser/ssl/ios_ssl_blocking_page.h
index f4fdcec..22c0e5c 100644
--- a/ios/chrome/browser/ssl/ios_ssl_blocking_page.h
+++ b/ios/chrome/browser/ssl/ios_ssl_blocking_page.h
@@ -63,10 +63,6 @@
   const net::SSLInfo ssl_info_;
   const bool overridable_;  // The UI allows the user to override the error.
 
-  // The user previously allowed a bad certificate, but the decision has now
-  // expired.
-  const bool expired_but_previously_allowed_;
-
   std::unique_ptr<IOSChromeControllerClient> controller_;
   std::unique_ptr<security_interstitials::SSLErrorUI> ssl_error_ui_;
 
diff --git a/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm b/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
index f338c2f..f93523af 100644
--- a/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
+++ b/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
@@ -30,39 +30,6 @@
 using security_interstitials::SSLErrorUI;
 
 namespace {
-// Events for UMA. Do not reorder or change!
-enum SSLExpirationAndDecision {
-  EXPIRED_AND_PROCEED,
-  EXPIRED_AND_DO_NOT_PROCEED,
-  NOT_EXPIRED_AND_PROCEED,
-  NOT_EXPIRED_AND_DO_NOT_PROCEED,
-  END_OF_SSL_EXPIRATION_AND_DECISION,
-};
-
-void RecordSSLExpirationPageEventState(bool expired_but_previously_allowed,
-                                       bool proceed,
-                                       bool overridable) {
-  SSLExpirationAndDecision event;
-  if (expired_but_previously_allowed && proceed)
-    event = EXPIRED_AND_PROCEED;
-  else if (expired_but_previously_allowed && !proceed)
-    event = EXPIRED_AND_DO_NOT_PROCEED;
-  else if (!expired_but_previously_allowed && proceed)
-    event = NOT_EXPIRED_AND_PROCEED;
-  else
-    event = NOT_EXPIRED_AND_DO_NOT_PROCEED;
-
-  if (overridable) {
-    UMA_HISTOGRAM_ENUMERATION(
-        "interstitial.ssl.expiration_and_decision.overridable", event,
-        END_OF_SSL_EXPIRATION_AND_DECISION);
-  } else {
-    UMA_HISTOGRAM_ENUMERATION(
-        "interstitial.ssl.expiration_and_decision.nonoverridable", event,
-        END_OF_SSL_EXPIRATION_AND_DECISION);
-  }
-}
-
 IOSChromeMetricsHelper* CreateMetricsHelper(web::WebState* web_state,
                                             const GURL& request_url,
                                             bool overridable) {
@@ -88,8 +55,6 @@
       callback_(std::move(callback)),
       ssl_info_(ssl_info),
       overridable_(IsOverridable(options_mask)),
-      expired_but_previously_allowed_(
-          (options_mask & SSLErrorUI::EXPIRED_BUT_PREVIOUSLY_ALLOWED) != 0),
       controller_(new IOSChromeControllerClient(
           web_state,
           base::WrapUnique(CreateMetricsHelper(web_state,
@@ -117,8 +82,6 @@
   if (!callback_.is_null()) {
     // The page is closed without the user having chosen what to do, default to
     // deny.
-    RecordSSLExpirationPageEventState(expired_but_previously_allowed_, false,
-                                      overridable_);
     NotifyDenyCertificate();
   }
 }
@@ -148,18 +111,12 @@
 }
 
 void IOSSSLBlockingPage::OnProceed() {
-  RecordSSLExpirationPageEventState(expired_but_previously_allowed_, true,
-                                    overridable_);
-
   // Accepting the certificate resumes the loading of the page.
   DCHECK(!callback_.is_null());
   std::move(callback_).Run(true);
 }
 
 void IOSSSLBlockingPage::OnDontProceed() {
-  RecordSSLExpirationPageEventState(expired_but_previously_allowed_, false,
-                                    overridable_);
-
   NotifyDenyCertificate();
 }
 
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index 19a3778..f8a32d58 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -47,6 +47,7 @@
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
@@ -62,7 +63,6 @@
 #include "ios/chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "ios/chrome/browser/sync/session_sync_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory.cc b/ios/chrome/browser/sync/profile_sync_service_factory.cc
index 1f4a2fdc..d28e6ee 100644
--- a/ios/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/ios/chrome/browser/sync/profile_sync_service_factory.cc
@@ -27,6 +27,7 @@
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
 #include "ios/chrome/browser/gcm/ios_chrome_gcm_profile_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
@@ -45,7 +46,6 @@
 #include "ios/chrome/browser/sync/model_type_store_service_factory.h"
 #include "ios/chrome/browser/sync/session_sync_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
diff --git a/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm b/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
index b6e3843..14bc40a 100644
--- a/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
+++ b/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
@@ -16,9 +16,9 @@
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/sync/glue/sync_start_util.h"
 #include "ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h"
-#include "ios/chrome/browser/tab_parenting_global_observer.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_list.h"
+#include "ios/chrome/browser/tabs/tab_parenting_global_observer.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index 0467a87..36979f39 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -12,7 +12,8 @@
     "tab_model_list_observer.h",
     "tab_model_synced_window_delegate.h",
     "tab_model_synced_window_delegate_getter.h",
-    "tab_private.h",
+    "tab_parenting_global_observer.cc",
+    "tab_parenting_global_observer.h",
     "tab_title_util.h",
   ]
   public_deps = [
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index c083ac54..9a21f36 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #import "ios/chrome/browser/tabs/tab.h"
-#import "ios/chrome/browser/tabs/tab_private.h"
 
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
@@ -73,15 +72,3 @@
 }
 
 @end
-
-#pragma mark - TestingSupport
-
-@implementation Tab (TestingSupport)
-
-// TODO(crbug.com/620465): this require the Tab's WebState to be a WebStateImpl,
-// remove this helper once this is no longer true (and fix the unit tests).
-- (CRWWebController*)webController {
-  return _webStateImpl ? _webStateImpl->GetWebController() : nil;
-}
-
-@end
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 7988d66c..d4a7cd82 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -38,7 +38,6 @@
 #import "ios/chrome/browser/sessions/session_window_ios.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
-#include "ios/chrome/browser/tab_parenting_global_observer.h"
 #import "ios/chrome/browser/tabs/legacy_tab_helper.h"
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model_closing_web_state_observer.h"
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm
index 995e622..36f322c9 100644
--- a/ios/chrome/browser/tabs/tab_model_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -23,7 +23,6 @@
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_helper_util.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/tabs/tab_private.h"
 #import "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
diff --git a/ios/chrome/browser/tab_parenting_global_observer.cc b/ios/chrome/browser/tabs/tab_parenting_global_observer.cc
similarity index 92%
rename from ios/chrome/browser/tab_parenting_global_observer.cc
rename to ios/chrome/browser/tabs/tab_parenting_global_observer.cc
index 0f2c13f..8aa1996 100644
--- a/ios/chrome/browser/tab_parenting_global_observer.cc
+++ b/ios/chrome/browser/tabs/tab_parenting_global_observer.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 "ios/chrome/browser/tab_parenting_global_observer.h"
+#include "ios/chrome/browser/tabs/tab_parenting_global_observer.h"
 
 #include "base/no_destructor.h"
 
diff --git a/ios/chrome/browser/tab_parenting_global_observer.h b/ios/chrome/browser/tabs/tab_parenting_global_observer.h
similarity index 86%
rename from ios/chrome/browser/tab_parenting_global_observer.h
rename to ios/chrome/browser/tabs/tab_parenting_global_observer.h
index 1af30b8..092bbfa 100644
--- a/ios/chrome/browser/tab_parenting_global_observer.h
+++ b/ios/chrome/browser/tabs/tab_parenting_global_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_TAB_PARENTING_GLOBAL_OBSERVER_H_
-#define IOS_CHROME_BROWSER_TAB_PARENTING_GLOBAL_OBSERVER_H_
+#ifndef IOS_CHROME_BROWSER_TABS_TAB_PARENTING_GLOBAL_OBSERVER_H_
+#define IOS_CHROME_BROWSER_TABS_TAB_PARENTING_GLOBAL_OBSERVER_H_
 
 #include <memory>
 
@@ -43,4 +43,4 @@
   DISALLOW_COPY_AND_ASSIGN(TabParentingGlobalObserver);
 };
 
-#endif  // IOS_CHROME_BROWSER_TAB_PARENTING_GLOBAL_OBSERVER_H_
+#endif  // IOS_CHROME_BROWSER_TABS_TAB_PARENTING_GLOBAL_OBSERVER_H_
diff --git a/ios/chrome/browser/tabs/tab_parenting_observer.mm b/ios/chrome/browser/tabs/tab_parenting_observer.mm
index a993f2c..7e907ac 100644
--- a/ios/chrome/browser/tabs/tab_parenting_observer.mm
+++ b/ios/chrome/browser/tabs/tab_parenting_observer.mm
@@ -4,7 +4,7 @@
 
 #import "ios/chrome/browser/tabs/tab_parenting_observer.h"
 
-#include "ios/chrome/browser/tab_parenting_global_observer.h"
+#include "ios/chrome/browser/tabs/tab_parenting_global_observer.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/tabs/tab_private.h b/ios/chrome/browser/tabs/tab_private.h
deleted file mode 100644
index 2ab0577..0000000
--- a/ios/chrome/browser/tabs/tab_private.h
+++ /dev/null
@@ -1,20 +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_TABS_TAB_PRIVATE_H_
-#define IOS_CHROME_BROWSER_TABS_TAB_PRIVATE_H_
-
-@class CRWWebController;
-
-// Exposed private methods for testing purpose.
-@interface Tab (TestingSupport)
-
-// The CRWWebController from the Tab's WebState. This should only be used
-// by tests and will be removed when Tab can wrap TestWebState (see issue
-// crbug.com/620465 for progress).
-@property(nonatomic, readonly) CRWWebController* webController;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_TABS_TAB_PRIVATE_H_
diff --git a/ios/chrome/browser/tabs/tab_unittest.mm b/ios/chrome/browser/tabs/tab_unittest.mm
index f0964b9..6708fe5 100644
--- a/ios/chrome/browser/tabs/tab_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_unittest.mm
@@ -33,7 +33,6 @@
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_helper_util.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/tabs/tab_private.h"
 #import "ios/chrome/browser/ui/open_in/open_in_controller.h"
 #import "ios/chrome/browser/ui/open_in/open_in_controller_testing.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
diff --git a/ios/chrome/browser/ui/authentication/cells/BUILD.gn b/ios/chrome/browser/ui/authentication/cells/BUILD.gn
index 72bf9216..2b217a8 100644
--- a/ios/chrome/browser/ui/authentication/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/cells/BUILD.gn
@@ -35,6 +35,7 @@
     "//ios/chrome/browser/ui/table_view:styler",
     "//ios/chrome/browser/ui/table_view/cells",
     "//ios/chrome/common/ui_util",
+    "//ios/chrome/common/ui_util:semantic_colors",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/signin",
     "//ui/base",
@@ -80,6 +81,7 @@
     "//ios/chrome/browser/ui/table_view:styler",
     "//ios/chrome/browser/ui/table_view/cells",
     "//ios/chrome/browser/unified_consent",
+    "//ios/chrome/common/ui_util:semantic_colors",
     "//ios/chrome/test:test_support",
     "//ios/public/provider/chrome/browser/signin:test_support",
     "//ios/third_party/material_components_ios",
diff --git a/ios/chrome/browser/ui/authentication/cells/account_control_item.h b/ios/chrome/browser/ui/authentication/cells/account_control_item.h
index 2ec7c75..c257d13 100644
--- a/ios/chrome/browser/ui/authentication/cells/account_control_item.h
+++ b/ios/chrome/browser/ui/authentication/cells/account_control_item.h
@@ -12,6 +12,9 @@
 // Item for account collection view and sign-in confirmation view.
 @interface AccountControlItem : TableViewItem
 
+// If this image should be tinted to match the text color (e.g. in dark mode),
+// the provided image should have rendering mode
+// UIImageRenderingModeAlwaysTemplate.
 @property(nonatomic, strong) UIImage* image;
 @property(nonatomic, copy) NSString* text;
 @property(nonatomic, copy) NSString* detailText;
diff --git a/ios/chrome/browser/ui/authentication/cells/account_control_item.mm b/ios/chrome/browser/ui/authentication/cells/account_control_item.mm
index 9d50692..1cc956b4 100644
--- a/ios/chrome/browser/ui/authentication/cells/account_control_item.mm
+++ b/ios/chrome/browser/ui/authentication/cells/account_control_item.mm
@@ -6,6 +6,7 @@
 
 #include "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -28,15 +29,15 @@
            withStyler:(ChromeTableViewStyler*)styler {
   [super configureCell:cell withStyler:styler];
   cell.imageView.image = self.image;
+  cell.imageView.tintColor = UIColor.cr_labelColor;
 
   cell.textLabel.text = self.text;
-  cell.textLabel.textColor = UIColor.blackColor;
+  cell.textLabel.textColor = UIColor.cr_labelColor;
 
   cell.detailTextLabel.text = self.detailText;
-  cell.detailTextLabel.textColor =
-      self.shouldDisplayError
-          ? UIColor.redColor
-          : UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+  cell.detailTextLabel.textColor = self.shouldDisplayError
+                                       ? UIColor.redColor
+                                       : UIColor.cr_secondaryLabelColor;
 }
 
 #pragma mark - Helper methods
@@ -167,8 +168,7 @@
   self.imageView.image = nil;
   self.textLabel.text = nil;
   self.detailTextLabel.text = nil;
-  self.detailTextLabel.textColor =
-      UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+  self.detailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
 }
 
 #pragma mark - NSObject(Accessibility)
diff --git a/ios/chrome/browser/ui/authentication/cells/account_control_item_unittest.mm b/ios/chrome/browser/ui/authentication/cells/account_control_item_unittest.mm
index 2ef8463..78299eec 100644
--- a/ios/chrome/browser/ui/authentication/cells/account_control_item_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/cells/account_control_item_unittest.mm
@@ -7,6 +7,7 @@
 #include "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -38,7 +39,7 @@
   EXPECT_FALSE(accountCell.textLabel.text);
   EXPECT_FALSE(accountCell.detailTextLabel.text);
   EXPECT_EQ(UITableViewCellAccessoryNone, accountCell.accessoryType);
-  EXPECT_NSEQ(UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor),
+  EXPECT_NSEQ(UIColor.cr_secondaryLabelColor,
               accountCell.detailTextLabel.textColor);
 
   [item configureCell:cell withStyler:[[ChromeTableViewStyler alloc] init]];
@@ -46,7 +47,7 @@
   EXPECT_NSEQ(mainText, accountCell.textLabel.text);
   EXPECT_NSEQ(detailText, accountCell.detailTextLabel.text);
   EXPECT_EQ(UITableViewCellAccessoryNone, accountCell.accessoryType);
-  EXPECT_NSEQ(UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor),
+  EXPECT_NSEQ(UIColor.cr_secondaryLabelColor,
               accountCell.detailTextLabel.textColor);
 }
 
@@ -73,7 +74,7 @@
   EXPECT_FALSE(accountCell.textLabel.text);
   EXPECT_FALSE(accountCell.detailTextLabel.text);
   EXPECT_EQ(UITableViewCellAccessoryNone, accountCell.accessoryType);
-  EXPECT_NSEQ(UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor),
+  EXPECT_NSEQ(UIColor.cr_secondaryLabelColor,
               accountCell.detailTextLabel.textColor);
 
   [item configureCell:cell withStyler:[[ChromeTableViewStyler alloc] init]];
diff --git a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
index 9b51ac7..89dfe70f 100644
--- a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
+++ b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #include "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -53,8 +54,7 @@
     cell.detailTextLabel.textColor = UIColor.redColor;
   } else {
     cell.errorIcon.image = nil;
-    cell.detailTextLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    cell.detailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
   }
 
   cell.userInteractionEnabled = self.mode == TableViewAccountModeEnabled;
@@ -125,7 +125,7 @@
   _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
   _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
   _textLabel.adjustsFontForContentSizeCategory = YES;
-  _textLabel.textColor = UIColor.blackColor;
+  _textLabel.textColor = UIColor.cr_labelColor;
   [contentView addSubview:_textLabel];
 
   _detailTextLabel = [[UILabel alloc] init];
@@ -133,7 +133,7 @@
   _detailTextLabel.font =
       [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
   _detailTextLabel.adjustsFontForContentSizeCategory = YES;
-  _detailTextLabel.textColor = UIColorFromRGB(kSettingsCellsDetailTextColor);
+  _detailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
   [contentView addSubview:_detailTextLabel];
 }
 
@@ -243,9 +243,8 @@
   self.imageView.image = nil;
   self.textLabel.text = nil;
   self.detailTextLabel.text = nil;
-  self.textLabel.textColor = UIColor.blackColor;
-  self.detailTextLabel.textColor =
-      UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+  self.textLabel.textColor = UIColor.cr_labelColor;
+  self.detailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
   self.errorIcon.image = nil;
   self.userInteractionEnabled = YES;
   self.contentView.alpha = 1;
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index d25dac1..a53aa21 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -27,6 +27,7 @@
 #include "ios/chrome/browser/autofill/autocomplete_history_manager_factory.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/autofill/strike_database_factory.h"
+#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/infobars/infobar.h"
 #include "ios/chrome/browser/infobars/infobar_utils.h"
 #include "ios/chrome/browser/metrics/ukm_url_recorder.h"
@@ -36,7 +37,6 @@
 #include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
 #include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
 #include "ios/chrome/browser/ui/autofill/save_card_infobar_controller.h"
-#include "ios/chrome/browser/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index 4dc7c41..333e403 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -314,7 +314,7 @@
   // same TableView.
   self.searchController =
       [[UISearchController alloc] initWithSearchResultsController:nil];
-  self.searchController.dimsBackgroundDuringPresentation = NO;
+  self.searchController.obscuresBackgroundDuringPresentation = NO;
   self.searchController.searchBar.userInteractionEnabled = NO;
   self.searchController.delegate = self;
   self.searchController.searchResultsUpdater = self;
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index 3af3e3ea..9b5ea0b 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -168,6 +168,7 @@
     "//ios/web/common",
     "//ios/web/public",
     "//ios/web/public/deprecated",
+    "//ios/web/public/deprecated:deprecated_web_util",
     "//third_party/google_toolbox_for_mac",
     "//ui/base",
     "//ui/gfx",
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 9623fe2..e8996aa7 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -69,7 +69,6 @@
 #include "ios/chrome/browser/system_flags.h"
 #import "ios/chrome/browser/tabs/legacy_tab_helper.h"
 #import "ios/chrome/browser/tabs/tab.h"
-#import "ios/chrome/browser/tabs/tab_private.h"
 #import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
 #import "ios/chrome/browser/ui/activity_services/activity_service_legacy_coordinator.h"
 #import "ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h"
@@ -190,6 +189,7 @@
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
 #import "ios/web/public/deprecated/crw_native_content_holder.h"
 #import "ios/web/public/deprecated/crw_native_content_provider.h"
+#import "ios/web/public/deprecated/crw_web_controller_util.h"
 #include "ios/web/public/navigation_item.h"
 #include "ios/web/public/thread/web_thread.h"
 #include "ios/web/public/url_scheme_util.h"
@@ -724,8 +724,6 @@
 
 // Tab creation and selection
 // --------------------------
-// Whether the given tab's URL is an application specific URL.
-- (BOOL)isTabNativePage:(Tab*)tab;
 // Add all delegates to the provided |webState|.
 - (void)installDelegatesForWebState:(web::WebState*)webState;
 // Remove delegates from the provided |webState|.
@@ -736,8 +734,8 @@
 // is notified that the webState has changed.
 - (void)webStateSelected:(web::WebState*)webState
            notifyToolbar:(BOOL)notifyToolbar;
-// Returns the native controller being used by |tab|'s web controller.
-- (id)nativeControllerForTab:(Tab*)tab;
+// Returns the native controller being used by |web_state|'s web controller.
+- (id)nativeControllerForWebState:(web::WebState*)webState;
 
 // Voice Search
 // ------------
@@ -1570,15 +1568,6 @@
   self.secondaryToolbarNoFullscreenHeightConstraint.constant =
       [self secondaryToolbarHeightWithInset];
 
-  // Native content pages depend on |self.view|'s safeArea.  If the BVC is
-  // presented underneath another view (such as the first time welcome view),
-  // the BVC has no safe area set during webController's layout initial, and
-  // won't automatically get another layout without forcing it here.
-  Tab* currentTab = self.tabModel.currentTab;
-  if ([self isTabNativePage:currentTab]) {
-    [currentTab.webController.view setNeedsLayout];
-  }
-
   // Update the tab strip placement.
   if (self.tabStripView) {
     [self showTabStripView:self.tabStripView];
@@ -2757,17 +2746,6 @@
 
 #pragma mark - Private Methods: Tab creation and selection
 
-- (BOOL)isTabNativePage:(Tab*)tab {
-  web::WebState* webState = tab.webState;
-  if (!webState)
-    return NO;
-  web::NavigationItem* visibleItem =
-      webState->GetNavigationManager()->GetVisibleItem();
-  if (!visibleItem)
-    return NO;
-  return web::GetWebClient()->IsAppSpecificURL(visibleItem->GetURL());
-}
-
 - (void)installDelegatesForWebState:(web::WebState*)webState {
   // Unregistration happens when the WebState is removed from the WebStateList.
   DCHECK_NE(webState->GetDelegate(), _webStateDelegate.get());
@@ -2792,13 +2770,11 @@
     OverscrollActionsTabHelper::FromWebState(webState)->SetDelegate(self);
   }
 
-  // TODO(crbug.com/960950): Remove this once webController is moved out of tab.
-  Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
-
   // Install the proper CRWWebController delegates.
-  [tab.webController nativeContentHolder].nativeProvider = self;
-  tab.webController.swipeRecognizerProvider = self.sideSwipeController;
-  tab.webState->SetDelegate(_webStateDelegate.get());
+  web_deprecated::SetNativeProvider(webState, self);
+  web_deprecated::SetSwipeRecognizerProvider(webState,
+                                             self.sideSwipeController);
+  webState->SetDelegate(_webStateDelegate.get());
   SadTabTabHelper::FromWebState(webState)->SetDelegate(_sadTabCoordinator);
   NetExportTabHelper::CreateForWebState(webState, self);
   CaptivePortalDetectorTabHelper::CreateForWebState(webState, self);
@@ -2847,11 +2823,8 @@
     OverscrollActionsTabHelper::FromWebState(webState)->SetDelegate(nil);
   }
 
-  // TODO(crbug.com/960950): Remove this once webController is moved out of tab.
-  Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
-
-  [tab.webController nativeContentHolder].nativeProvider = nil;
-  tab.webController.swipeRecognizerProvider = nil;
+  web_deprecated::SetNativeProvider(webState, nil);
+  web_deprecated::SetSwipeRecognizerProvider(webState, nil);
   webState->SetDelegate(nullptr);
   if (AccountConsistencyService* accountConsistencyService =
           ios::AccountConsistencyServiceFactory::GetForBrowserState(
@@ -2888,9 +2861,8 @@
   }
 }
 
-- (id)nativeControllerForTab:(Tab*)tab {
-  id nativeController =
-      [tab.webController nativeContentHolder].nativeController;
+- (id)nativeControllerForWebState:(web::WebState*)webState {
+  id nativeController = web_deprecated::GetNativeController(webState);
   return nativeController ? nativeController : _temporaryNativeController;
 }
 
@@ -3064,9 +3036,7 @@
 - (void)snapshotGenerator:(SnapshotGenerator*)snapshotGenerator
     willUpdateSnapshotForWebState:(web::WebState*)webState {
   DCHECK(webState);
-  Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
-  DCHECK([self.tabModel indexOfTab:tab] != NSNotFound);
-  id nativeController = [self nativeControllerForTab:tab];
+  id nativeController = [self nativeControllerForWebState:webState];
   if ([nativeController respondsToSelector:@selector(willUpdateSnapshot)]) {
     [nativeController willUpdateSnapshot];
   }
@@ -3686,8 +3656,14 @@
 
 - (CGFloat)overscrollActionsControllerHeaderInset:
     (OverscrollActionsController*)controller {
+  // The current WebState can be nil if the Browser's WebStateList is empty
+  // (e.g. after closing the last tab, etc).
+  web::WebState* currentWebState = self.currentWebState;
+  if (!currentWebState)
+    return 0.0;
+
   OverscrollActionsTabHelper* activeTabHelper =
-      OverscrollActionsTabHelper::FromWebState(self.currentWebState);
+      OverscrollActionsTabHelper::FromWebState(currentWebState);
   if (controller == activeTabHelper->GetOverscrollActionsController()) {
     if (!base::ios::IsRunningOnIOS12OrLater() &&
         self.currentWebState->GetContentsMimeType() == "application/pdf") {
@@ -3754,10 +3730,9 @@
   // be used as the native controller key.
   // TODO(crbug.com/498568): To reduce complexity here, refactor the flow so
   // that native controllers vended here always correspond to the current tab.
-  Tab* currentTab = self.tabModel.currentTab;
   if (!self.currentWebState ||
       self.currentWebState->GetLastCommittedURL() != url ||
-      [[currentTab.webController nativeContentHolder].nativeController
+      [web_deprecated::GetNativeController(self.currentWebState)
           isKindOfClass:[nativeController class]]) {
     _temporaryNativeController = nativeController;
   }
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.mm b/ios/chrome/browser/ui/history/history_table_view_controller.mm
index 3ba97221..89a0051 100644
--- a/ios/chrome/browser/ui/history/history_table_view_controller.mm
+++ b/ios/chrome/browser/ui/history/history_table_view_controller.mm
@@ -214,7 +214,7 @@
   // TableView.
   self.searchController =
       [[UISearchController alloc] initWithSearchResultsController:nil];
-  self.searchController.dimsBackgroundDuringPresentation = NO;
+  self.searchController.obscuresBackgroundDuringPresentation = NO;
   self.searchController.searchBar.delegate = self;
   self.searchController.searchResultsUpdater = self;
   self.searchController.searchBar.backgroundColor = [UIColor clearColor];
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
index dcbe446..8856f80 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
@@ -37,6 +37,8 @@
 
 // Delegate that holds the Infobar information and actions.
 @property(nonatomic, readonly) infobars::InfoBarDelegate* infobarDelegate;
+// NavigationController that contains the modalViewController.
+@property(nonatomic, weak) UINavigationController* modalNavigationController;
 // The transition delegate used by the Coordinator to present the InfobarBanner.
 // nil if no Banner is being presented.
 @property(nonatomic, strong)
@@ -173,7 +175,8 @@
 }
 
 - (void)detachView {
-  [self dismissInfobarBanner:self animated:YES completion:nil];
+  [self dismissInfobarBanner:self animated:NO completion:nil];
+  [self dismissInfobarModal:self animated:NO completion:nil];
   [self stop];
 }
 
@@ -269,14 +272,14 @@
                  completion:(ProceduralBlock)completion {
   DCHECK(self.baseViewController);
   if (self.baseViewController.presentedViewController) {
-    // Deselect infobar badge in parallel with modal dismissal.
-    [self.badgeDelegate infobarModalWillDismiss:self.infobarType];
-    __weak __typeof(self) weakSelf = self;
-
     // If the Modal is being presented by the Banner, call dismiss on it.
     // This way the modal dismissal will animate correctly and the completion
     // block cleans up the banner correctly.
-    if (self.bannerViewController.presentedViewController) {
+    if (self.baseViewController.presentedViewController ==
+        self.bannerViewController) {
+      // Deselect infobar badge in parallel with modal dismissal.
+      [self.badgeDelegate infobarModalWillDismiss:self.infobarType];
+      __weak __typeof(self) weakSelf = self;
       [self.bannerViewController
           dismissViewControllerAnimated:animated
                              completion:^{
@@ -284,7 +287,11 @@
                                    dismissInfobarBannerAnimated:NO
                                                      completion:completion];
                              }];
-    } else {
+
+    } else if (self.baseViewController.presentedViewController ==
+               self.modalNavigationController) {
+      // Deselect infobar badge in parallel with modal dismissal.
+      [self.badgeDelegate infobarModalWillDismiss:self.infobarType];
       [self.baseViewController dismissViewControllerAnimated:animated
                                                   completion:^{
                                                     if (completion)
@@ -359,6 +366,7 @@
       initWithRootViewController:self.modalViewController];
   navController.transitioningDelegate = driver;
   navController.modalPresentationStyle = UIModalPresentationCustom;
+  self.modalNavigationController = navController;
   [presentingViewController presentViewController:navController
                                          animated:YES
                                        completion:nil];
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
index e4ff59f..672c2040 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
@@ -196,9 +196,13 @@
 
 - (void)neverSaveCredentialsForCurrentSite {
   self.passwordInfoBarDelegate->Cancel();
-  // Completely remove the Infobar along with its badge after blacklisting the
-  // Website.
-  [self detachView];
+  [self dismissInfobarModal:self
+                   animated:YES
+                 completion:^{
+                   // Completely remove the Infobar along with its badge after
+                   // blacklisting the Website.
+                   [self detachView];
+                 }];
 }
 
 - (void)presentPasswordSettings {
diff --git a/ios/chrome/browser/ui/ntp/ntp_tile_saver.mm b/ios/chrome/browser/ui/ntp/ntp_tile_saver.mm
index 08ec461..f8c34e5 100644
--- a/ios/chrome/browser/ui/ntp/ntp_tile_saver.mm
+++ b/ios/chrome/browser/ui/ntp/ntp_tile_saver.mm
@@ -149,7 +149,16 @@
 }
 
 void WriteSavedMostVisited(NSDictionary<NSURL*, NTPTile*>* most_visited_data) {
-  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:most_visited_data];
+  NSError* error = nil;
+  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:most_visited_data
+                                       requiringSecureCoding:NO
+                                                       error:&error];
+  if (!data || error) {
+    DLOG(WARNING) << "Error serializing most visited: "
+                  << base::SysNSStringToUTF8([error description]);
+    return;
+  }
+
   NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
   [sharedDefaults setObject:data forKey:app_group::kSuggestedItems];
 
@@ -159,10 +168,19 @@
 
 NSDictionary* ReadSavedMostVisited() {
   NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
+  NSError* error = nil;
+  NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc]
+      initForReadingFromData:[sharedDefaults
+                                 objectForKey:app_group::kSuggestedItems]
+                       error:&error];
+  if (!unarchiver || error) {
+    DLOG(WARNING) << "Error creating unarchiver for most visited: "
+                  << base::SysNSStringToUTF8([error description]);
+    return [[NSMutableDictionary alloc] init];
+  }
 
-  return [NSKeyedUnarchiver
-      unarchiveObjectWithData:[sharedDefaults
-                                  objectForKey:app_group::kSuggestedItems]];
+  unarchiver.requiresSecureCoding = NO;
+  return [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
 }
 
 void UpdateSingleFavicon(const GURL& site_url,
diff --git a/ios/chrome/browser/ui/overlays/BUILD.gn b/ios/chrome/browser/ui/overlays/BUILD.gn
index ddeeabe..c729e88 100644
--- a/ios/chrome/browser/ui/overlays/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/BUILD.gn
@@ -11,8 +11,8 @@
     "overlay_container_coordinator.mm",
     "overlay_coordinator_factory+initialization.h",
     "overlay_coordinator_factory.mm",
-    "overlay_presenter_ui_delegate_impl.h",
-    "overlay_presenter_ui_delegate_impl.mm",
+    "overlay_presentation_context_impl.h",
+    "overlay_presentation_context_impl.mm",
     "overlay_request_ui_state.h",
     "overlay_request_ui_state.mm",
   ]
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm
index e3695f5..b280aaa 100644
--- a/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm
@@ -10,7 +10,7 @@
 #include "base/logging.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/overlays/overlay_container_view_controller.h"
-#import "ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -22,7 +22,7 @@
 // Whether the coordinator is started.
 @property(nonatomic, assign, getter=isStarted) BOOL started;
 // The UI delegate that is used to drive presentation for this container.
-@property(nonatomic, readonly) OverlayPresenterUIDelegateImpl* UIDelegate;
+@property(nonatomic, readonly) OverlayPresentationContextImpl* UIDelegate;
 @end
 
 @implementation OverlayContainerCoordinator
@@ -32,11 +32,11 @@
                                   modality:(OverlayModality)modality {
   if (self = [super initWithBaseViewController:viewController
                                        browser:browser]) {
-    OverlayPresenterUIDelegateImpl::Container::CreateForUserData(browser,
+    OverlayPresentationContextImpl::Container::CreateForUserData(browser,
                                                                  browser);
     _UIDelegate =
-        OverlayPresenterUIDelegateImpl::Container::FromUserData(browser)
-            ->UIDelegateForModality(modality);
+        OverlayPresentationContextImpl::Container::FromUserData(browser)
+            ->PresentationContextForModality(modality);
     DCHECK(_UIDelegate);
   }
   return self;
diff --git a/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
similarity index 73%
rename from ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h
rename to ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
index f73ce9b..041c4b9 100644
--- a/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTER_UI_DELEGATE_IMPL_H_
-#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTER_UI_DELEGATE_IMPL_H_
+#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_IMPL_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_IMPL_H_
 
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #import "ios/chrome/browser/main/browser_observer.h"
-#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#include "ios/chrome/browser/overlays/public/overlay_modality.h"
+#import "ios/chrome/browser/overlays/public/overlay_presentation_context.h"
 #import "ios/chrome/browser/overlays/public/overlay_user_data.h"
 #import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
 #import "ios/chrome/browser/ui/overlays/overlay_request_ui_state.h"
@@ -16,27 +18,27 @@
 @class OverlayRequestCoordinatorFactory;
 @class OverlayContainerCoordinator;
 
-// Implementation of OverlayPresenter::UIDelegate.  An instance of this class
+// Implementation of OverlayPresentationContext.  An instance of this class
 // exists for every OverlayModality for each Browser.  This delegate is scoped
 // to the Browser because it needs to store state even when a Browser's UI is
 // not on screen.  When a Browser's UI is shown, the OverlayContainerCoordinator
 // for each of its OverlayModalities will supply itself to the delegate, which
 // will then present the UI using the container coordinator's presentation
 // context.
-class OverlayPresenterUIDelegateImpl : public OverlayPresenter::UIDelegate {
+class OverlayPresentationContextImpl : public OverlayPresentationContext {
  public:
-  ~OverlayPresenterUIDelegateImpl() override;
+  ~OverlayPresentationContextImpl() override;
 
   // Container that stores the UI delegate for each modality.  Usage example:
   //
-  // OverlayPresenterUIDelegateImpl::Container::FromUserData(browser)->
-  //     UIDelegateForModality(OverlayModality::kWebContentArea);
+  // OverlayPresentationContextImpl::Container::FromUserData(browser)->
+  //     PresentationContextForModality(OverlayModality::kWebContentArea);
   class Container : public OverlayUserData<Container> {
    public:
     ~Container() override;
 
-    // Returns the OverlayPresenterUIDelegateImpl for |modality|.
-    OverlayPresenterUIDelegateImpl* UIDelegateForModality(
+    // Returns the OverlayPresentationContextImpl for |modality|.
+    OverlayPresentationContextImpl* PresentationContextForModality(
         OverlayModality modality);
 
    private:
@@ -44,7 +46,7 @@
     explicit Container(Browser* browser);
 
     Browser* browser_ = nullptr;
-    std::map<OverlayModality, std::unique_ptr<OverlayPresenterUIDelegateImpl>>
+    std::map<OverlayModality, std::unique_ptr<OverlayPresentationContextImpl>>
         ui_delegates_;
   };
 
@@ -54,7 +56,10 @@
   OverlayContainerCoordinator* coordinator() const { return coordinator_; }
   void SetCoordinator(OverlayContainerCoordinator* coordinator);
 
-  // OverlayPresenter::UIDelegate:
+  // OverlayPresentationContext:
+  void AddObserver(OverlayPresentationContextObserver* observer) override;
+  void RemoveObserver(OverlayPresentationContextObserver* observer) override;
+  bool IsActive() const override;
   void ShowOverlayUI(OverlayPresenter* presenter,
                      OverlayRequest* request,
                      OverlayDismissalCallback dismissal_callback) override;
@@ -64,7 +69,7 @@
                        OverlayRequest* request) override;
 
  private:
-  OverlayPresenterUIDelegateImpl(Browser* browser, OverlayModality modality);
+  OverlayPresentationContextImpl(Browser* browser, OverlayModality modality);
 
   // Setter for |request_|.  Setting to a new value will attempt to
   // present the UI for |request|.
@@ -82,9 +87,6 @@
   // Called when the UI for |request_| has finished being dismissed.
   void OverlayUIWasDismissed();
 
-  // Notifies the state for |request_| that its UI has finished being dismissed.
-  void NotifyStateOfDismissal();
-
   // Helper object that detaches the UI delegate for Browser shudown.
   class BrowserShutdownHelper : public BrowserObserver {
    public:
@@ -102,14 +104,15 @@
   // Helper object that listens for UI dismissal events.
   class OverlayDismissalHelper : public OverlayUIDismissalDelegate {
    public:
-    OverlayDismissalHelper(OverlayPresenterUIDelegateImpl* ui_delegate);
+    OverlayDismissalHelper(
+        OverlayPresentationContextImpl* presentation_context);
     ~OverlayDismissalHelper() override;
 
     // OverlayUIDismissalDelegate:
     void OverlayUIDidFinishDismissal(OverlayRequest* request) override;
 
    private:
-    OverlayPresenterUIDelegateImpl* ui_delegate_ = nullptr;
+    OverlayPresentationContextImpl* presentation_context_ = nullptr;
   };
 
   // The presenter whose UI is being handled by this delegate.
@@ -130,8 +133,11 @@
   OverlayRequest* request_ = nullptr;
   // Map storing the UI state for each OverlayRequest.
   std::map<OverlayRequest*, std::unique_ptr<OverlayRequestUIState>> states_;
+  base::ObserverList<OverlayPresentationContextObserver,
+                     /* check_empty= */ true>
+      observers_;
   // Weak pointer factory.
-  base::WeakPtrFactory<OverlayPresenterUIDelegateImpl> weak_factory_;
+  base::WeakPtrFactory<OverlayPresentationContextImpl> weak_factory_;
 };
 
-#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTER_UI_DELEGATE_IMPL_H_
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_IMPL_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
similarity index 69%
rename from ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.mm
rename to ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
index c08e118..74767b8 100644
--- a/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
@@ -2,11 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
 
 #include "base/bind.h"
 #include "base/callback.h"
 #import "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
 #import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
 #import "ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h"
 
@@ -14,31 +16,31 @@
 #error "This file requires ARC support."
 #endif
 
-#pragma mark - OverlayPresenterUIDelegateImpl::Container
+#pragma mark - OverlayPresentationContextImpl::Container
 
-OVERLAY_USER_DATA_SETUP_IMPL(OverlayPresenterUIDelegateImpl::Container);
+OVERLAY_USER_DATA_SETUP_IMPL(OverlayPresentationContextImpl::Container);
 
-OverlayPresenterUIDelegateImpl::Container::Container(Browser* browser)
+OverlayPresentationContextImpl::Container::Container(Browser* browser)
     : browser_(browser) {
   DCHECK(browser_);
 }
 
-OverlayPresenterUIDelegateImpl::Container::~Container() = default;
+OverlayPresentationContextImpl::Container::~Container() = default;
 
-OverlayPresenterUIDelegateImpl*
-OverlayPresenterUIDelegateImpl::Container::UIDelegateForModality(
+OverlayPresentationContextImpl*
+OverlayPresentationContextImpl::Container::PresentationContextForModality(
     OverlayModality modality) {
   auto& ui_delegate = ui_delegates_[modality];
   if (!ui_delegate) {
     ui_delegate = base::WrapUnique(
-        new OverlayPresenterUIDelegateImpl(browser_, modality));
+        new OverlayPresentationContextImpl(browser_, modality));
   }
   return ui_delegate.get();
 }
 
-#pragma mark - OverlayPresenterUIDelegateImpl
+#pragma mark - OverlayPresentationContextImpl
 
-OverlayPresenterUIDelegateImpl::OverlayPresenterUIDelegateImpl(
+OverlayPresentationContextImpl::OverlayPresentationContextImpl(
     Browser* browser,
     OverlayModality modality)
     : presenter_(OverlayPresenter::FromBrowser(browser, modality)),
@@ -50,31 +52,50 @@
       weak_factory_(this) {
   DCHECK(presenter_);
   DCHECK(coordinator_factory_);
-  presenter_->SetUIDelegate(this);
+  presenter_->SetPresentationContext(this);
 }
 
-OverlayPresenterUIDelegateImpl::~OverlayPresenterUIDelegateImpl() = default;
+OverlayPresentationContextImpl::~OverlayPresentationContextImpl() = default;
 
 #pragma mark Public
 
-void OverlayPresenterUIDelegateImpl::SetCoordinator(
+void OverlayPresentationContextImpl::SetCoordinator(
     OverlayContainerCoordinator* coordinator) {
   if (coordinator_ == coordinator)
     return;
-  if (coordinator_ && request_)
-    DismissPresentedUI(OverlayDismissalReason::kHiding);
+
+  for (auto& observer : observers_) {
+    observer.OverlayPresentationContextWillChangeActivationState(this,
+                                                                 !!coordinator);
+  }
 
   coordinator_ = coordinator;
 
   // The new coordinator should be started before provided to the UI delegate.
   DCHECK(!coordinator_ || coordinator_.viewController);
 
-  ShowUIForPresentedRequest();
+  for (auto& observer : observers_) {
+    observer.OverlayPresentationContextDidChangeActivationState(this);
+  }
 }
 
-#pragma mark OverlayPresenter::UIDelegate
+#pragma mark OverlayPresentationContext
 
-void OverlayPresenterUIDelegateImpl::ShowOverlayUI(
+void OverlayPresentationContextImpl::AddObserver(
+    OverlayPresentationContextObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void OverlayPresentationContextImpl::RemoveObserver(
+    OverlayPresentationContextObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool OverlayPresentationContextImpl::IsActive() const {
+  return !!coordinator_;
+}
+
+void OverlayPresentationContextImpl::ShowOverlayUI(
     OverlayPresenter* presenter,
     OverlayRequest* request,
     OverlayDismissalCallback dismissal_callback) {
@@ -88,7 +109,7 @@
   SetRequest(request);
 }
 
-void OverlayPresenterUIDelegateImpl::HideOverlayUI(OverlayPresenter* presenter,
+void OverlayPresentationContextImpl::HideOverlayUI(OverlayPresenter* presenter,
                                                    OverlayRequest* request) {
   DCHECK_EQ(presenter_, presenter);
   DCHECK_EQ(request_, request);
@@ -103,11 +124,11 @@
   } else {
     // Simulate dismissal if there is no container coordinator.
     state->set_dismissal_reason(OverlayDismissalReason::kHiding);
-    NotifyStateOfDismissal();
+    OverlayUIWasDismissed();
   }
 }
 
-void OverlayPresenterUIDelegateImpl::CancelOverlayUI(
+void OverlayPresentationContextImpl::CancelOverlayUI(
     OverlayPresenter* presenter,
     OverlayRequest* request) {
   DCHECK_EQ(presenter_, presenter);
@@ -121,13 +142,11 @@
   }
 
   // If the current request is being cancelled (e.g. for WebState closures) when
-  // there is no coordinator, simulate a dismissal and reset the current
-  // request.
+  // there is no coordinator, simulate a dismissal.
   if (!coordinator_) {
     DCHECK_EQ(request_, request);
     state->set_dismissal_reason(OverlayDismissalReason::kCancellation);
     state->OverlayUIWasDismissed();
-    SetRequest(nullptr);
     return;
   }
 
@@ -136,7 +155,7 @@
 
 #pragma mark Accesors
 
-void OverlayPresenterUIDelegateImpl::SetRequest(OverlayRequest* request) {
+void OverlayPresentationContextImpl::SetRequest(OverlayRequest* request) {
   if (request_ == request)
     return;
   if (request_) {
@@ -164,14 +183,14 @@
   ShowUIForPresentedRequest();
 }
 
-OverlayRequestUIState* OverlayPresenterUIDelegateImpl::GetRequestUIState(
+OverlayRequestUIState* OverlayPresentationContextImpl::GetRequestUIState(
     OverlayRequest* request) {
   return request ? states_[request].get() : nullptr;
 }
 
 #pragma mark Presentation and Dismissal helpers
 
-void OverlayPresenterUIDelegateImpl::ShowUIForPresentedRequest() {
+void OverlayPresentationContextImpl::ShowUIForPresentedRequest() {
   OverlayRequestUIState* state = GetRequestUIState(request_);
   if (!state || !coordinator_)
     return;
@@ -192,7 +211,7 @@
   state->OverlayUIWasPresented();
 }
 
-void OverlayPresenterUIDelegateImpl::DismissPresentedUI(
+void OverlayPresentationContextImpl::DismissPresentedUI(
     OverlayDismissalReason reason) {
   OverlayRequestUIState* state = GetRequestUIState(request_);
   DCHECK(state);
@@ -204,18 +223,7 @@
       stopAnimated:reason == OverlayDismissalReason::kUserInteraction];
 }
 
-void OverlayPresenterUIDelegateImpl::OverlayUIWasDismissed() {
-  DCHECK(request_);
-  // Overlays are dismissed without animation when the container coordinator is
-  // reset, but the state should not be notified of these dismissals since the
-  // same UI will be presented again once a new container coordinator is
-  // provided.
-  if (!coordinator_)
-    return;
-  NotifyStateOfDismissal();
-}
-
-void OverlayPresenterUIDelegateImpl::NotifyStateOfDismissal() {
+void OverlayPresentationContextImpl::OverlayUIWasDismissed() {
   DCHECK(request_);
   DCHECK(GetRequestUIState(request_)->has_callback());
   // If there is another request in the active WebState's OverlayRequestQueue,
@@ -231,7 +239,7 @@
 
 #pragma mark BrowserShutdownHelper
 
-OverlayPresenterUIDelegateImpl::BrowserShutdownHelper::BrowserShutdownHelper(
+OverlayPresentationContextImpl::BrowserShutdownHelper::BrowserShutdownHelper(
     Browser* browser,
     OverlayPresenter* presenter)
     : presenter_(presenter) {
@@ -239,29 +247,29 @@
   browser->AddObserver(this);
 }
 
-OverlayPresenterUIDelegateImpl::BrowserShutdownHelper::
+OverlayPresentationContextImpl::BrowserShutdownHelper::
     ~BrowserShutdownHelper() = default;
 
-void OverlayPresenterUIDelegateImpl::BrowserShutdownHelper::BrowserDestroyed(
+void OverlayPresentationContextImpl::BrowserShutdownHelper::BrowserDestroyed(
     Browser* browser) {
-  presenter_->SetUIDelegate(nullptr);
+  presenter_->SetPresentationContext(nullptr);
   browser->RemoveObserver(this);
 }
 
 #pragma mark OverlayDismissalHelper
 
-OverlayPresenterUIDelegateImpl::OverlayDismissalHelper::OverlayDismissalHelper(
-    OverlayPresenterUIDelegateImpl* ui_delegate)
-    : ui_delegate_(ui_delegate) {
-  DCHECK(ui_delegate_);
+OverlayPresentationContextImpl::OverlayDismissalHelper::OverlayDismissalHelper(
+    OverlayPresentationContextImpl* presentation_context)
+    : presentation_context_(presentation_context) {
+  DCHECK(presentation_context_);
 }
 
-OverlayPresenterUIDelegateImpl::OverlayDismissalHelper::
+OverlayPresentationContextImpl::OverlayDismissalHelper::
     ~OverlayDismissalHelper() = default;
 
-void OverlayPresenterUIDelegateImpl::OverlayDismissalHelper::
+void OverlayPresentationContextImpl::OverlayDismissalHelper::
     OverlayUIDidFinishDismissal(OverlayRequest* request) {
   DCHECK(request);
-  DCHECK_EQ(ui_delegate_->request_, request);
-  ui_delegate_->OverlayUIWasDismissed();
+  DCHECK_EQ(presentation_context_->request_, request);
+  presentation_context_->OverlayUIWasDismissed();
 }
diff --git a/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm b/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm
index 29aa382..d54a210 100644
--- a/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm
@@ -108,7 +108,7 @@
   self.searchController =
       [[UISearchController alloc] initWithSearchResultsController:nil];
   self.searchController.searchResultsUpdater = self;
-  self.searchController.dimsBackgroundDuringPresentation = NO;
+  self.searchController.obscuresBackgroundDuringPresentation = NO;
   self.searchController.hidesNavigationBarDuringPresentation = NO;
   self.searchController.searchBar.accessibilityIdentifier =
       kPaymentRequestPickerSearchBarAccessibilityID;
diff --git a/ios/chrome/browser/ui/qr_scanner/camera_controller.mm b/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
index e83efbe..2157e8b5 100644
--- a/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
+++ b/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
@@ -29,6 +29,9 @@
 // The current availability of the torch.
 @property(nonatomic, readwrite, assign, getter=isTorchAvailable)
     BOOL torchAvailable;
+// The state of KVO for the camera. Used to stop observing on dealloc.
+@property(nonatomic, readwrite, assign, getter=isObservingCamera)
+    BOOL observingCamera;
 // The capture session for recording video and detecting QR codes.
 @property(nonatomic, readwrite) AVCaptureSession* captureSession;
 // The metadata output attached to the capture session.
@@ -69,7 +72,7 @@
   self = [super init];
   if (self) {
     DCHECK(delegate);
-    self.cameraState = qr_scanner::CAMERA_NOT_LOADED;
+    _cameraState = qr_scanner::CAMERA_NOT_LOADED;
     _delegate = delegate;
     std::string queueName =
         base::StringPrintf("%s.chrome.ios.QRScannerCaptureSessionQueue",
@@ -202,7 +205,7 @@
 - (void)continueLoadCaptureSession:(AVCaptureVideoPreviewLayer*)previewLayer {
   // Get the back camera.
   NSArray* videoCaptureDevices = nil;
-  NSString* cameraType = @"AVCaptureDeviceTypeBuiltInWideAngleCamera";
+  NSString* cameraType = AVCaptureDeviceTypeBuiltInWideAngleCamera;
   AVCaptureDeviceDiscoverySession* discoverySession =
       [AVCaptureDeviceDiscoverySession
           discoverySessionWithDeviceTypes:@[ cameraType ]
@@ -331,18 +334,17 @@
            forKeyPath:@"torchActive"
               options:NSKeyValueObservingOptionNew
               context:nil];
+  self.observingCamera = YES;
 }
 
 - (void)stopReceivingNotifications {
   // We only start receiving notifications if the camera is available.
-  if (!self.isCameraAvailable) {
-    return;
+  if ([self isObservingCamera]) {
+    AVCaptureDevice* camera = [self getCamera];
+    [camera removeObserver:self forKeyPath:@"hasTorch"];
+    [camera removeObserver:self forKeyPath:@"torchAvailable"];
+    [camera removeObserver:self forKeyPath:@"torchActive"];
   }
-  [[NSNotificationCenter defaultCenter] removeObserver:self];
-  AVCaptureDevice* camera = [self getCamera];
-  [camera removeObserver:self forKeyPath:@"hasTorch"];
-  [camera removeObserver:self forKeyPath:@"torchAvailable"];
-  [camera removeObserver:self forKeyPath:@"torchActive"];
 }
 
 - (AVCaptureDevice*)getCamera {
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.mm
index 49778d5..42b54074c 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.mm
@@ -61,6 +61,7 @@
       initWithPresentationProvider:self
                        queryLoader:static_cast<id<LoadQueryCommands>>(
                                        self.dispatcher)];
+  self.viewController.modalPresentationStyle = UIModalPresentationFullScreen;
 
   [self.baseViewController
       presentViewController:[self.viewController getViewControllerToPresent]
@@ -76,6 +77,7 @@
             self.baseViewController.presentedViewController);
   [self.baseViewController dismissViewControllerAnimated:YES
                                               completion:completion];
+  self.viewController = nil;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn b/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
index fb8c043d..94bfbf5 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
+++ b/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
@@ -11,20 +11,34 @@
   deps = [
     ":send_tab_to_self_ui",
     "//base",
+    "//components/send_tab_to_self",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/main",
+    "//ios/chrome/browser/sync",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/infobars/presentation",
   ]
 }
 source_set("send_tab_to_self_ui") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "send_tab_to_self_modal_positioner.h",
     "send_tab_to_self_modal_presentation_controller.h",
     "send_tab_to_self_modal_presentation_controller.mm",
     "send_tab_to_self_table_view_controller.h",
     "send_tab_to_self_table_view_controller.mm",
   ]
   deps = [
+    "resources:send_tab_to_self_devices",
+    "resources:send_tab_to_self_laptop",
+    "resources:send_tab_to_self_smartphone",
+    "resources:send_tab_to_self_tablet",
     "//base",
+    "//components/send_tab_to_self",
+    "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view:styler",
     "//ios/chrome/browser/ui/table_view/cells",
+    "//ui/base",
   ]
 }
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/BUILD.gn b/ios/chrome/browser/ui/send_tab_to_self/resources/BUILD.gn
new file mode 100644
index 0000000..98a06020b
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/BUILD.gn
@@ -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.
+
+import("//build/config/ios/asset_catalog.gni")
+
+imageset("send_tab_to_self_devices") {
+  sources = [
+    "send_tab_to_self_devices.imageset/Contents.json",
+    "send_tab_to_self_devices.imageset/send_tab_to_self_devices_2x.png",
+    "send_tab_to_self_devices.imageset/send_tab_to_self_devices_3x.png",
+  ]
+}
+
+imageset("send_tab_to_self_laptop") {
+  sources = [
+    "send_tab_to_self_laptop.imageset/Contents.json",
+    "send_tab_to_self_laptop.imageset/send_tab_to_self_laptop_2x.png",
+    "send_tab_to_self_laptop.imageset/send_tab_to_self_laptop_3x.png",
+  ]
+}
+
+imageset("send_tab_to_self_smartphone") {
+  sources = [
+    "send_tab_to_self_smartphone.imageset/Contents.json",
+    "send_tab_to_self_smartphone.imageset/send_tab_to_self_smartphone_2x.png",
+    "send_tab_to_self_smartphone.imageset/send_tab_to_self_smartphone_3x.png",
+  ]
+}
+
+imageset("send_tab_to_self_tablet") {
+  sources = [
+    "send_tab_to_self_tablet.imageset/Contents.json",
+    "send_tab_to_self_tablet.imageset/send_tab_to_self_tablet_2x.png",
+    "send_tab_to_self_tablet.imageset/send_tab_to_self_tablet_3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/Contents.json b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/Contents.json
new file mode 100644
index 0000000..97b5d9d
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "filename": "send_tab_to_self_devices_2x.png",
+            "idiom": "universal",
+            "scale": "2x"
+        },
+        {
+            "filename": "send_tab_to_self_devices_3x.png",
+            "idiom": "universal",
+            "scale": "3x"
+        }
+    ],
+    "info": {
+        "author": "xcode",
+        "version": 1
+    }
+}
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/send_tab_to_self_devices_2x.png b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/send_tab_to_self_devices_2x.png
new file mode 100644
index 0000000..dc1d46c
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/send_tab_to_self_devices_2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/send_tab_to_self_devices_3x.png b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/send_tab_to_self_devices_3x.png
new file mode 100644
index 0000000..3ea6e2c
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_devices.imageset/send_tab_to_self_devices_3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/Contents.json b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/Contents.json
new file mode 100644
index 0000000..6c7f4f4d
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "filename": "send_tab_to_self_laptop_2x.png",
+            "idiom": "universal",
+            "scale": "2x"
+        },
+        {
+            "filename": "send_tab_to_self_laptop_3x.png",
+            "idiom": "universal",
+            "scale": "3x"
+        }
+    ],
+    "info": {
+        "author": "xcode",
+        "version": 1
+    }
+}
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/send_tab_to_self_laptop_2x.png b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/send_tab_to_self_laptop_2x.png
new file mode 100644
index 0000000..add9096f
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/send_tab_to_self_laptop_2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/send_tab_to_self_laptop_3x.png b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/send_tab_to_self_laptop_3x.png
new file mode 100644
index 0000000..fd79be5b
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_laptop.imageset/send_tab_to_self_laptop_3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/Contents.json b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/Contents.json
new file mode 100644
index 0000000..2646ff8f
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "filename": "send_tab_to_self_smartphone_2x.png",
+            "idiom": "universal",
+            "scale": "2x"
+        },
+        {
+            "filename": "send_tab_to_self_smartphone_3x.png",
+            "idiom": "universal",
+            "scale": "3x"
+        }
+    ],
+    "info": {
+        "author": "xcode",
+        "version": 1
+    }
+}
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/send_tab_to_self_smartphone_2x.png b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/send_tab_to_self_smartphone_2x.png
new file mode 100644
index 0000000..dff22a7
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/send_tab_to_self_smartphone_2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/send_tab_to_self_smartphone_3x.png b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/send_tab_to_self_smartphone_3x.png
new file mode 100644
index 0000000..4f54ac0
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_smartphone.imageset/send_tab_to_self_smartphone_3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/Contents.json b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/Contents.json
new file mode 100644
index 0000000..160267e
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "filename": "send_tab_to_self_tablet_2x.png",
+            "idiom": "universal",
+            "scale": "2x"
+        },
+        {
+            "filename": "send_tab_to_self_tablet_3x.png",
+            "idiom": "universal",
+            "scale": "3x"
+        }
+    ],
+    "info": {
+        "author": "xcode",
+        "version": 1
+    }
+}
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/send_tab_to_self_tablet_2x.png b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/send_tab_to_self_tablet_2x.png
new file mode 100644
index 0000000..fdfd93b
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/send_tab_to_self_tablet_2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/send_tab_to_self_tablet_3x.png b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/send_tab_to_self_tablet_3x.png
new file mode 100644
index 0000000..70859ba
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/resources/send_tab_to_self_tablet.imageset/send_tab_to_self_tablet_3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
index c1ed51f..6d0d0ca4 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
@@ -5,6 +5,10 @@
 #import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.h"
 
 #include "base/logging.h"
+#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
+#import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_positioner.h"
 #import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.h"
 #import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h"
 
@@ -12,12 +16,17 @@
 #error "This file requires ARC support."
 #endif
 
-@interface SendTabToSelfCoordinator () <UIViewControllerTransitioningDelegate>
+@interface SendTabToSelfCoordinator () <UIViewControllerTransitioningDelegate,
+                                        SendTabToSelfModalPositioner>
 
 // The presentationController that shows the Send Tab To Self UI.
 @property(nonatomic, strong) SendTabToSelfModalPresentationController*
     sendTabToSelfModalPresentationController;
 
+// The presentationController that shows the Send Tab To Self UI.
+@property(nonatomic, strong)
+    SendTabToSelfTableViewController* sendTabToSelfViewController;
+
 @end
 
 @implementation SendTabToSelfCoordinator
@@ -25,10 +34,16 @@
 #pragma mark - ChromeCoordinator Methods
 
 - (void)start {
-  SendTabToSelfTableViewController* tableViewController =
-      [[SendTabToSelfTableViewController alloc] init];
+  send_tab_to_self::SendTabToSelfSyncService* syncService =
+      SendTabToSelfSyncServiceFactory::GetForBrowserState(self.browserState);
+  // This modal should not be launched in incognito mode where syncService is
+  // undefined.
+  DCHECK(syncService);
+
+  self.sendTabToSelfViewController = [[SendTabToSelfTableViewController alloc]
+      initWithModel:syncService->GetSendTabToSelfModel()];
   UINavigationController* navigationController = [[UINavigationController alloc]
-      initWithRootViewController:tableViewController];
+      initWithRootViewController:self.sendTabToSelfViewController];
 
   navigationController.transitioningDelegate = self;
   navigationController.modalPresentationStyle = UIModalPresentationCustom;
@@ -53,8 +68,24 @@
       [[SendTabToSelfModalPresentationController alloc]
           initWithPresentedViewController:presented
                  presentingViewController:presenting];
-  // TODO(crbug.com/970284) flesh out presentationController.
+  presentationController.modalPositioner = self;
   return presentationController;
 }
 
+#pragma mark - SendTabToSelfModalPositioner
+
+- (CGFloat)modalHeight {
+  UITableView* tableView = self.sendTabToSelfViewController.tableView;
+  [tableView setNeedsLayout];
+  [tableView layoutIfNeeded];
+
+  // Since the TableView is contained in a NavigationController get the
+  // navigation bar height.
+  CGFloat navigationBarHeight =
+      self.sendTabToSelfViewController.navigationController.navigationBar.frame
+          .size.height;
+
+  return tableView.contentSize.height + navigationBarHeight;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_positioner.h b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_positioner.h
new file mode 100644
index 0000000..8e007c3
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_positioner.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODAL_POSITIONER_H_
+#define IOS_CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODAL_POSITIONER_H_
+
+#import <UIKit/UIKit.h>
+
+// SendTabToSelfModalPositioner contains methods used to position the send tab
+// to self modal dialog.
+@protocol SendTabToSelfModalPositioner
+
+// The target height for the modal view to be presented.
+- (CGFloat)modalHeight;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODAL_POSITIONER_H_
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.h b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.h
index 91a1079..d42462b 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.h
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.h
@@ -7,9 +7,14 @@
 
 #import <UIKit/UIKit.h>
 
+@protocol SendTabToSelfModalPositioner;
+
 // PresentationController for the modal dialog.
 @interface SendTabToSelfModalPresentationController : UIPresentationController
 
+// Delegate used to position the modal dialog.
+@property(nonatomic, weak) id<SendTabToSelfModalPositioner> modalPositioner;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODAL_PRESENTATION_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.mm b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.mm
index eaa8c45..ad7851a5 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.mm
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.mm
@@ -4,10 +4,60 @@
 
 #import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.h"
 
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_positioner.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// The presented view outer horizontal margins.
+const CGFloat kPresentedViewMargin = 10.0;
+// The presented view maximum width.
+const CGFloat kPresentedViewMaxWidth = 394.0;
+// The rounded corner radius for the container view.
+const CGFloat kContainerCornerRadius = 13.0;
+// The background color for the container view.
+const int kContainerBackgroundColor = 0x2F2F2F;
+// The alpha component for the container view background color.
+const CGFloat kContainerBackgroundColorAlpha = 0.5;
+}  // namespace
+
 @implementation SendTabToSelfModalPresentationController
 
+- (void)containerViewWillLayoutSubviews {
+  self.presentedView.frame = [self frameForPresentedView];
+
+  // Style the presented and container views.
+  self.presentedView.layer.cornerRadius = kContainerCornerRadius;
+  self.presentedView.layer.masksToBounds = YES;
+  self.presentedView.clipsToBounds = YES;
+  self.containerView.backgroundColor =
+      [UIColorFromRGB(kContainerBackgroundColor)
+          colorWithAlphaComponent:kContainerBackgroundColorAlpha];
+}
+
+- (CGRect)frameForPresentedView {
+  DCHECK(self.modalPositioner);
+  CGFloat containerWidth = CGRectGetWidth(self.containerView.bounds);
+  CGFloat containerHeight = CGRectGetHeight(self.containerView.bounds);
+
+  // Calculate the frame width.
+  CGFloat maxAvailableWidth = containerWidth - 2 * kPresentedViewMargin;
+  CGFloat frameWidth = fmin(maxAvailableWidth, kPresentedViewMaxWidth);
+
+  CGFloat modalTargetHeight = [self.modalPositioner modalHeight];
+  CGFloat maxAvailableHeight = containerHeight - 2 * kPresentedViewMargin;
+  CGFloat frameHeight = fmin(maxAvailableHeight, modalTargetHeight);
+
+  // Based on the container width calculate the values in order to center the
+  // frame in the X and Y axis.
+  CGFloat modalXPosition = (containerWidth / 2) - (frameWidth / 2);
+  CGFloat modalYPosition = (containerHeight / 2) - (frameHeight / 2);
+
+  return CGRectMake(modalXPosition, modalYPosition, frameWidth, frameHeight);
+}
+
 @end
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h
index 7b8a1ae..73926f8 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h
@@ -7,15 +7,26 @@
 
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
 
+namespace send_tab_to_self {
+class SendTabToSelfModel;
+}
 // SendTabToSelfTableViewController represents the content for the
 // Send Tab To Self Modal dialog.
 @interface SendTabToSelfTableViewController : ChromeTableViewController
 
-- (instancetype)init NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithModel:
+    (send_tab_to_self::SendTabToSelfModel*)sendTabToSelfModel
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
 - (instancetype)initWithTableViewStyle:(UITableViewStyle)style
                            appBarStyle:
                                (ChromeTableViewControllerStyle)appBarStyle
     NS_UNAVAILABLE;
+
+// The text used for the cancel button.
+@property(nonatomic, copy) NSString* cancelButtonText;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
index 4e28edc1..17fbc2cb 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
@@ -5,27 +5,164 @@
 #import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h"
 
 #include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/send_tab_to_self/send_tab_to_self_model.h"
+#include "components/send_tab_to_self/target_device_info.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_detail_icon_item.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h"
 #include "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// Text color for the Cancel button.
+const CGFloat kSendButtonBackgroundColorBlue = 0x1A73E8;
+
+// Accessibility identifier of the Modal Cancel Button.
+NSString* const kSendTabToSelfModalCancelButton =
+    @"kSendTabToSelfModalCancelButton";
+// Accessibility identifier of the Modal Cancel Button.
+NSString* const kSendTabToSelfModalSendButton =
+    @"kSendTabToSelfModalSendButton";
+
+}  // namespace
+
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierDevicesToSend = kSectionIdentifierEnumZero,
+  SectionIdentifierActionButton,
 };
 
+typedef NS_ENUM(NSInteger, ItemType) {
+  ItemTypeSend = kItemTypeEnumZero,
+  ItemTypeDevice,
+};
+
+@interface SendTabToSelfTableViewController () {
+  std::map<std::string, send_tab_to_self::TargetDeviceInfo> _target_device_map;
+}
+
+// Item that holds the cancel Button for this Infobar. e.g. "Never Save for this
+// site".
+@property(nonatomic, strong) TableViewTextButtonItem* sendToDevice;
+@end
+
 @implementation SendTabToSelfTableViewController
 
-- (instancetype)init {
-  return [super initWithTableViewStyle:UITableViewStylePlain
+- (instancetype)initWithModel:
+    (send_tab_to_self::SendTabToSelfModel*)sendTabToSelfModel {
+  self = [super initWithTableViewStyle:UITableViewStylePlain
                            appBarStyle:ChromeTableViewControllerStyleNoAppBar];
+
+  if (self) {
+    _target_device_map =
+        sendTabToSelfModel->GetTargetDeviceNameToCacheInfoMap();
+  }
+  return self;
 }
 
 #pragma mark - ViewController Lifecycle
 
 - (void)viewDidLoad {
-  NOTIMPLEMENTED();
+  [super viewDidLoad];
+  self.view.backgroundColor = [UIColor whiteColor];
+  self.styler.cellBackgroundColor = [UIColor whiteColor];
+  self.tableView.sectionHeaderHeight = 0;
+  [self.tableView
+      setSeparatorInset:UIEdgeInsetsMake(0, kTableViewHorizontalSpacing, 0, 0)];
+
+  // Configure the NavigationBar.
+  UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
+      initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
+                           target:self
+                           action:nil];
+  cancelButton.accessibilityIdentifier = kSendTabToSelfModalCancelButton;
+
+  self.navigationItem.leftBarButtonItem = cancelButton;
+  self.navigationItem.title =
+      l10n_util::GetNSString(IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_TITLE);
+  self.navigationController.navigationBar.prefersLargeTitles = NO;
+
+  [self loadModel];
+}
+
+#pragma mark - TableViewModel
+
+- (void)loadModel {
+  [super loadModel];
+
+  TableViewModel* model = self.tableViewModel;
+  [model addSectionWithIdentifier:SectionIdentifierDevicesToSend];
+
+  for (auto const& iter : _target_device_map) {
+    int daysSinceLastUpdate =
+        (base::Time::Now() - iter.second.last_updated_timestamp).InDays();
+
+    TableViewDetailIconItem* deviceItem =
+        [[TableViewDetailIconItem alloc] initWithType:ItemTypeDevice];
+    deviceItem.text = base::SysUTF8ToNSString(iter.first);
+    deviceItem.detailText =
+        [self sendTabToSelfdaysSinceLastUpdate:daysSinceLastUpdate];
+    switch (iter.second.device_type) {
+      case sync_pb::SyncEnums::TYPE_TABLET:
+        deviceItem.iconImageName = @"send_tab_to_self_tablet";
+        break;
+      case sync_pb::SyncEnums::TYPE_PHONE:
+        deviceItem.iconImageName = @"send_tab_to_self_smartphone";
+        break;
+      case sync_pb::SyncEnums::TYPE_WIN:
+      case sync_pb::SyncEnums::TYPE_MAC:
+      case sync_pb::SyncEnums::TYPE_LINUX:
+      case sync_pb::SyncEnums::TYPE_CROS:
+        deviceItem.iconImageName = @"send_tab_to_self_laptop";
+        break;
+      default:
+        deviceItem.iconImageName = @"send_tab_to_self_devices";
+    }
+
+    [model addItem:deviceItem
+        toSectionWithIdentifier:SectionIdentifierDevicesToSend];
+  }
+
+  [model addSectionWithIdentifier:SectionIdentifierActionButton];
+  self.sendToDevice =
+      [[TableViewTextButtonItem alloc] initWithType:ItemTypeSend];
+  self.sendToDevice.buttonText =
+      l10n_util::GetNSString(IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_ACTION);
+  self.sendToDevice.buttonTextColor = [UIColor whiteColor];
+  ;
+  self.sendToDevice.buttonBackgroundColor =
+      UIColorFromRGB(kSendButtonBackgroundColorBlue);
+  self.sendToDevice.boldButtonText = NO;
+  self.sendToDevice.accessibilityIdentifier = kSendTabToSelfModalSendButton;
+  [model addItem:self.sendToDevice
+      toSectionWithIdentifier:SectionIdentifierActionButton];
+}
+
+#pragma mark - Helpers
+
+- (NSString*)sendTabToSelfdaysSinceLastUpdate:(int)days {
+  NSString* active_time;
+  if (days == 0) {
+    active_time = l10n_util::GetNSString(
+        IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_ITEM_SUBTITLE_TODAY);
+  } else if (days == 1) {
+    active_time = l10n_util::GetNSString(
+        IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_ITEM_SUBTITLE_DAY);
+  } else {
+    active_time = l10n_util::GetNSStringF(
+        IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_ITEM_SUBTITLE_DAYS,
+        base::NumberToString16(days));
+  }
+  return active_time;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 0bef28d3..4f2a6c4 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -80,6 +80,7 @@
     "resources:settings_addresses",
     "resources:settings_article_suggestions",
     "resources:settings_bandwidth",
+    "resources:settings_catalog_example_text",
     "resources:settings_content_settings",
     "resources:settings_debug",
     "resources:settings_error",
@@ -372,6 +373,7 @@
     "//ios/chrome/browser/ui/table_view/cells",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/ui/util:util",
+    "//ios/chrome/test:eg_test_support",
     "//ios/chrome/test:test_support",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
diff --git a/ios/chrome/browser/ui/settings/about_chrome_table_view_controller.mm b/ios/chrome/browser/ui/settings/about_chrome_table_view_controller.mm
index 7a092825..b6e7263 100644
--- a/ios/chrome/browser/ui/settings/about_chrome_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/about_chrome_table_view_controller.mm
@@ -18,6 +18,7 @@
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #include "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/common/channel_info.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/third_party/material_components_ios/src/components/Snackbar/src/MaterialSnackbar.h"
@@ -74,7 +75,7 @@
   self.tableView.estimatedRowHeight = kDefaultHeight;
   self.tableView.sectionFooterHeight = UITableViewAutomaticDimension;
   self.tableView.estimatedSectionFooterHeight = kDefaultHeight;
-  self.styler.cellTitleColor = [UIColor blackColor];
+  self.styler.cellTitleColor = UIColor.cr_labelColor;
 }
 
 #pragma mark - SettingsRootTableViewController
diff --git a/ios/chrome/browser/ui/settings/block_popups_egtest.mm b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
index ed29e5d..26e9aaf 100644
--- a/ios/chrome/browser/ui/settings/block_popups_egtest.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
@@ -17,7 +17,7 @@
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#include "ios/chrome/test/scoped_block_popups_pref.h"
+#include "ios/chrome/test/earl_grey/scoped_block_popups_pref.h"
 #import "ios/web/public/test/http_server/http_server.h"
 #include "ios/web/public/test/http_server/http_server_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -126,8 +126,7 @@
   responses[openedWindowURL] = kOpenedWindowResponse;
   web::test::SetUpSimpleHttpServer(responses);
 
-  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW,
-                                   GetOriginalBrowserState());
+  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW);
   [ChromeEarlGrey loadURL:blockPopupsURL];
   [ChromeEarlGrey waitForMainTabCount:1];
 
@@ -156,8 +155,7 @@
   responses[openedWindowURL] = kOpenedWindowResponse;
   web::test::SetUpSimpleHttpServer(responses);
 
-  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_BLOCK,
-                                   GetOriginalBrowserState());
+  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_BLOCK);
   [ChromeEarlGrey loadURL:blockPopupsURL];
   [ChromeEarlGrey waitForMainTabCount:1];
 
@@ -185,8 +183,7 @@
 // revealed properly when the preference switch is toggled.
 - (void)testSettingsPageWithExceptions {
   std::string allowedPattern = "[*.]example.com";
-  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_BLOCK,
-                                   GetOriginalBrowserState());
+  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_BLOCK);
   ScopedBlockPopupsException exceptionSetter(allowedPattern);
 
   [ChromeEarlGreyUI openSettingsMenu];
diff --git a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
index 5a2dea1..9d89155 100644
--- a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
@@ -7,6 +7,7 @@
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -43,7 +44,7 @@
         l10n_util::GetNSString(IDS_IOS_AUTOFILL_DESCRIBE_LOCAL_COPY);
     _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
     _textLabel.adjustsFontForContentSizeCategory = YES;
-    _textLabel.textColor = UIColor.blackColor;
+    _textLabel.textColor = UIColor.cr_labelColor;
     [_textLabel
         setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                         forAxis:
diff --git a/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.mm b/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.mm
index e4e0c9f2..12b862b 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.mm
+++ b/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.mm
@@ -47,6 +47,7 @@
 
   _imageView = [[UIImageView alloc] init];
   _imageView.translatesAutoresizingMaskIntoConstraints = NO;
+  _imageView.tintColor = UIColor.cr_labelColor;
   [contentView addSubview:_imageView];
 
   _textLabel = [[UILabel alloc] init];
diff --git a/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h b/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h
index e77bc0ae..4e94366 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h
+++ b/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h
@@ -13,9 +13,15 @@
 // a detail text (optional). This item uses multi-lines text field.
 @interface SettingsImageDetailTextItem : TableViewItem
 
-// The image to display (required).
+// The image to display (required). If this image should be tinted to match the
+// text color (e.g. in dark mode), the provided image should have rendering mode
+// UIImageRenderingModeAlwaysTemplate.
 @property(nonatomic, strong) UIImage* image;
 
+// Whether the image should be tinted as an icon or not (if it is already
+// colored). The tint color will match the text color.
+@property(nonatomic, assign) BOOL imageShouldBeTinted;
+
 // The title text to display.
 @property(nonatomic, copy) NSString* text;
 
diff --git a/ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.mm b/ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.mm
index 5b650d4..a4e50f1 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.mm
@@ -7,6 +7,7 @@
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -54,7 +55,7 @@
     _textLabel.numberOfLines = 0;
     _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
     _textLabel.adjustsFontForContentSizeCategory = YES;
-    _textLabel.textColor = UIColor.blackColor;
+    _textLabel.textColor = UIColor.cr_labelColor;
     [contentView addSubview:_textLabel];
 
     _detailTextLabel = [[UILabel alloc] init];
@@ -63,8 +64,7 @@
     _detailTextLabel.font =
         [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
     _detailTextLabel.adjustsFontForContentSizeCategory = YES;
-    _detailTextLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    _detailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
     [contentView addSubview:_detailTextLabel];
 
     // Set up the constraints.
diff --git a/ios/chrome/browser/ui/settings/dataplan_usage_table_view_controller.mm b/ios/chrome/browser/ui/settings/dataplan_usage_table_view_controller.mm
index 955d8c703..86f5e17 100644
--- a/ios/chrome/browser/ui/settings/dataplan_usage_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/dataplan_usage_table_view_controller.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -76,7 +77,7 @@
 
 - (void)loadModel {
   [super loadModel];
-  self.styler.cellTitleColor = [UIColor blackColor];
+  self.styler.cellTitleColor = UIColor.cr_labelColor;
 
   TableViewModel<TableViewItem*>* model = self.tableViewModel;
   [model addSectionWithIdentifier:SectionIdentifierOptions];
diff --git a/ios/chrome/browser/ui/settings/language/add_language_table_view_controller.mm b/ios/chrome/browser/ui/settings/language/add_language_table_view_controller.mm
index 7f93e591..b9a4c58 100644
--- a/ios/chrome/browser/ui/settings/language/add_language_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/language/add_language_table_view_controller.mm
@@ -92,7 +92,7 @@
   // Search controller.
   self.searchController =
       [[UISearchController alloc] initWithSearchResultsController:nil];
-  self.searchController.dimsBackgroundDuringPresentation = NO;
+  self.searchController.obscuresBackgroundDuringPresentation = NO;
   self.searchController.searchResultsUpdater = self;
   self.searchController.searchBar.accessibilityIdentifier =
       kAddLanguageSearchControllerAccessibilityIdentifier;
diff --git a/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
index 902cdad4..c1fd3ad 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
@@ -1538,7 +1538,7 @@
     // Tap on the "Cancel" or "X" button accompanying the activity view to
     // dismiss it.
     NSString* dismissLabel =
-        base::ios::IsRunningOnIOS13OrLater() ? @"xmark" : @"Cancel";
+        base::ios::IsRunningOnIOS13OrLater() ? @"Close" : @"Cancel";
     [[EarlGrey
         selectElementWithMatcher:grey_allOf(
                                      ButtonWithAccessibilityLabel(dismissLabel),
diff --git a/ios/chrome/browser/ui/settings/resources/BUILD.gn b/ios/chrome/browser/ui/settings/resources/BUILD.gn
index bb1a314..dfe0806 100644
--- a/ios/chrome/browser/ui/settings/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/resources/BUILD.gn
@@ -4,6 +4,12 @@
 
 import("//build/config/ios/asset_catalog.gni")
 
+colorset("settings_catalog_example_text") {
+  sources = [
+    "settings_catalog_example_text.colorset/Contents.json",
+  ]
+}
+
 imageset("app_icon_placeholder") {
   sources = [
     "app_icon_placeholder.imageset/Contents.json",
diff --git a/ios/chrome/browser/ui/settings/resources/settings_catalog_example_text.colorset/Contents.json b/ios/chrome/browser/ui/settings/resources/settings_catalog_example_text.colorset/Contents.json
new file mode 100644
index 0000000..e084b53
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_catalog_example_text.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "colors" : [
+    {
+      "idiom" : "universal",
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "red" : "0.000",
+          "blue" : "0.000",
+          "green" : "0.000",
+          "alpha" : "1.000"
+        }
+      }
+    },
+    {
+      "idiom" : "universal",
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "red" : "1.000",
+          "blue" : "1.000",
+          "green" : "1.000",
+          "alpha" : "1.000"
+        }
+      }
+    }
+  ]
+}
diff --git a/ios/chrome/browser/ui/settings/settings_root_collection_view_controller.mm b/ios/chrome/browser/ui/settings/settings_root_collection_view_controller.mm
index 83b61fd..8f5f215 100644
--- a/ios/chrome/browser/ui/settings/settings_root_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_root_collection_view_controller.mm
@@ -16,6 +16,7 @@
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/third_party/material_components_ios/src/components/Collections/src/MaterialCollections.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -54,11 +55,11 @@
       self.collectionViewAccessibilityIdentifier;
 
   // Customize collection view settings.
-  self.collectionView.backgroundColor = [UIColor groupTableViewBackgroundColor];
+  self.collectionView.backgroundColor = UIColor.cr_systemGroupedBackgroundColor;
   self.styler.cellStyle = MDCCollectionViewCellStyleGrouped;
   self.styler.separatorColor = UIColorFromRGB(kUIKitSeparatorColor);
   self.appBarViewController.headerView.backgroundColor =
-      [UIColor groupTableViewBackgroundColor];
+      UIColor.cr_systemGroupedBackgroundColor;
   self.styler.separatorInset = UIEdgeInsetsMake(0, 16, 0, 16);
 
   self.navigationItem.largeTitleDisplayMode =
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
index c31909f..5a699e0d 100644
--- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -28,6 +28,7 @@
 #import "ios/chrome/browser/ui/table_view/table_view_model.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/signin/signin_resources_provider.h"
 #include "url/gurl.h"
@@ -115,7 +116,7 @@
       [[TableViewTextItem alloc] initWithType:ItemTypeText];
   textItem.text = @"Simple Text Cell";
   textItem.textAlignment = NSTextAlignmentCenter;
-  textItem.textColor = [UIColor blackColor];
+  textItem.textColor = UIColor.cr_labelColor;
   [model addItem:textItem toSectionWithIdentifier:SectionIdentifierText];
 
   textItem = [[TableViewTextItem alloc] initWithType:ItemTypeText];
@@ -192,7 +193,8 @@
   textActionButtonColorItem.text = @"Hello, you should do something.";
   textActionButtonColorItem.disableButtonIntrinsicWidth = YES;
   textActionButtonColorItem.buttonBackgroundColor = [UIColor lightGrayColor];
-  textActionButtonColorItem.buttonTextColor = [UIColor greenColor];
+  textActionButtonColorItem.buttonTextColor =
+    [UIColor colorNamed:@"settings_catalog_example_text"];
   textActionButtonColorItem.buttonText = @"Do something, different Colors";
   [model addItem:textActionButtonColorItem
       toSectionWithIdentifier:SectionIdentifierText];
@@ -347,7 +349,8 @@
   imageDetailTextItem.text = @"This is an error description about sync";
   imageDetailTextItem.detailText =
       @"This is more detail about the sync error description";
-  imageDetailTextItem.image = [ChromeIcon infoIcon];
+  imageDetailTextItem.image = [[ChromeIcon infoIcon]
+      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
   [model addItem:imageDetailTextItem
       toSectionWithIdentifier:SectionIdentifierSettings];
 
@@ -422,7 +425,8 @@
 
   AccountControlItem* accountControlItemWithExtraLongText =
       [[AccountControlItem alloc] initWithType:ItemTypeAccount];
-  accountControlItemWithExtraLongText.image = [ChromeIcon infoIcon];
+  accountControlItemWithExtraLongText.image = [[ChromeIcon infoIcon]
+      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
   accountControlItemWithExtraLongText.text =
       @"Account Control Settings - long title";
   accountControlItemWithExtraLongText.detailText =
diff --git a/ios/chrome/browser/ui/table_view/cells/BUILD.gn b/ios/chrome/browser/ui/table_view/cells/BUILD.gn
index 72b232199..13246226 100644
--- a/ios/chrome/browser/ui/table_view/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/cells/BUILD.gn
@@ -86,6 +86,7 @@
     ":cells",
     "//base",
     "//ios/chrome/browser/ui/table_view:styler",
+    "//ios/chrome/common/ui_util:semantic_colors",
     "//net:net",
     "//testing/gtest",
     "//third_party/ocmock:ocmock",
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
index f6c853c..27c83f8 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
@@ -9,6 +9,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "ios/third_party/material_components_ios/src/components/ActivityIndicator/src/MaterialActivityIndicator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -63,8 +64,7 @@
     self.subtitleLabel = [[UILabel alloc] init];
     self.subtitleLabel.font =
         [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
-    self.subtitleLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    self.subtitleLabel.textColor = UIColor.cr_secondaryLabelColor;
     [self.subtitleLabel
         setContentCompressionResistancePriority:UILayoutPriorityRequired
                                         forAxis:UILayoutConstraintAxisVertical];
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_cell.mm b/ios/chrome/browser/ui/table_view/cells/table_view_cell.mm
index 30adfce..ec6aac94 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_cell.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_cell.mm
@@ -7,13 +7,13 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
 namespace {
-const int kTableViewCustomSeparatorColor = 0xDADDE2;
 const CGFloat kTableViewCustomSeparatorHeight = 0.75;
 }  // namespace
 
@@ -28,8 +28,7 @@
   if (self) {
     _customSeparator = [[UIView alloc] init];
     _customSeparator.translatesAutoresizingMaskIntoConstraints = NO;
-    _customSeparator.backgroundColor =
-        UIColorFromRGB(kTableViewCustomSeparatorColor);
+    _customSeparator.backgroundColor = UIColor.cr_opaqueSeparatorColor;
     [self addSubview:_customSeparator];
 
     NSArray* constraints = @[
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h
index 170e6e7e..2a6bf92 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h
@@ -18,7 +18,7 @@
 @property(nonatomic, assign) NSTextAlignment textAlignment;
 
 // UIColor for the cell's textLabel. Default is
-// kTableViewTextLabelColorLightGrey. ChromeTableViewStyler's |cellTitleColor|
+// UIColor.cr_labelColor. ChromeTableViewStyler's |cellTitleColor|
 // takes precedence over the default color, but not over |textColor|.
 @property(nonatomic, strong) UIColor* textColor;
 // Main text to be displayed.
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.mm
index 96791bb..e14339d 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.mm
@@ -7,6 +7,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -50,14 +51,12 @@
   } else if (styler.cellTitleColor) {
     cell.textLabel.textColor = styler.cellTitleColor;
   } else {
-    cell.textLabel.textColor =
-        UIColorFromRGB(kTableViewTextLabelColorLightGrey);
+    cell.textLabel.textColor = UIColor.cr_labelColor;
   }
   if (self.detailTextColor) {
     cell.detailTextLabel.textColor = self.detailTextColor;
   } else {
-    cell.detailTextLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    cell.detailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
   }
   cell.textLabel.textAlignment =
       self.textAlignment ? self.textAlignment : NSTextAlignmentNatural;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item_unittest.mm
index 867148e..63d4918 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item_unittest.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item_unittest.mm
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -25,8 +26,8 @@
 TEST_F(TableViewDetailTextItemTest, ItemProperties) {
   NSString* text = @"Cell text";
   NSString* detailText = @"Cell detail text";
-  UIColor* textColor = [UIColor yellowColor];
-  UIColor* detailTextColor = [UIColor blueColor];
+  UIColor* textColor = UIColor.yellowColor;
+  UIColor* detailTextColor = UIColor.blueColor;
 
   TableViewDetailTextItem* item =
       [[TableViewDetailTextItem alloc] initWithType:0];
@@ -42,7 +43,7 @@
       base::mac::ObjCCastStrict<TableViewDetailTextCell>(cell);
 
   ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-  styler.cellTitleColor = [UIColor redColor];
+  styler.cellTitleColor = UIColor.redColor;
   ASSERT_FALSE([styler.cellTitleColor isEqual:textColor]);
   [item configureCell:cell withStyler:styler];
   EXPECT_NSEQ(text, detailCell.textLabel.text);
@@ -56,7 +57,7 @@
 TEST_F(TableViewDetailTextItemTest, ItemPropertiesStylerColor) {
   NSString* text = @"Cell text";
   NSString* detailText = @"Cell detail text";
-  UIColor* titleColor = [UIColor blueColor];
+  UIColor* titleColor = UIColor.blueColor;
 
   TableViewDetailTextItem* item =
       [[TableViewDetailTextItem alloc] initWithType:0];
@@ -88,8 +89,6 @@
   [item configureCell:cell withStyler:[[ChromeTableViewStyler alloc] init]];
   EXPECT_NSEQ(text, cell.textLabel.text);
   EXPECT_NSEQ(detailText, cell.detailTextLabel.text);
-  EXPECT_NSEQ(UIColorFromRGB(kTableViewTextLabelColorLightGrey),
-              cell.textLabel.textColor);
-  EXPECT_NSEQ(UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor),
-              cell.detailTextLabel.textColor);
+  EXPECT_NSEQ(UIColor.cr_labelColor, cell.textLabel.textColor);
+  EXPECT_NSEQ(UIColor.cr_secondaryLabelColor, cell.detailTextLabel.textColor);
 }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
index 816de6a..8bb0525e 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/rtl_geometry.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -95,7 +96,7 @@
     _subtitleLabel = [[UILabel alloc] init];
     _subtitleLabel.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
-    _subtitleLabel.textColor = [UIColor lightGrayColor];
+    _subtitleLabel.textColor = UIColor.cr_secondaryLabelColor;
     [_subtitleLabel
         setContentCompressionResistancePriority:UILayoutPriorityRequired
                                         forAxis:UILayoutConstraintAxisVertical];
@@ -252,7 +253,7 @@
   if (!_cellDefaultBackgroundColor) {
     _cellDefaultBackgroundColor = self.contentView.backgroundColor
                                       ? self.contentView.backgroundColor
-                                      : [UIColor clearColor];
+                                      : UIColor.clearColor;
   }
   return _cellDefaultBackgroundColor;
 }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
index 0a40256..62da64b 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
@@ -9,6 +9,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -54,13 +55,12 @@
   } else if (styler.cellTitleColor) {
     cell.textLabel.textColor = styler.cellTitleColor;
   } else {
-    cell.textLabel.textColor = UIColor.blackColor;
+    cell.textLabel.textColor = UIColor.cr_labelColor;
   }
   if (self.detailTextColor) {
     cell.detailTextLabel.textColor = self.detailTextColor;
   } else {
-    cell.detailTextLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    cell.detailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
   }
 
   cell.userInteractionEnabled = self.enabled;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_item_unittest.mm
index 59d081c..42bf403 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_item_unittest.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_item_unittest.mm
@@ -38,7 +38,7 @@
   ASSERT_TRUE([cell isMemberOfClass:[TableViewCell class]]);
 
   ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-  UIColor* testColor = [UIColor redColor];
+  UIColor* testColor = UIColor.redColor;
   styler.tableViewBackgroundColor = testColor;
   [item configureCell:cell withStyler:styler];
   EXPECT_NSEQ(testColor, cell.backgroundColor);
@@ -54,7 +54,7 @@
   cell.backgroundView = [[UIView alloc] init];
 
   ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-  UIColor* testColor = [UIColor redColor];
+  UIColor* testColor = UIColor.redColor;
   styler.tableViewBackgroundColor = testColor;
   [item configureCell:cell withStyler:styler];
   EXPECT_FALSE([testColor isEqual:cell.backgroundColor]);
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm
index 08f4ac1..3610ce9 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm
@@ -7,6 +7,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/string_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "net/base/mac/url_conversions.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -20,8 +21,6 @@
 // Padding used on the top and bottom edges of the cell.
 const CGFloat kVerticalPadding = 8;
 
-// Text color.
-const int kTextColor = kTableViewSecondaryLabelLightGrayTextColor;
 }  // namespace
 
 @implementation TableViewLinkHeaderFooterItem
@@ -66,7 +65,7 @@
     _textView.scrollEnabled = NO;
     _textView.editable = NO;
     _textView.delegate = self;
-    _textView.backgroundColor = [UIColor clearColor];
+    _textView.backgroundColor = UIColor.clearColor;
     _textView.font =
         [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
     _textView.adjustsFontForContentSizeCategory = YES;
@@ -99,7 +98,7 @@
   NSMutableAttributedString* attributedText =
       [[NSMutableAttributedString alloc] initWithString:strippedText];
   [attributedText addAttribute:NSForegroundColorAttributeName
-                         value:UIColorFromRGB(kTextColor)
+                         value:UIColor.cr_secondaryLabelColor
                          range:fullRange];
 
   [attributedText
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_multi_detail_text_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_multi_detail_text_item.mm
index 2d7e93a..02b7c7c 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_multi_detail_text_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_multi_detail_text_item.mm
@@ -6,6 +6,7 @@
 
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -89,20 +90,18 @@
   _textLabel.lineBreakMode = NSLineBreakByWordWrapping;
   _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
   _textLabel.adjustsFontForContentSizeCategory = YES;
-  _textLabel.textColor = UIColor.blackColor;
+  _textLabel.textColor = UIColor.cr_labelColor;
 
   _leadingDetailTextLabel.numberOfLines = 0;
   _leadingDetailTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
   _leadingDetailTextLabel.font =
       [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
   _leadingDetailTextLabel.adjustsFontForContentSizeCategory = YES;
-  _leadingDetailTextLabel.textColor =
-      UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+  _leadingDetailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
 
   _trailingDetailTextLabel.font =
       [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
-  _trailingDetailTextLabel.textColor =
-      UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+  _trailingDetailTextLabel.textColor = UIColor.cr_secondaryLabelColor;
 }
 
 // Sets constraints on subviews.
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.mm
index 97634b0cc..44cbdbc 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.mm
@@ -7,6 +7,7 @@
 #include "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -111,8 +112,7 @@
     self.textLabel.textAlignment = NSTextAlignmentCenter;
     self.textLabel.font =
         [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
-    self.textLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    self.textLabel.textColor = UIColor.cr_secondaryLabelColor;
 
     // Create button.
     self.button = [UIButton buttonWithType:UIButtonTypeSystem];
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
index afe1105..a74801196 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
@@ -9,6 +9,7 @@
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/rtl_geometry.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -61,12 +62,10 @@
 
   if (self.hideEditIcon) {
     cell.textField.textColor =
-        self.textFieldEnabled
-            ? UIColorFromRGB(kTableViewTextLabelColorBlue)
-            : UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+        self.textFieldEnabled ? UIColorFromRGB(kTableViewTextLabelColorBlue)
+                              : UIColor.cr_secondaryLabelColor;
   } else {
-    cell.textField.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    cell.textField.textColor = UIColor.cr_secondaryLabelColor;
     cell.editIconDisplayed = self.textFieldEnabled;
   }
   [cell.textField addTarget:self
@@ -165,8 +164,7 @@
     UIImage* editImage = [[UIImage imageNamed:@"table_view_cell_edit_icon"]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
     _editIconView = [[UIImageView alloc] initWithImage:editImage];
-    _editIconView.tintColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    _editIconView.tintColor = UIColor.cr_secondaryLabelColor;
     _editIconView.translatesAutoresizingMaskIntoConstraints = NO;
     [contentView addSubview:_editIconView];
 
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_item.mm
index c72ed30..0704c39 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_item.mm
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -55,14 +56,13 @@
   // Decide cell.textLabel.textColor in order:
   //   1. this.textColor;
   //   2. styler.cellTitleColor;
-  //   3. kTableViewTextLabelColorLightGrey.
+  //   3. UIColor.cr_labelColor.
   if (self.textColor) {
     cell.textLabel.textColor = self.textColor;
   } else if (styler.cellTitleColor) {
     cell.textLabel.textColor = styler.cellTitleColor;
   } else {
-    cell.textLabel.textColor =
-        UIColorFromRGB(kTableViewTextLabelColorLightGrey);
+    cell.textLabel.textColor = UIColor.cr_labelColor;
   }
   cell.textLabel.textAlignment =
       self.textAlignment ? self.textAlignment : NSTextAlignmentNatural;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_item_unittest.mm
index b2395881..1a547dd8 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_item_unittest.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_item_unittest.mm
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -66,9 +67,9 @@
   ASSERT_TRUE([cell isMemberOfClass:[TableViewTextCell class]]);
 
   ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-  UIColor* testTextColor = [UIColor redColor];
+  UIColor* testTextColor = UIColor.redColor;
   styler.cellTitleColor = testTextColor;
-  UIColor* testBackgroundColor = [UIColor blueColor];
+  UIColor* testBackgroundColor = UIColor.blueColor;
   styler.tableViewBackgroundColor = testBackgroundColor;
   [item configureCell:cell withStyler:styler];
   EXPECT_NSEQ(testBackgroundColor, cell.textLabel.backgroundColor);
@@ -77,13 +78,13 @@
 
 TEST_F(TableViewTextItemTest, ConfigureLabelColorWithProperty) {
   TableViewTextItem* item = [[TableViewTextItem alloc] initWithType:0];
-  UIColor* textColor = [UIColor blueColor];
+  UIColor* textColor = UIColor.blueColor;
   item.textColor = textColor;
   TableViewTextCell* cell = [[[item cellClass] alloc] init];
   ASSERT_TRUE([cell isMemberOfClass:[TableViewTextCell class]]);
 
   ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-  UIColor* testColor = [UIColor redColor];
+  UIColor* testColor = UIColor.redColor;
   styler.tableViewBackgroundColor = testColor;
   [item configureCell:cell withStyler:styler];
   EXPECT_NSEQ(textColor, cell.textLabel.textColor);
@@ -96,6 +97,5 @@
   ASSERT_TRUE([cell isMemberOfClass:[TableViewTextCell class]]);
   ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
   [item configureCell:cell withStyler:styler];
-  EXPECT_NSEQ(UIColorFromRGB(kTableViewTextLabelColorLightGrey),
-              cell.textLabel.textColor);
+  EXPECT_NSEQ(UIColor.cr_labelColor, cell.textLabel.textColor);
 }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_link_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_link_item.mm
index f443fea629..f5c2034 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_link_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_link_item.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/util/label_link_controller.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/string_util.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -72,8 +73,7 @@
     _textLabel.lineBreakMode = NSLineBreakByWordWrapping;
     _textLabel.font =
         [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
-    _textLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    _textLabel.textColor = UIColor.cr_secondaryLabelColor;
 
     // Add subviews to View Hierarchy.
     [self.contentView addSubview:_textLabel];
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
index d7af69f..4dd3046 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
@@ -12,6 +12,7 @@
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/favicon/favicon_view.h"
+#import "ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "url/gurl.h"
 
@@ -72,6 +73,7 @@
     cell.URLLabel.backgroundColor = styler.tableViewBackgroundColor;
     cell.metadataLabel.backgroundColor = styler.tableViewBackgroundColor;
   }
+
   if (styler.cellTitleColor)
     cell.titleLabel.textColor = styler.cellTitleColor;
 
@@ -165,13 +167,11 @@
     _URLLabel.font =
         [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
     _URLLabel.adjustsFontForContentSizeCategory = YES;
-    _URLLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    _URLLabel.textColor = UIColor.cr_secondaryLabelColor;
     _URLLabel.hidden = YES;
     _metadataLabel.font =
         [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
-    _metadataLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    _metadataLabel.textColor = UIColor.cr_secondaryLabelColor;
     _metadataLabel.adjustsFontForContentSizeCategory = YES;
     _metadataLabel.hidden = YES;
 
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
index 294b67c..0af59da 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
@@ -84,7 +84,7 @@
   ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
 
   ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-  UIColor* testColor = [UIColor redColor];
+  UIColor* testColor = UIColor.redColor;
   styler.tableViewBackgroundColor = testColor;
   [item configureCell:cell withStyler:styler];
   EXPECT_NSEQ(testColor, cell.faviconContainerView.backgroundColor);
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
index 49db509..34c759c 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -239,8 +239,8 @@
                                    grey_kindOfClass([UILabel class]), nil)]
       performAction:grey_tap()];
 
-  [[[EarlGrey selectElementWithMatcher:SystemSelectionCalloutCopyButton()]
-      inRoot:SystemSelectionCallout()] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SystemSelectionCalloutCopyButton()]
+      performAction:grey_tap()];
 
   if ([ChromeEarlGrey isIPadIdiom]) {
     [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"Typing Shield")]
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index b99b35c..a0a5f625 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -37,3 +37,6 @@
 
 const base::Feature kLanguageSettings{"LanguageSettings",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kOptionalArticleThumbnail{
+    "OptionalArticleThumbnail", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index eb8c72a..f315429 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -43,4 +43,7 @@
 // Feature flag for the language settings page.
 extern const base::Feature kLanguageSettings;
 
+// Feature flag for the optional article thumbnail.
+extern const base::Feature kOptionalArticleThumbnail;
+
 #endif  // IOS_CHROME_BROWSER_UI_UI_FEATURE_FLAGS_H_
diff --git a/ios/chrome/browser/ui/webui/BUILD.gn b/ios/chrome/browser/ui/webui/BUILD.gn
index 634b672c..5e0e3a3 100644
--- a/ios/chrome/browser/ui/webui/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/BUILD.gn
@@ -58,6 +58,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/crash_report",
     "//ios/chrome/browser/favicon:favicon",
+    "//ios/chrome/browser/flags",
     "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/ntp_tiles",
     "//ios/chrome/browser/passwords",
diff --git a/ios/chrome/browser/ui/webui/flags_ui.cc b/ios/chrome/browser/ui/webui/flags_ui.cc
index a03ee05..f852803 100644
--- a/ios/chrome/browser/ui/webui/flags_ui.cc
+++ b/ios/chrome/browser/ui/webui/flags_ui.cc
@@ -23,10 +23,10 @@
 #include "components/strings/grit/components_chromium_strings.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/version_info/version_info.h"
-#include "ios/chrome/browser/about_flags.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/flags/about_flags.h"
 #include "ios/web/public/webui/web_ui_ios.h"
 #include "ios/web/public/webui/web_ui_ios_data_source.h"
 #include "ios/web/public/webui/web_ui_ios_message_handler.h"
diff --git a/ios/chrome/browser/ui/webui/web_ui_egtest.mm b/ios/chrome/browser/ui/webui/web_ui_egtest.mm
index e7af7e6..a68e8d5 100644
--- a/ios/chrome/browser/ui/webui/web_ui_egtest.mm
+++ b/ios/chrome/browser/ui/webui/web_ui_egtest.mm
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #import <EarlGrey/EarlGrey.h>
+#import <EarlGrey/GREYKeyboard.h>
 #import <XCTest/XCTest.h>
 
 #include "base/mac/foundation_util.h"
@@ -141,6 +142,11 @@
   // Navigate to the second URL chrome://flags.
   LoadWebUIUrl(kChromeUIFlagsHost);
 
+  // Navigating to chrome://flags should trigger the keyboard, but pre-iOS13
+  // this is broken.
+  if ([GREYKeyboard isKeyboardShown])
+    [EarlGrey dismissKeyboardWithError:nil];
+
   // Tap the back button in the toolbar and verify that the resulting page's URL
   // corresponds to the first URL chrome://version that was loaded.
   [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()];
diff --git a/ios/chrome/browser/web/browsing_egtest.mm b/ios/chrome/browser/web/browsing_egtest.mm
index 2ee385e..6acace4d 100644
--- a/ios/chrome/browser/web/browsing_egtest.mm
+++ b/ios/chrome/browser/web/browsing_egtest.mm
@@ -18,7 +18,7 @@
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#include "ios/chrome/test/scoped_block_popups_pref.h"
+#include "ios/chrome/test/earl_grey/scoped_block_popups_pref.h"
 #include "ios/net/url_test_util.h"
 #import "ios/web/public/test/earl_grey/web_view_actions.h"
 #import "ios/web/public/test/earl_grey/web_view_matchers.h"
@@ -184,8 +184,7 @@
   responses[destinationURL] = "You've arrived!";
   web::test::SetUpSimpleHttpServer(responses);
 
-  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW,
-                                   GetOriginalBrowserState());
+  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW);
 
   [ChromeEarlGrey loadURL:URL];
   [ChromeEarlGrey waitForMainTabCount:1];
@@ -214,8 +213,7 @@
   responses[destinationURL] = "You've arrived!";
   web::test::SetUpSimpleHttpServer(responses);
 
-  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW,
-                                   GetOriginalBrowserState());
+  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW);
 
   [ChromeEarlGrey loadURL:URL];
   [ChromeEarlGrey waitForMainTabCount:1];
@@ -257,8 +255,7 @@
 
   web::test::SetUpSimpleHttpServer(responses);
 
-  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW,
-                                   GetOriginalBrowserState());
+  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW);
 
   [ChromeEarlGrey loadURL:URL];
   [ChromeEarlGrey waitForMainTabCount:1];
@@ -296,8 +293,7 @@
 
   web::test::SetUpSimpleHttpServer(responses);
 
-  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW,
-                                   GetOriginalBrowserState());
+  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW);
 
   [ChromeEarlGrey loadURL:URL];
   [ChromeEarlGrey waitForMainTabCount:1];
diff --git a/ios/chrome/browser/web/browsing_prevent_default_egtest.mm b/ios/chrome/browser/web/browsing_prevent_default_egtest.mm
index f13262b6..bd7d4b76 100644
--- a/ios/chrome/browser/web/browsing_prevent_default_egtest.mm
+++ b/ios/chrome/browser/web/browsing_prevent_default_egtest.mm
@@ -12,7 +12,7 @@
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#include "ios/chrome/test/scoped_block_popups_pref.h"
+#include "ios/chrome/test/earl_grey/scoped_block_popups_pref.h"
 #import "ios/web/public/test/http_server/http_server.h"
 #include "ios/web/public/test/http_server/http_server_util.h"
 #include "url/url_constants.h"
@@ -48,8 +48,7 @@
 - (void)runTestAndVerifyNoNavigationForLinkID:(const std::string&)linkID {
   // Disable popup blocking, because that will mask failures that try to open
   // new tabs.
-  ScopedBlockPopupsPref scoper(CONTENT_SETTING_ALLOW,
-                               GetOriginalBrowserState());
+  ScopedBlockPopupsPref scoper(CONTENT_SETTING_ALLOW);
   web::test::SetUpFileBasedHttpServer();
 
   const GURL testURL = GetTestUrl();
@@ -89,8 +88,7 @@
 - (void)testPreventDefaultOverridesWindowOpen {
   // Disable popup blocking, because that will mask failures that try to open
   // new tabs.
-  ScopedBlockPopupsPref scoper(CONTENT_SETTING_ALLOW,
-                               GetOriginalBrowserState());
+  ScopedBlockPopupsPref scoper(CONTENT_SETTING_ALLOW);
   web::test::SetUpFileBasedHttpServer();
 
   const GURL testURL = GetTestUrl();
diff --git a/ios/chrome/browser/web/cache_egtest.mm b/ios/chrome/browser/web/cache_egtest.mm
index c9075a7c3..b592ffe 100644
--- a/ios/chrome/browser/web/cache_egtest.mm
+++ b/ios/chrome/browser/web/cache_egtest.mm
@@ -14,7 +14,7 @@
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#include "ios/chrome/test/scoped_block_popups_pref.h"
+#include "ios/chrome/test/earl_grey/scoped_block_popups_pref.h"
 #include "ios/web/public/test/http_server/html_response_provider.h"
 #import "ios/web/public/test/http_server/http_server.h"
 #include "ios/web/public/test/http_server/http_server_util.h"
@@ -174,8 +174,7 @@
 
   // Open the first page in a new tab. Verify that cache was not used. Must
   // first allow popups.
-  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW,
-                                   GetOriginalBrowserState());
+  ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_ALLOW);
   [ChromeEarlGrey
       tapWebStateElementWithID:[NSString
                                    stringWithUTF8String:kCacheTestLinkID]];
diff --git a/ios/chrome/browser/web/http_auth_egtest.mm b/ios/chrome/browser/web/http_auth_egtest.mm
index bc962d5..012e167 100644
--- a/ios/chrome/browser/web/http_auth_egtest.mm
+++ b/ios/chrome/browser/web/http_auth_egtest.mm
@@ -37,14 +37,24 @@
 
 // Returns matcher for Username text field.
 id<GREYMatcher> UsernameField() {
-  return chrome_test_util::StaticTextWithAccessibilityLabelId(
-      IDS_IOS_HTTP_LOGIN_DIALOG_USERNAME_PLACEHOLDER);
+  if (@available(iOS 13.0, *)) {
+    return grey_accessibilityValue(l10n_util::GetNSStringWithFixup(
+        IDS_IOS_HTTP_LOGIN_DIALOG_USERNAME_PLACEHOLDER));
+  } else {
+    return chrome_test_util::StaticTextWithAccessibilityLabelId(
+        IDS_IOS_HTTP_LOGIN_DIALOG_USERNAME_PLACEHOLDER);
+  }
 }
 
 // Returns matcher for Password text field.
 id<GREYMatcher> PasswordField() {
-  return chrome_test_util::StaticTextWithAccessibilityLabelId(
-      IDS_IOS_HTTP_LOGIN_DIALOG_PASSWORD_PLACEHOLDER);
+  if (@available(iOS 13.0, *)) {
+    return grey_accessibilityValue(l10n_util::GetNSStringWithFixup(
+        IDS_IOS_HTTP_LOGIN_DIALOG_PASSWORD_PLACEHOLDER));
+  } else {
+    return chrome_test_util::StaticTextWithAccessibilityLabelId(
+        IDS_IOS_HTTP_LOGIN_DIALOG_PASSWORD_PLACEHOLDER);
+  }
 }
 
 // Returns matcher for Login button.
diff --git a/ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h b/ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h
index 544ca9c2a..f9c644d 100644
--- a/ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h
+++ b/ios/chrome/common/ui_util/UIColor+cr_semantic_colors.h
@@ -24,6 +24,9 @@
 @property(class, nonatomic, readonly) UIColor* cr_labelColor;
 @property(class, nonatomic, readonly) UIColor* cr_secondaryLabelColor;
 
+// Separator Colors
+@property(class, nonatomic, readonly) UIColor* cr_opaqueSeparatorColor;
+
 @end
 
 #endif  // IOS_CHROME_COMMON_UI_UTIL_UICOLOR_CR_SEMANTIC_COLORS_H_
diff --git a/ios/chrome/common/ui_util/UIColor+cr_semantic_colors.mm b/ios/chrome/common/ui_util/UIColor+cr_semantic_colors.mm
index 5d020469..d713a07 100644
--- a/ios/chrome/common/ui_util/UIColor+cr_semantic_colors.mm
+++ b/ios/chrome/common/ui_util/UIColor+cr_semantic_colors.mm
@@ -65,4 +65,19 @@
                          alpha:0.6];
 }
 
+#pragma mark - Separator Colors
+
++ (UIColor*)cr_opaqueSeparatorColor {
+#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0)
+  if (@available(iOS 13, *)) {
+    return UIColor.opaqueSeparatorColor;
+  }
+#endif
+  // This is the value for opaqueSeparatorColor in light mode.
+  return [UIColor colorWithRed:0xC7 / (CGFloat)0xFF
+                         green:0xC7 / (CGFloat)0xFF
+                          blue:0xC7 / (CGFloat)0xFF
+                         alpha:1];
+}
+
 @end
diff --git a/ios/chrome/content_widget_extension/content_widget_view_controller.mm b/ios/chrome/content_widget_extension/content_widget_view_controller.mm
index 5b694c15..809e96f0 100644
--- a/ios/chrome/content_widget_extension/content_widget_view_controller.mm
+++ b/ios/chrome/content_widget_extension/content_widget_view_controller.mm
@@ -142,9 +142,21 @@
 
 - (BOOL)updateWidget {
   NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
-  NSDictionary<NSURL*, NTPTile*>* newSites = [NSKeyedUnarchiver
-      unarchiveObjectWithData:[sharedDefaults
-                                  objectForKey:app_group::kSuggestedItems]];
+  NSData* data = [sharedDefaults objectForKey:app_group::kSuggestedItems];
+  NSError* error = nil;
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error];
+
+  if (!unarchiver || error) {
+    DLOG(WARNING) << "Error creating unarchiver for widget extension: "
+                  << base::SysNSStringToUTF8([error description]);
+    return NO;
+  }
+
+  unarchiver.requiresSecureCoding = NO;
+
+  NSDictionary<NSURL*, NTPTile*>* newSites =
+      [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
   if ([newSites isEqualToDictionary:self.sites]) {
     return NO;
   }
diff --git a/ios/chrome/share_extension/share_view_controller.mm b/ios/chrome/share_extension/share_view_controller.mm
index ae116d68..6cf5ff6 100644
--- a/ios/chrome/share_extension/share_view_controller.mm
+++ b/ios/chrome/share_extension/share_view_controller.mm
@@ -8,6 +8,7 @@
 
 #import "base/ios/block_types.h"
 #import "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/common/app_group/app_group_command.h"
 #import "ios/chrome/common/app_group/app_group_constants.h"
 #import "ios/chrome/share_extension/share_extension_view.h"
@@ -308,7 +309,18 @@
 
   [dict setValue:[NSNumber numberWithBool:cancel]
           forKey:app_group::kShareItemCancel];
-  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:dict];
+  NSError* error = nil;
+  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:dict
+                                       requiringSecureCoding:NO
+                                                       error:&error];
+
+  if (!data || error) {
+    DLOG(WARNING) << "Error serializing data for title: "
+                  << base::SysNSStringToUTF8(title)
+                  << base::SysNSStringToUTF8([error description]);
+    return;
+  }
+
   [[NSFileManager defaultManager] createFileAtPath:[fileURL path]
                                           contents:data
                                         attributes:nil];
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 6aee749d..fd3c012 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -31,8 +31,6 @@
     "ios_chrome_unit_test_suite.mm",
     "root_view_controller_test.h",
     "root_view_controller_test.mm",
-    "scoped_block_popups_pref.h",
-    "scoped_block_popups_pref.mm",
     "scoped_key_window.h",
     "testing_application_context.h",
     "testing_application_context.mm",
@@ -77,7 +75,7 @@
   ]
   deps = [
     ":test_support",
-    "//base",
+    "//base:base",
     "//ios/third_party/earl_grey:earl_grey+link",
   ]
 }
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 53868f6f6..cf617cc 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -219,6 +219,8 @@
     "chrome_test_case.mm",
     "chrome_test_case_app_interface.h",
     "chrome_test_case_app_interface.mm",
+    "scoped_block_popups_pref.h",
+    "scoped_block_popups_pref.mm",
   ]
 
   deps = [
@@ -232,6 +234,7 @@
     "//components/ukm/ios:features",
     "//components/unified_consent",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/content_settings:content_settings",
     "//ios/chrome/browser/ntp:features",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/authentication:authentication",
@@ -331,6 +334,7 @@
     "//components/ukm/ios:features",
     "//components/unified_consent",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/content_settings:content_settings",
     "//ios/chrome/browser/ntp:features",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/authentication/cells",
@@ -394,6 +398,8 @@
     "chrome_test_case.h",
     "chrome_test_case.mm",
     "chrome_test_case_app_interface.h",
+    "scoped_block_popups_pref.h",
+    "scoped_block_popups_pref.mm",
   ]
 
   deps = [
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 85a49e5..7cf6f02 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -345,6 +345,16 @@
 // Returns YES if WebPaymentsModifiers feature is enabled.
 - (BOOL)isWebPaymentsModifiersEnabled WARN_UNUSED_RESULT;
 
+#pragma mark - Popup Blocking
+
+// Gets the current value of the popup content setting preference for the
+// original browser state.
+- (ContentSetting)popupPrefValue;
+
+// Sets the popup content setting preference to the given value for the original
+// browser state.
+- (void)setPopupPrefValue:(ContentSetting)value;
+
 @end
 
 // Helpers that only compile under EarlGrey 1 are included in this "EG1"
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 4c446155..7ccbf94 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -558,6 +558,16 @@
   return [ChromeEarlGreyAppInterface isWebPaymentsModifiersEnabled];
 }
 
+#pragma mark - ScopedBlockPopupsPref
+
+- (ContentSetting)popupPrefValue {
+  return [ChromeEarlGreyAppInterface popupPrefValue];
+}
+
+- (void)setPopupPrefValue:(ContentSetting)value {
+  return [ChromeEarlGreyAppInterface setPopupPrefValue:value];
+}
+
 @end
 
 // The helpers below only compile under EarlGrey1.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index 0e237038..e699009 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -283,6 +283,16 @@
 // Returns YES if WebPaymentsModifiers feature is enabled.
 + (BOOL)isWebPaymentsModifiersEnabled WARN_UNUSED_RESULT;
 
+#pragma mark - Popup Blocking
+
+// Gets the current value of the popup content setting preference for the
+// original browser state.
++ (ContentSetting)popupPrefValue;
+
+// Sets the popup content setting preference to the given value for the original
+// browser state.
++ (void)setPopupPrefValue:(ContentSetting)value;
+
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_APP_INTERFACE_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index 1d4228b0..579e1ad 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -6,8 +6,10 @@
 #import "base/test/ios/wait_util.h"
 
 #include "base/strings/sys_string_conversions.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #import "components/payments/core/features.h"
 #import "components/ukm/ios/features.h"
+#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
 #import "ios/chrome/browser/ntp/features.h"
 #import "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/test/app/bookmarks_test_util.h"
@@ -486,4 +488,19 @@
       payments::features::kWebPaymentsModifiers);
 }
 
+#pragma mark - ScopedBlockPopupsPref
+
++ (ContentSetting)popupPrefValue {
+  return ios::HostContentSettingsMapFactory::GetForBrowserState(
+             chrome_test_util::GetOriginalBrowserState())
+      ->GetDefaultContentSetting(CONTENT_SETTINGS_TYPE_POPUPS, NULL);
+}
+
++ (void)setPopupPrefValue:(ContentSetting)value {
+  DCHECK(value == CONTENT_SETTING_BLOCK || value == CONTENT_SETTING_ALLOW);
+  ios::HostContentSettingsMapFactory::GetForBrowserState(
+      chrome_test_util::GetOriginalBrowserState())
+      ->SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_POPUPS, value);
+}
+
 @end
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 9506310..f7e648d 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -558,7 +558,8 @@
 }
 
 + (id<GREYMatcher>)systemSelectionCalloutCopyButton {
-  return grey_accessibilityLabel(@"Copy");
+  return grey_allOf(grey_accessibilityLabel(@"Copy"),
+                    [self systemSelectionCallout], nil);
 }
 
 + (id<GREYMatcher>)contextMenuCopyButton {
diff --git a/ios/chrome/test/earl_grey/scoped_block_popups_pref.h b/ios/chrome/test/earl_grey/scoped_block_popups_pref.h
new file mode 100644
index 0000000..20d41bf
--- /dev/null
+++ b/ios/chrome/test/earl_grey/scoped_block_popups_pref.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_TEST_EARL_GREY_SCOPED_BLOCK_POPUPS_PREF_H_
+#define IOS_CHROME_TEST_EARL_GREY_SCOPED_BLOCK_POPUPS_PREF_H_
+
+#include "base/macros.h"
+#include "components/content_settings/core/common/content_settings.h"
+
+// ScopedBlockPopupsPref modifies the block popups preference for the original
+// browser state and resets the preference to its original value when this
+// object goes out of scope.
+class ScopedBlockPopupsPref {
+ public:
+  explicit ScopedBlockPopupsPref(ContentSetting setting);
+  ~ScopedBlockPopupsPref();
+
+ private:
+  // Saves the original pref setting so that it can be restored when the scoper
+  // is destroyed.
+  ContentSetting original_setting_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedBlockPopupsPref);
+};
+
+#endif  // IOS_CHROME_TEST_EARL_GREY_SCOPED_BLOCK_POPUPS_PREF_H_
diff --git a/ios/chrome/test/earl_grey/scoped_block_popups_pref.mm b/ios/chrome/test/earl_grey/scoped_block_popups_pref.mm
new file mode 100644
index 0000000..7786c0d
--- /dev/null
+++ b/ios/chrome/test/earl_grey/scoped_block_popups_pref.mm
@@ -0,0 +1,20 @@
+// 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 "ios/chrome/test/earl_grey/scoped_block_popups_pref.h"
+
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+ScopedBlockPopupsPref::ScopedBlockPopupsPref(ContentSetting setting)
+    : original_setting_([ChromeEarlGrey popupPrefValue]) {
+  [ChromeEarlGrey setPopupPrefValue:setting];
+}
+
+ScopedBlockPopupsPref::~ScopedBlockPopupsPref() {
+  [ChromeEarlGrey setPopupPrefValue:original_setting_];
+}
diff --git a/ios/chrome/test/scoped_block_popups_pref.h b/ios/chrome/test/scoped_block_popups_pref.h
deleted file mode 100644
index fd307a9..0000000
--- a/ios/chrome/test/scoped_block_popups_pref.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_TEST_SCOPED_BLOCK_POPUPS_PREF_H_
-#define IOS_CHROME_TEST_SCOPED_BLOCK_POPUPS_PREF_H_
-
-#include "base/macros.h"
-#include "components/content_settings/core/common/content_settings.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
-
-// ScopedBlockPopupsPref modifies the block popups preference
-// and resets the preference to its original value when this object goes out of
-// scope.
-class ScopedBlockPopupsPref {
- public:
-  explicit ScopedBlockPopupsPref(ContentSetting setting,
-                                 ios::ChromeBrowserState* browser_state);
-  ~ScopedBlockPopupsPref();
-
- private:
-  // Gets the current value of the preference.
-  ContentSetting GetPrefValue();
-
-  // Sets the preference to the given value.
-  void SetPrefValue(ContentSetting setting);
-
-  // Saves the browser state, which is required when getting and setting prefs.
-  ios::ChromeBrowserState* browser_state_;
-
-  // Saves the original pref setting so that it can be restored when the scoper
-  // is destroyed.
-  ContentSetting original_setting_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedBlockPopupsPref);
-};
-
-#endif  // IOS_CHROME_TEST_SCOPED_BLOCK_POPUPS_PREF_H_
diff --git a/ios/chrome/test/scoped_block_popups_pref.mm b/ios/chrome/test/scoped_block_popups_pref.mm
deleted file mode 100644
index 96ca9e4..0000000
--- a/ios/chrome/test/scoped_block_popups_pref.mm
+++ /dev/null
@@ -1,34 +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 "ios/chrome/test/scoped_block_popups_pref.h"
-
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-ScopedBlockPopupsPref::ScopedBlockPopupsPref(
-    ContentSetting setting,
-    ios::ChromeBrowserState* browser_state)
-    : browser_state_(browser_state), original_setting_(GetPrefValue()) {
-  SetPrefValue(setting);
-}
-
-ScopedBlockPopupsPref::~ScopedBlockPopupsPref() {
-  SetPrefValue(original_setting_);
-}
-
-ContentSetting ScopedBlockPopupsPref::GetPrefValue() {
-  return ios::HostContentSettingsMapFactory::GetForBrowserState(browser_state_)
-      ->GetDefaultContentSetting(CONTENT_SETTINGS_TYPE_POPUPS, NULL);
-}
-
-void ScopedBlockPopupsPref::SetPrefValue(ContentSetting setting) {
-  DCHECK(setting == CONTENT_SETTING_BLOCK || setting == CONTENT_SETTING_ALLOW);
-  ios::HostContentSettingsMapFactory::GetForBrowserState(browser_state_)
-      ->SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_POPUPS, setting);
-}
diff --git a/ios/showcase/core/showcase_view_controller.mm b/ios/showcase/core/showcase_view_controller.mm
index 0c657d7..b5ee046 100644
--- a/ios/showcase/core/showcase_view_controller.mm
+++ b/ios/showcase/core/showcase_view_controller.mm
@@ -66,7 +66,7 @@
   self.searchController =
       [[UISearchController alloc] initWithSearchResultsController:nil];
   self.searchController.searchResultsUpdater = self;
-  self.searchController.dimsBackgroundDuringPresentation = NO;
+  self.searchController.obscuresBackgroundDuringPresentation = NO;
   self.tableView.tableHeaderView = self.searchController.searchBar;
   self.navigationController.navigationBar.translucent = NO;
 
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm
index c0e3ee2..8311c78 100644
--- a/ios/testing/earl_grey/base_earl_grey_test_case.mm
+++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -7,6 +7,8 @@
 #import <UIKit/UIKit.h>
 #import <objc/runtime.h>
 
+#include "base/logging.h"
+#include "base/strings/sys_string_conversions.h"
 #import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/base_earl_grey_test_case_app_interface.h"
 #import "ios/testing/earl_grey/coverage_utils.h"
@@ -31,6 +33,7 @@
 
 #if defined(CHROME_EARL_GREY_2)
   [self launchAppForTestMethod];
+  [self handleSystemAlertIfVisible];
 
   NSString* logFormat = @"*********************************\nStarting test: %@";
   [BaseEarlGreyTestCaseAppInterface
@@ -50,6 +53,45 @@
   });
 }
 
+// Handles system alerts if any are present, closing them to unblock the UI.
+- (void)handleSystemAlertIfVisible {
+#if defined(CHROME_EARL_GREY_2)
+  NSError* systemAlertFoundError = nil;
+  [[EarlGrey selectElementWithMatcher:grey_systemAlertViewShown()]
+      assertWithMatcher:grey_nil()
+                  error:&systemAlertFoundError];
+
+  if (systemAlertFoundError) {
+    NSError* alertGetTextError = nil;
+    NSString* alertText =
+        [self grey_systemAlertTextWithError:&alertGetTextError];
+    GREYAssertNil(alertGetTextError, @"Error getting alert text.\n%@",
+                  alertGetTextError);
+
+    // If the system alert is of a known type, accept it.
+    // Otherwise, reject it, as unknown types include alerts which are not
+    // desirable to accept, including OS upgrades.
+    if ([self grey_systemAlertType] != GREYSystemAlertTypeUnknown) {
+      DLOG(WARNING) << "Accepting iOS system alert: "
+                    << base::SysNSStringToUTF8(alertText);
+
+      NSError* acceptAlertError = nil;
+      [self grey_acceptSystemDialogWithError:&acceptAlertError];
+      GREYAssertNil(acceptAlertError, @"Error accepting system alert.\n%@",
+                    acceptAlertError);
+    } else {
+      DLOG(WARNING) << "Denying iOS system alert of unknown type: "
+                    << base::SysNSStringToUTF8(alertText);
+
+      NSError* denyAlertError = nil;
+      [self grey_denySystemDialogWithError:&denyAlertError];
+      GREYAssertNil(denyAlertError, @"Error denying system alert.\n%@",
+                    denyAlertError);
+    }
+  }
+#endif  // CHROME_EARL_GREY_2
+}
+
 - (void)launchAppForTestMethod {
   [[AppLaunchManager sharedManager] ensureAppLaunchedWithArgs:nil
                                                  forceRestart:false];
diff --git a/ios/third_party/earl_grey/BUILD.gn b/ios/third_party/earl_grey/BUILD.gn
index 5b1c3ec..710332d1 100644
--- a/ios/third_party/earl_grey/BUILD.gn
+++ b/ios/third_party/earl_grey/BUILD.gn
@@ -295,6 +295,10 @@
     "//ios/third_party/ochamcrest:ochamcrest+link",
   ]
 
+  # EG1 is no longer maintained and will be dropped once the migration to EG2
+  # is complete.
+  cflags_objc = [ "-Wno-deprecated-declarations" ]
+
   libs = [
     "CoreData.framework",
     "CoreGraphics.framework",
diff --git a/ios/web/js_messaging/resources/message.js b/ios/web/js_messaging/resources/message.js
index d128e69a..ed3ca50 100644
--- a/ios/web/js_messaging/resources/message.js
+++ b/ios/web/js_messaging/resources/message.js
@@ -82,16 +82,6 @@
 };
 
 /**
- * Returns the message queue as a string.
- * @return {string} The current message queue as a JSON string.
- */
-__gCrWeb.message.getMessageQueue = function() {
-  var messageQueueString = __gCrWeb.common.JSONStringify(messageQueue_.queue);
-  messageQueue_.reset();
-  return messageQueueString;
-};
-
-/**
  * Sends both queues if they contain messages.
  */
 __gCrWeb.message.invokeQueues = function() {
diff --git a/ios/web/navigation/crw_navigation_item_storage_unittest.mm b/ios/web/navigation/crw_navigation_item_storage_unittest.mm
index 41470b5a..9f97cfd5 100644
--- a/ios/web/navigation/crw_navigation_item_storage_unittest.mm
+++ b/ios/web/navigation/crw_navigation_item_storage_unittest.mm
@@ -52,7 +52,13 @@
 // Tests that unarchiving CRWNavigationItemStorage data results in an equivalent
 // storage.
 TEST_F(CRWNavigationItemStorageTest, EncodeDecode) {
-  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:item_storage()];
-  id decoded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:item_storage()
+                                       requiringSecureCoding:NO
+                                                       error:nil];
+
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
+  unarchiver.requiresSecureCoding = NO;
+  id decoded = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
   EXPECT_TRUE(web::ItemStoragesAreEqual(item_storage(), decoded));
 }
diff --git a/ios/web/navigation/crw_session_storage_unittest.mm b/ios/web/navigation/crw_session_storage_unittest.mm
index bbb7078..e1f4065 100644
--- a/ios/web/navigation/crw_session_storage_unittest.mm
+++ b/ios/web/navigation/crw_session_storage_unittest.mm
@@ -95,7 +95,14 @@
 // Tests that unarchiving CRWSessionStorage data results in an equivalent
 // storage.
 TEST_F(CRWNSessionStorageTest, EncodeDecode) {
-  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:session_storage_];
-  id decoded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+  NSKeyedArchiver* archiver =
+      [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
+  [archiver encodeObject:session_storage_ forKey:NSKeyedArchiveRootObjectKey];
+  [archiver finishEncoding];
+  NSData* data = [archiver encodedData];
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
+  unarchiver.requiresSecureCoding = NO;
+  id decoded = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
   EXPECT_TRUE(SessionStoragesAreEqual(session_storage_, decoded));
 }
diff --git a/ios/web/navigation/nscoder_util_unittest.mm b/ios/web/navigation/nscoder_util_unittest.mm
index 24f5ae7..71e2b99 100644
--- a/ios/web/navigation/nscoder_util_unittest.mm
+++ b/ios/web/navigation/nscoder_util_unittest.mm
@@ -30,15 +30,13 @@
 
 TEST_F(NSCoderStdStringTest, encodeDecode) {
   for (size_t i = 0; i < base::size(testStrings); ++i) {
-    NSMutableData* data = [NSMutableData data];
-
     NSKeyedArchiver* archiver =
-        [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
+        [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
     nscoder_util::EncodeString(archiver, @"test", testStrings[i]);
-    [archiver finishEncoding];
 
     NSKeyedUnarchiver* unarchiver =
-        [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
+        [[NSKeyedUnarchiver alloc] initForReadingFromData:[archiver encodedData]
+                                                    error:nil];
     const std::string decoded = nscoder_util::DecodeString(unarchiver, @"test");
 
     EXPECT_EQ(decoded, testStrings[i]);
@@ -46,14 +44,12 @@
 }
 
 TEST_F(NSCoderStdStringTest, decodeEmpty) {
-  NSMutableData* data = [NSMutableData data];
-
   NSKeyedArchiver* archiver =
-      [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
-  [archiver finishEncoding];
+      [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
 
   NSKeyedUnarchiver* unarchiver =
-      [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:[archiver encodedData]
+                                                  error:nil];
   const std::string decoded = nscoder_util::DecodeString(unarchiver, @"test");
 
   EXPECT_EQ(decoded, "");
diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.mm b/ios/web/navigation/wk_based_navigation_manager_impl.mm
index e5e9a33..ab04397 100644
--- a/ios/web/navigation/wk_based_navigation_manager_impl.mm
+++ b/ios/web/navigation/wk_based_navigation_manager_impl.mm
@@ -781,6 +781,17 @@
       last_committed_web_view_item_->SetUntrusted();
     }
     last_committed_web_view_item_->SetURL(document_url);
+    // Don't expose internal restore session URL's.
+    GURL virtual_url;
+    if (wk_navigation_util::IsRestoreSessionUrl(document_url) &&
+        wk_navigation_util::ExtractTargetURL(document_url, &virtual_url)) {
+      if (wk_navigation_util::IsPlaceholderUrl(virtual_url)) {
+        last_committed_web_view_item_->SetVirtualURL(
+            wk_navigation_util::ExtractUrlFromPlaceholderUrl(virtual_url));
+      } else {
+        last_committed_web_view_item_->SetVirtualURL(virtual_url);
+      }
+    }
     last_committed_web_view_item_->SetTimestamp(
         time_smoother_.GetSmoothedTime(base::Time::Now()));
     return last_committed_web_view_item_.get();
diff --git a/ios/web/public/deprecated/BUILD.gn b/ios/web/public/deprecated/BUILD.gn
index 62e57a6..331ccfc 100644
--- a/ios/web/public/deprecated/BUILD.gn
+++ b/ios/web/public/deprecated/BUILD.gn
@@ -27,6 +27,23 @@
   ]
 }
 
+# A separate target is needed for util to prevent cyclic dependency, as
+# "web_state:web_state_impl_header" depends on ":deprecated" target.
+source_set("deprecated_web_util") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":deprecated",
+    "//ios/web/public",
+    "//ios/web/web_state:web_state_impl_header",
+    "//ios/web/web_state/ui",
+  ]
+  sources = [
+    "crw_web_controller_util.h",
+    "crw_web_controller_util.mm",
+  ]
+}
+
 source_set("test_doubles") {
   configs += [ "//build/config/compiler:enable_arc" ]
 
diff --git a/ios/web/public/deprecated/crw_web_controller_util.h b/ios/web/public/deprecated/crw_web_controller_util.h
new file mode 100644
index 0000000..cc349e52
--- /dev/null
+++ b/ios/web/public/deprecated/crw_web_controller_util.h
@@ -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.
+
+#ifndef IOS_WEB_PUBLIC_DEPRECATED_CRW_WEB_CONTROLLER_UTIL_H_
+#define IOS_WEB_PUBLIC_DEPRECATED_CRW_WEB_CONTROLLER_UTIL_H_
+
+@protocol CRWNativeContentProvider;
+@protocol CRWSwipeRecognizerProvider;
+@protocol CRWNativeContent;
+
+namespace web {
+class WebState;
+}
+
+// Utility functions needed to access CRWWebController methods via WebState.
+// All functions defined here should be removed after native content removal and
+// slim navigation launching.
+namespace web_deprecated {
+
+// Sets native provider for the |web_state|.
+// |web_state| can't be null.
+void SetNativeProvider(web::WebState* web_state,
+                       id<CRWNativeContentProvider> delegate);
+
+// Sets side swipe recognizer for the |web_state|.
+// |web_state| can't be null.
+void SetSwipeRecognizerProvider(web::WebState* web_state,
+                                id<CRWSwipeRecognizerProvider> delegate);
+
+// Gets the native controller associated with |web_state|.
+// |web_state| can't be null.
+id<CRWNativeContent> GetNativeController(web::WebState* web_state);
+
+}  // namespace web_deprecated
+
+#endif  // IOS_WEB_PUBLIC_DEPRECATED_CRW_WEB_CONTROLLER_UTIL_H_
diff --git a/ios/web/public/deprecated/crw_web_controller_util.mm b/ios/web/public/deprecated/crw_web_controller_util.mm
new file mode 100644
index 0000000..49075b6
--- /dev/null
+++ b/ios/web/public/deprecated/crw_web_controller_util.mm
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/public/deprecated/crw_web_controller_util.h"
+
+#import <Foundation/Foundation.h>
+
+#import "ios/web/public/deprecated/crw_native_content.h"
+#import "ios/web/public/deprecated/crw_native_content_holder.h"
+#import "ios/web/public/deprecated/crw_native_content_provider.h"
+#import "ios/web/public/web_state/web_state.h"
+#import "ios/web/web_state/ui/crw_swipe_recognizer_provider.h"
+#import "ios/web/web_state/ui/crw_web_controller.h"
+#import "ios/web/web_state/web_state_impl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Gets the web controller from |web_state|.
+CRWWebController* GetWebController(web::WebState* web_state) {
+  return static_cast<web::WebStateImpl*>(web_state)->GetWebController();
+}
+}  // namespace
+
+namespace web_deprecated {
+
+void SetNativeProvider(web::WebState* web_state,
+                       id<CRWNativeContentProvider> delegate) {
+  GetWebController(web_state).nativeContentHolder.nativeProvider = delegate;
+}
+
+void SetSwipeRecognizerProvider(web::WebState* web_state,
+                                id<CRWSwipeRecognizerProvider> delegate) {
+  GetWebController(web_state).swipeRecognizerProvider = delegate;
+}
+
+id<CRWNativeContent> GetNativeController(web::WebState* web_state) {
+  return GetWebController(web_state).nativeContentHolder.nativeController;
+}
+
+}  // namespace web_deprecated
diff --git a/ios/web/session/crw_session_certificate_policy_cache_storage_unittest.mm b/ios/web/session/crw_session_certificate_policy_cache_storage_unittest.mm
index e78d0cfb..712247f 100644
--- a/ios/web/session/crw_session_certificate_policy_cache_storage_unittest.mm
+++ b/ios/web/session/crw_session_certificate_policy_cache_storage_unittest.mm
@@ -64,8 +64,13 @@
 // Tests that unarchiving CRWSessionCertificatePolicyCacheStorage data results
 // in an equivalent storage.
 TEST_F(CRWSessionCertificatePolicyCacheStorageTest, EncodeDecode) {
-  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:cache_storage_];
-  id decoded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:cache_storage_
+                                       requiringSecureCoding:NO
+                                                       error:nil];
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
+  unarchiver.requiresSecureCoding = NO;
+  id decoded = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
   EXPECT_TRUE(CacheStoragesAreEqual(cache_storage_, decoded));
 }
 
@@ -74,15 +79,19 @@
 // Tests that unarchiving a CRWSessionCertificateStorage returns nil if the
 // certificate data does not correctly decode to a certificate.
 TEST_F(CRWSessionCertificateStorageTest, InvalidCertData) {
-  NSMutableData* data = [[NSMutableData alloc] init];
   NSKeyedArchiver* archiver =
-      [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
+      [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
   [archiver encodeObject:[@"not a  cert" dataUsingEncoding:NSUTF8StringEncoding]
                   forKey:web::kCertificateSerializationKey];
   [archiver encodeObject:@"host" forKey:web::kHostSerializationKey];
   [archiver encodeObject:@(net::CERT_STATUS_INVALID)
                   forKey:web::kStatusSerializationKey];
   [archiver finishEncoding];
-  id decoded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+  NSData* data = [archiver encodedData];
+
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
+  unarchiver.requiresSecureCoding = NO;
+  id decoded = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
   EXPECT_FALSE(decoded);
 }
diff --git a/ios/web/session/serializable_user_data_manager_unittest.mm b/ios/web/session/serializable_user_data_manager_unittest.mm
index 3130d695..cef49014 100644
--- a/ios/web/session/serializable_user_data_manager_unittest.mm
+++ b/ios/web/session/serializable_user_data_manager_unittest.mm
@@ -43,15 +43,16 @@
       manager()->CreateSerializableUserData();
 
   // Archive the serializable user data.
-  NSMutableData* data = [[NSMutableData alloc] init];
   NSKeyedArchiver* archiver =
-      [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
+      [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
   user_data->Encode(archiver);
   [archiver finishEncoding];
+  NSData* data = [archiver encodedData];
 
   // Create a new SerializableUserData by unarchiving.
   NSKeyedUnarchiver* unarchiver =
-      [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
+  unarchiver.requiresSecureCoding = NO;
   std::unique_ptr<web::SerializableUserData> decoded_data =
       web::SerializableUserData::Create();
   decoded_data->Decode(unarchiver);
@@ -70,15 +71,17 @@
 // SerializableUserDataManager still allow reading and writing user data
 // (see http://crbug.com/699249 for details).
 TEST_F(SerializableUserDataManagerTest, DecodeNoData) {
-  NSMutableData* data = [[NSMutableData alloc] init];
   NSKeyedArchiver* archiver =
-      [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
+      [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
   [archiver finishEncoding];
+  NSData* data = [archiver encodedData];
 
   std::unique_ptr<web::SerializableUserData> user_data =
       web::SerializableUserData::Create();
+
   NSKeyedUnarchiver* unarchiver =
-      [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
+  unarchiver.requiresSecureCoding = NO;
   user_data->Decode(unarchiver);
 
   web::TestWebState web_state;
diff --git a/ios/web/shell/BUILD.gn b/ios/web/shell/BUILD.gn
index ec7fd87a..354d36f 100644
--- a/ios/web/shell/BUILD.gn
+++ b/ios/web/shell/BUILD.gn
@@ -71,9 +71,6 @@
     "//net",
     "//net:extras",
     "//services/service_manager/public/cpp",
-    "//services/test/echo:lib",
-    "//services/test/echo/public/cpp:manifest",
-    "//services/test/echo/public/mojom",
     "//services/test/user_id:lib",
     "//services/test/user_id/public/cpp:manifest",
     "//services/test/user_id/public/mojom",
diff --git a/ios/web/shell/shell_web_client.h b/ios/web/shell/shell_web_client.h
index 9acc639c..6988330 100644
--- a/ios/web/shell/shell_web_client.h
+++ b/ios/web/shell/shell_web_client.h
@@ -29,12 +29,8 @@
       ui::ScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override;
   bool IsDataResourceGzipped(int resource_id) const override;
-  std::unique_ptr<service_manager::Service> HandleServiceRequest(
-      const std::string& service_name,
-      service_manager::mojom::ServiceRequest request) override;
   base::Optional<service_manager::Manifest> GetServiceManifestOverlay(
       base::StringPiece name) override;
-  std::vector<service_manager::Manifest> GetExtraServiceManifests() override;
   void BindInterfaceRequestFromMainFrame(
       WebState* web_state,
       const std::string& interface_name,
diff --git a/ios/web/shell/shell_web_client.mm b/ios/web/shell/shell_web_client.mm
index b549fce..62ac42de 100644
--- a/ios/web/shell/shell_web_client.mm
+++ b/ios/web/shell/shell_web_client.mm
@@ -14,9 +14,6 @@
 #import "ios/web/shell/web_usage_controller.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/manifest_builder.h"
-#include "services/test/echo/echo_service.h"
-#include "services/test/echo/public/cpp/manifest.h"
-#include "services/test/echo/public/mojom/echo.mojom.h"
 #include "services/test/user_id/public/cpp/manifest.h"
 #include "ui/base/resource/resource_bundle.h"
 
@@ -89,20 +86,10 @@
   return ui::ResourceBundle::GetSharedInstance().IsGzipped(resource_id);
 }
 
-std::unique_ptr<service_manager::Service> ShellWebClient::HandleServiceRequest(
-    const std::string& service_name,
-    service_manager::mojom::ServiceRequest request) {
-  if (service_name == echo::mojom::kServiceName)
-    return std::make_unique<echo::EchoService>(std::move(request));
-
-  return nullptr;
-}
-
 base::Optional<service_manager::Manifest>
 ShellWebClient::GetServiceManifestOverlay(base::StringPiece name) {
   if (name == mojom::kBrowserServiceName) {
     return service_manager::ManifestBuilder()
-        .RequireCapability(echo::mojom::kServiceName, "echo")
         .RequireCapability("user_id", "user_id")
         .PackageService(user_id::GetManifest())
         .Build();
@@ -111,12 +98,6 @@
   return base::nullopt;
 }
 
-std::vector<service_manager::Manifest>
-ShellWebClient::GetExtraServiceManifests() {
-  return std::vector<service_manager::Manifest>{echo::GetManifest(
-      service_manager::Manifest::ExecutionMode::kInProcessBuiltin)};
-}
-
 void ShellWebClient::BindInterfaceRequestFromMainFrame(
     WebState* web_state,
     const std::string& interface_name,
diff --git a/ios/web/shell/test/earl_grey/service_manager_app_interface.h b/ios/web/shell/test/earl_grey/service_manager_app_interface.h
index eefa71c2..783beec 100644
--- a/ios/web/shell/test/earl_grey/service_manager_app_interface.h
+++ b/ios/web/shell/test/earl_grey/service_manager_app_interface.h
@@ -12,11 +12,6 @@
 // cases will properly synchronize the UI for Earl Grey tests.
 @interface ServiceManagerTestAppInterface : NSObject
 
-// Asynchronously echos the given |string| via "echo" Mojo service and logs the
-// response. Logs are accessible via +logs method. eDO does not support
-// asynchronous communication, but clients can poll +logs for echoed string.
-+ (void)echoAndLogString:(NSString*)string;
-
 // Asynchronously logs instance group name via "user_id" Mojo service. Logs are
 // accessible via +logs method. eDO does not support asynchronous communication,
 // but clients can poll +logs to get instance group.
diff --git a/ios/web/shell/test/earl_grey/service_manager_app_interface.mm b/ios/web/shell/test/earl_grey/service_manager_app_interface.mm
index 45d1786..eb746e75 100644
--- a/ios/web/shell/test/earl_grey/service_manager_app_interface.mm
+++ b/ios/web/shell/test/earl_grey/service_manager_app_interface.mm
@@ -14,7 +14,6 @@
 #import "ios/web/shell/test/app/web_shell_test_util.h"
 #import "ios/web/shell/web_usage_controller.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/test/echo/public/mojom/echo.mojom.h"
 #include "services/test/user_id/public/mojom/user_id.mojom.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -25,22 +24,6 @@
 
 @implementation ServiceManagerTestAppInterface
 
-+ (void)echoAndLogString:(NSString*)string {
-  // Connect to the echo service once and bind an Echo instance.
-  static echo::mojom::EchoPtr echo;
-  static dispatch_once_t onceToken;
-  dispatch_once(&onceToken, ^{
-    web::ServiceManagerConnection::Get()->GetConnector()->BindInterface(
-        "echo", mojo::MakeRequest(&echo));
-  });
-
-  std::string UTF8String = base::SysNSStringToUTF8(string);
-  echo->EchoString(UTF8String, base::BindOnce(^(const std::string& UTF8Reply) {
-                     NSString* reply = base::SysUTF8ToNSString(UTF8Reply);
-                     [[self mutableLogs] addObject:reply];
-                   }));
-}
-
 + (void)logInstanceGroup {
   // Connect to the user ID service once and bind a UserId instance.
   static user_id::mojom::UserIdPtr userID;
diff --git a/ios/web/shell/test/service_manager_egtest.mm b/ios/web/shell/test/service_manager_egtest.mm
index 04a88493..6d9f26a 100644
--- a/ios/web/shell/test/service_manager_egtest.mm
+++ b/ios/web/shell/test/service_manager_egtest.mm
@@ -21,12 +21,6 @@
 
 namespace {
 
-// Asynchronously echos the given |string| via "echo" Mojo service and logs the
-// response. Logs are accessible via +logs method.
-void MojoEchoAndLogString(NSString* string) {
-  [ServiceManagerTestAppInterface echoAndLogString:string];
-}
-
 // Asynchronously logs instance group name via "user_id" Mojo service. Logs are
 // accessible via +logs method.
 void MojoLogInstanceGroup() {
@@ -71,21 +65,6 @@
   [super tearDown];
 }
 
-// Tests that it is possible to connect to an all-users embedded service that
-// was registered by web_shell.
-- (void)testConnectionToAllUsersEmbeddedService {
-  NSString* const kTestInput = @"Livingston, I presume";
-  MojoEchoAndLogString(kTestInput);
-
-  // Wait for log to appear.
-  bool has_log = WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^{
-    return MojoGetLogs().count == 1;
-  });
-  GREYAssert(has_log, @"No log appeared");
-  GREYAssertEqualObjects(MojoGetLogs().firstObject, kTestInput,
-                         @"echo service returned incorrect string");
-}
-
 // Tests that it is possible to connect to a per-user embedded service that
 // was registered by web_shell.
 - (void)testConnectionToPerUserEmbeddedService {
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.h b/ios/web/web_state/ui/crw_context_menu_controller.h
index 3c38ea09..e7465bc 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.h
+++ b/ios/web/web_state/ui/crw_context_menu_controller.h
@@ -10,6 +10,7 @@
 
 namespace web {
 class BrowserState;
+class WebState;
 }  // namespace web
 
 @protocol CRWContextMenuDelegate;
@@ -33,6 +34,12 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
+// WebState associated with this controller.
+// When the |webState| is set, the WKWebView default context menu gesture
+// recognizer is overridden after each navigation. If it is never set, the
+// default gesture recognizer is only overridden in this object -init method.
+@property(nonatomic, assign) web::WebState* webState;
+
 // By default, this controller "hooks" long touches to suppress the system
 // default behavior (which shows the system context menu) and show its own
 // context menu instead. This method disables the hook for the current on-going
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index fcd8d49f..e4b0abb 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -16,6 +16,9 @@
 #import "ios/web/js_messaging/crw_wk_script_message_router.h"
 #import "ios/web/public/deprecated/crw_context_menu_delegate.h"
 #import "ios/web/public/web_state/context_menu_params.h"
+#import "ios/web/public/web_state/navigation_context.h"
+#import "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state_observer_bridge.h"
 #import "ios/web/web_state/context_menu_constants.h"
 #import "ios/web/web_state/context_menu_params_utils.h"
 #import "ios/web/web_state/ui/html_element_fetch_request.h"
@@ -88,9 +91,65 @@
   NSDictionary* dom_element;
 };
 
+// Returns a gesture recognizers with |fragment| in it's description and being a
+// subview.
+UIGestureRecognizer* GestureRecognizerWithDescriptionFragment(
+    NSString* fragment,
+    WKWebView* webView) {
+  for (UIView* view in [[webView scrollView] subviews]) {
+    for (UIGestureRecognizer* recognizer in [view gestureRecognizers]) {
+      if ([recognizer.description rangeOfString:fragment].length) {
+        return recognizer;
+      }
+    }
+  }
+  return nil;
+}
+
+// WKWebView's default context menu gesture recognizer interferes with
+// the detection of a long press by |_contextMenuRecognizer|. Calling this
+// method ensures that WKWebView's context menu gesture recognizer should fail
+// if |_contextMenuRecognizer| detects a long press.
+void OverrideGestureRecognizers(UIGestureRecognizer* contextMenuRecognizer,
+                                WKWebView* webView) {
+  NSString* fragment = nil;
+  if (@available(iOS 13, *)) {
+    fragment = @"action=_handleGestureRecognizer:";
+  } else {
+    fragment = @"action=_longPressRecognized:";
+  }
+  UIGestureRecognizer* systemContextMenuRecognizer =
+      GestureRecognizerWithDescriptionFragment(fragment, webView);
+  if (systemContextMenuRecognizer) {
+    [systemContextMenuRecognizer
+        requireGestureRecognizerToFail:contextMenuRecognizer];
+    // requireGestureRecognizerToFail: doesn't retain the recognizer, so it
+    // is possible for |iRecognizer| to outlive |recognizer| and end up with
+    // a dangling pointer. Add a retaining associative reference to ensure
+    // that the lifetimes work out.
+    // Note that normally using the value as the key wouldn't make any
+    // sense, but here it's fine since nothing needs to look up the value.
+    void* associated_object_key = (__bridge void*)contextMenuRecognizer;
+    objc_setAssociatedObject(systemContextMenuRecognizer.view,
+                             associated_object_key, contextMenuRecognizer,
+                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+  }
+
+  if (@available(iOS 13, *)) {
+    fragment = @"com.apple.UIKit.clickPresentationFailure";
+    systemContextMenuRecognizer =
+        GestureRecognizerWithDescriptionFragment(fragment, webView);
+    if (systemContextMenuRecognizer) {
+      [systemContextMenuRecognizer
+          requireGestureRecognizerToFail:contextMenuRecognizer];
+    }
+  }
+}
+
 }  // namespace
 
-@interface CRWContextMenuController ()<UIGestureRecognizerDelegate>
+@interface CRWContextMenuController () <CRWWebStateObserver,
+                                        UIGestureRecognizerDelegate>
 
 // The |webView|.
 @property(nonatomic, readonly, weak) WKWebView* webView;
@@ -99,9 +158,6 @@
 // Returns the x, y offset the content has been scrolled.
 @property(nonatomic, readonly) CGPoint scrollPosition;
 
-// Returns a gesture recognizers with |fragment| in it's description.
-- (UIGestureRecognizer*)gestureRecognizerWithDescriptionFragment:
-    (NSString*)fragment;
 // Called when the |_contextMenuRecognizer| finishes recognizing a long press.
 - (void)longPressDetectedByGestureRecognizer:
     (UIGestureRecognizer*)gestureRecognizer;
@@ -135,6 +191,7 @@
 @end
 
 @implementation CRWContextMenuController {
+  std::unique_ptr<web::WebStateObserverBridge> _observer;
   // Long press recognizer that allows showing context menus.
   UILongPressGestureRecognizer* _contextMenuRecognizer;
   // DOM element information for the point where the user made the last touch.
@@ -180,33 +237,7 @@
     [_contextMenuRecognizer setDelegate:self];
     [_webView addGestureRecognizer:_contextMenuRecognizer];
 
-    // WKWebView's default context menu gesture recognizer interferes with
-    // the detection of a long press by |_contextMenuRecognizer|. WKWebView's
-    // context menu gesture recognizer should fail if |_contextMenuRecognizer|
-    // detects a long press.
-    NSString* fragment = nil;
-    if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET &&
-        base::ios::IsRunningOnIOS13OrLater()) {
-      fragment = @"action=_handleGestureRecognizer:";
-    } else {
-      fragment = @"action=_longPressRecognized:";
-    }
-    UIGestureRecognizer* systemContextMenuRecognizer =
-        [self gestureRecognizerWithDescriptionFragment:fragment];
-    if (systemContextMenuRecognizer) {
-      [systemContextMenuRecognizer
-          requireGestureRecognizerToFail:_contextMenuRecognizer];
-      // requireGestureRecognizerToFail: doesn't retain the recognizer, so it
-      // is possible for |iRecognizer| to outlive |recognizer| and end up with
-      // a dangling pointer. Add a retaining associative reference to ensure
-      // that the lifetimes work out.
-      // Note that normally using the value as the key wouldn't make any
-      // sense, but here it's fine since nothing needs to look up the value.
-      void* associated_object_key = (__bridge void*)_contextMenuRecognizer;
-      objc_setAssociatedObject(systemContextMenuRecognizer.view,
-                               associated_object_key, _contextMenuRecognizer,
-                               OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-    }
+    OverrideGestureRecognizers(_contextMenuRecognizer, _webView);
 
     // Listen for fetched element response.
     web::WKWebViewConfigurationProvider& configurationProvider =
@@ -223,6 +254,24 @@
   return self;
 }
 
+- (void)dealloc {
+  if (_webState)
+    _webState->RemoveObserver(_observer.get());
+}
+
+- (void)setWebState:(web::WebState*)webState {
+  if (_webState)
+    _webState->RemoveObserver(_observer.get());
+
+  _webState = webState;
+  _observer.reset();
+
+  if (webState) {
+    _observer = std::make_unique<web::WebStateObserverBridge>(self);
+    webState->AddObserver(_observer.get());
+  }
+}
+
 - (void)allowSystemUIForCurrentGesture {
   // Reset the state of the recognizer so that it doesn't recognize the on-going
   // touch.
@@ -230,18 +279,6 @@
   _contextMenuRecognizer.enabled = YES;
 }
 
-- (UIGestureRecognizer*)gestureRecognizerWithDescriptionFragment:
-    (NSString*)fragment {
-  for (UIView* view in [[_webView scrollView] subviews]) {
-    for (UIGestureRecognizer* recognizer in [view gestureRecognizers]) {
-      if ([recognizer.description rangeOfString:fragment].length) {
-        return recognizer;
-      }
-    }
-  }
-  return nil;
-}
-
 - (UIScrollView*)webScrollView {
   return [_webView scrollView];
 }
@@ -475,4 +512,18 @@
   [self executeJavaScript:getElementScript completionHandler:nil];
 }
 
+#pragma mark - CRWWebStateObserver
+
+- (void)webStateDestroyed:(web::WebState*)webState {
+  self.webState = nullptr;
+}
+
+- (void)webState:(web::WebState*)webState
+    didFinishNavigation:(web::NavigationContext*)navigation {
+  if (@available(iOS 13, *)) {
+    if (!navigation->IsSameDocument() && navigation->HasCommitted())
+      OverrideGestureRecognizers(_contextMenuRecognizer, _webView);
+  }
+}
+
 @end
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 1c23acf8e..29bf2064 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -8,6 +8,7 @@
 
 #include "base/base64.h"
 #import "base/ios/block_types.h"
+#include "base/ios/ios_util.h"
 #include "base/json/string_escape.h"
 #include "base/mac/foundation_util.h"
 #include "base/metrics/histogram_macros.h"
@@ -1847,6 +1848,7 @@
         [[CRWContextMenuController alloc] initWithWebView:self.webView
                                              browserState:browserState
                                                  delegate:self];
+    self.UIHandler.contextMenuController.webState = self.webStateImpl;
 
     // WKWebViews with invalid or empty frames have exhibited rendering bugs, so
     // resize the view to match the container view upon creation.
@@ -2158,7 +2160,12 @@
 
   // Is it ok that newURL can be restore session URL?
   if (!IsRestoreSessionUrl(_documentURL) && !IsRestoreSessionUrl(newURL)) {
-    DCHECK_EQ(_documentURL.host(), newURL.host());
+    bool ignore_host_change =
+        // On iOS13 document.write() can change URL origin for about:blank page.
+        (_documentURL.IsAboutBlank() && base::ios::IsRunningOnIOS13OrLater());
+    if (!ignore_host_change) {
+      DCHECK_EQ(_documentURL.host(), newURL.host());
+    }
   }
   DCHECK(_documentURL != newURL);
 
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 2ef6169..283340a 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -283,6 +283,7 @@
   "//components/prefs",
   "//components/proxy_config",
   "//components/signin/core/browser",
+  "//components/signin/core/browser/webdata",
   "//components/signin/ios/browser",
   "//components/signin/ios/browser:active_state_manager",
   "//components/strings:components_strings_grit",
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
index bb2f116..3bef63d 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
@@ -28,12 +28,11 @@
 #import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
 #import "ios/web_view/public/cwv_autofill_controller_delegate.h"
+#include "ios/web_view/test/test_with_locale_and_resources.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-#include "ui/base/resource/resource_bundle.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -53,13 +52,9 @@
 
 }  // namespace
 
-class CWVAutofillControllerTest : public PlatformTest {
+class CWVAutofillControllerTest : public TestWithLocaleAndResources {
  protected:
-  CWVAutofillControllerTest()
-      : browser_state_(
-            // Using comma-operator to perform required initialization before
-            // creating browser_state.
-            (InitializeLocaleAndResources(), /*off_the_record=*/false)) {
+  CWVAutofillControllerTest() : browser_state_(/*off_the_record=*/false) {
     web::SetWebClient(&web_client_);
 
     web_state_.SetBrowserState(&browser_state_);
@@ -85,17 +80,6 @@
         std::make_unique<autofill::TestFormActivityTabHelper>(&web_state_);
   }
 
-  ~CWVAutofillControllerTest() override {
-    ui::ResourceBundle::CleanupSharedInstance();
-  }
-
-  static void InitializeLocaleAndResources() {
-    l10n_util::OverrideLocaleWithCocoaLocale();
-    ui::ResourceBundle::InitSharedInstanceWithLocale(
-        l10n_util::GetLocaleOverride(), /*delegate=*/nullptr,
-        ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
-  }
-
   web::WebClient web_client_;
   web::TestWebThreadBundle web_thread_bundle_;
   ios_web_view::WebViewBrowserState browser_state_;
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
index 8bed9560..0013cf7 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/test/bind_test_util.h"
 #include "components/signin/core/browser/signin_error_controller.h"
+#include "components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.h"
 #include "components/sync/driver/mock_sync_service.h"
 #include "components/sync/driver/sync_service_observer.h"
 #include "google_apis/gaia/google_service_auth_error.h"
@@ -22,6 +23,7 @@
 #import "ios/web_view/public/cwv_identity.h"
 #import "ios/web_view/public/cwv_sync_controller_data_source.h"
 #import "ios/web_view/public/cwv_sync_controller_delegate.h"
+#include "ios/web_view/test/test_with_locale_and_resources.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -29,8 +31,6 @@
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-#include "ui/base/resource/resource_bundle.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -43,15 +43,24 @@
 using testing::Invoke;
 using testing::Return;
 
+const char kTestGaiaId[] = "1337";
+const char kTestEmail[] = "johndoe@chromium.org";
+const char kTestFullName[] = "John Doe";
+const char kTestPassphrase[] = "dummy-passphrase";
+const char kTestScope1[] = "scope1.chromium.org";
+const char kTestScope2[] = "scope2.chromium.org";
+
 }  // namespace
 
-class CWVSyncControllerTest : public PlatformTest {
+class CWVSyncControllerTest : public TestWithLocaleAndResources {
  protected:
   CWVSyncControllerTest()
-      : browser_state_(
-            // Using comma-operator to perform required initialization before
-            // creating browser_state.
-            (InitializeLocaleAndResources(), /*off_the_record=*/false)),
+      : browser_state_(/*off_the_record=*/false),
+        identity_test_env_(nullptr,
+                           nullptr,
+                           signin::AccountConsistencyMethod::kDisabled,
+                           nullptr,
+                           CreateExtraParams()),
         signin_error_controller_(
             SigninErrorController::AccountMode::ANY_ACCOUNT,
             identity_test_env_.identity_manager()) {
@@ -69,7 +78,6 @@
   }
 
   ~CWVSyncControllerTest() override {
-    ui::ResourceBundle::CleanupSharedInstance();
     EXPECT_CALL(*profile_sync_service_, RemoveObserver(_));
   }
 
@@ -77,11 +85,13 @@
     sync_service_observer_ = observer;
   }
 
-  static void InitializeLocaleAndResources() {
-    l10n_util::OverrideLocaleWithCocoaLocale();
-    ui::ResourceBundle::InitSharedInstanceWithLocale(
-        l10n_util::GetLocaleOverride(), /*delegate=*/nullptr,
-        ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
+  static identity::IdentityTestEnvironment::ExtraParams CreateExtraParams() {
+    auto provider =
+        std::make_unique<FakeProfileOAuth2TokenServiceIOSProvider>();
+    provider->AddAccount(kTestGaiaId, kTestEmail);
+    identity::IdentityTestEnvironment::ExtraParams extra_params;
+    extra_params.token_service_provider = std::move(provider);
+    return extra_params;
   }
 
   web::TestWebThreadBundle web_thread_bundle_;
@@ -105,19 +115,17 @@
     [[data_source expect]
                  syncController:sync_controller_
         getAccessTokenForScopes:[OCMArg checkWithBlock:^BOOL(NSArray* scopes) {
-          return [scopes containsObject:@"scope1.chromium.org"] &&
-                 [scopes containsObject:@"scope2.chromium.org"];
+          return [scopes containsObject:@(kTestScope1)] &&
+                 [scopes containsObject:@(kTestScope2)];
         }]
               completionHandler:[OCMArg any]];
 
-    CWVIdentity* identity =
-        [[CWVIdentity alloc] initWithEmail:@"johndoe@chromium.org"
-                                  fullName:@"John Doe"
-                                    gaiaID:@"1337"];
+    CWVIdentity* identity = [[CWVIdentity alloc] initWithEmail:@(kTestEmail)
+                                                      fullName:@(kTestFullName)
+                                                        gaiaID:@(kTestGaiaId)];
     [sync_controller_ startSyncWithIdentity:identity dataSource:data_source];
 
-    std::set<std::string> scopes = {"scope1.chromium.org",
-                                    "scope2.chromium.org"};
+    std::set<std::string> scopes = {kTestScope1, kTestScope2};
     ProfileOAuth2TokenServiceIOSProvider::AccessTokenCallback callback;
     [sync_controller_ fetchAccessTokenForScopes:scopes callback:callback];
 
@@ -143,17 +151,19 @@
           return error.code == CWVSyncErrorInvalidGAIACredentials;
         }]];
 
+    id data_source = OCMProtocolMock(@protocol(CWVSyncControllerDataSource));
+
+    CWVIdentity* identity = [[CWVIdentity alloc] initWithEmail:@(kTestEmail)
+                                                      fullName:@(kTestFullName)
+                                                        gaiaID:@(kTestGaiaId)];
+    [sync_controller_ startSyncWithIdentity:identity dataSource:data_source];
+
     // Create authentication error.
     GoogleServiceAuthError auth_error(
         GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
-    std::string account_id =
-        identity_test_env_.MakePrimaryAccountAvailable("email@example.com")
-            .account_id;
-    // TODO(crbug.com/930094): Eliminate this.
-    identity_test_env_.identity_manager()->LegacyAddAccountFromSystem(
-        account_id);
     identity_test_env_.UpdatePersistentErrorOfRefreshTokenForAccount(
-        account_id, auth_error);
+        identity_test_env_.identity_manager()->GetPrimaryAccountId(),
+        auth_error);
 
     [[delegate expect] syncController:sync_controller_
                 didStopSyncWithReason:CWVStopSyncReasonServer];
@@ -166,10 +176,9 @@
 
 // Verifies CWVSyncController properly maintains the current syncing user.
 TEST_F(CWVSyncControllerTest, CurrentIdentity) {
-  CWVIdentity* identity =
-      [[CWVIdentity alloc] initWithEmail:@"johndoe@chromium.org"
-                                fullName:@"John Doe"
-                                  gaiaID:@"1337"];
+  CWVIdentity* identity = [[CWVIdentity alloc] initWithEmail:@(kTestEmail)
+                                                    fullName:@(kTestFullName)
+                                                      gaiaID:@(kTestGaiaId)];
   id unused_mock = OCMProtocolMock(@protocol(CWVSyncControllerDataSource));
   [sync_controller_ startSyncWithIdentity:identity dataSource:unused_mock];
   CWVIdentity* currentIdentity = sync_controller_.currentIdentity;
@@ -189,9 +198,9 @@
       .WillOnce(Return(true));
   EXPECT_TRUE(sync_controller_.passphraseNeeded);
   EXPECT_CALL(*profile_sync_service_->GetMockUserSettings(),
-              SetDecryptionPassphrase("dummy-passphrase"))
+              SetDecryptionPassphrase(kTestPassphrase))
       .WillOnce(Return(true));
-  EXPECT_TRUE([sync_controller_ unlockWithPassphrase:@"dummy-passphrase"]);
+  EXPECT_TRUE([sync_controller_ unlockWithPassphrase:@(kTestPassphrase)]);
 }
 
 }  // namespace ios_web_view
diff --git a/ios/web_view/internal/translate/cwv_translation_controller_unittest.mm b/ios/web_view/internal/translate/cwv_translation_controller_unittest.mm
index a05f267..be3ae11 100644
--- a/ios/web_view/internal/translate/cwv_translation_controller_unittest.mm
+++ b/ios/web_view/internal/translate/cwv_translation_controller_unittest.mm
@@ -20,12 +20,11 @@
 #include "ios/web_view/internal/web_view_browser_state.h"
 #import "ios/web_view/public/cwv_translation_controller_delegate.h"
 #import "ios/web_view/public/cwv_translation_policy.h"
+#include "ios/web_view/test/test_with_locale_and_resources.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-#include "ui/base/resource/resource_bundle.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -39,13 +38,9 @@
 NSString* const kTestPageHost = @"www.chromium.org";
 }  // namespace
 
-class CWVTranslationControllerTest : public PlatformTest {
+class CWVTranslationControllerTest : public TestWithLocaleAndResources {
  protected:
-  CWVTranslationControllerTest()
-      : browser_state_(
-            // Using comma-operator to perform required initialization before
-            // creating browser_state.
-            (InitializeLocaleAndResources(), /*off_the_record=*/false)) {
+  CWVTranslationControllerTest() : browser_state_(/*off_the_record=*/false) {
     web_state_.SetBrowserState(&browser_state_);
     auto test_navigation_manager =
         std::make_unique<web::TestNavigationManager>();
@@ -66,7 +61,6 @@
 
   ~CWVTranslationControllerTest() override {
     translate_prefs_->ResetToDefaults();
-    ui::ResourceBundle::CleanupSharedInstance();
   }
 
   // Checks if |lang_code| matches the OCMArg's CWVTranslationLanguage.
@@ -76,13 +70,6 @@
     }];
   }
 
-  static void InitializeLocaleAndResources() {
-    l10n_util::OverrideLocaleWithCocoaLocale();
-    ui::ResourceBundle::InitSharedInstanceWithLocale(
-        l10n_util::GetLocaleOverride(), /*delegate=*/nullptr,
-        ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
-  }
-
   web::TestWebThreadBundle web_thread_bundle_;
   WebViewBrowserState browser_state_;
   std::unique_ptr<FakeWebViewTranslateClient> translate_client_;
diff --git a/ios/web_view/public/cwv_navigation_delegate.h b/ios/web_view/public/cwv_navigation_delegate.h
index 3924bbe..5e587f5 100644
--- a/ios/web_view/public/cwv_navigation_delegate.h
+++ b/ios/web_view/public/cwv_navigation_delegate.h
@@ -10,6 +10,8 @@
 #import "cwv_export.h"
 #import "cwv_navigation_type.h"
 
+NS_ASSUME_NONNULL_BEGIN
+
 @protocol CRIWVTranslateDelegate;
 @class CWVDownloadTask;
 @class CWVSSLStatus;
@@ -102,4 +104,6 @@
 
 @end
 
+NS_ASSUME_NONNULL_END
+
 #endif  // IOS_WEB_VIEW_PUBLIC_CWV_NAVIGATION_DELEGATE_H_
diff --git a/ios/web_view/public/cwv_navigation_type.h b/ios/web_view/public/cwv_navigation_type.h
index 0383f0a..99cc701 100644
--- a/ios/web_view/public/cwv_navigation_type.h
+++ b/ios/web_view/public/cwv_navigation_type.h
@@ -7,6 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
+NS_ASSUME_NONNULL_BEGIN
+
 // Types of transitions between pages.
 typedef NS_OPTIONS(NSUInteger, CWVNavigationType) {
   // User got to this page by clicking a link on another page.
@@ -129,4 +131,6 @@
   CWVNavigationTypeQualifierMask = 0xFFFFFF00,
 };
 
+NS_ASSUME_NONNULL_END
+
 #endif  // IOS_WEB_VIEW_PUBLIC_CWV_PAGE_TRANSITION_H_
diff --git a/ios/web_view/public/cwv_preferences.h b/ios/web_view/public/cwv_preferences.h
index 9eb9eea..3489be3 100644
--- a/ios/web_view/public/cwv_preferences.h
+++ b/ios/web_view/public/cwv_preferences.h
@@ -9,6 +9,8 @@
 
 #import "cwv_export.h"
 
+NS_ASSUME_NONNULL_BEGIN
+
 // Preferences for user settings. The preferences are stored on the local
 // storage.
 CWV_EXPORT
@@ -30,4 +32,6 @@
 
 @end
 
+NS_ASSUME_NONNULL_END
+
 #endif  // IOS_WEB_VIEW_PUBLIC_CWV_PREFERENCES_H_
diff --git a/ios/web_view/public/cwv_preferences_autofill.h b/ios/web_view/public/cwv_preferences_autofill.h
index 016e4ec..46f0d66 100644
--- a/ios/web_view/public/cwv_preferences_autofill.h
+++ b/ios/web_view/public/cwv_preferences_autofill.h
@@ -9,6 +9,8 @@
 
 #import "cwv_preferences.h"
 
+NS_ASSUME_NONNULL_BEGIN
+
 @interface CWVPreferences (Autofill)
 
 // Whether or not profile autofill is turned on. Defaults to |YES|.
@@ -34,4 +36,6 @@
 
 @end
 
+NS_ASSUME_NONNULL_END
+
 #endif  // IOS_WEB_VIEW_PUBLIC_CWV_PREFERENCES_AUTOFILL_H_
diff --git a/ios/web_view/public/cwv_web_view_autofill.h b/ios/web_view/public/cwv_web_view_autofill.h
index 996141e..002cdbb 100644
--- a/ios/web_view/public/cwv_web_view_autofill.h
+++ b/ios/web_view/public/cwv_web_view_autofill.h
@@ -7,6 +7,8 @@
 
 #import "cwv_web_view.h"
 
+NS_ASSUME_NONNULL_BEGIN
+
 @class CWVAutofillController;
 
 @interface CWVWebView (Autofill)
@@ -16,4 +18,6 @@
 
 @end
 
+NS_ASSUME_NONNULL_END
+
 #endif  // IOS_WEB_VIEW_PUBLIC_CWV_WEB_VIEW_AUTOFILL_H_
diff --git a/ios/web_view/test/BUILD.gn b/ios/web_view/test/BUILD.gn
index 728429d..2b81dbe 100644
--- a/ios/web_view/test/BUILD.gn
+++ b/ios/web_view/test/BUILD.gn
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//ios/build/config.gni")
 import("//build/config/ios/rules.gni")
+import("//ios/build/config.gni")
 import("//testing/test.gni")
 
 source_set("inttests") {
@@ -46,6 +46,8 @@
     "//ios/web_view/public/cwv_web_view_configuration.h",
     "observer.h",
     "observer.mm",
+    "test_with_locale_and_resources.cc",
+    "test_with_locale_and_resources.h",
     "web_view_test_util.h",
     "web_view_test_util.mm",
   ]
@@ -53,6 +55,8 @@
   deps = [
     "//base:base",
     "//base/test:test_support",
+    "//testing/gtest",
+    "//ui/base",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/web_view/test/DEPS b/ios/web_view/test/DEPS
index fa59dfb..c6be3c2 100644
--- a/ios/web_view/test/DEPS
+++ b/ios/web_view/test/DEPS
@@ -4,4 +4,5 @@
   "+ios/web_view/public",
   "+mojo/core/embedder",
   "+net",
+  "+ui/base",
 ]
diff --git a/ios/web_view/test/test_with_locale_and_resources.cc b/ios/web_view/test/test_with_locale_and_resources.cc
new file mode 100644
index 0000000..2d934058
--- /dev/null
+++ b/ios/web_view/test/test_with_locale_and_resources.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 "ios/web_view/test/test_with_locale_and_resources.h"
+
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace ios_web_view {
+
+TestWithLocaleAndResources::TestWithLocaleAndResources() {
+  l10n_util::OverrideLocaleWithCocoaLocale();
+  ui::ResourceBundle::InitSharedInstanceWithLocale(
+      l10n_util::GetLocaleOverride(), /*delegate=*/nullptr,
+      ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
+}
+
+TestWithLocaleAndResources::~TestWithLocaleAndResources() {
+  ui::ResourceBundle::CleanupSharedInstance();
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/test/test_with_locale_and_resources.h b/ios/web_view/test/test_with_locale_and_resources.h
new file mode 100644
index 0000000..ffb5ce20
--- /dev/null
+++ b/ios/web_view/test/test_with_locale_and_resources.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_TEST_TEST_WITH_LOCALE_AND_RESOURCES_H_
+#define IOS_WEB_VIEW_TEST_TEST_WITH_LOCALE_AND_RESOURCES_H_
+
+#include "testing/platform_test.h"
+
+namespace ios_web_view {
+
+// A test suite that ensure the locale is correctly set and the resources
+// are available. Use this base class for tests instead of PlatformTest if
+// they depend on localized strings.
+class TestWithLocaleAndResources : public PlatformTest {
+ protected:
+  TestWithLocaleAndResources();
+  ~TestWithLocaleAndResources() override;
+};
+
+}  // namespace ios_web_view
+
+#endif  // IOS_WEB_VIEW_TEST_TEST_WITH_LOCALE_AND_RESOURCES_H_
diff --git a/ios/web_view/test/web_view_test_util.mm b/ios/web_view/test/web_view_test_util.mm
index 6a2d162..d1beee76 100644
--- a/ios/web_view/test/web_view_test_util.mm
+++ b/ios/web_view/test/web_view_test_util.mm
@@ -85,13 +85,14 @@
 
 void CopyWebViewState(CWVWebView* source_web_view,
                       CWVWebView* destination_web_view) {
-  NSMutableData* data = [[NSMutableData alloc] init];
   NSKeyedArchiver* archiver =
-      [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
+      [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
   [source_web_view encodeRestorableStateWithCoder:archiver];
-  [archiver finishEncoding];
+
   NSKeyedUnarchiver* unarchiver =
-      [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:[archiver encodedData]
+                                                  error:nil];
+  unarchiver.requiresSecureCoding = NO;
   [destination_web_view decodeRestorableStateWithCoder:unarchiver];
 }
 
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc
index e66cf4d..3a183ea 100644
--- a/ipc/ipc_channel_proxy.cc
+++ b/ipc/ipc_channel_proxy.cc
@@ -223,10 +223,7 @@
 }
 
 // Called on the IPC::Channel thread
-void ChannelProxy::Context::OnSendMessage(std::unique_ptr<Message> message,
-                                          const char* debug_name) {
-  const char* context = debug_name ? debug_name : "";
-  TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION scoped_event(context);
+void ChannelProxy::Context::OnSendMessage(std::unique_ptr<Message> message) {
   if (!channel_) {
     OnChannelClosed();
     return;
@@ -348,7 +345,8 @@
 void ChannelProxy::Context::RemoveListenerTaskRunner(int32_t routing_id) {
   DCHECK(default_listener_task_runner_->BelongsToCurrentThread());
   base::AutoLock lock(listener_thread_task_runners_lock_);
-  listener_thread_task_runners_.erase(routing_id);
+  if (base::Contains(listener_thread_task_runners_, routing_id))
+    listener_thread_task_runners_.erase(routing_id);
 }
 
 // Called on the IPC::Channel thread.
@@ -359,11 +357,10 @@
     return default_listener_task_runner_.get();
 
   base::AutoLock lock(listener_thread_task_runners_lock_);
-  auto task_runner = listener_thread_task_runners_.find(routing_id);
-  if (task_runner != listener_thread_task_runners_.end()) {
-    DCHECK(task_runner->second.get());
-    return task_runner->second.get();
-  }
+  base::SingleThreadTaskRunner* task_runner =
+      listener_thread_task_runners_[routing_id].get();
+  if (task_runner)
+    return task_runner;
   return default_listener_task_runner_.get();
 }
 
@@ -422,10 +419,10 @@
     support->AddGenericAssociatedInterface(name, factory);
 }
 
-void ChannelProxy::Context::Send(Message* message, const char* debug_name) {
+void ChannelProxy::Context::Send(Message* message) {
   ipc_task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&ChannelProxy::Context::OnSendMessage, this,
-                                base::WrapUnique(message), debug_name));
+                                base::WrapUnique(message)));
 }
 
 //-----------------------------------------------------------------------------
@@ -552,11 +549,11 @@
 
 bool ChannelProxy::Send(Message* message) {
   DCHECK(!message->is_sync()) << "Need to use IPC::SyncChannel";
-  SendInternal(message, TRACE_HEAP_PROFILER_API_GET_CURRENT_TASK_CONTEXT());
+  SendInternal(message);
   return true;
 }
 
-void ChannelProxy::SendInternal(Message* message, const char* debug_name) {
+void ChannelProxy::SendInternal(Message* message) {
   DCHECK(did_init_);
 
   // TODO(alexeypa): add DCHECK(CalledOnValidThread()) here. Currently there are
@@ -574,7 +571,7 @@
   Logging::GetInstance()->OnSendMessage(message);
 #endif
 
-  context_->Send(message, debug_name);
+  context_->Send(message);
 }
 
 void ChannelProxy::AddFilter(MessageFilter* filter) {
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index 55ea097..8352a93 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -270,7 +270,7 @@
     void OnDispatchMessage(const Message& message);
 
     // Sends |message| from appropriate thread.
-    void Send(Message* message, const char* debug_name);
+    void Send(Message* message);
 
     // Adds |task_runner| for the task to be executed later.
     void AddListenerTaskRunner(
@@ -324,8 +324,7 @@
     void CreateChannel(std::unique_ptr<ChannelFactory> factory);
 
     // Methods called on the IO thread.
-    void OnSendMessage(std::unique_ptr<Message> message_ptr,
-                       const char* debug_name);
+    void OnSendMessage(std::unique_ptr<Message> message_ptr);
     void OnAddFilter();
     void OnRemoveFilter(MessageFilter* filter);
 
@@ -411,7 +410,7 @@
   bool did_init() const { return did_init_; }
 
   // A Send() which doesn't DCHECK if the message is synchronous.
-  void SendInternal(Message* message, const char* debug_name);
+  void SendInternal(Message* message);
 
  private:
   friend class IpcSecurityTestUtil;
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index d8004662..5b342cc 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -615,8 +615,7 @@
                "line", IPC_MESSAGE_ID_LINE(message->type()));
 #endif
   if (!message->is_sync()) {
-    ChannelProxy::SendInternal(
-        message, TRACE_HEAP_PROFILER_API_GET_CURRENT_TASK_CONTEXT());
+    ChannelProxy::SendInternal(message);
     return true;
   }
 
@@ -631,8 +630,7 @@
     return false;
   }
 
-  ChannelProxy::SendInternal(
-      message, TRACE_HEAP_PROFILER_API_GET_CURRENT_TASK_CONTEXT());
+  ChannelProxy::SendInternal(message);
 
   // Wait for reply, or for any other incoming synchronous messages.
   // |this| might get deleted, so only call static functions at this point.
diff --git a/media/audio/audio_output_resampler.cc b/media/audio/audio_output_resampler.cc
index 727c2ff..84b11c2 100644
--- a/media/audio/audio_output_resampler.cc
+++ b/media/audio/audio_output_resampler.cc
@@ -109,56 +109,6 @@
       static_cast<AudioSampleRate>(kAudioSampleRateMax + 1));
 }
 
-// Record UMA statistics for input/output rebuffering.
-static void RecordRebufferingStats(const AudioParameters& input_params,
-                                   const AudioParameters& output_params) {
-  const int input_buffer_size = input_params.frames_per_buffer();
-  const int output_buffer_size = output_params.frames_per_buffer();
-  DCHECK_NE(0, input_buffer_size);
-  DCHECK_NE(0, output_buffer_size);
-
-  // Buffer size mismatch; see Media.Audio.Render.BrowserCallbackRegularity
-  // histogram for explanation.
-  int value = 0;
-  if (input_buffer_size >= output_buffer_size) {
-    // 0 if input size is a multiple of output size; otherwise -1.
-    value = (input_buffer_size % output_buffer_size) ? -1 : 0;
-  } else {
-    value = (output_buffer_size / input_buffer_size - 1) * 2;
-    if (output_buffer_size % input_buffer_size) {
-      // One more callback is issued periodically.
-      value += 1;
-    }
-  }
-
-  const int value_cap = (4096 / 128 - 1) * 2 + 1;
-  if (value > value_cap)
-    value = value_cap;
-
-  switch (input_params.latency_tag()) {
-    case AudioLatency::LATENCY_EXACT_MS:
-      base::UmaHistogramSparse(
-          "Media.Audio.Render.BrowserCallbackRegularity.LatencyExactMs", value);
-      return;
-    case AudioLatency::LATENCY_INTERACTIVE:
-      base::UmaHistogramSparse(
-          "Media.Audio.Render.BrowserCallbackRegularity.LatencyInteractive",
-          value);
-      return;
-    case AudioLatency::LATENCY_RTC:
-      base::UmaHistogramSparse(
-          "Media.Audio.Render.BrowserCallbackRegularity.LatencyRtc", value);
-      return;
-    case AudioLatency::LATENCY_PLAYBACK:
-      base::UmaHistogramSparse(
-          "Media.Audio.Render.BrowserCallbackRegularity.LatencyPlayback",
-          value);
-      return;
-    default:
-      DVLOG(1) << "Latency tag is not set";
-  }
-}
-
 // Only Windows has a high latency output driver that is not the same as the low
 // latency path.
 #if defined(OS_WIN)
@@ -481,9 +431,7 @@
       error_occurred_(false),
       input_buffer_size_(input_params.frames_per_buffer()),
       output_buffer_size_(output_params.frames_per_buffer()),
-      debug_recorder_(std::move(debug_recorder)) {
-  RecordRebufferingStats(input_params, output_params);
-}
+      debug_recorder_(std::move(debug_recorder)) {}
 
 OnMoreDataConverter::~OnMoreDataConverter() {
   // Ensure Stop() has been called so we don't end up with an AudioOutputStream
diff --git a/media/base/android/media_drm_bridge_unittest.cc b/media/base/android/media_drm_bridge_unittest.cc
index bed53cb..1083e5e 100644
--- a/media/base/android/media_drm_bridge_unittest.cc
+++ b/media/base/android/media_drm_bridge_unittest.cc
@@ -174,7 +174,7 @@
   CreateWithoutSessionSupport(kWidevineKeySystem, kTestOrigin, kL1);
 }
 
-TEST_F(MediaDrmBridgeTest, DISABLED_Provision_Widevine) {
+TEST_F(MediaDrmBridgeTest, Provision_Widevine) {
   // Only test this if Widevine is supported. Otherwise
   // CreateWithoutSessionSupport() will return null and it can't be tested.
   if (!MediaDrmBridge::IsKeySystemSupported(kWidevineKeySystem)) {
@@ -189,6 +189,16 @@
     return;
   }
 
+  // On Android M occasionally MediaDrm.getProvisionRequest() throws and thus a
+  // request can not be generated. This has been fixed in Android N. As Android
+  // M is unlikely to be fixed, disabling this test if running on Android M.
+  // http://crbug.com/973096#c21
+  if (base::android::BuildInfo::GetInstance()->sdk_int() ==
+      base::android::SDK_VERSION_MARSHMALLOW) {
+    VLOG(0) << "Disabled for Android M.";
+    return;
+  }
+
   // Calling Provision() later should trigger a provisioning request. As we
   // can't pass the request to a license server,
   // MockProvisionFetcher::Retrieve() simply drops the request and never
diff --git a/media/base/fake_demuxer_stream.cc b/media/base/fake_demuxer_stream.cc
index f71aff3..fe74fc7 100644
--- a/media/base/fake_demuxer_stream.cc
+++ b/media/base/fake_demuxer_stream.cc
@@ -157,9 +157,10 @@
 void FakeDemuxerStream::UpdateVideoDecoderConfig() {
   const gfx::Rect kVisibleRect(kStartWidth, kStartHeight);
   video_decoder_config_.Initialize(
-      kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_I420,
-      VideoColorSpace(), kNoTransformation, next_coded_size_, kVisibleRect,
-      next_coded_size_, EmptyExtraData(),
+      kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
+      VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(),
+      kNoTransformation, next_coded_size_, kVisibleRect, next_coded_size_,
+      EmptyExtraData(),
       is_encrypted_ ? AesCtrEncryptionScheme() : Unencrypted());
   next_coded_size_.Enlarge(kWidthDelta, kHeightDelta);
 }
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 33a6d05..1260407 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -506,10 +506,6 @@
   // implementations call DemuxerHost on the media thread.
   media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET,
                                                    "duration", duration));
-  UMA_HISTOGRAM_CUSTOM_TIMES(
-      "Media.Duration2", duration, base::TimeDelta::FromMilliseconds(1),
-      base::TimeDelta::FromDays(1), 50 /* bucket_count */);
-
   main_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&PipelineImpl::OnDurationChange, weak_pipeline_,
                                 duration));
diff --git a/media/base/supported_types.cc b/media/base/supported_types.cc
index 13d3fe51..0f5b4aa6 100644
--- a/media/base/supported_types.cc
+++ b/media/base/supported_types.cc
@@ -5,6 +5,7 @@
 #include "media/base/supported_types.h"
 
 #include "base/feature_list.h"
+#include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "media/base/media_client.h"
 #include "media/base/media_switches.h"
@@ -12,12 +13,19 @@
 #include "ui/display/display_switches.h"
 
 #if BUILDFLAG(ENABLE_LIBVPX)
+// TODO(dalecurtis): This technically should not be allowed in media/base. See
+// TODO below about moving outside of base.
 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
 #include "third_party/libvpx/source/libvpx/vpx/vpx_codec.h"
 #endif
 
 #if defined(OS_ANDROID)
 #include "base/android/build_info.h"
+
+// TODO(dalecurtis): This include is not allowed by media/base since
+// media/base/android is technically a different component. We should move
+// supported_types*.{cc,h} out of media/base to fix this.
+#include "media/base/android/media_codec_util.h"  // nogncheck
 #endif
 
 namespace media {
@@ -120,13 +128,11 @@
   return true;
 }
 
-bool IsVp9ProfileSupported(VideoCodecProfile profile) {
+bool IsVp9ProfileSupportedByLibvpx(VideoCodecProfile profile) {
 #if BUILDFLAG(ENABLE_LIBVPX)
   // High bit depth capabilities may be toggled via LibVPX config flags.
-  static bool vpx_supports_high_bit_depth =
-      (vpx_codec_get_caps(vpx_codec_vp9_dx()) & VPX_CODEC_CAP_HIGHBITDEPTH) !=
-      0;
-
+  static const bool vpx_supports_hbd = (vpx_codec_get_caps(vpx_codec_vp9_dx()) &
+                                        VPX_CODEC_CAP_HIGHBITDEPTH) != 0;
   switch (profile) {
     // LibVPX always supports Profiles 0 and 1.
     case VP9PROFILE_PROFILE0:
@@ -134,7 +140,7 @@
       return true;
     case VP9PROFILE_PROFILE2:
     case VP9PROFILE_PROFILE3:
-      return vpx_supports_high_bit_depth;
+      return vpx_supports_hbd;
     default:
       NOTREACHED();
   }
@@ -142,6 +148,45 @@
   return false;
 }
 
+bool IsVp9ProfileSupported(VideoCodecProfile profile) {
+  if (IsVp9ProfileSupportedByLibvpx(profile))
+    return true;
+
+#if defined(OS_ANDROID)
+  // Support for VP9.2, VP9.3 was not added until Nougat.
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_NOUGAT) {
+    return false;
+  }
+
+  struct Vp9Support {
+    bool has_profile2 = false;
+    bool has_profile3 = false;
+  };
+  static const base::NoDestructor<Vp9Support> vp9_support([]() {
+    std::vector<CodecProfileLevel> profiles;
+    MediaCodecUtil::AddSupportedCodecProfileLevels(&profiles);
+
+    Vp9Support vp9_support;
+    for (const auto& c : profiles) {
+      if (c.profile == VP9PROFILE_PROFILE2)
+        vp9_support.has_profile2 = true;
+      else if (c.profile == VP9PROFILE_PROFILE3)
+        vp9_support.has_profile3 = true;
+    }
+
+    return vp9_support;
+  }());
+
+  if (vp9_support->has_profile2 && profile == VP9PROFILE_PROFILE2)
+    return true;
+  if (vp9_support->has_profile3 && profile == VP9PROFILE_PROFILE3)
+    return true;
+#endif
+
+  return false;
+}
+
 bool IsAudioCodecProprietary(AudioCodec codec) {
   switch (codec) {
     case media::kCodecAAC:
@@ -275,4 +320,8 @@
   return false;
 }
 
+bool IsVp9ProfileSupportedForTesting(VideoCodecProfile profile) {
+  return IsVp9ProfileSupported(profile);
+}
+
 }  // namespace media
diff --git a/media/base/supported_types.h b/media/base/supported_types.h
index ba4ab01..091137e 100644
--- a/media/base/supported_types.h
+++ b/media/base/supported_types.h
@@ -21,6 +21,9 @@
 MEDIA_EXPORT bool IsDefaultSupportedAudioType(const AudioType& type);
 MEDIA_EXPORT bool IsDefaultSupportedVideoType(const VideoType& type);
 
+// Test helper for navigating the complexities of VP9.2 and VP9.3 support.
+MEDIA_EXPORT bool IsVp9ProfileSupportedForTesting(VideoCodecProfile profile);
+
 }  // namespace media
 
 #endif  // MEDIA_BASE_SUPPORTED_TYPES_H_
diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc
index 5fa7bae4..0bc65a0 100644
--- a/media/base/test_helpers.cc
+++ b/media/base/test_helpers.cc
@@ -136,7 +136,7 @@
   gfx::Size natural_size = coded_size;
 
   return VideoDecoderConfig(
-      codec, profile, PIXEL_FORMAT_I420, color_space,
+      codec, profile, VideoDecoderConfig::AlphaMode::kIsOpaque, color_space,
       VideoTransformation(rotation), coded_size, visible_rect, natural_size,
       EmptyExtraData(),
       is_encrypted ? AesCtrEncryptionScheme() : Unencrypted());
diff --git a/media/base/video_decoder_config.cc b/media/base/video_decoder_config.cc
index c16ce85..d758928 100644
--- a/media/base/video_decoder_config.cc
+++ b/media/base/video_decoder_config.cc
@@ -8,13 +8,20 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "media/base/limits.h"
 #include "media/base/media_util.h"
-#include "media/base/video_frame.h"
 #include "media/base/video_types.h"
 #include "media/base/video_util.h"
 
 namespace media {
 
+static bool IsValidSize(const gfx::Size& size) {
+  const int area = size.GetCheckedArea().ValueOrDefault(INT_MAX);
+  return area && area <= limits::kMaxCanvas &&
+         size.width() <= limits::kMaxDimension &&
+         size.height() <= limits::kMaxDimension;
+}
+
 VideoCodec VideoCodecProfileToVideoCodec(VideoCodecProfile profile) {
   switch (profile) {
     case VIDEO_CODEC_PROFILE_UNKNOWN:
@@ -63,13 +70,13 @@
 VideoDecoderConfig::VideoDecoderConfig()
     : codec_(kUnknownVideoCodec),
       profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
-      format_(PIXEL_FORMAT_UNKNOWN),
+      alpha_mode_(AlphaMode::kIsOpaque),
       transformation_(kNoTransformation) {}
 
 VideoDecoderConfig::VideoDecoderConfig(
     VideoCodec codec,
     VideoCodecProfile profile,
-    VideoPixelFormat format,
+    AlphaMode alpha_mode,
     const VideoColorSpace& color_space,
     VideoTransformation rotation,
     const gfx::Size& coded_size,
@@ -77,7 +84,7 @@
     const gfx::Size& natural_size,
     const std::vector<uint8_t>& extra_data,
     const EncryptionScheme& encryption_scheme) {
-  Initialize(codec, profile, format, color_space, rotation, coded_size,
+  Initialize(codec, profile, alpha_mode, color_space, rotation, coded_size,
              visible_rect, natural_size, extra_data, encryption_scheme);
 }
 
@@ -105,7 +112,7 @@
 
 void VideoDecoderConfig::Initialize(VideoCodec codec,
                                     VideoCodecProfile profile,
-                                    VideoPixelFormat format,
+                                    AlphaMode alpha_mode,
                                     const VideoColorSpace& color_space,
                                     VideoTransformation transformation,
                                     const gfx::Size& coded_size,
@@ -115,7 +122,7 @@
                                     const EncryptionScheme& encryption_scheme) {
   codec_ = codec;
   profile_ = profile;
-  format_ = format;
+  alpha_mode_ = alpha_mode;
   transformation_ = transformation;
   coded_size_ = coded_size;
   visible_rect_ = visible_rect;
@@ -126,30 +133,31 @@
 }
 
 bool VideoDecoderConfig::IsValidConfig() const {
-  return codec_ != kUnknownVideoCodec && natural_size_.width() > 0 &&
-         natural_size_.height() > 0 &&
-         VideoFrame::IsValidConfig(format_, VideoFrame::STORAGE_UNOWNED_MEMORY,
-                                   coded_size_, visible_rect_, natural_size_);
+  return codec_ != kUnknownVideoCodec && IsValidSize(coded_size_) &&
+         IsValidSize(natural_size_) &&
+         gfx::Rect(coded_size_).Contains(visible_rect_);
 }
 
 bool VideoDecoderConfig::Matches(const VideoDecoderConfig& config) const {
-  return ((codec() == config.codec()) && (format() == config.format()) &&
-          (profile() == config.profile()) &&
-          (video_transformation() == config.video_transformation()) &&
-          (coded_size() == config.coded_size()) &&
-          (visible_rect() == config.visible_rect()) &&
-          (natural_size() == config.natural_size()) &&
-          (extra_data() == config.extra_data()) &&
-          (encryption_scheme().Matches(config.encryption_scheme())) &&
-          (color_space_info() == config.color_space_info()) &&
-          (hdr_metadata() == config.hdr_metadata()));
+  return codec() == config.codec() && profile() == config.profile() &&
+         alpha_mode() == config.alpha_mode() &&
+         video_transformation() == config.video_transformation() &&
+         coded_size() == config.coded_size() &&
+         visible_rect() == config.visible_rect() &&
+         natural_size() == config.natural_size() &&
+         extra_data() == config.extra_data() &&
+         encryption_scheme().Matches(config.encryption_scheme()) &&
+         color_space_info() == config.color_space_info() &&
+         hdr_metadata() == config.hdr_metadata();
 }
 
 std::string VideoDecoderConfig::AsHumanReadableString() const {
   std::ostringstream s;
-  s << "codec: " << GetCodecName(codec()) << ", format: " << format()
-    << ", profile: " << GetProfileName(profile()) << ", coded size: ["
-    << coded_size().width() << "," << coded_size().height() << "]"
+  s << "codec: " << GetCodecName(codec())
+    << ", profile: " << GetProfileName(profile()) << ", alpha_mode: "
+    << (alpha_mode() == AlphaMode::kHasAlpha ? "has_alpha" : "is_opaque")
+    << ", coded size: [" << coded_size().width() << "," << coded_size().height()
+    << "]"
     << ", visible rect: [" << visible_rect().x() << "," << visible_rect().y()
     << "," << visible_rect().width() << "," << visible_rect().height() << "]"
     << ", natural size: [" << natural_size().width() << ","
diff --git a/media/base/video_decoder_config.h b/media/base/video_decoder_config.h
index ef65fee..c65e35f 100644
--- a/media/base/video_decoder_config.h
+++ b/media/base/video_decoder_config.h
@@ -35,11 +35,13 @@
   // appropriate values before using.
   VideoDecoderConfig();
 
+  enum class AlphaMode { kHasAlpha, kIsOpaque };
+
   // Constructs an initialized object. It is acceptable to pass in NULL for
   // |extra_data|, otherwise the memory is copied.
   VideoDecoderConfig(VideoCodec codec,
                      VideoCodecProfile profile,
-                     VideoPixelFormat format,
+                     AlphaMode alpha_mode,
                      const VideoColorSpace& color_space,
                      VideoTransformation transformation,
                      const gfx::Size& coded_size,
@@ -47,7 +49,6 @@
                      const gfx::Size& natural_size,
                      const std::vector<uint8_t>& extra_data,
                      const EncryptionScheme& encryption_scheme);
-
   VideoDecoderConfig(const VideoDecoderConfig& other);
 
   ~VideoDecoderConfig();
@@ -55,7 +56,7 @@
   // Resets the internal state of this object.
   void Initialize(VideoCodec codec,
                   VideoCodecProfile profile,
-                  VideoPixelFormat format,
+                  AlphaMode alpha_mode,
                   const VideoColorSpace& color_space,
                   VideoTransformation transformation,
                   const gfx::Size& coded_size,
@@ -79,14 +80,9 @@
 
   static std::string GetHumanReadableProfile(VideoCodecProfile profile);
 
-  // Video codec and profile.
   VideoCodec codec() const { return codec_; }
   VideoCodecProfile profile() const { return profile_; }
-
-  // Encoded video pixel format. Lossy codecs rarely have actual pixel formats,
-  // this should usually be interpreted as a subsampling specification. (The
-  // decoder determines the actual pixel format.)
-  VideoPixelFormat format() const { return format_; }
+  AlphaMode alpha_mode() const { return alpha_mode_; }
 
   // Difference between encoded and display orientation.
   //
@@ -152,7 +148,7 @@
   VideoCodec codec_;
   VideoCodecProfile profile_;
 
-  VideoPixelFormat format_;
+  AlphaMode alpha_mode_;
 
   VideoTransformation transformation_;
 
diff --git a/media/base/video_decoder_config_unittest.cc b/media/base/video_decoder_config_unittest.cc
index 3c7b4e3..636e7a4 100644
--- a/media/base/video_decoder_config_unittest.cc
+++ b/media/base/video_decoder_config_unittest.cc
@@ -10,52 +10,59 @@
 
 namespace media {
 
-static const VideoPixelFormat kVideoFormat = PIXEL_FORMAT_I420;
 static const gfx::Size kCodedSize(320, 240);
 static const gfx::Rect kVisibleRect(320, 240);
 static const gfx::Size kNaturalSize(320, 240);
 
-TEST(VideoDecoderConfigTest, Invalid_UnsupportedPixelFormat) {
+TEST(VideoDecoderConfigTest, AlphaModeSetCorrectly) {
   VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
-                            PIXEL_FORMAT_UNKNOWN, VideoColorSpace(),
-                            kNoTransformation, kCodedSize, kVisibleRect,
-                            kNaturalSize, EmptyExtraData(), Unencrypted());
-  EXPECT_FALSE(config.IsValidConfig());
+                            VideoDecoderConfig::AlphaMode::kIsOpaque,
+                            VideoColorSpace(), kNoTransformation, kCodedSize,
+                            kVisibleRect, kNaturalSize, EmptyExtraData(),
+                            Unencrypted());
+  EXPECT_TRUE(config.IsValidConfig());
+  EXPECT_EQ(config.alpha_mode(), VideoDecoderConfig::AlphaMode::kIsOpaque);
+
+  config.Initialize(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
+                    VideoDecoderConfig::AlphaMode::kHasAlpha, VideoColorSpace(),
+                    kNoTransformation, kCodedSize, kVisibleRect, kNaturalSize,
+                    EmptyExtraData(), Unencrypted());
+  EXPECT_EQ(config.alpha_mode(), VideoDecoderConfig::AlphaMode::kHasAlpha);
 }
 
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorZero) {
   gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1);
-  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
-                            VideoColorSpace(), kNoTransformation, kCodedSize,
-                            kVisibleRect, natural_size, EmptyExtraData(),
-                            Unencrypted());
+  VideoDecoderConfig config(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      natural_size, EmptyExtraData(), Unencrypted());
   EXPECT_FALSE(config.IsValidConfig());
 }
 
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorZero) {
   gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0);
-  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
-                            VideoColorSpace(), kNoTransformation, kCodedSize,
-                            kVisibleRect, natural_size, EmptyExtraData(),
-                            Unencrypted());
+  VideoDecoderConfig config(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      natural_size, EmptyExtraData(), Unencrypted());
   EXPECT_FALSE(config.IsValidConfig());
 }
 
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorNegative) {
   gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1);
-  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
-                            VideoColorSpace(), kNoTransformation, kCodedSize,
-                            kVisibleRect, natural_size, EmptyExtraData(),
-                            Unencrypted());
+  VideoDecoderConfig config(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      natural_size, EmptyExtraData(), Unencrypted());
   EXPECT_FALSE(config.IsValidConfig());
 }
 
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorNegative) {
   gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1);
-  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
-                            VideoColorSpace(), kNoTransformation, kCodedSize,
-                            kVisibleRect, natural_size, EmptyExtraData(),
-                            Unencrypted());
+  VideoDecoderConfig config(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      natural_size, EmptyExtraData(), Unencrypted());
   EXPECT_FALSE(config.IsValidConfig());
 }
 
@@ -63,10 +70,10 @@
   int width = kVisibleRect.size().width();
   int num = ceil(static_cast<double>(limits::kMaxDimension + 1) / width);
   gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1);
-  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
-                            VideoColorSpace(), kNoTransformation, kCodedSize,
-                            kVisibleRect, natural_size, EmptyExtraData(),
-                            Unencrypted());
+  VideoDecoderConfig config(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      natural_size, EmptyExtraData(), Unencrypted());
   EXPECT_FALSE(config.IsValidConfig());
 }
 
@@ -77,10 +84,10 @@
   gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, den);
   EXPECT_EQ(320, natural_size.width());
   EXPECT_EQ(240 * 641, natural_size.height());
-  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
-                            VideoColorSpace(), kNoTransformation, kCodedSize,
-                            kVisibleRect, natural_size, EmptyExtraData(),
-                            Unencrypted());
+  VideoDecoderConfig config(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      natural_size, EmptyExtraData(), Unencrypted());
   EXPECT_FALSE(config.IsValidConfig());
 }
 
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 40c2b4483..2e493ada 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -98,7 +98,6 @@
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
     case PIXEL_FORMAT_RGB24:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_Y16:
     case PIXEL_FORMAT_ABGR:
     case PIXEL_FORMAT_XBGR:
@@ -188,6 +187,26 @@
   return VideoFrameLayout::CreateWithPlanes(format, coded_size, planes);
 }
 
+#if defined(OS_LINUX)
+// This class allows us to embed a vector<ScopedFD> into a scoped_refptr, and
+// thus to have several VideoFrames share the same set of DMABUF FDs.
+class VideoFrame::DmabufHolder
+    : public base::RefCountedThreadSafe<DmabufHolder> {
+ public:
+  DmabufHolder() = default;
+  DmabufHolder(std::vector<base::ScopedFD>&& fds) : fds_(std::move(fds)) {}
+
+  const std::vector<base::ScopedFD>& fds() const { return fds_; }
+  size_t size() const { return fds_.size(); }
+
+ private:
+  std::vector<base::ScopedFD> fds_;
+
+  friend class base::RefCountedThreadSafe<DmabufHolder>;
+  ~DmabufHolder() = default;
+};
+#endif  // defined(OS_LINUX)
+
 // static
 bool VideoFrame::IsValidConfig(VideoPixelFormat format,
                                StorageType storage_type,
@@ -273,8 +292,8 @@
     const gfx::Size& natural_size,
     base::TimeDelta timestamp) {
   if (format != PIXEL_FORMAT_ARGB && format != PIXEL_FORMAT_XRGB &&
-      format != PIXEL_FORMAT_RGB32 && format != PIXEL_FORMAT_UYVY &&
-      format != PIXEL_FORMAT_NV12 && format != PIXEL_FORMAT_I420) {
+      format != PIXEL_FORMAT_UYVY && format != PIXEL_FORMAT_NV12 &&
+      format != PIXEL_FORMAT_I420 && format != PIXEL_FORMAT_ABGR) {
     DLOG(ERROR) << "Unsupported pixel format: "
                 << VideoPixelFormatToString(format);
     return nullptr;
@@ -526,7 +545,8 @@
   memcpy(&frame->mailbox_holders_, mailbox_holders,
          sizeof(frame->mailbox_holders_));
   frame->mailbox_holders_release_cb_ = ReleaseMailboxCB();
-  frame->dmabuf_fds_ = std::move(dmabuf_fds);
+  frame->dmabuf_fds_ =
+      base::MakeRefCounted<DmabufHolder>(std::move(dmabuf_fds));
   DCHECK(frame->HasDmaBufs());
 
   return frame;
@@ -624,14 +644,9 @@
   }
 
 #if defined(OS_LINUX)
-  // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
-  if (frame.storage_type() == STORAGE_DMABUFS) {
-    wrapping_frame->dmabuf_fds_ = DuplicateFDs(frame.dmabuf_fds_);
-    if (wrapping_frame->dmabuf_fds_.empty()) {
-      DLOG(ERROR) << __func__ << " Couldn't duplicate fds.";
-      return nullptr;
-    }
-  }
+  DCHECK(frame.dmabuf_fds_);
+  // If there are any |dmabuf_fds_| plugged in, we should refer them too.
+  wrapping_frame->dmabuf_fds_ = frame.dmabuf_fds_;
 #endif
 
   if (frame.storage_type() == STORAGE_SHMEM) {
@@ -765,7 +780,6 @@
   switch (format) {
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_ABGR:
     case PIXEL_FORMAT_XBGR:
       return 4;
@@ -951,11 +965,17 @@
 const std::vector<base::ScopedFD>& VideoFrame::DmabufFds() const {
   DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
 
-  return dmabuf_fds_;
+  return dmabuf_fds_->fds();
 }
 
 bool VideoFrame::HasDmaBufs() const {
-  return !dmabuf_fds_.empty();
+  return dmabuf_fds_->size() > 0;
+}
+
+bool VideoFrame::IsSameDmaBufsAs(const VideoFrame& frame) const {
+  return storage_type_ == STORAGE_DMABUFS &&
+         frame.storage_type_ == STORAGE_DMABUFS &&
+         &DmabufFds() == &frame.DmabufFds();
 }
 #endif
 
@@ -1093,6 +1113,9 @@
       visible_rect_(Intersection(visible_rect, gfx::Rect(layout.coded_size()))),
       natural_size_(natural_size),
       shared_memory_offset_(0),
+#if defined(OS_LINUX)
+      dmabuf_fds_(base::MakeRefCounted<DmabufHolder>()),
+#endif
       timestamp_(timestamp),
       unique_id_(g_unique_id_generator.GetNext()) {
   DCHECK(IsValidConfig(format(), storage_type, coded_size(), visible_rect_,
@@ -1243,7 +1266,6 @@
         case PIXEL_FORMAT_ARGB:
         case PIXEL_FORMAT_XRGB:
         case PIXEL_FORMAT_RGB24:
-        case PIXEL_FORMAT_RGB32:
         case PIXEL_FORMAT_MJPEG:
         case PIXEL_FORMAT_ABGR:
         case PIXEL_FORMAT_XBGR:
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index 9f24faa..ead0b64 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -477,6 +477,10 @@
 
   // Returns true if |frame| has DmaBufs.
   bool HasDmaBufs() const;
+
+  // Returns true if both VideoFrames are backed by DMABUF memory and point
+  // to the same set of DMABUFs, meaning that both frames use the same memory.
+  bool IsSameDmaBufsAs(const VideoFrame& frame) const;
 #endif
 
   void AddReadOnlySharedMemoryRegion(base::ReadOnlySharedMemoryRegion* region);
@@ -664,10 +668,17 @@
   size_t shared_memory_offset_;
 
 #if defined(OS_LINUX)
+  class DmabufHolder;
+
   // Dmabufs for the frame, used when storage is STORAGE_DMABUFS. Size is either
   // equal or less than the number of planes of the frame. If it is less, then
   // the memory area represented by the last FD contains the remaining planes.
-  std::vector<base::ScopedFD> dmabuf_fds_;
+  // If a STORAGE_DMABUFS frame is wrapped into another, the wrapping frame
+  // will get an extra reference to the FDs (i.e. no duplication is involved).
+  // This makes it possible to test whether two VideoFrame instances point to
+  // the same DMABUF memory by testing for
+  // (&vf1->DmabufFds() == &vf2->DmabufFds()).
+  scoped_refptr<DmabufHolder> dmabuf_fds_;
 #endif
 
 #if defined(OS_MACOSX)
diff --git a/media/base/video_frame_layout.cc b/media/base/video_frame_layout.cc
index 9451874..9f80a03 100644
--- a/media/base/video_frame_layout.cc
+++ b/media/base/video_frame_layout.cc
@@ -48,7 +48,6 @@
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
     case PIXEL_FORMAT_RGB24:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_MJPEG:
     case PIXEL_FORMAT_Y16:
     case PIXEL_FORMAT_ABGR:
diff --git a/media/base/video_frame_unittest.cc b/media/base/video_frame_unittest.cc
index 25104ba..bfb2aa621 100644
--- a/media/base/video_frame_unittest.cc
+++ b/media/base/video_frame_unittest.cc
@@ -425,6 +425,19 @@
   EXPECT_EQ(frame->coded_size(), coded_size);
   EXPECT_EQ(frame->visible_rect(), visible_rect);
   EXPECT_EQ(frame->timestamp(), timestamp);
+
+  // Wrapped DMABUF frames must share the same memory as their wrappee.
+  auto wrapped_frame = VideoFrame::WrapVideoFrame(
+      *frame, frame->format(), visible_rect, visible_rect.size());
+  ASSERT_NE(wrapped_frame, nullptr);
+  ASSERT_EQ(wrapped_frame->IsSameDmaBufsAs(*frame), true);
+
+  // Multi-level wrapping should share same memory as well.
+  auto wrapped_frame2 = VideoFrame::WrapVideoFrame(
+      *wrapped_frame, frame->format(), visible_rect, visible_rect.size());
+  ASSERT_NE(wrapped_frame2, nullptr);
+  ASSERT_EQ(wrapped_frame2->IsSameDmaBufsAs(*wrapped_frame), true);
+  ASSERT_EQ(wrapped_frame2->IsSameDmaBufsAs(*frame), true);
 }
 #endif
 
@@ -608,7 +621,6 @@
       case PIXEL_FORMAT_ARGB:
       case PIXEL_FORMAT_XRGB:
       case PIXEL_FORMAT_I420A:
-      case PIXEL_FORMAT_RGB32:
       case PIXEL_FORMAT_ABGR:
       case PIXEL_FORMAT_XBGR:
       case PIXEL_FORMAT_P016LE:
diff --git a/media/base/video_thumbnail_decoder_unittest.cc b/media/base/video_thumbnail_decoder_unittest.cc
index 9468987..c36cfe4 100644
--- a/media/base/video_thumbnail_decoder_unittest.cc
+++ b/media/base/video_thumbnail_decoder_unittest.cc
@@ -36,9 +36,9 @@
     auto mock_video_decoder = std::make_unique<MockVideoDecoder>();
     mock_video_decoder_ = mock_video_decoder.get();
     VideoDecoderConfig valid_config(
-        kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420, VideoColorSpace(),
-        kNoTransformation, gfx::Size(1, 1), gfx::Rect(1, 1), gfx::Size(1, 1),
-        EmptyExtraData(), Unencrypted());
+        kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+        VideoColorSpace(), kNoTransformation, gfx::Size(1, 1), gfx::Rect(1, 1),
+        gfx::Size(1, 1), EmptyExtraData(), Unencrypted());
 
     thumbnail_decoder_ = std::make_unique<VideoThumbnailDecoder>(
         std::move(mock_video_decoder), valid_config, std::vector<uint8_t>{0u});
diff --git a/media/base/video_types.cc b/media/base/video_types.cc
index 4c7a7474..ac7b7cc 100644
--- a/media/base/video_types.cc
+++ b/media/base/video_types.cc
@@ -37,8 +37,6 @@
       return "PIXEL_FORMAT_XRGB";
     case PIXEL_FORMAT_RGB24:
       return "PIXEL_FORMAT_RGB24";
-    case PIXEL_FORMAT_RGB32:
-      return "PIXEL_FORMAT_RGB32";
     case PIXEL_FORMAT_MJPEG:
       return "PIXEL_FORMAT_MJPEG";
     case PIXEL_FORMAT_MT21:
@@ -118,7 +116,6 @@
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
     case PIXEL_FORMAT_RGB24:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_MJPEG:
     case PIXEL_FORMAT_Y16:
     case PIXEL_FORMAT_ABGR:
@@ -158,7 +155,6 @@
       return true;
     case PIXEL_FORMAT_I420A:
     case PIXEL_FORMAT_ARGB:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_ABGR:
       break;
   }
@@ -182,7 +178,6 @@
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
     case PIXEL_FORMAT_RGB24:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_MJPEG:
     case PIXEL_FORMAT_MT21:
     case PIXEL_FORMAT_ABGR:
diff --git a/media/base/video_types.h b/media/base/video_types.h
index af74e68..22c4a78 100644
--- a/media/base/video_types.h
+++ b/media/base/video_types.h
@@ -44,9 +44,8 @@
   PIXEL_FORMAT_ARGB = 10,   // 32bpp BGRA (byte-order), 1 plane.
   PIXEL_FORMAT_XRGB = 11,   // 24bpp BGRX (byte-order), 1 plane.
   PIXEL_FORMAT_RGB24 = 12,  // 24bpp BGR (byte-order), 1 plane.
-  // TODO(crbug.com/953128): Deprecate PIXEL_FORMAT_RGB32 in favor of
-  // PIXEL_FORMAT_ARGB.
-  PIXEL_FORMAT_RGB32 = 13,  // 32bpp BGRA (byte-order), 1 plane.
+
+  /* PIXEL_FORMAT_RGB32 = 13,  Deprecated */
   PIXEL_FORMAT_MJPEG = 14,  // MJPEG compressed.
   // MediaTek proprietary format. MT21 is similar to NV21 except the memory
   // layout and pixel layout (swizzles). 12bpp with Y plane followed by a 2x2
diff --git a/media/blink/video_decode_stats_reporter_unittest.cc b/media/blink/video_decode_stats_reporter_unittest.cc
index 29de734..b0392f81 100644
--- a/media/blink/video_decode_stats_reporter_unittest.cc
+++ b/media/blink/video_decode_stats_reporter_unittest.cc
@@ -48,10 +48,10 @@
                                    gfx::Size natural_size) {
   gfx::Size coded_size = natural_size;
   gfx::Rect visible_rect(coded_size.width(), coded_size.height());
-  return VideoDecoderConfig(codec, profile, PIXEL_FORMAT_I420,
-                            VideoColorSpace::JPEG(), kNoTransformation,
-                            coded_size, visible_rect, natural_size,
-                            EmptyExtraData(), Unencrypted());
+  return VideoDecoderConfig(
+      codec, profile, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace::JPEG(), kNoTransformation, coded_size, visible_rect,
+      natural_size, EmptyExtraData(), Unencrypted());
 }
 
 PipelineStatistics MakeStats(int frames_decoded,
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index e514cb2..e8c3104 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1266,6 +1266,10 @@
   return GetPipelineStatistics().video_bytes_decoded;
 }
 
+bool WebMediaPlayerImpl::HasAvailableVideoFrame() const {
+  return has_first_frame_;
+}
+
 bool WebMediaPlayerImpl::CopyVideoTextureToPlatformTexture(
     gpu::gles2::GLES2Interface* gl,
     unsigned int target,
@@ -3474,6 +3478,10 @@
   const base::TimeDelta elapsed = frame_time - load_start_time_;
   media_metrics_provider_->SetTimeToFirstFrame(elapsed);
   RecordTimingUMA("Media.TimeToFirstFrame", elapsed);
+
+  // Needed to signal HTMLVideoElement that it should remove the poster image.
+  if (client_ && did_lazy_load_ && has_poster_)
+    client_->Repaint();
 }
 
 void WebMediaPlayerImpl::RecordTimingUMA(const std::string& key,
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index f747f7d4..feabb157 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -182,6 +182,8 @@
   uint64_t AudioDecodedByteCount() const override;
   uint64_t VideoDecodedByteCount() const override;
 
+  bool HasAvailableVideoFrame() const override;
+
   bool CopyVideoTextureToPlatformTexture(
       gpu::gles2::GLES2Interface* gl,
       unsigned int target,
diff --git a/media/capture/mojom/video_capture_types.mojom b/media/capture/mojom/video_capture_types.mojom
index ee201ab12..1dafadb 100644
--- a/media/capture/mojom/video_capture_types.mojom
+++ b/media/capture/mojom/video_capture_types.mojom
@@ -25,7 +25,6 @@
   ARGB,
   XRGB,
   RGB24,
-  RGB32,
   MJPEG,
   MT21,
   YUV420P9,
diff --git a/media/capture/mojom/video_capture_types_mojom_traits.cc b/media/capture/mojom/video_capture_types_mojom_traits.cc
index ff08009..cc92e8c 100644
--- a/media/capture/mojom/video_capture_types_mojom_traits.cc
+++ b/media/capture/mojom/video_capture_types_mojom_traits.cc
@@ -113,8 +113,6 @@
       return media::mojom::VideoCapturePixelFormat::XRGB;
     case media::VideoPixelFormat::PIXEL_FORMAT_RGB24:
       return media::mojom::VideoCapturePixelFormat::RGB24;
-    case media::VideoPixelFormat::PIXEL_FORMAT_RGB32:
-      return media::mojom::VideoCapturePixelFormat::RGB32;
     case media::VideoPixelFormat::PIXEL_FORMAT_MJPEG:
       return media::mojom::VideoCapturePixelFormat::MJPEG;
     case media::VideoPixelFormat::PIXEL_FORMAT_MT21:
@@ -195,9 +193,6 @@
     case media::mojom::VideoCapturePixelFormat::RGB24:
       *output = media::PIXEL_FORMAT_RGB24;
       return true;
-    case media::mojom::VideoCapturePixelFormat::RGB32:
-      *output = media::PIXEL_FORMAT_RGB32;
-      return true;
     case media::mojom::VideoCapturePixelFormat::MJPEG:
       *output = media::PIXEL_FORMAT_MJPEG;
       return true;
diff --git a/media/capture/video/chromeos/cros_image_capture_impl.cc b/media/capture/video/chromeos/cros_image_capture_impl.cc
index 8dba5ffc..1d14432 100644
--- a/media/capture/video/chromeos/cros_image_capture_impl.cc
+++ b/media/capture/video/chromeos/cros_image_capture_impl.cc
@@ -18,11 +18,6 @@
 
 CrosImageCaptureImpl::~CrosImageCaptureImpl() = default;
 
-void CrosImageCaptureImpl::BindRequest(
-    cros::mojom::CrosImageCaptureRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
 void CrosImageCaptureImpl::GetCameraInfo(const std::string& device_id,
                                          GetCameraInfoCallback callback) {
   reprocess_manager_->GetCameraInfo(
diff --git a/media/capture/video/chromeos/cros_image_capture_impl.h b/media/capture/video/chromeos/cros_image_capture_impl.h
index a7a81b1..9a5c78f1 100644
--- a/media/capture/video/chromeos/cros_image_capture_impl.h
+++ b/media/capture/video/chromeos/cros_image_capture_impl.h
@@ -35,8 +35,6 @@
 
   ReprocessManager* reprocess_manager_;  // weak
 
-  mojo::BindingSet<cros::mojom::CrosImageCapture> bindings_;
-
   DISALLOW_COPY_AND_ASSIGN(CrosImageCaptureImpl);
 };
 
diff --git a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
index 03c20618..6a4fc14 100644
--- a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
+++ b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
@@ -11,6 +11,7 @@
 #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
 #include "media/capture/video/chromeos/cros_image_capture_impl.h"
 #include "media/capture/video/chromeos/reprocess_manager.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace media {
 
@@ -31,8 +32,6 @@
                           base::Unretained(this));
   reprocess_manager_ =
       std::make_unique<ReprocessManager>(std::move(get_camera_info));
-  cros_image_capture_ =
-      std::make_unique<CrosImageCaptureImpl>(reprocess_manager_.get());
 }
 
 VideoCaptureDeviceFactoryChromeOS::~VideoCaptureDeviceFactoryChromeOS() {
@@ -108,7 +107,9 @@
 
 void VideoCaptureDeviceFactoryChromeOS::BindCrosImageCaptureRequest(
     cros::mojom::CrosImageCaptureRequest request) {
-  cros_image_capture_->BindRequest(std::move(request));
+  mojo::MakeStrongBinding(
+      std::make_unique<CrosImageCaptureImpl>(reprocess_manager_.get()),
+      std::move(request));
 }
 
 }  // namespace media
diff --git a/media/capture/video/chromeos/video_capture_device_factory_chromeos.h b/media/capture/video/chromeos/video_capture_device_factory_chromeos.h
index 9f72384..cee840f 100644
--- a/media/capture/video/chromeos/video_capture_device_factory_chromeos.h
+++ b/media/capture/video/chromeos/video_capture_device_factory_chromeos.h
@@ -19,7 +19,6 @@
 using MojoMjpegDecodeAcceleratorFactoryCB = base::RepeatingCallback<void(
     chromeos_camera::mojom::MjpegDecodeAcceleratorRequest)>;
 
-class CrosImageCaptureImpl;
 class ReprocessManager;
 
 class CAPTURE_EXPORT VideoCaptureDeviceFactoryChromeOS final
@@ -70,8 +69,6 @@
 
   std::unique_ptr<ReprocessManager> reprocess_manager_;
 
-  std::unique_ptr<CrosImageCaptureImpl> cros_image_capture_;
-
   bool initialized_;
 
   base::WeakPtrFactory<VideoCaptureDeviceFactoryChromeOS> weak_ptr_factory_;
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc
index efcc40a..79caed3 100644
--- a/media/capture/video/fake_video_capture_device.cc
+++ b/media/capture/video/fake_video_capture_device.cc
@@ -136,7 +136,6 @@
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
     case PIXEL_FORMAT_RGB24:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_MJPEG:
     case PIXEL_FORMAT_ABGR:
     case PIXEL_FORMAT_XBGR:
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 96b06d2c..65d7015f 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -78,7 +78,6 @@
     case media::PIXEL_FORMAT_ARGB:
     case media::PIXEL_FORMAT_XRGB:
     case media::PIXEL_FORMAT_RGB24:
-    case media::PIXEL_FORMAT_RGB32:
     case media::PIXEL_FORMAT_ABGR:
     case media::PIXEL_FORMAT_XBGR:
       // Check if we can merge data 's primary and transfer function into the
@@ -314,8 +313,6 @@
       flip = true;
 #endif
       break;
-    // TODO(crbug.com/953128): Deprecate PIXEL_FORMAT_RGB32
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_ARGB:
       // Windows platforms e.g. send the data vertically flipped sometimes.
       flip = flip_y;
diff --git a/media/capture/video/video_capture_device_client_unittest.cc b/media/capture/video/video_capture_device_client_unittest.cc
index 31e84a6b..ab65734a 100644
--- a/media/capture/video/video_capture_device_client_unittest.cc
+++ b/media/capture/video/video_capture_device_client_unittest.cc
@@ -228,7 +228,6 @@
 #if defined(OS_WIN) || defined(OS_LINUX)
     PIXEL_FORMAT_RGB24,
 #endif
-    PIXEL_FORMAT_RGB32,
     PIXEL_FORMAT_ARGB,
     PIXEL_FORMAT_Y16,
   };
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index 1570d4c..0fbac5c 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -90,7 +90,9 @@
     "0c45:64d0", "0c45:64d2",
     // Lenovo Thinkpad Model 20CG0006FMZ front and rear cameras, see
     // also https://crbug.com/924528
-    "04ca:7047", "04ca:7048"};
+    "04ca:7047", "04ca:7048",
+    // HP Elitebook 840 G1
+    "04f2:b3ed"};
 
 const std::pair<VideoCaptureApi, std::vector<std::pair<GUID, GUID>>>
     kMfAttributes[] = {{VideoCaptureApi::WIN_MEDIA_FOUNDATION,
diff --git a/media/capture/video/win/video_capture_device_win.cc b/media/capture/video/win/video_capture_device_win.cc
index f03d8c2..37739b0 100644
--- a/media/capture/video/win/video_capture_device_win.cc
+++ b/media/capture/video/win/video_capture_device_win.cc
@@ -338,7 +338,7 @@
       {kMediaSubTypeI420, PIXEL_FORMAT_I420},
       {MEDIASUBTYPE_IYUV, PIXEL_FORMAT_I420},
       {MEDIASUBTYPE_RGB24, PIXEL_FORMAT_RGB24},
-      {MEDIASUBTYPE_RGB32, PIXEL_FORMAT_RGB32},
+      {MEDIASUBTYPE_RGB32, PIXEL_FORMAT_ARGB},
       {MEDIASUBTYPE_YUY2, PIXEL_FORMAT_YUY2},
       {MEDIASUBTYPE_MJPG, PIXEL_FORMAT_MJPEG},
       {MEDIASUBTYPE_UYVY, PIXEL_FORMAT_UYVY},
diff --git a/media/capture/video_capture_types.cc b/media/capture/video_capture_types.cc
index 07a7b78c..585dafba 100644
--- a/media/capture/video_capture_types.cc
+++ b/media/capture/video_capture_types.cc
@@ -14,10 +14,9 @@
 
 // This list is ordered by precedence of use.
 static VideoPixelFormat const kSupportedCapturePixelFormats[] = {
-    PIXEL_FORMAT_I420,  PIXEL_FORMAT_YV12,  PIXEL_FORMAT_NV12,
-    PIXEL_FORMAT_NV21,  PIXEL_FORMAT_UYVY,  PIXEL_FORMAT_YUY2,
-    PIXEL_FORMAT_RGB24, PIXEL_FORMAT_RGB32, PIXEL_FORMAT_ARGB,
-    PIXEL_FORMAT_MJPEG,
+    PIXEL_FORMAT_I420,  PIXEL_FORMAT_YV12, PIXEL_FORMAT_NV12,
+    PIXEL_FORMAT_NV21,  PIXEL_FORMAT_UYVY, PIXEL_FORMAT_YUY2,
+    PIXEL_FORMAT_RGB24, PIXEL_FORMAT_ARGB, PIXEL_FORMAT_MJPEG,
 };
 
 VideoCaptureFormat::VideoCaptureFormat()
diff --git a/media/cast/sender/h264_vt_encoder_unittest.cc b/media/cast/sender/h264_vt_encoder_unittest.cc
index 7be9432..db064be 100644
--- a/media/cast/sender/h264_vt_encoder_unittest.cc
+++ b/media/cast/sender/h264_vt_encoder_unittest.cc
@@ -299,8 +299,11 @@
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
 // Failed on mac-rel trybot. http://crbug.com/627260
 TEST_F(H264VideoToolboxEncoderTest, DISABLED_CheckFramesAreDecodable) {
+  const auto alpha_mode = IsOpaque(frame_->format())
+                              ? VideoDecoderConfig::AlphaMode::kIsOpaque
+                              : VideoDecoderConfig::AlphaMode::kHasAlpha;
   VideoDecoderConfig config(
-      kCodecH264, H264PROFILE_MAIN, frame_->format(), VideoColorSpace(),
+      kCodecH264, H264PROFILE_MAIN, alpha_mode, VideoColorSpace(),
       kNoTransformation, frame_->coded_size(), frame_->visible_rect(),
       frame_->natural_size(), EmptyExtraData(), Unencrypted());
   scoped_refptr<EndToEndFrameChecker> checker(new EndToEndFrameChecker(config));
diff --git a/media/cdm/cdm_adapter.cc b/media/cdm/cdm_adapter.cc
index 5ae679b..fcab10d 100644
--- a/media/cdm/cdm_adapter.cc
+++ b/media/cdm/cdm_adapter.cc
@@ -530,6 +530,14 @@
   DCHECK(!video_init_cb_);
   TRACE_EVENT0("media", "CdmAdapter::InitializeVideoDecoder");
 
+  // Alpha decoding is not supported by the CDM.
+  if (config.alpha_mode() != VideoDecoderConfig::AlphaMode::kIsOpaque) {
+    DVLOG(1) << __func__
+             << ": Unsupported config: " << config.AsHumanReadableString();
+    init_cb.Run(false);
+    return;
+  }
+
   // cdm::kUnknownVideoCodecProfile and cdm::kUnknownVideoFormat are not checked
   // because it's possible the container has wrong information or the demuxer
   // doesn't parse them correctly.
diff --git a/media/cdm/cdm_type_conversion.cc b/media/cdm/cdm_type_conversion.cc
index 67b9780..159d63d2 100644
--- a/media/cdm/cdm_type_conversion.cc
+++ b/media/cdm/cdm_type_conversion.cc
@@ -569,7 +569,11 @@
   cdm::VideoDecoderConfig_3 cdm_config = {};
   cdm_config.codec = ToCdmVideoCodec(config.codec());
   cdm_config.profile = ToCdmVideoCodecProfile(config.profile());
-  cdm_config.format = ToCdmVideoFormat(config.format());
+
+  // TODO(dalecurtis): CDM doesn't support alpha, so delete |format|.
+  DCHECK_EQ(config.alpha_mode(), VideoDecoderConfig::AlphaMode::kIsOpaque);
+  cdm_config.format = cdm::kI420;
+
   cdm_config.color_space = ToCdmColorSpace(config.color_space_info());
   cdm_config.coded_size.width = config.coded_size().width();
   cdm_config.coded_size.height = config.coded_size().height();
diff --git a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
index 9504b1f..ff7ddf4 100644
--- a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
@@ -54,8 +54,9 @@
 
   VideoDecoderConfig media_config(
       ToMediaVideoCodec(config.codec), ToMediaVideoCodecProfile(config.profile),
-      ToMediaVideoFormat(config.format), ToMediaColorSpace(config.color_space),
-      kNoTransformation, coded_size, gfx::Rect(coded_size), coded_size,
+      VideoDecoderConfig::AlphaMode::kIsOpaque,
+      ToMediaColorSpace(config.color_space), kNoTransformation, coded_size,
+      gfx::Rect(coded_size), coded_size,
       std::vector<uint8_t>(config.extra_data,
                            config.extra_data + config.extra_data_size),
       Unencrypted());
diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc
index 1d5d0851..afd81df 100644
--- a/media/ffmpeg/ffmpeg_common.cc
+++ b/media/ffmpeg/ffmpeg_common.cc
@@ -469,9 +469,6 @@
   gfx::Size natural_size =
       GetNaturalSize(visible_rect.size(), aspect_ratio.num, aspect_ratio.den);
 
-  VideoPixelFormat format =
-      AVPixelFormatToVideoPixelFormat(codec_context->pix_fmt);
-
   // Without the ffmpeg decoder configured, libavformat is unable to get the
   // profile, format, or coded size. So choose sensible defaults and let
   // decoders fail later if the configuration is actually unsupported.
@@ -496,15 +493,10 @@
       // All the heuristics failed, let's assign a default profile
       if (profile == VIDEO_CODEC_PROFILE_UNKNOWN)
         profile = H264PROFILE_BASELINE;
-
-      format = PIXEL_FORMAT_I420;
       break;
     }
 #endif
     case kCodecVP8:
-#if !BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
-      format = PIXEL_FORMAT_I420;
-#endif
       profile = VP8PROFILE_ANY;
       break;
     case kCodecVP9:
@@ -525,10 +517,8 @@
           profile = VP9PROFILE_MIN;
           break;
       }
-      format = PIXEL_FORMAT_I420;
       break;
     case kCodecAV1:
-      format = PIXEL_FORMAT_I420;
       profile = AV1PROFILE_PROFILE_MAIN;
       break;
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
@@ -543,18 +533,8 @@
       profile = ProfileIDToVideoCodecProfile(codec_context->profile);
   }
 
-  // Pad out |coded_size| for subsampled YUV formats.
-  if (format != PIXEL_FORMAT_I444 && format != PIXEL_FORMAT_UNKNOWN) {
-    coded_size.set_width((coded_size.width() + 1) / 2 * 2);
-    if (format != PIXEL_FORMAT_I422)
-      coded_size.set_height((coded_size.height() + 1) / 2 * 2);
-  }
-
-  AVDictionaryEntry* webm_alpha =
-      av_dict_get(stream->metadata, "alpha_mode", nullptr, 0);
-  if (webm_alpha && !strcmp(webm_alpha->value, "1")) {
-    format = PIXEL_FORMAT_I420A;
-  }
+  auto* alpha_mode = av_dict_get(stream->metadata, "alpha_mode", nullptr, 0);
+  const bool has_alpha = alpha_mode && !strcmp(alpha_mode->value, "1");
 
   VideoRotation video_rotation = VIDEO_ROTATION_0;
   int rotation = 0;
@@ -622,9 +602,11 @@
                       codec_context->extradata + codec_context->extradata_size);
   }
   // TODO(tmathmeyer) ffmpeg can't provide us with an actual video rotation yet.
-  config->Initialize(codec, profile, format, color_space,
-                     VideoTransformation(video_rotation), coded_size,
-                     visible_rect, natural_size, extra_data,
+  config->Initialize(codec, profile,
+                     has_alpha ? VideoDecoderConfig::AlphaMode::kHasAlpha
+                               : VideoDecoderConfig::AlphaMode::kIsOpaque,
+                     color_space, VideoTransformation(video_rotation),
+                     coded_size, visible_rect, natural_size, extra_data,
                      GetEncryptionScheme(stream));
 
   if (stream->nb_side_data) {
@@ -670,7 +652,6 @@
   codec_context->profile = VideoCodecProfileToProfileID(config.profile());
   codec_context->coded_width = config.coded_size().width();
   codec_context->coded_height = config.coded_size().height();
-  codec_context->pix_fmt = VideoPixelFormatToAVPixelFormat(config.format());
   if (config.color_space_info().range == gfx::ColorSpace::RangeID::FULL)
     codec_context->color_range = AVCOL_RANGE_JPEG;
 
@@ -806,43 +787,6 @@
   return PIXEL_FORMAT_UNKNOWN;
 }
 
-AVPixelFormat VideoPixelFormatToAVPixelFormat(VideoPixelFormat video_format) {
-  switch (video_format) {
-    case PIXEL_FORMAT_I420:
-      return AV_PIX_FMT_YUV420P;
-    case PIXEL_FORMAT_I422:
-      return AV_PIX_FMT_YUV422P;
-    case PIXEL_FORMAT_I420A:
-      return AV_PIX_FMT_YUVA420P;
-    case PIXEL_FORMAT_I444:
-      return AV_PIX_FMT_YUV444P;
-    case PIXEL_FORMAT_YUV420P9:
-      return AV_PIX_FMT_YUV420P9LE;
-    case PIXEL_FORMAT_YUV420P10:
-      return AV_PIX_FMT_YUV420P10LE;
-    case PIXEL_FORMAT_YUV420P12:
-      return AV_PIX_FMT_YUV420P12LE;
-    case PIXEL_FORMAT_YUV422P9:
-      return AV_PIX_FMT_YUV422P9LE;
-    case PIXEL_FORMAT_YUV422P10:
-      return AV_PIX_FMT_YUV422P10LE;
-    case PIXEL_FORMAT_YUV422P12:
-      return AV_PIX_FMT_YUV422P12LE;
-    case PIXEL_FORMAT_YUV444P9:
-      return AV_PIX_FMT_YUV444P9LE;
-    case PIXEL_FORMAT_YUV444P10:
-      return AV_PIX_FMT_YUV444P10LE;
-    case PIXEL_FORMAT_YUV444P12:
-      return AV_PIX_FMT_YUV444P12LE;
-    case PIXEL_FORMAT_P016LE:
-      return AV_PIX_FMT_P016LE;
-
-    default:
-      DVLOG(1) << "Unsupported Format: " << video_format;
-  }
-  return AV_PIX_FMT_NONE;
-}
-
 VideoColorSpace AVColorSpaceToColorSpace(AVColorSpace color_space,
                                          AVColorRange color_range) {
   // TODO(hubbe): make this better
diff --git a/media/ffmpeg/ffmpeg_common.h b/media/ffmpeg/ffmpeg_common.h
index 8f7d8f4..3c6c62b 100644
--- a/media/ffmpeg/ffmpeg_common.h
+++ b/media/ffmpeg/ffmpeg_common.h
@@ -135,9 +135,6 @@
 MEDIA_EXPORT VideoPixelFormat
 AVPixelFormatToVideoPixelFormat(AVPixelFormat pixel_format);
 
-// Converts video formats to its corresponding FFmpeg's pixel formats.
-AVPixelFormat VideoPixelFormatToAVPixelFormat(VideoPixelFormat video_format);
-
 VideoColorSpace AVColorSpaceToColorSpace(AVColorSpace color_space,
                                          AVColorRange color_range);
 
diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc
index 7dafb02b..ad1468ea 100644
--- a/media/filters/decrypting_demuxer_stream_unittest.cc
+++ b/media/filters/decrypting_demuxer_stream_unittest.cc
@@ -306,7 +306,7 @@
   EXPECT_EQ(DemuxerStream::VIDEO, demuxer_stream_->type());
   EXPECT_FALSE(output_config.is_encrypted());
   EXPECT_EQ(input_config.codec(), output_config.codec());
-  EXPECT_EQ(input_config.format(), output_config.format());
+  EXPECT_EQ(input_config.alpha_mode(), output_config.alpha_mode());
   EXPECT_EQ(input_config.profile(), output_config.profile());
   EXPECT_EQ(input_config.coded_size(), output_config.coded_size());
   EXPECT_EQ(input_config.visible_rect(), output_config.visible_rect());
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index fa9e64d3..19b29d8 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -178,8 +178,6 @@
                              video_config.visible_rect().width());
   UmaHistogramAspectRatio("Media.VideoVisibleAspectRatio",
                           video_config.visible_rect());
-  UMA_HISTOGRAM_ENUMERATION("Media.VideoPixelFormatUnion",
-                            video_config.format(), PIXEL_FORMAT_MAX + 1);
 
   // TODO(hubbe): make better color space statistics
 
@@ -1626,8 +1624,6 @@
           base::StringPrintf("%d/%d", video_av_stream->time_base.num,
                              video_av_stream->time_base.den));
 
-      params.SetString("video_format" + suffix,
-                       VideoPixelFormatToString(video_config.format()));
       params.SetBoolean("video_is_encrypted" + suffix,
                         video_config.is_encrypted());
     }
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 198e4d9..b0033da 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -379,7 +379,8 @@
 
   const VideoDecoderConfig& video_config = stream->video_decoder_config();
   EXPECT_EQ(kCodecVP8, video_config.codec());
-  EXPECT_EQ(PIXEL_FORMAT_I420, video_config.format());
+  EXPECT_EQ(VideoDecoderConfig::AlphaMode::kIsOpaque,
+            video_config.alpha_mode());
   EXPECT_EQ(320, video_config.coded_size().width());
   EXPECT_EQ(240, video_config.coded_size().height());
   EXPECT_EQ(0, video_config.visible_rect().x());
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index 523d381..f806b697 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -44,7 +44,6 @@
 
 namespace media {
 
-static const VideoPixelFormat kVideoFormat = PIXEL_FORMAT_I420;
 static const gfx::Size kCodedSize(320, 240);
 static const gfx::Rect kVisibleRect(320, 240);
 static const gfx::Size kNaturalSize(320, 240);
@@ -223,9 +222,10 @@
 TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
   // Specify Theora w/o extra data so that avcodec_open2() fails.
   VideoDecoderConfig config(kCodecTheora, VIDEO_CODEC_PROFILE_UNKNOWN,
-                            kVideoFormat, VideoColorSpace(), kNoTransformation,
-                            kCodedSize, kVisibleRect, kNaturalSize,
-                            EmptyExtraData(), Unencrypted());
+                            VideoDecoderConfig::AlphaMode::kIsOpaque,
+                            VideoColorSpace(), kNoTransformation, kCodedSize,
+                            kVisibleRect, kNaturalSize, EmptyExtraData(),
+                            Unencrypted());
   InitializeWithConfigWithResult(config, false);
 }
 
diff --git a/media/filters/source_buffer_state_unittest.cc b/media/filters/source_buffer_state_unittest.cc
index e45afe2b..c0b0dba 100644
--- a/media/filters/source_buffer_state_unittest.cc
+++ b/media/filters/source_buffer_state_unittest.cc
@@ -36,9 +36,10 @@
   gfx::Size size(w, h);
   gfx::Rect visible_rect(size);
   return VideoDecoderConfig(codec, VIDEO_CODEC_PROFILE_UNKNOWN,
-                            PIXEL_FORMAT_I420, VideoColorSpace::REC709(),
-                            kNoTransformation, size, visible_rect, size,
-                            EmptyExtraData(), Unencrypted());
+                            VideoDecoderConfig::AlphaMode::kIsOpaque,
+                            VideoColorSpace::REC709(), kNoTransformation, size,
+                            visible_rect, size, EmptyExtraData(),
+                            Unencrypted());
 }
 
 void AddAudioTrack(std::unique_ptr<MediaTracks>& t, AudioCodec codec, int id) {
diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc
index b27a9f7..7f88a6a 100644
--- a/media/filters/vpx_video_decoder.cc
+++ b/media/filters/vpx_video_decoder.cc
@@ -202,19 +202,13 @@
   if (config.codec() != kCodecVP8 && config.codec() != kCodecVP9)
     return false;
 
-  // These are the combinations of codec-pixel format supported in principle.
-  DCHECK(
-      (config.codec() == kCodecVP8 && config.format() == PIXEL_FORMAT_I420) ||
-      (config.codec() == kCodecVP8 && config.format() == PIXEL_FORMAT_I420A) ||
-      (config.codec() == kCodecVP9 && config.format() == PIXEL_FORMAT_I420) ||
-      (config.codec() == kCodecVP9 && config.format() == PIXEL_FORMAT_I420A) ||
-      (config.codec() == kCodecVP9 && config.format() == PIXEL_FORMAT_I444));
-
 #if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
   // When FFmpegVideoDecoder is available it handles VP8 that doesn't have
   // alpha, and VpxVideoDecoder will handle VP8 with alpha.
-  if (config.codec() == kCodecVP8 && config.format() != PIXEL_FORMAT_I420A)
+  if (config.codec() == kCodecVP8 &&
+      config.alpha_mode() == VideoDecoderConfig::AlphaMode::kIsOpaque) {
     return false;
+  }
 #endif
 
   DCHECK(!vpx_codec_);
@@ -241,7 +235,7 @@
     }
   }
 
-  if (config.format() != PIXEL_FORMAT_I420A)
+  if (config.alpha_mode() == VideoDecoderConfig::AlphaMode::kIsOpaque)
     return true;
 
   DCHECK(!vpx_codec_alpha_);
@@ -306,7 +300,7 @@
   }
 
   const vpx_image_t* vpx_image_alpha = nullptr;
-  AlphaDecodeStatus alpha_decode_status =
+  const auto alpha_decode_status =
       DecodeAlphaPlane(vpx_image, &vpx_image_alpha, buffer);
   if (alpha_decode_status == kAlphaPlaneError) {
     return false;
@@ -314,9 +308,10 @@
     *video_frame = nullptr;
     return true;
   }
-  if (!CopyVpxImageToVideoFrame(vpx_image, vpx_image_alpha, video_frame)) {
+
+  if (!CopyVpxImageToVideoFrame(vpx_image, vpx_image_alpha, video_frame))
     return false;
-  }
+
   if (vpx_image_alpha && config_.codec() == kCodecVP8) {
     libyuv::CopyPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
                       vpx_image_alpha->stride[VPX_PLANE_Y],
@@ -328,71 +323,61 @@
 
   (*video_frame)->set_timestamp(buffer->timestamp());
 
-  // Default to the color space from the config, but if the bistream specifies
-  // one, prefer that instead.
+  // Prefer the color space from the config if available. It generally comes
+  // from the color tag which is more expressive than the vp8 and vp9 bitstream.
   if (config_.color_space_info().IsSpecified()) {
     (*video_frame)
         ->set_color_space(config_.color_space_info().ToGfxColorSpace());
+    return true;
   }
 
-  if (config_.color_space_info().IsSpecified()) {
-    // config_.color_space_info() comes from the color tag which is
-    // more expressive than the bitstream, so prefer it over the
-    // bitstream data below.
-    (*video_frame)
-        ->set_color_space(config_.color_space_info().ToGfxColorSpace());
-  } else {
-    gfx::ColorSpace::PrimaryID primaries = gfx::ColorSpace::PrimaryID::INVALID;
-    gfx::ColorSpace::TransferID transfer = gfx::ColorSpace::TransferID::INVALID;
-    gfx::ColorSpace::MatrixID matrix = gfx::ColorSpace::MatrixID::INVALID;
-    gfx::ColorSpace::RangeID range = vpx_image->range == VPX_CR_FULL_RANGE
-                                         ? gfx::ColorSpace::RangeID::FULL
-                                         : gfx::ColorSpace::RangeID::LIMITED;
+  auto primaries = gfx::ColorSpace::PrimaryID::INVALID;
+  auto transfer = gfx::ColorSpace::TransferID::INVALID;
+  auto matrix = gfx::ColorSpace::MatrixID::INVALID;
+  auto range = vpx_image->range == VPX_CR_FULL_RANGE
+                   ? gfx::ColorSpace::RangeID::FULL
+                   : gfx::ColorSpace::RangeID::LIMITED;
 
-    switch (vpx_image->cs) {
-      case VPX_CS_BT_601:
-      case VPX_CS_SMPTE_170:
-        primaries = gfx::ColorSpace::PrimaryID::SMPTE170M;
-        transfer = gfx::ColorSpace::TransferID::SMPTE170M;
-        matrix = gfx::ColorSpace::MatrixID::SMPTE170M;
-        break;
-      case VPX_CS_SMPTE_240:
-        primaries = gfx::ColorSpace::PrimaryID::SMPTE240M;
-        transfer = gfx::ColorSpace::TransferID::SMPTE240M;
-        matrix = gfx::ColorSpace::MatrixID::SMPTE240M;
-        break;
-      case VPX_CS_BT_709:
-        primaries = gfx::ColorSpace::PrimaryID::BT709;
+  switch (vpx_image->cs) {
+    case VPX_CS_BT_601:
+    case VPX_CS_SMPTE_170:
+      primaries = gfx::ColorSpace::PrimaryID::SMPTE170M;
+      transfer = gfx::ColorSpace::TransferID::SMPTE170M;
+      matrix = gfx::ColorSpace::MatrixID::SMPTE170M;
+      break;
+    case VPX_CS_SMPTE_240:
+      primaries = gfx::ColorSpace::PrimaryID::SMPTE240M;
+      transfer = gfx::ColorSpace::TransferID::SMPTE240M;
+      matrix = gfx::ColorSpace::MatrixID::SMPTE240M;
+      break;
+    case VPX_CS_BT_709:
+      primaries = gfx::ColorSpace::PrimaryID::BT709;
+      transfer = gfx::ColorSpace::TransferID::BT709;
+      matrix = gfx::ColorSpace::MatrixID::BT709;
+      break;
+    case VPX_CS_BT_2020:
+      primaries = gfx::ColorSpace::PrimaryID::BT2020;
+      if (vpx_image->bit_depth >= 12)
+        transfer = gfx::ColorSpace::TransferID::BT2020_12;
+      else if (vpx_image->bit_depth >= 10)
+        transfer = gfx::ColorSpace::TransferID::BT2020_10;
+      else
         transfer = gfx::ColorSpace::TransferID::BT709;
-        matrix = gfx::ColorSpace::MatrixID::BT709;
-        break;
-      case VPX_CS_BT_2020:
-        primaries = gfx::ColorSpace::PrimaryID::BT2020;
-        if (vpx_image->bit_depth >= 12) {
-          transfer = gfx::ColorSpace::TransferID::BT2020_12;
-        } else if (vpx_image->bit_depth >= 10) {
-          transfer = gfx::ColorSpace::TransferID::BT2020_10;
-        } else {
-          transfer = gfx::ColorSpace::TransferID::BT709;
-        }
-        matrix = gfx::ColorSpace::MatrixID::BT2020_NCL;  // is this right?
-        break;
-      case VPX_CS_SRGB:
-        primaries = gfx::ColorSpace::PrimaryID::BT709;
-        transfer = gfx::ColorSpace::TransferID::IEC61966_2_1;
-        matrix = gfx::ColorSpace::MatrixID::BT709;
-        break;
+      matrix = gfx::ColorSpace::MatrixID::BT2020_NCL;  // is this right?
+      break;
+    case VPX_CS_SRGB:
+      primaries = gfx::ColorSpace::PrimaryID::BT709;
+      transfer = gfx::ColorSpace::TransferID::IEC61966_2_1;
+      matrix = gfx::ColorSpace::MatrixID::BT709;
+      break;
+    default:
+      break;
+  }
 
-      default:
-        break;
-    }
-
-    // TODO(ccameron): Set a color space even for unspecified values.
-    if (primaries != gfx::ColorSpace::PrimaryID::INVALID) {
-      (*video_frame)
-          ->set_color_space(
-              gfx::ColorSpace(primaries, transfer, matrix, range));
-    }
+  // TODO(ccameron): Set a color space even for unspecified values.
+  if (primaries != gfx::ColorSpace::PrimaryID::INVALID) {
+    (*video_frame)
+        ->set_color_space(gfx::ColorSpace(primaries, transfer, matrix, range));
   }
 
   return true;
diff --git a/media/filters/vpx_video_decoder_fuzzertest.cc b/media/filters/vpx_video_decoder_fuzzertest.cc
index d784d58..8d91bdf 100644
--- a/media/filters/vpx_video_decoder_fuzzertest.cc
+++ b/media/filters/vpx_video_decoder_fuzzertest.cc
@@ -63,26 +63,15 @@
   // Compute randomized constants. Put all rng() usages here.
   // Use only values that pass DCHECK in VpxVideoDecoder::ConfigureDecoder().
   VideoCodec codec;
-  VideoPixelFormat pixel_format;
+
+  bool has_alpha = false;
   if (rng() & 1) {
     codec = kCodecVP8;
-    // PIXEL_FORMAT_I420 disabled for kCodecVP8 on Linux.
-    pixel_format = PIXEL_FORMAT_I420A;
+    // non-Alpha VP8 decoding isn't supported by VpxVideoDecoder on Linux.
+    has_alpha = true;
   } else {
     codec = kCodecVP9;
-    switch (rng() % 3) {
-      case 0:
-        pixel_format = PIXEL_FORMAT_I420;
-        break;
-      case 1:
-        pixel_format = PIXEL_FORMAT_I420A;
-        break;
-      case 2:
-        pixel_format = PIXEL_FORMAT_I444;
-        break;
-      default:
-        return 0;
-    }
+    has_alpha = rng() & 1;
   }
 
   auto profile =
@@ -97,10 +86,12 @@
   auto natural_size = gfx::Size(1 + (rng() % 127), 1 + (rng() % 127));
   uint8_t reflection = rng() % 4;
 
-  VideoDecoderConfig config(codec, profile, pixel_format, color_space,
-                            VideoTransformation(rotation, reflection),
-                            coded_size, visible_rect, natural_size,
-                            EmptyExtraData(), Unencrypted());
+  VideoDecoderConfig config(
+      codec, profile,
+      has_alpha ? VideoDecoderConfig::AlphaMode::kHasAlpha
+                : VideoDecoderConfig::AlphaMode::kIsOpaque,
+      color_space, VideoTransformation(rotation, reflection), coded_size,
+      visible_rect, natural_size, EmptyExtraData(), Unencrypted());
 
   if (!config.IsValidConfig())
     return 0;
diff --git a/media/formats/mp2t/es_adapter_video_unittest.cc b/media/formats/mp2t/es_adapter_video_unittest.cc
index 6ae3659..228a969 100644
--- a/media/formats/mp2t/es_adapter_video_unittest.cc
+++ b/media/formats/mp2t/es_adapter_video_unittest.cc
@@ -32,10 +32,10 @@
   gfx::Size coded_size(320, 240);
   gfx::Rect visible_rect(0, 0, 320, 240);
   gfx::Size natural_size(320, 240);
-  return VideoDecoderConfig(kCodecH264, H264PROFILE_MAIN, PIXEL_FORMAT_I420,
-                            VideoColorSpace(), kNoTransformation, coded_size,
-                            visible_rect, natural_size, EmptyExtraData(),
-                            Unencrypted());
+  return VideoDecoderConfig(
+      kCodecH264, H264PROFILE_MAIN, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, coded_size, visible_rect,
+      natural_size, EmptyExtraData(), Unencrypted());
 }
 
 BufferQueue GenerateFakeBuffers(const int* frame_pts_ms,
@@ -58,7 +58,7 @@
   return buffers;
 }
 
-}
+}  // namespace
 
 class EsAdapterVideoTest : public testing::Test {
  public:
diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc
index 29e577b7..f48337d 100644
--- a/media/formats/mp2t/es_parser_h264.cc
+++ b/media/formats/mp2t/es_parser_h264.cc
@@ -526,9 +526,9 @@
   }
 
   VideoDecoderConfig video_decoder_config(
-      kCodecH264, profile, PIXEL_FORMAT_I420, VideoColorSpace::REC709(),
-      kNoTransformation, coded_size.value(), visible_rect.value(), natural_size,
-      EmptyExtraData(), scheme);
+      kCodecH264, profile, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace::REC709(), kNoTransformation, coded_size.value(),
+      visible_rect.value(), natural_size, EmptyExtraData(), scheme);
 
   if (!video_decoder_config.IsValidConfig()) {
     DVLOG(1) << "Invalid video config: "
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index 71f47b6..a16162f 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -523,7 +523,8 @@
           return false;
       }
       video_config.Initialize(entry.video_codec, entry.video_codec_profile,
-                              PIXEL_FORMAT_I420, VideoColorSpace::REC709(),
+                              VideoDecoderConfig::AlphaMode::kIsOpaque,
+                              VideoColorSpace::REC709(),
                               CalculateRotation(track->header, moov_->header),
                               coded_size, visible_rect, natural_size,
                               // No decoder-specific buffer needed for AVC;
diff --git a/media/formats/webm/webm_video_client.cc b/media/formats/webm/webm_video_client.cc
index 9a986ba..1500ff5 100644
--- a/media/formats/webm/webm_video_client.cc
+++ b/media/formats/webm/webm_video_client.cc
@@ -90,9 +90,6 @@
     return false;
   }
 
-  VideoPixelFormat format =
-      (alpha_mode_ == 1) ? PIXEL_FORMAT_I420A : PIXEL_FORMAT_I420;
-
   if (pixel_width_ <= 0 || pixel_height_ <= 0)
     return false;
 
@@ -134,9 +131,12 @@
   }
   gfx::Size natural_size = gfx::Size(display_width_, display_height_);
 
-  config->Initialize(video_codec, profile, format, color_space,
-                     kNoTransformation, coded_size, visible_rect, natural_size,
-                     codec_private, encryption_scheme);
+  config->Initialize(video_codec, profile,
+                     alpha_mode_ == 1
+                         ? VideoDecoderConfig::AlphaMode::kHasAlpha
+                         : VideoDecoderConfig::AlphaMode::kIsOpaque,
+                     color_space, kNoTransformation, coded_size, visible_rect,
+                     natural_size, codec_private, encryption_scheme);
   return config->IsValidConfig();
 }
 
diff --git a/media/formats/webm/webm_video_client_unittest.cc b/media/formats/webm/webm_video_client_unittest.cc
index b3ebf0b..4cc41190 100644
--- a/media/formats/webm/webm_video_client_unittest.cc
+++ b/media/formats/webm/webm_video_client_unittest.cc
@@ -138,9 +138,9 @@
                                                   EncryptionScheme(), &config));
 
   VideoDecoderConfig expected_config(
-      kCodecVP9, profile, PIXEL_FORMAT_I420, VideoColorSpace::REC709(),
-      kNoTransformation, kCodedSize, gfx::Rect(kCodedSize), kCodedSize,
-      codec_private, Unencrypted());
+      kCodecVP9, profile, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace::REC709(), kNoTransformation, kCodedSize,
+      gfx::Rect(kCodedSize), kCodedSize, codec_private, Unencrypted());
 
   EXPECT_TRUE(config.Matches(expected_config))
       << "Config (" << config.AsHumanReadableString()
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 61652aa8..2faee6a 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -241,6 +241,7 @@
     "codec_picture.h",
     "format_utils.cc",
     "format_utils.h",
+    "gpu_video_decode_accelerator_helpers.cc",
     "gpu_video_decode_accelerator_helpers.h",
     "gpu_video_encode_accelerator_helpers.cc",
     "gpu_video_encode_accelerator_helpers.h",
diff --git a/media/gpu/android/device_info.cc b/media/gpu/android/device_info.cc
index c5005f3..cdf0395 100644
--- a/media/gpu/android/device_info.cc
+++ b/media/gpu/android/device_info.cc
@@ -61,4 +61,9 @@
   return SdkVersion() >= base::android::SDK_VERSION_NOUGAT;
 }
 
+bool DeviceInfo::AddSupportedCodecProfileLevels(
+    std::vector<CodecProfileLevel>* result) {
+  return MediaCodecUtil::AddSupportedCodecProfileLevels(result);
+}
+
 }  // namespace media
diff --git a/media/gpu/android/device_info.h b/media/gpu/android/device_info.h
index 487b1aa..ff98e98 100644
--- a/media/gpu/android/device_info.h
+++ b/media/gpu/android/device_info.h
@@ -25,6 +25,8 @@
   virtual bool SupportsOverlaySurfaces();
   virtual bool CodecNeedsFlushWorkaround(MediaCodecBridge* codec);
   virtual bool IsAsyncApiSupported();
+  virtual bool AddSupportedCodecProfileLevels(
+      std::vector<CodecProfileLevel>* result);
 };
 
 }  // namespace media
diff --git a/media/gpu/android/image_reader_gl_owner.cc b/media/gpu/android/image_reader_gl_owner.cc
index 87bea5e..e2d764f 100644
--- a/media/gpu/android/image_reader_gl_owner.cc
+++ b/media/gpu/android/image_reader_gl_owner.cc
@@ -27,6 +27,23 @@
 
 namespace media {
 
+namespace {
+bool IsSurfaceControl(TextureOwner::Mode mode) {
+  switch (mode) {
+    case TextureOwner::Mode::kAImageReaderInsecureSurfaceControl:
+    case TextureOwner::Mode::kAImageReaderSecureSurfaceControl:
+      return true;
+    case TextureOwner::Mode::kAImageReaderInsecure:
+      return false;
+    case TextureOwner::Mode::kSurfaceTextureInsecure:
+      NOTREACHED();
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
+}  // namespace
+
 // FrameAvailableEvent_ImageReader is a RefCounted wrapper for a WaitableEvent
 // (it's not possible to put one in RefCountedData). This lets us safely signal
 // an event on any thread.
@@ -96,21 +113,31 @@
   // Set the width, height and format to some default value. This parameters
   // are/maybe overriden by the producer sending buffers to this imageReader's
   // Surface.
-  int32_t width = 1, height = 1, max_images = 4;
-  AIMAGE_FORMATS format = mode == Mode::kAImageReaderSecure
+  int32_t width = 1, height = 1;
+
+  // This should be as small as possible to limit the memory usage.
+  // ImageReader needs 2 images to mimic the behavior of SurfaceTexture. For
+  // SurfaceControl we need 3 images instead of 2 since 1 frame(and hence image
+  // associated with it) will be with system compositor and 2 frames will be in
+  // flight. Also note that we always acquire an image before deleting the
+  // previous acquired image. This causes 2 acquired images to be in flight at
+  // the image acquisition point until the previous image is deleted.
+  max_images_ = IsSurfaceControl(mode) ? 3 : 2;
+  AIMAGE_FORMATS format = mode == Mode::kAImageReaderSecureSurfaceControl
                               ? AIMAGE_FORMAT_PRIVATE
                               : AIMAGE_FORMAT_YUV_420_888;
   AImageReader* reader = nullptr;
+
   // The usage flag below should be used when the buffer will be read from by
   // the GPU as a texture.
-  uint64_t usage = mode == Mode::kAImageReaderSecure
+  uint64_t usage = mode == Mode::kAImageReaderSecureSurfaceControl
                        ? AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT
                        : AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
   usage |= gl::SurfaceControl::RequiredUsage();
 
   // Create a new reader for images of the desired size and format.
   media_status_t return_code = loader_.AImageReader_newWithUsage(
-      width, height, format, usage, max_images, &reader);
+      width, height, format, usage, max_images_, &reader);
   if (return_code != AMEDIA_OK) {
     LOG(ERROR) << " Image reader creation failed.";
     if (return_code == AMEDIA_ERROR_INVALID_PARAMETER)
@@ -212,8 +239,19 @@
   AImage* image = nullptr;
   int acquire_fence_fd = -1;
   media_status_t return_code = AMEDIA_OK;
-  return_code = loader_.AImageReader_acquireLatestImageAsync(
-      image_reader_, &image, &acquire_fence_fd);
+  DCHECK_GT(max_images_, static_cast<int32_t>(image_refs_.size()));
+  if (max_images_ - image_refs_.size() < 2) {
+    // acquireNextImageAsync is required here since as per the spec calling
+    // AImageReader_acquireLatestImage with less than two images of margin, that
+    // is (maxImages - currentAcquiredImages < 2) will not discard as expected.
+    // We always have currentAcquiredImages as 1 since we delete a previous
+    // image only after acquiring a new image.
+    return_code = loader_.AImageReader_acquireNextImageAsync(
+        image_reader_, &image, &acquire_fence_fd);
+  } else {
+    return_code = loader_.AImageReader_acquireLatestImageAsync(
+        image_reader_, &image, &acquire_fence_fd);
+  }
 
   // TODO(http://crbug.com/846050).
   // Need to add some better error handling if below error occurs. Currently we
diff --git a/media/gpu/android/image_reader_gl_owner.h b/media/gpu/android/image_reader_gl_owner.h
index 6ef30b3..8d9fd9c 100644
--- a/media/gpu/android/image_reader_gl_owner.h
+++ b/media/gpu/android/image_reader_gl_owner.h
@@ -46,6 +46,7 @@
   GetAHardwareBuffer() override;
 
   const AImageReader* image_reader_for_testing() const { return image_reader_; }
+  int32_t max_images_for_testing() const { return max_images_; }
 
  protected:
   void OnTextureDestroyed(gpu::gles2::AbstractTexture*) override;
@@ -125,6 +126,7 @@
   // IgnorePendingRelease() or WaitForFrameAvailable() have been called since.
   base::TimeTicks release_time_;
   scoped_refptr<FrameAvailableEvent_ImageReader> frame_available_event_;
+  int32_t max_images_ = 0;
 
   THREAD_CHECKER(thread_checker_);
   DISALLOW_COPY_AND_ASSIGN(ImageReaderGLOwner);
diff --git a/media/gpu/android/image_reader_gl_owner_unittest.cc b/media/gpu/android/image_reader_gl_owner_unittest.cc
index b5b5966..3c10602 100644
--- a/media/gpu/android/image_reader_gl_owner_unittest.cc
+++ b/media/gpu/android/image_reader_gl_owner_unittest.cc
@@ -143,14 +143,25 @@
   new_surface = nullptr;
 }
 
-class ImageReaderGLOwnerSecureTest : public ImageReaderGLOwnerTest {
+// The max number of images used by the ImageReader must be 2 for non-Surface
+// control.
+TEST_F(ImageReaderGLOwnerTest, MaxImageExpectation) {
+  if (!IsImageReaderSupported())
+    return;
+  EXPECT_EQ(static_cast<ImageReaderGLOwner*>(image_reader_.get())
+                ->max_images_for_testing(),
+            2);
+}
+
+class ImageReaderGLOwnerSecureSurfaceControlTest
+    : public ImageReaderGLOwnerTest {
  public:
   TextureOwner::Mode SecureMode() final {
-    return TextureOwner::Mode::kAImageReaderSecure;
+    return TextureOwner::Mode::kAImageReaderSecureSurfaceControl;
   }
 };
 
-TEST_F(ImageReaderGLOwnerSecureTest, CreatesSecureAImageReader) {
+TEST_F(ImageReaderGLOwnerSecureSurfaceControlTest, CreatesSecureAImageReader) {
   if (!IsImageReaderSupported())
     return;
 
@@ -163,4 +174,32 @@
   EXPECT_EQ(format, AIMAGE_FORMAT_PRIVATE);
 }
 
+// The max number of images used by the ImageReader must be 3 for Surface
+// control.
+TEST_F(ImageReaderGLOwnerSecureSurfaceControlTest, MaxImageExpectation) {
+  if (!IsImageReaderSupported())
+    return;
+  EXPECT_EQ(static_cast<ImageReaderGLOwner*>(image_reader_.get())
+                ->max_images_for_testing(),
+            3);
+}
+
+class ImageReaderGLOwnerInsecureSurfaceControlTest
+    : public ImageReaderGLOwnerTest {
+ public:
+  TextureOwner::Mode SecureMode() final {
+    return TextureOwner::Mode::kAImageReaderInsecureSurfaceControl;
+  }
+};
+
+// The max number of images used by the ImageReader must be 3 for Surface
+// control.
+TEST_F(ImageReaderGLOwnerInsecureSurfaceControlTest, MaxImageExpectation) {
+  if (!IsImageReaderSupported())
+    return;
+  EXPECT_EQ(static_cast<ImageReaderGLOwner*>(image_reader_.get())
+                ->max_images_for_testing(),
+            3);
+}
+
 }  // namespace media
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc
index 8067bad..89b9581b 100644
--- a/media/gpu/android/media_codec_video_decoder.cc
+++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/android/build_info.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
@@ -78,31 +79,67 @@
   // TODO(dalecurtis): This needs to actually check the profiles available. This
   // can be done by calling MediaCodecUtil::AddSupportedCodecProfileLevels.
   if (device_info->IsVp9DecoderAvailable()) {
-    // For unencrypted content, require that the size is at least 360p and that
-    // the MediaCodec implementation is hardware; otherwise fall back to libvpx.
-    if (!device_info->IsDecoderKnownUnaccelerated(kCodecVP9)) {
-      supported_configs.emplace_back(VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE3,
-                                     gfx::Size(480, 360), gfx::Size(3840, 2160),
-                                     false,   // allow_encrypted
-                                     false);  // require_encrypted
-      supported_configs.emplace_back(VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE3,
-                                     gfx::Size(360, 480), gfx::Size(2160, 3840),
-                                     false,   // allow_encrypted
-                                     false);  // require_encrypted
-    }
+    const bool is_sw = device_info->IsDecoderKnownUnaccelerated(kCodecVP9);
 
-    // Encrypted content must be decoded by MediaCodec.
-    supported_configs.emplace_back(VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE3,
-                                   gfx::Size(0, 0), gfx::Size(3840, 2160),
-                                   true,   // allow_encrypted
-                                   true);  // require_encrypted
-    supported_configs.emplace_back(VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE3,
-                                   gfx::Size(0, 0), gfx::Size(2160, 3840),
-                                   true,   // allow_encrypted
-                                   true);  // require_encrypted
+    std::vector<CodecProfileLevel> profiles;
+
+    // Support for VP9.2, VP9.3 was not added until Nougat.
+    if (device_info->SdkVersion() >= base::android::SDK_VERSION_NOUGAT)
+      device_info->AddSupportedCodecProfileLevels(&profiles);
+
+    // If we think a VP9 decoder is available, but we didn't get any profiles
+    // returned, just assume support for vp9.0 only.
+    if (profiles.empty())
+      profiles.push_back({kCodecVP9, VP9PROFILE_PROFILE0, 0});
+
+    for (const auto& p : profiles) {
+      if (p.codec != kCodecVP9)
+        continue;
+
+      // We don't compile support into libvpx for these profiles, so allow them
+      // for all resolutions. See notes on H264 profiles below for more detail.
+      if (p.profile > VP9PROFILE_PROFILE1) {
+        supported_configs.emplace_back(p.profile, p.profile, gfx::Size(0, 0),
+                                       gfx::Size(3840, 2160),
+                                       true,    // allow_encrypted
+                                       false);  // require_encrypted
+        supported_configs.emplace_back(p.profile, p.profile, gfx::Size(0, 0),
+                                       gfx::Size(2160, 3840),
+                                       true,    // allow_encrypted
+                                       false);  // require_encrypted
+        continue;
+      }
+
+      // For unencrypted vp9.0 and vp9.1 content, require that the size is at
+      // least 360p and that the MediaCodec implementation is hardware;
+      // otherwise fall back to libvpx.
+      if (!is_sw) {
+        supported_configs.emplace_back(
+            p.profile, p.profile, gfx::Size(480, 360), gfx::Size(3840, 2160),
+            false,   // allow_encrypted
+            false);  // require_encrypted
+        supported_configs.emplace_back(
+            p.profile, p.profile, gfx::Size(360, 480), gfx::Size(2160, 3840),
+            false,   // allow_encrypted
+            false);  // require_encrypted
+      }
+
+      // Encrypted content must be decoded by MediaCodec.
+      supported_configs.emplace_back(p.profile, p.profile, gfx::Size(0, 0),
+                                     gfx::Size(3840, 2160),
+                                     true,   // allow_encrypted
+                                     true);  // require_encrypted
+      supported_configs.emplace_back(p.profile, p.profile, gfx::Size(0, 0),
+                                     gfx::Size(2160, 3840),
+                                     true,   // allow_encrypted
+                                     true);  // require_encrypted
+    }
   }
 
   if (device_info->IsAv1DecoderAvailable()) {
+    // Technically we should check which profiles are supported, but since we
+    // don't have an AV1 SW decoder, just allow them all. See notes below for
+    // H264 profiles on the reasons why.
     supported_configs.emplace_back(AV1PROFILE_MIN, AV1PROFILE_MAX,
                                    gfx::Size(0, 0), gfx::Size(3840, 2160),
                                    true,    // allow_encrypted
@@ -118,19 +155,17 @@
   // support others. Advertise support for all H.264 profiles and let the
   // MediaCodec fail when decoding if it's not actually supported. It's assumed
   // that there is not software fallback for H.264 on Android.
-  supported_configs.emplace_back(H264PROFILE_BASELINE,
-                                 H264PROFILE_MULTIVIEWHIGH, gfx::Size(0, 0),
-                                 gfx::Size(3840, 2160),
+  supported_configs.emplace_back(H264PROFILE_MIN, H264PROFILE_MAX,
+                                 gfx::Size(0, 0), gfx::Size(3840, 2160),
                                  true,    // allow_encrypted
                                  false);  // require_encrypted
-  supported_configs.emplace_back(H264PROFILE_BASELINE,
-                                 H264PROFILE_MULTIVIEWHIGH, gfx::Size(0, 0),
-                                 gfx::Size(2160, 3840),
+  supported_configs.emplace_back(H264PROFILE_MIN, H264PROFILE_MAX,
+                                 gfx::Size(0, 0), gfx::Size(2160, 3840),
                                  true,    // allow_encrypted
                                  false);  // require_encrypted
 
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
-  supported_configs.emplace_back(HEVCPROFILE_MAIN, HEVCPROFILE_MAIN10,
+  supported_configs.emplace_back(HEVCPROFILE_MIN, HEVCPROFILE_MAX,
                                  gfx::Size(0, 0), gfx::Size(3840, 2160),
                                  true,    // allow_encrypted
                                  false);  // require_encrypted
diff --git a/media/gpu/android/mock_device_info.h b/media/gpu/android/mock_device_info.h
index 41758e9..756efed 100644
--- a/media/gpu/android/mock_device_info.h
+++ b/media/gpu/android/mock_device_info.h
@@ -26,6 +26,8 @@
   MOCK_METHOD0(SupportsOverlaySurfaces, bool());
   MOCK_METHOD1(CodecNeedsFlushWorkaround, bool(MediaCodecBridge* codec));
   MOCK_METHOD0(IsAsyncApiSupported, bool());
+  MOCK_METHOD1(AddSupportedCodecProfileLevels,
+               bool(std::vector<CodecProfileLevel>*));
 };
 
 }  // namespace media
diff --git a/media/gpu/android/texture_owner.cc b/media/gpu/android/texture_owner.cc
index c5402531..8e6ed6d4c 100644
--- a/media/gpu/android/texture_owner.cc
+++ b/media/gpu/android/texture_owner.cc
@@ -43,9 +43,9 @@
     std::unique_ptr<gpu::gles2::AbstractTexture> texture,
     Mode mode) {
   switch (mode) {
-    case Mode::kAImageReaderSecure:
-      return new ImageReaderGLOwner(std::move(texture), mode);
     case Mode::kAImageReaderInsecure:
+    case Mode::kAImageReaderInsecureSurfaceControl:
+    case Mode::kAImageReaderSecureSurfaceControl:
       return new ImageReaderGLOwner(std::move(texture), mode);
     case Mode::kSurfaceTextureInsecure:
       return new SurfaceTextureGLOwner(std::move(texture));
diff --git a/media/gpu/android/texture_owner.h b/media/gpu/android/texture_owner.h
index afba4fc..af807f0 100644
--- a/media/gpu/android/texture_owner.h
+++ b/media/gpu/android/texture_owner.h
@@ -47,10 +47,12 @@
   // |texture| should be either from CreateAbstractTexture() or a mock.  The
   // corresponding GL context must be current.
   // Mode indicates which framework API to use and whether the video textures
-  // created using this owner should be hardware protected.
+  // created using this owner should be hardware protected. It also indicates
+  // whether SurfaceControl is being used or not.
   enum class Mode {
-    kAImageReaderSecure,
     kAImageReaderInsecure,
+    kAImageReaderInsecureSurfaceControl,
+    kAImageReaderSecureSurfaceControl,
     kSurfaceTextureInsecure
   };
   static scoped_refptr<TextureOwner> Create(
diff --git a/media/gpu/android/video_frame_factory_impl.cc b/media/gpu/android/video_frame_factory_impl.cc
index 7ece4f7..a6e92a72 100644
--- a/media/gpu/android/video_frame_factory_impl.cc
+++ b/media/gpu/android/video_frame_factory_impl.cc
@@ -62,10 +62,10 @@
                  : TextureOwner::Mode::kSurfaceTextureInsecure;
     case VideoFrameFactory::OverlayMode::kSurfaceControlSecure:
       DCHECK(a_image_reader_supported);
-      return TextureOwner::Mode::kAImageReaderSecure;
+      return TextureOwner::Mode::kAImageReaderSecureSurfaceControl;
     case VideoFrameFactory::OverlayMode::kSurfaceControlInsecure:
       DCHECK(a_image_reader_supported);
-      return TextureOwner::Mode::kAImageReaderInsecure;
+      return TextureOwner::Mode::kAImageReaderInsecureSurfaceControl;
   }
 
   NOTREACHED();
diff --git a/media/gpu/chromeos/chromeos_video_decoder_factory.cc b/media/gpu/chromeos/chromeos_video_decoder_factory.cc
index d86e96c..0b68d5a 100644
--- a/media/gpu/chromeos/chromeos_video_decoder_factory.cc
+++ b/media/gpu/chromeos/chromeos_video_decoder_factory.cc
@@ -50,6 +50,27 @@
 }  // namespace
 
 // static
+SupportedVideoDecoderConfigs
+ChromeosVideoDecoderFactory::GetSupportedConfigs() {
+  SupportedVideoDecoderConfigs supported_configs;
+  SupportedVideoDecoderConfigs configs;
+
+#if BUILDFLAG(USE_VAAPI)
+  configs = VaapiVideoDecoder::GetSupportedConfigs();
+  supported_configs.insert(supported_configs.end(), configs.begin(),
+                           configs.end());
+#endif  // BUILDFLAG(USE_VAAPI)
+
+#if BUILDFLAG(USE_V4L2_CODEC)
+  configs = V4L2SliceVideoDecoder::GetSupportedConfigs();
+  supported_configs.insert(supported_configs.end(), configs.begin(),
+                           configs.end());
+#endif  // BUILDFLAG(USE_V4L2_CODEC)
+
+  return supported_configs;
+}
+
+// static
 std::unique_ptr<VideoDecoder> ChromeosVideoDecoderFactory::Create(
     scoped_refptr<base::SequencedTaskRunner> client_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
diff --git a/media/gpu/chromeos/chromeos_video_decoder_factory.h b/media/gpu/chromeos/chromeos_video_decoder_factory.h
index 2f09b5d..0a5ec39 100644
--- a/media/gpu/chromeos/chromeos_video_decoder_factory.h
+++ b/media/gpu/chromeos/chromeos_video_decoder_factory.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "media/gpu/media_gpu_export.h"
+#include "media/video/supported_video_decoder_config.h"
 
 namespace base {
 class SequencedTaskRunner;
@@ -27,6 +28,8 @@
  public:
   using GetCommandBufferStubCB = base::OnceCallback<gpu::CommandBufferStub*()>;
 
+  static SupportedVideoDecoderConfigs GetSupportedConfigs();
+
   // Create VideoDecoder instance that does convert the output VideoFrame
   // to mailbox-backed VideoFrame by CommandBufferHelper.
   // We convert the frame by MailboxVideoFrameConverter. See the description for
diff --git a/media/gpu/gpu_video_decode_accelerator_helpers.cc b/media/gpu/gpu_video_decode_accelerator_helpers.cc
new file mode 100644
index 0000000..5392cad
--- /dev/null
+++ b/media/gpu/gpu_video_decode_accelerator_helpers.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/gpu_video_decode_accelerator_helpers.h"
+
+namespace media {
+
+SupportedVideoDecoderConfigs ConvertFromSupportedProfiles(
+    const VideoDecodeAccelerator::SupportedProfiles& profiles,
+    bool allow_encrypted) {
+  SupportedVideoDecoderConfigs configs;
+  for (const auto& profile : profiles) {
+    configs.push_back(SupportedVideoDecoderConfig(
+        profile.profile,           // profile_min
+        profile.profile,           // profile_max
+        profile.min_resolution,    // coded_size_min
+        profile.max_resolution,    // coded_size_max
+        allow_encrypted,           // allow_encrypted
+        profile.encrypted_only));  // require_encrypted);
+  }
+  return configs;
+}
+
+}  // namespace media
diff --git a/media/gpu/gpu_video_decode_accelerator_helpers.h b/media/gpu/gpu_video_decode_accelerator_helpers.h
index 8de9ed40..6c225f2 100644
--- a/media/gpu/gpu_video_decode_accelerator_helpers.h
+++ b/media/gpu/gpu_video_decode_accelerator_helpers.h
@@ -7,6 +7,9 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "media/gpu/media_gpu_export.h"
+#include "media/video/supported_video_decoder_config.h"
+#include "media/video/video_decode_accelerator.h"
 
 namespace gl {
 class GLContext;
@@ -64,6 +67,12 @@
         unsigned /* GLenum */ format,
         unsigned /* GLenum */ type)>;
 
+// Convert vector of VDA::SupportedProfile to vector of
+// SupportedVideoDecoderConfig.
+MEDIA_GPU_EXPORT SupportedVideoDecoderConfigs ConvertFromSupportedProfiles(
+    const VideoDecodeAccelerator::SupportedProfiles& profiles,
+    bool allow_encrypted);
+
 }  // namespace media
 
 #endif  // MEDIA_GPU_GPU_VIDEO_DECODE_ACCELERATOR_HELPERS_H_
diff --git a/media/gpu/ipc/service/vda_video_decoder.cc b/media/gpu/ipc/service/vda_video_decoder.cc
index 57f15d5..734dbeb 100644
--- a/media/gpu/ipc/service/vda_video_decoder.cc
+++ b/media/gpu/ipc/service/vda_video_decoder.cc
@@ -253,7 +253,7 @@
   // TODO(sandersd): Change this to a capability if any VDA starts supporting
   // alpha channels. This is believed to be impossible right now because VPx
   // alpha channel data is passed in side data, which isn't sent to VDAs.
-  if (!IsOpaque(config.format())) {
+  if (config.alpha_mode() != VideoDecoderConfig::AlphaMode::kIsOpaque) {
     MEDIA_LOG(INFO, media_log_) << "Alpha formats are not supported";
     EnterErrorState();
     return;
diff --git a/media/gpu/ipc/service/vda_video_decoder_unittest.cc b/media/gpu/ipc/service/vda_video_decoder_unittest.cc
index 4e34ed6..a7ba09d6 100644
--- a/media/gpu/ipc/service/vda_video_decoder_unittest.cc
+++ b/media/gpu/ipc/service/vda_video_decoder_unittest.cc
@@ -141,10 +141,10 @@
         .WillOnce(Return(GetParam()));
     EXPECT_CALL(init_cb_, Run(true));
     InitializeWithConfig(VideoDecoderConfig(
-        kCodecVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420,
-        VideoColorSpace::REC709(), kNoTransformation, gfx::Size(1920, 1088),
-        gfx::Rect(1920, 1080), gfx::Size(1920, 1080), EmptyExtraData(),
-        Unencrypted()));
+        kCodecVP9, VP9PROFILE_PROFILE0,
+        VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace::REC709(),
+        kNoTransformation, gfx::Size(1920, 1088), gfx::Rect(1920, 1080),
+        gfx::Size(1920, 1080), EmptyExtraData(), Unencrypted()));
     RunUntilIdle();
   }
 
@@ -317,21 +317,21 @@
 }
 
 TEST_P(VdaVideoDecoderTest, Initialize_UnsupportedSize) {
-  InitializeWithConfig(
-      VideoDecoderConfig(kCodecVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420,
-                         VideoColorSpace::REC601(), kNoTransformation,
-                         gfx::Size(320, 240), gfx::Rect(320, 240),
-                         gfx::Size(320, 240), EmptyExtraData(), Unencrypted()));
+  InitializeWithConfig(VideoDecoderConfig(
+      kCodecVP9, VP9PROFILE_PROFILE0, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace::REC601(), kNoTransformation, gfx::Size(320, 240),
+      gfx::Rect(320, 240), gfx::Size(320, 240), EmptyExtraData(),
+      Unencrypted()));
   EXPECT_CALL(init_cb_, Run(false));
   RunUntilIdle();
 }
 
 TEST_P(VdaVideoDecoderTest, Initialize_UnsupportedCodec) {
   InitializeWithConfig(VideoDecoderConfig(
-      kCodecH264, H264PROFILE_BASELINE, PIXEL_FORMAT_I420,
-      VideoColorSpace::REC709(), kNoTransformation, gfx::Size(1920, 1088),
-      gfx::Rect(1920, 1080), gfx::Size(1920, 1080), EmptyExtraData(),
-      Unencrypted()));
+      kCodecH264, H264PROFILE_BASELINE,
+      VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace::REC709(),
+      kNoTransformation, gfx::Size(1920, 1088), gfx::Rect(1920, 1080),
+      gfx::Size(1920, 1080), EmptyExtraData(), Unencrypted()));
   EXPECT_CALL(init_cb_, Run(false));
   RunUntilIdle();
 }
@@ -339,7 +339,7 @@
 TEST_P(VdaVideoDecoderTest, Initialize_RejectedByVda) {
   EXPECT_CALL(*vda_, Initialize(_, vdavd_.get())).WillOnce(Return(false));
   InitializeWithConfig(VideoDecoderConfig(
-      kCodecVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420,
+      kCodecVP9, VP9PROFILE_PROFILE0, VideoDecoderConfig::AlphaMode::kIsOpaque,
       VideoColorSpace::REC709(), kNoTransformation, gfx::Size(1920, 1088),
       gfx::Rect(1920, 1080), gfx::Size(1920, 1080), EmptyExtraData(),
       Unencrypted()));
@@ -422,7 +422,7 @@
   EXPECT_CALL(*vda_, TryToSetupDecodeOnSeparateThread(_, _))
       .WillOnce(Return(GetParam()));
   InitializeWithConfig(VideoDecoderConfig(
-      kCodecVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420,
+      kCodecVP9, VP9PROFILE_PROFILE0, VideoDecoderConfig::AlphaMode::kIsOpaque,
       VideoColorSpace::REC709(), kNoTransformation, gfx::Size(640, 480),
       gfx::Rect(640, 480), gfx::Size(1280, 480), EmptyExtraData(),
       Unencrypted()));
diff --git a/media/gpu/mac/vt_video_decode_accelerator_mac.cc b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
index e92a946..b440d54 100644
--- a/media/gpu/mac/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
@@ -58,11 +58,14 @@
     H264PROFILE_BASELINE, H264PROFILE_EXTENDED, H264PROFILE_MAIN,
     H264PROFILE_HIGH,
 
-    // TODO(hubbe): Re-enable this once software fallback is working.
-    // http://crbug.com/605790
+    // TODO(sandersd): Hi10p fails during
+    // CMVideoFormatDescriptionCreateFromH264ParameterSets with
+    // kCMFormatDescriptionError_InvalidParameter.
+    //
     // H264PROFILE_HIGH10PROFILE,
 
     // TODO(sandersd): Find and test media with these profiles before enabling.
+    //
     // H264PROFILE_SCALABLEBASELINE,
     // H264PROFILE_SCALABLEHIGH,
     // H264PROFILE_STEREOHIGH,
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc
index 660fa8a..3bdea89e 100644
--- a/media/gpu/test/video_player/video_decoder_client.cc
+++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -165,7 +165,8 @@
   LOG_ASSERT(video_);
 
   VideoDecoderConfig config(
-      video_->Codec(), video_->Profile(), PIXEL_FORMAT_I420, VideoColorSpace(),
+      video_->Codec(), video_->Profile(),
+      VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(),
       kNoTransformation, video_->Resolution(), gfx::Rect(video_->Resolution()),
       video_->Resolution(), std::vector<uint8_t>(0), EncryptionScheme());
 
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.cc b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
index 9732fe57..a8037ed6 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
@@ -14,6 +14,7 @@
 #include "media/base/scopedfd_helper.h"
 #include "media/gpu/accelerated_video_decoder.h"
 #include "media/gpu/chromeos/dmabuf_video_frame_pool.h"
+#include "media/gpu/gpu_video_decode_accelerator_helpers.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_h264_accelerator.h"
 #include "media/gpu/v4l2/v4l2_vp8_accelerator.h"
@@ -33,6 +34,13 @@
 constexpr size_t kNumInputBuffers = 16;
 constexpr size_t kNumInputPlanes = 1;
 
+// Input format V4L2 fourccs this class supports.
+constexpr uint32_t kSupportedInputFourccs[] = {
+    V4L2_PIX_FMT_H264_SLICE,
+    V4L2_PIX_FMT_VP8_FRAME,
+    V4L2_PIX_FMT_VP9_FRAME,
+};
+
 // Checks an underlying video frame buffer of |frame| is valid for VIDIOC_DQBUF
 // that requires |target_num_fds| fds.
 bool IsValidFrameForQueueDMABuf(const VideoFrame* frame,
@@ -120,6 +128,18 @@
       std::move(frame_converter)));
 }
 
+// static
+SupportedVideoDecoderConfigs V4L2SliceVideoDecoder::GetSupportedConfigs() {
+  scoped_refptr<V4L2Device> device = V4L2Device::Create();
+  if (!device)
+    return SupportedVideoDecoderConfigs();
+
+  return ConvertFromSupportedProfiles(
+      device->GetSupportedDecodeProfiles(base::size(kSupportedInputFourccs),
+                                         kSupportedInputFourccs),
+      false);
+}
+
 V4L2SliceVideoDecoder::V4L2SliceVideoDecoder(
     scoped_refptr<base::SequencedTaskRunner> client_task_runner,
     scoped_refptr<V4L2Device> device,
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.h b/media/gpu/v4l2/v4l2_slice_video_decoder.h
index b7d91b31..c540ee06 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.h
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.h
@@ -23,6 +23,7 @@
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/v4l2/v4l2_decode_surface_handler.h"
 #include "media/gpu/v4l2/v4l2_device.h"
+#include "media/video/supported_video_decoder_config.h"
 
 namespace media {
 
@@ -42,6 +43,8 @@
       std::unique_ptr<DmabufVideoFramePool> frame_pool,
       std::unique_ptr<VideoFrameConverter> frame_converter);
 
+  static SupportedVideoDecoderConfigs GetSupportedConfigs();
+
   // VideoDecoder implementation.
   std::string GetDisplayName() const override;
   bool IsPlatformDecoder() const override;
diff --git a/media/gpu/vaapi/vaapi_image_decoder.cc b/media/gpu/vaapi/vaapi_image_decoder.cc
index 0eff563..8e35bce 100644
--- a/media/gpu/vaapi/vaapi_image_decoder.cc
+++ b/media/gpu/vaapi/vaapi_image_decoder.cc
@@ -11,9 +11,7 @@
 namespace media {
 
 VaapiImageDecoder::VaapiImageDecoder(VAProfile va_profile)
-    : va_profile_(va_profile),
-      va_surface_id_(VA_INVALID_SURFACE),
-      va_rt_format_(kInvalidVaRtFormat) {}
+    : va_profile_(va_profile) {}
 
 VaapiImageDecoder::~VaapiImageDecoder() = default;
 
diff --git a/media/gpu/vaapi/vaapi_image_decoder.h b/media/gpu/vaapi/vaapi_image_decoder.h
index 85108ab..a23ac89 100644
--- a/media/gpu/vaapi/vaapi_image_decoder.h
+++ b/media/gpu/vaapi/vaapi_image_decoder.h
@@ -14,26 +14,19 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "gpu/config/gpu_info.h"
-#include "ui/gfx/geometry/size.h"
 
 namespace media {
 
 class VASurface;
 class VaapiWrapper;
 
-constexpr unsigned int kInvalidVaRtFormat = 0u;
-
 enum class VaapiImageDecodeStatus : uint32_t {
   kSuccess,
-  kParseJpegFailed,
-  kUnsupportedJpeg,
+  kParseFailed,
+  kUnsupportedImage,
   kUnsupportedSubsampling,
   kSurfaceCreationFailed,
-  kSubmitPicParamsFailed,
-  kSubmitIQMatrixFailed,
-  kSubmitHuffmanFailed,
-  kSubmitSliceParamsFailed,
-  kSubmitSliceDataFailed,
+  kSubmitVABuffersFailed,
   kExecuteDecodeFailed,
   kUnsupportedSurfaceFormat,
   kCannotGetImage,
@@ -77,14 +70,9 @@
 
   scoped_refptr<VaapiWrapper> vaapi_wrapper_;
 
+ private:
   // The VA profile used for the current image decoder.
   const VAProfile va_profile_;
-  // The current VA surface for decoding.
-  VASurfaceID va_surface_id_;
-  // The coded size associated with |va_surface_id_|.
-  gfx::Size coded_size_;
-  // The VA RT format associated with |va_surface_id_|.
-  unsigned int va_rt_format_;
 
   DISALLOW_COPY_AND_ASSIGN(VaapiImageDecoder);
 };
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder.cc b/media/gpu/vaapi/vaapi_jpeg_decoder.cc
index 1036f3d..044fe2c 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder.cc
@@ -213,7 +213,9 @@
 }
 
 VaapiJpegDecoder::VaapiJpegDecoder()
-    : VaapiImageDecoder(VAProfileJPEGBaseline) {}
+    : VaapiImageDecoder(VAProfileJPEGBaseline),
+      va_surface_id_(VA_INVALID_SURFACE),
+      va_rt_format_(kInvalidVaRtFormat) {}
 
 VaapiJpegDecoder::~VaapiJpegDecoder() {
   if (vaapi_wrapper_) {
@@ -236,7 +238,7 @@
   if (!ParseJpegPicture(encoded_image.data(), encoded_image.size(),
                         &parse_result)) {
     VLOGF(1) << "ParseJpegPicture failed";
-    *status = VaapiImageDecodeStatus::kParseJpegFailed;
+    *status = VaapiImageDecodeStatus::kParseFailed;
     return nullptr;
   }
 
@@ -252,7 +254,7 @@
   // Make sure this JPEG can be decoded.
   if (!IsVaapiSupportedJpeg(parse_result)) {
     VLOGF(1) << "The supplied JPEG is unsupported";
-    *status = VaapiImageDecodeStatus::kUnsupportedJpeg;
+    *status = VaapiImageDecodeStatus::kUnsupportedImage;
     return nullptr;
   }
 
@@ -283,7 +285,7 @@
   FillPictureParameters(parse_result.frame_header, &pic_param);
   if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, &pic_param)) {
     VLOGF(1) << "Could not submit VAPictureParameterBufferType";
-    *status = VaapiImageDecodeStatus::kSubmitPicParamsFailed;
+    *status = VaapiImageDecodeStatus::kSubmitVABuffersFailed;
     return nullptr;
   }
 
@@ -292,7 +294,7 @@
   FillIQMatrix(parse_result.q_table, &iq_matrix);
   if (!vaapi_wrapper_->SubmitBuffer(VAIQMatrixBufferType, &iq_matrix)) {
     VLOGF(1) << "Could not submit VAIQMatrixBufferType";
-    *status = VaapiImageDecodeStatus::kSubmitIQMatrixFailed;
+    *status = VaapiImageDecodeStatus::kSubmitVABuffersFailed;
     return nullptr;
   }
 
@@ -302,7 +304,7 @@
                    &huffman_table);
   if (!vaapi_wrapper_->SubmitBuffer(VAHuffmanTableBufferType, &huffman_table)) {
     VLOGF(1) << "Could not submit VAHuffmanTableBufferType";
-    *status = VaapiImageDecodeStatus::kSubmitHuffmanFailed;
+    *status = VaapiImageDecodeStatus::kSubmitVABuffersFailed;
     return nullptr;
   }
 
@@ -311,7 +313,7 @@
   FillSliceParameters(parse_result, &slice_param);
   if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, &slice_param)) {
     VLOGF(1) << "Could not submit VASliceParameterBufferType";
-    *status = VaapiImageDecodeStatus::kSubmitSliceParamsFailed;
+    *status = VaapiImageDecodeStatus::kSubmitVABuffersFailed;
     return nullptr;
   }
 
@@ -320,7 +322,7 @@
                                     parse_result.data_size,
                                     const_cast<char*>(parse_result.data))) {
     VLOGF(1) << "Could not submit VASliceDataBufferType";
-    *status = VaapiImageDecodeStatus::kSubmitSliceDataFailed;
+    *status = VaapiImageDecodeStatus::kSubmitVABuffersFailed;
     return nullptr;
   }
 
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder.h b/media/gpu/vaapi/vaapi_jpeg_decoder.h
index fa11980..09721921 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder.h
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder.h
@@ -11,12 +11,15 @@
 
 #include "base/macros.h"
 #include "media/gpu/vaapi/vaapi_image_decoder.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace media {
 
 struct JpegFrameHeader;
 class ScopedVAImage;
 
+constexpr unsigned int kInvalidVaRtFormat = 0u;
+
 // Returns the internal format required for a JPEG image given its parsed
 // |frame_header|. If the image's subsampling format is not one of 4:2:0, 4:2:2,
 // or 4:4:4, returns kInvalidVaRtFormat.
@@ -42,6 +45,14 @@
   std::unique_ptr<ScopedVAImage> GetImage(uint32_t preferred_image_fourcc,
                                           VaapiImageDecodeStatus* status);
 
+ private:
+  // The current VA surface for decoding.
+  VASurfaceID va_surface_id_;
+  // The coded size associated with |va_surface_id_|.
+  gfx::Size coded_size_;
+  // The VA RT format associated with |va_surface_id_|.
+  unsigned int va_rt_format_;
+
   DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecoder);
 };
 
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
index 8c064c94f..2f7cd43 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "media/base/test_data_util.h"
 #include "media/base/video_types.h"
@@ -63,6 +64,22 @@
     {"OddDimensionsImage41x23", kOddDimensionsImageFilename},
 };
 
+constexpr size_t kMaxNumberPlanes = base::size(VAImage().pitches);
+static_assert(kMaxNumberPlanes <= 3u, "The number of planes should be <= 3");
+static_assert(
+    base::size(VAImage().pitches) == base::size(VAImage().offsets),
+    "The number of VAImage pitches is not equal to the number of offsets");
+
+struct DecodedVAImage {
+  uint32_t va_fourcc;
+  uint32_t number_of_planes;  // Can not be greater than kMaxNumberPlanes.
+  gfx::Size coded_size;
+  struct {
+    uint8_t* data;
+    int stride;
+  } planes[kMaxNumberPlanes];
+};
+
 constexpr double kMinSsim = 0.995;
 
 // This file is not supported by the VAAPI, so we don't define expectations on
@@ -77,25 +94,55 @@
 // JPEG decoding.
 constexpr gfx::Size kLargestSupportedSize(16 * 1024, 16 * 1024);
 
+// Takes a ScopedVAImage and returns a DecodedVAImage object that represents
+// the same decoded result.
+DecodedVAImage ScopedVAImageToDecodedVAImage(
+    const ScopedVAImage* decoded_image) {
+  DecodedVAImage converted_result{};
+
+  converted_result.va_fourcc = decoded_image->image()->format.fourcc;
+  converted_result.number_of_planes = decoded_image->image()->num_planes;
+  converted_result.coded_size =
+      gfx::Size(base::strict_cast<int>(decoded_image->image()->width),
+                base::strict_cast<int>(decoded_image->image()->height));
+
+  DCHECK_LE(base::strict_cast<size_t>(converted_result.number_of_planes),
+            kMaxNumberPlanes);
+
+  // This is safe because |number_of_planes| is retrieved from the VA-API and it
+  // can not be greater than 3, which is also the size of the |planes| array.
+  for (uint32_t i = 0u; i < converted_result.number_of_planes; ++i) {
+    converted_result.planes[i].data =
+        static_cast<uint8_t*>(decoded_image->va_buffer()->data()) +
+        decoded_image->image()->offsets[i];
+    converted_result.planes[i].stride =
+        base::checked_cast<int>(decoded_image->image()->pitches[i]);
+  }
+
+  return converted_result;
+}
+
 // Compares the result of sw decoding |encoded_image| with |decoded_image| using
 // SSIM. Returns true if all conversions work and SSIM is above a given
 // threshold (kMinSsim), or false otherwise.
 bool CompareImages(base::span<const uint8_t> encoded_image,
-                   const ScopedVAImage* decoded_image) {
+                   const DecodedVAImage& decoded_image) {
   JpegParseResult parse_result;
   const bool result = ParseJpegPicture(encoded_image.data(),
                                        encoded_image.size(), &parse_result);
   if (!result)
     return false;
 
-  const uint16_t coded_width = parse_result.frame_header.coded_width;
-  const uint16_t coded_height = parse_result.frame_header.coded_height;
-  if (coded_width != decoded_image->image()->width ||
-      coded_height != decoded_image->image()->height) {
+  const int coded_width =
+      base::strict_cast<int>(parse_result.frame_header.coded_width);
+  const int coded_height =
+      base::strict_cast<int>(parse_result.frame_header.coded_height);
+  if (coded_width != decoded_image.coded_size.width() ||
+      coded_height != decoded_image.coded_size.height()) {
     DLOG(ERROR) << "Wrong expected decoded JPEG coded size, " << coded_width
                 << "x" << coded_height << " versus VaAPI provided "
-                << decoded_image->image()->width << "x"
-                << decoded_image->image()->height;
+                << decoded_image.coded_size.width() << "x"
+                << decoded_image.coded_size.height();
     return false;
   }
 
@@ -117,28 +164,15 @@
     return false;
   }
 
-  const uint32_t va_fourcc = decoded_image->image()->format.fourcc;
+  const uint32_t va_fourcc = decoded_image.va_fourcc;
   double ssim = 0;
   if (va_fourcc == VA_FOURCC_I420) {
-    const auto* decoded_data_y =
-        static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
-        decoded_image->image()->offsets[0];
-    const auto* decoded_data_u =
-        static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
-        decoded_image->image()->offsets[1];
-    const auto* decoded_data_v =
-        static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
-        decoded_image->image()->offsets[2];
-
     ssim = libyuv::I420Ssim(
         libyuv_y_plane.get(), width, libyuv_u_plane.get(), half_width,
-        libyuv_v_plane.get(), half_width, decoded_data_y,
-        base::checked_cast<int>(decoded_image->image()->pitches[0]),
-        decoded_data_u,
-        base::checked_cast<int>(decoded_image->image()->pitches[1]),
-        decoded_data_v,
-        base::checked_cast<int>(decoded_image->image()->pitches[2]), width,
-        height);
+        libyuv_v_plane.get(), half_width, decoded_image.planes[0].data,
+        decoded_image.planes[0].stride, decoded_image.planes[1].data,
+        decoded_image.planes[1].stride, decoded_image.planes[2].data,
+        decoded_image.planes[2].stride, width, height);
   } else if (va_fourcc == VA_FOURCC_NV12 || va_fourcc == VA_FOURCC_YUY2 ||
              va_fourcc == VA_FOURCC('Y', 'U', 'Y', 'V')) {
     // Temporary planes to hold intermediate conversions to I420 (i.e. NV12 to
@@ -148,30 +182,16 @@
     auto temp_v = std::make_unique<uint8_t[]>(half_width * half_height);
 
     if (va_fourcc == VA_FOURCC_NV12) {
-      const auto* decoded_data_y =
-          static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
-          decoded_image->image()->offsets[0];
-      const auto* decoded_data_uv =
-          static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
-          decoded_image->image()->offsets[1];
-
       conversion_result = libyuv::NV12ToI420(
-          decoded_data_y,
-          base::checked_cast<int>(decoded_image->image()->pitches[0]),
-          decoded_data_uv,
-          base::checked_cast<int>(decoded_image->image()->pitches[1]),
+          decoded_image.planes[0].data, decoded_image.planes[0].stride,
+          decoded_image.planes[1].data, decoded_image.planes[1].stride,
           temp_y.get(), width, temp_u.get(), half_width, temp_v.get(),
           half_width, width, height);
     } else {
       // |va_fourcc| is YUY2 or YUYV, which are handled the same.
-      const auto* decoded_data_yuyv =
-          static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
-          decoded_image->image()->offsets[0];
-
       // TODO(crbug.com/868400): support other formats/planarities/pitches.
       conversion_result = libyuv::YUY2ToI420(
-          decoded_data_yuyv,
-          base::checked_cast<int>(decoded_image->image()->pitches[0]),
+          decoded_image.planes[0].data, decoded_image.planes[0].stride,
           temp_y.get(), width, temp_u.get(), half_width, temp_v.get(),
           half_width, width, height);
     }
@@ -465,7 +485,8 @@
     if (actual_fourcc == VA_FOURCC_I420 || actual_fourcc == VA_FOURCC_NV12 ||
         actual_fourcc == VA_FOURCC_YUY2 ||
         actual_fourcc == VA_FOURCC('Y', 'U', 'Y', 'V')) {
-      ASSERT_TRUE(CompareImages(encoded_image, scoped_image.get()));
+      ASSERT_TRUE(CompareImages(
+          encoded_image, ScopedVAImageToDecodedVAImage(scoped_image.get())));
     }
     DVLOG(1) << "Got a " << FourccToString(scoped_image->image()->format.fourcc)
              << " VAImage (preferred " << FourccToString(image_format.fourcc)
@@ -513,7 +534,8 @@
     std::unique_ptr<ScopedVAImage> scoped_image = Decode(jpeg_data_span);
     ASSERT_TRUE(scoped_image)
         << "Decode unexpectedly failed for size = " << test_size.ToString();
-    EXPECT_TRUE(CompareImages(jpeg_data_span, scoped_image.get()))
+    EXPECT_TRUE(CompareImages(
+        jpeg_data_span, ScopedVAImageToDecodedVAImage(scoped_image.get())))
         << "The SSIM check unexpectedly failed for size = "
         << test_size.ToString();
   }
@@ -568,7 +590,7 @@
                             jpeg_data.size()),
                         &status))
         << "Decode unexpectedly succeeded for size = " << test_size.ToString();
-    EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedJpeg, status);
+    EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status);
   }
 }
 
@@ -617,7 +639,7 @@
                             jpeg_data.size()),
                         &status))
         << "Decode unexpectedly succeeded for size = " << test_size.ToString();
-    EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedJpeg, status);
+    EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status);
   }
 }
 
diff --git a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
index e0e78c1..a1873f1 100644
--- a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
@@ -58,7 +58,7 @@
   switch (status) {
     case VaapiImageDecodeStatus::kSuccess:
       return chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS;
-    case VaapiImageDecodeStatus::kParseJpegFailed:
+    case VaapiImageDecodeStatus::kParseFailed:
       return chromeos_camera::MjpegDecodeAccelerator::Error::PARSE_JPEG_FAILED;
     case VaapiImageDecodeStatus::kUnsupportedSubsampling:
       return chromeos_camera::MjpegDecodeAccelerator::Error::UNSUPPORTED_JPEG;
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index 716fbafa..9655c98d 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -15,6 +15,7 @@
 #include "media/gpu/chromeos/dmabuf_video_frame_pool.h"
 #include "media/gpu/chromeos/platform_video_frame_utils.h"
 #include "media/gpu/format_utils.h"
+#include "media/gpu/gpu_video_decode_accelerator_helpers.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/vaapi/va_surface.h"
 #include "media/gpu/vaapi/vaapi_h264_accelerator.h"
@@ -66,6 +67,12 @@
                             std::move(frame_pool), std::move(frame_converter)));
 }
 
+// static
+SupportedVideoDecoderConfigs VaapiVideoDecoder::GetSupportedConfigs() {
+  return ConvertFromSupportedProfiles(
+      VaapiWrapper::GetSupportedDecodeProfiles(), false);
+}
+
 VaapiVideoDecoder::VaapiVideoDecoder(
     scoped_refptr<base::SequencedTaskRunner> client_task_runner,
     std::unique_ptr<DmabufVideoFramePool> frame_pool,
diff --git a/media/gpu/vaapi/vaapi_video_decoder.h b/media/gpu/vaapi/vaapi_video_decoder.h
index 069e5f51..04f3ac29d 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.h
+++ b/media/gpu/vaapi/vaapi_video_decoder.h
@@ -22,6 +22,7 @@
 #include "media/base/video_codecs.h"
 #include "media/base/video_decoder.h"
 #include "media/gpu/decode_surface_handler.h"
+#include "media/video/supported_video_decoder_config.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -44,6 +45,8 @@
       std::unique_ptr<DmabufVideoFramePool> frame_pool,
       std::unique_ptr<VideoFrameConverter> frame_converter);
 
+  static SupportedVideoDecoderConfigs GetSupportedConfigs();
+
   // media::VideoDecoder implementation.
   std::string GetDisplayName() const override;
   bool IsPlatformDecoder() const override;
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index a71dc94b..a695ef7 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -1146,18 +1146,20 @@
   gfx::Size natural_size(visible_size.size());
   // The default output format of ffmpeg video decoder is YV12.
   VideoDecoderConfig config;
+  const auto alpha_mode = IsOpaque(pixel_format_)
+                              ? VideoDecoderConfig::AlphaMode::kIsOpaque
+                              : VideoDecoderConfig::AlphaMode::kHasAlpha;
   if (IsVP8(profile_)) {
-    config.Initialize(kCodecVP8, VP8PROFILE_ANY, pixel_format_,
-                      VideoColorSpace(), kNoTransformation, coded_size,
-                      visible_size, natural_size, EmptyExtraData(),
-                      Unencrypted());
+    config.Initialize(kCodecVP8, VP8PROFILE_ANY, alpha_mode, VideoColorSpace(),
+                      kNoTransformation, coded_size, visible_size, natural_size,
+                      EmptyExtraData(), Unencrypted());
   } else if (IsVP9(profile_)) {
-    config.Initialize(kCodecVP9, VP9PROFILE_PROFILE0, pixel_format_,
+    config.Initialize(kCodecVP9, VP9PROFILE_PROFILE0, alpha_mode,
                       VideoColorSpace(), kNoTransformation, coded_size,
                       visible_size, natural_size, EmptyExtraData(),
                       Unencrypted());
   } else if (IsH264(profile_)) {
-    config.Initialize(kCodecH264, H264PROFILE_MAIN, pixel_format_,
+    config.Initialize(kCodecH264, H264PROFILE_MAIN, alpha_mode,
                       VideoColorSpace(), kNoTransformation, coded_size,
                       visible_size, natural_size, EmptyExtraData(),
                       Unencrypted());
diff --git a/media/gpu/windows/d3d11_texture_selector_unittest.cc b/media/gpu/windows/d3d11_texture_selector_unittest.cc
index aa1e0d9d2..a132094 100644
--- a/media/gpu/windows/d3d11_texture_selector_unittest.cc
+++ b/media/gpu/windows/d3d11_texture_selector_unittest.cc
@@ -27,8 +27,8 @@
     EncryptionPattern pattern;
     result.Initialize(
         kUnknownVideoCodec,  // It doesn't matter because it won't be used.
-        profile, PIXEL_FORMAT_UNKNOWN, VideoColorSpace(), kNoTransformation,
-        size, {}, {}, {},
+        profile, VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(),
+        kNoTransformation, size, {}, {}, {},
         EncryptionScheme(encrypted ? EncryptionScheme::CIPHER_MODE_AES_CTR
                                    : EncryptionScheme::CIPHER_MODE_UNENCRYPTED,
                          pattern));
diff --git a/media/mojo/interfaces/media_types.mojom b/media/mojo/interfaces/media_types.mojom
index 9421417..4ce1268 100644
--- a/media/mojo/interfaces/media_types.mojom
+++ b/media/mojo/interfaces/media_types.mojom
@@ -162,7 +162,7 @@
 struct VideoDecoderConfig {
   VideoCodec codec;
   VideoCodecProfile profile;
-  VideoPixelFormat format;
+  bool has_alpha;
   VideoTransformation transformation;
   gfx.mojom.Size coded_size;
   gfx.mojom.Rect visible_rect;
diff --git a/media/mojo/interfaces/video_decoder_config_struct_traits.cc b/media/mojo/interfaces/video_decoder_config_struct_traits.cc
index df3f80f2..0b9f4c62 100644
--- a/media/mojo/interfaces/video_decoder_config_struct_traits.cc
+++ b/media/mojo/interfaces/video_decoder_config_struct_traits.cc
@@ -19,10 +19,6 @@
   if (!input.ReadProfile(&profile))
     return false;
 
-  media::VideoPixelFormat format;
-  if (!input.ReadFormat(&format))
-    return false;
-
   media::VideoTransformation transformation;
   if (!input.ReadTransformation(&transformation))
     return false;
@@ -55,9 +51,12 @@
   if (!input.ReadHdrMetadata(&hdr_metadata))
     return false;
 
-  output->Initialize(codec, profile, format, color_space, transformation,
-                     coded_size, visible_rect, natural_size, extra_data,
-                     encryption_scheme);
+  output->Initialize(codec, profile,
+                     input.has_alpha()
+                         ? media::VideoDecoderConfig::AlphaMode::kHasAlpha
+                         : media::VideoDecoderConfig::AlphaMode::kIsOpaque,
+                     color_space, transformation, coded_size, visible_rect,
+                     natural_size, extra_data, encryption_scheme);
 
   if (hdr_metadata)
     output->set_hdr_metadata(hdr_metadata.value());
diff --git a/media/mojo/interfaces/video_decoder_config_struct_traits.h b/media/mojo/interfaces/video_decoder_config_struct_traits.h
index 4f2ab00f..e198f1f 100644
--- a/media/mojo/interfaces/video_decoder_config_struct_traits.h
+++ b/media/mojo/interfaces/video_decoder_config_struct_traits.h
@@ -28,9 +28,9 @@
     return input.profile();
   }
 
-  static media::VideoPixelFormat format(
-      const media::VideoDecoderConfig& input) {
-    return input.format();
+  static bool has_alpha(const media::VideoDecoderConfig& input) {
+    return input.alpha_mode() ==
+           media::VideoDecoderConfig::AlphaMode::kHasAlpha;
   }
 
   static const gfx::Size& coded_size(const media::VideoDecoderConfig& input) {
diff --git a/media/mojo/interfaces/video_decoder_config_struct_traits_unittest.cc b/media/mojo/interfaces/video_decoder_config_struct_traits_unittest.cc
index 6619e13..1ee3d5dc 100644
--- a/media/mojo/interfaces/video_decoder_config_struct_traits_unittest.cc
+++ b/media/mojo/interfaces/video_decoder_config_struct_traits_unittest.cc
@@ -25,10 +25,10 @@
   const uint8_t kExtraData[] = "config extra data";
   const std::vector<uint8_t> kExtraDataVector(
       &kExtraData[0], &kExtraData[0] + base::size(kExtraData));
-  VideoDecoderConfig input(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420,
-                           VideoColorSpace(), kNoTransformation, kCodedSize,
-                           kVisibleRect, kNaturalSize, kExtraDataVector,
-                           Unencrypted());
+  VideoDecoderConfig input(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      kNaturalSize, kExtraDataVector, Unencrypted());
   std::vector<uint8_t> data =
       media::mojom::VideoDecoderConfig::Serialize(&input);
   VideoDecoderConfig output;
@@ -39,10 +39,10 @@
 
 TEST(VideoDecoderConfigStructTraitsTest,
      ConvertVideoDecoderConfig_EmptyExtraData) {
-  VideoDecoderConfig input(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420,
-                           VideoColorSpace(), kNoTransformation, kCodedSize,
-                           kVisibleRect, kNaturalSize, EmptyExtraData(),
-                           Unencrypted());
+  VideoDecoderConfig input(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      kNaturalSize, EmptyExtraData(), Unencrypted());
   std::vector<uint8_t> data =
       media::mojom::VideoDecoderConfig::Serialize(&input);
   VideoDecoderConfig output;
@@ -52,10 +52,10 @@
 }
 
 TEST(VideoDecoderConfigStructTraitsTest, ConvertVideoDecoderConfig_Encrypted) {
-  VideoDecoderConfig input(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420,
-                           VideoColorSpace(), kNoTransformation, kCodedSize,
-                           kVisibleRect, kNaturalSize, EmptyExtraData(),
-                           AesCtrEncryptionScheme());
+  VideoDecoderConfig input(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      kNaturalSize, EmptyExtraData(), AesCtrEncryptionScheme());
   std::vector<uint8_t> data =
       media::mojom::VideoDecoderConfig::Serialize(&input);
   VideoDecoderConfig output;
@@ -67,7 +67,7 @@
 TEST(VideoDecoderConfigStructTraitsTest,
      ConvertVideoDecoderConfig_ColorSpaceInfo) {
   VideoDecoderConfig input(
-      kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420,
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
       VideoColorSpace(VideoColorSpace::PrimaryID::BT2020,
                       VideoColorSpace::TransferID::SMPTEST2084,
                       VideoColorSpace::MatrixID::BT2020_CL,
@@ -84,10 +84,10 @@
 
 TEST(VideoDecoderConfigStructTraitsTest,
      ConvertVideoDecoderConfig_HDRMetadata) {
-  VideoDecoderConfig input(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420,
-                           VideoColorSpace(), kNoTransformation, kCodedSize,
-                           kVisibleRect, kNaturalSize, EmptyExtraData(),
-                           Unencrypted());
+  VideoDecoderConfig input(
+      kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
+      kNaturalSize, EmptyExtraData(), Unencrypted());
   HDRMetadata hdr_metadata;
   hdr_metadata.max_frame_average_light_level = 123;
   hdr_metadata.max_content_light_level = 456;
@@ -126,10 +126,10 @@
 
   // Next try an non-empty invalid config. Natural size must not be zero.
   const gfx::Size kInvalidNaturalSize(0, 0);
-  input.Initialize(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420,
-                   VideoColorSpace(), kNoTransformation, kCodedSize,
-                   kVisibleRect, kInvalidNaturalSize, EmptyExtraData(),
-                   Unencrypted());
+  input.Initialize(kCodecVP8, VP8PROFILE_ANY,
+                   VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(),
+                   kNoTransformation, kCodedSize, kVisibleRect,
+                   kInvalidNaturalSize, EmptyExtraData(), Unencrypted());
   EXPECT_FALSE(input.IsValidConfig());
 
   // Deserialize should again fail due to invalid config.
diff --git a/media/mojo/services/gpu_mojo_media_client.cc b/media/mojo/services/gpu_mojo_media_client.cc
index a1c7bfb..7ee5240b 100644
--- a/media/mojo/services/gpu_mojo_media_client.cc
+++ b/media/mojo/services/gpu_mojo_media_client.cc
@@ -18,10 +18,10 @@
 #include "media/gpu/buildflags.h"
 #include "media/gpu/gpu_video_accelerator_util.h"
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
+#include "media/gpu/gpu_video_decode_accelerator_helpers.h"
 #include "media/gpu/ipc/service/media_gpu_channel_manager.h"
 #include "media/gpu/ipc/service/vda_video_decoder.h"
 #include "media/mojo/interfaces/video_decoder.mojom.h"
-#include "media/video/supported_video_decoder_config.h"
 #include "media/video/video_decode_accelerator.h"
 
 #if defined(OS_ANDROID)
@@ -149,7 +149,18 @@
   }
   supported_config_map[VideoDecoderImplementation::kAlternate] =
       *d3d11_supported_configs_;
-#endif
+
+#elif defined(OS_CHROMEOS)
+  if (base::FeatureList::IsEnabled(kChromeosVideoDecoder)) {
+    if (!chromeos_supported_configs_) {
+      chromeos_supported_configs_ =
+          ChromeosVideoDecoderFactory::GetSupportedConfigs();
+    }
+    supported_config_map[VideoDecoderImplementation::kDefault] =
+        *chromeos_supported_configs_;
+    return supported_config_map;
+  }
+#endif  // defined(OS_WIN)
 
   auto& default_configs =
       supported_config_map[VideoDecoderImplementation::kDefault];
@@ -164,15 +175,10 @@
   bool allow_encrypted =
       capabilities.flags &
       VideoDecodeAccelerator::Capabilities::SUPPORTS_ENCRYPTED_STREAMS;
-  for (const auto& supported_profile : capabilities.supported_profiles) {
-    default_configs.push_back(SupportedVideoDecoderConfig(
-        supported_profile.profile,           // profile_min
-        supported_profile.profile,           // profile_max
-        supported_profile.min_resolution,    // coded_size_min
-        supported_profile.max_resolution,    // coded_size_max
-        allow_encrypted,                     // allow_encrypted
-        supported_profile.encrypted_only));  // require_encrypted
-  }
+  SupportedVideoDecoderConfigs supported_configs = ConvertFromSupportedProfiles(
+      capabilities.supported_profiles, allow_encrypted);
+  default_configs.insert(default_configs.end(), supported_configs.begin(),
+                         supported_configs.end());
 
   return supported_config_map;
 #endif  // defined(OS_ANDROID)
diff --git a/media/mojo/services/gpu_mojo_media_client.h b/media/mojo/services/gpu_mojo_media_client.h
index 8c599a0..9453ab6 100644
--- a/media/mojo/services/gpu_mojo_media_client.h
+++ b/media/mojo/services/gpu_mojo_media_client.h
@@ -19,6 +19,7 @@
 #include "media/base/android_overlay_mojo_factory.h"
 #include "media/cdm/cdm_proxy.h"
 #include "media/mojo/services/mojo_media_client.h"
+#include "media/video/supported_video_decoder_config.h"
 
 namespace media {
 
@@ -67,10 +68,13 @@
   AndroidOverlayMojoFactoryCB android_overlay_factory_cb_;
   CdmProxyFactoryCB cdm_proxy_factory_cb_;
 #if defined(OS_WIN)
-  base::Optional<std::vector<SupportedVideoDecoderConfig>>
-      d3d11_supported_configs_;
+  base::Optional<SupportedVideoDecoderConfigs> d3d11_supported_configs_;
 #endif  // defined(OS_WIN)
 
+#if defined(OS_CHROMEOS)
+  base::Optional<SupportedVideoDecoderConfigs> chromeos_supported_configs_;
+#endif  // defined(OS_CHROMEOS)
+
   DISALLOW_COPY_AND_ASSIGN(GpuMojoMediaClient);
 };
 
diff --git a/media/remoting/fake_media_resource.cc b/media/remoting/fake_media_resource.cc
index 1611d2e2..61a854e 100644
--- a/media/remoting/fake_media_resource.cc
+++ b/media/remoting/fake_media_resource.cc
@@ -28,9 +28,9 @@
     gfx::Size size(640, 480);
     gfx::Rect rect(0, 0, 640, 480);
     video_config_.Initialize(kCodecH264, H264PROFILE_BASELINE,
-                             PIXEL_FORMAT_I420, VideoColorSpace::REC601(),
-                             kNoTransformation, size, rect, size,
-                             std::vector<uint8_t>(), Unencrypted());
+                             VideoDecoderConfig::AlphaMode::kIsOpaque,
+                             VideoColorSpace::REC601(), kNoTransformation, size,
+                             rect, size, std::vector<uint8_t>(), Unencrypted());
   }
   ON_CALL(*this, Read(_))
       .WillByDefault(Invoke(this, &FakeDemuxerStream::FakeRead));
diff --git a/media/remoting/proto_enum_utils.cc b/media/remoting/proto_enum_utils.cc
index f592da04..6620bee 100644
--- a/media/remoting/proto_enum_utils.cc
+++ b/media/remoting/proto_enum_utils.cc
@@ -351,7 +351,6 @@
     CASE_RETURN_OTHER(PIXEL_FORMAT_ARGB);
     CASE_RETURN_OTHER(PIXEL_FORMAT_XRGB);
     CASE_RETURN_OTHER(PIXEL_FORMAT_RGB24);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_RGB32);
     CASE_RETURN_OTHER(PIXEL_FORMAT_MJPEG);
     CASE_RETURN_OTHER(PIXEL_FORMAT_MT21);
     CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P9);
@@ -363,7 +362,8 @@
     CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P12);
     CASE_RETURN_OTHER(PIXEL_FORMAT_YUV422P12);
     CASE_RETURN_OTHER(PIXEL_FORMAT_YUV444P12);
-    // PIXEL_FORMAT_Y8 is deprecated .
+    // PIXEL_FORMAT_RGB32 and PIXEL_FORMAT_Y8 are deprecated .
+    case pb::VideoDecoderConfig_Format_PIXEL_FORMAT_RGB32:
     case pb::VideoDecoderConfig_Format_PIXEL_FORMAT_Y8:
       return base::nullopt;
       CASE_RETURN_OTHER(PIXEL_FORMAT_Y16);
@@ -374,44 +374,6 @@
   return base::nullopt;  // Not a 'default' to ensure compile-time checks.
 }
 
-base::Optional<pb::VideoDecoderConfig::Format> ToProtoVideoDecoderConfigFormat(
-    VideoPixelFormat value) {
-  using OriginType = VideoPixelFormat;
-  using OtherType = pb::VideoDecoderConfig;
-  switch (value) {
-    CASE_RETURN_OTHER(PIXEL_FORMAT_UNKNOWN);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_I420);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YV12);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_I422);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_I420A);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_I444);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_NV12);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_NV21);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_UYVY);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUY2);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_ARGB);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_XRGB);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_RGB24);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_RGB32);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_MJPEG);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_MT21);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P9);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P10);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV422P9);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV422P10);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV444P9);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV444P10);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV420P12);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV422P12);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_YUV444P12);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_Y16);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_ABGR);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_XBGR);
-    CASE_RETURN_OTHER(PIXEL_FORMAT_P016LE);
-  }
-  return base::nullopt;  // Not a 'default' to ensure compile-time checks.
-}
-
 base::Optional<BufferingState> ToMediaBufferingState(
     pb::RendererClientOnBufferingStateChange::State value) {
   using OriginType = pb::RendererClientOnBufferingStateChange;
diff --git a/media/remoting/proto_enum_utils.h b/media/remoting/proto_enum_utils.h
index 878eade6..113cf9f 100644
--- a/media/remoting/proto_enum_utils.h
+++ b/media/remoting/proto_enum_utils.h
@@ -64,8 +64,6 @@
 
 base::Optional<VideoPixelFormat> ToMediaVideoPixelFormat(
     pb::VideoDecoderConfig::Format value);
-base::Optional<pb::VideoDecoderConfig::Format> ToProtoVideoDecoderConfigFormat(
-    VideoPixelFormat value);
 
 base::Optional<BufferingState> ToMediaBufferingState(
     pb::RendererClientOnBufferingStateChange::State value);
diff --git a/media/remoting/proto_utils.cc b/media/remoting/proto_utils.cc
index daac2b92..4fca7e8c9 100644
--- a/media/remoting/proto_utils.cc
+++ b/media/remoting/proto_utils.cc
@@ -308,8 +308,11 @@
       ToProtoVideoDecoderConfigCodec(video_config.codec()).value());
   video_message->set_profile(
       ToProtoVideoDecoderConfigProfile(video_config.profile()).value());
-  video_message->set_format(
-      ToProtoVideoDecoderConfigFormat(video_config.format()).value());
+  // TODO(dalecurtis): Remove |format| it's now unused.
+  video_message->set_format(video_config.alpha_mode() ==
+                                    VideoDecoderConfig::AlphaMode::kHasAlpha
+                                ? pb::VideoDecoderConfig::PIXEL_FORMAT_I420A
+                                : pb::VideoDecoderConfig::PIXEL_FORMAT_I420);
 
   // TODO(hubbe): Update proto to use color_space_info()
   if (video_config.color_space_info() == VideoColorSpace::JPEG()) {
@@ -376,8 +379,10 @@
   video_config->Initialize(
       ToMediaVideoCodec(video_message.codec()).value(),
       ToMediaVideoCodecProfile(video_message.profile()).value(),
-      ToMediaVideoPixelFormat(video_message.format()).value(), color_space,
-      kNoTransformation,
+      IsOpaque(ToMediaVideoPixelFormat(video_message.format()).value())
+          ? VideoDecoderConfig::AlphaMode::kIsOpaque
+          : VideoDecoderConfig::AlphaMode::kHasAlpha,
+      color_space, kNoTransformation,
       gfx::Size(video_message.coded_size().width(),
                 video_message.coded_size().height()),
       gfx::Rect(video_message.visible_rect().x(),
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 46decb6..2f58805 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -294,10 +294,10 @@
   DCHECK(PIXEL_FORMAT_ARGB == video_frame->format() ||
          PIXEL_FORMAT_XRGB == video_frame->format() ||
          PIXEL_FORMAT_RGB24 == video_frame->format() ||
-         PIXEL_FORMAT_RGB32 == video_frame->format() ||
+         PIXEL_FORMAT_ABGR == video_frame->format() ||
          PIXEL_FORMAT_NV12 == video_frame->format() ||
          PIXEL_FORMAT_UYVY == video_frame->format())
-      << "Format: " << (int)video_frame->format();
+      << "Format: " << VideoPixelFormatToString(video_frame->format());
 
   const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0);
   DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D ||
@@ -1054,7 +1054,6 @@
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
     case PIXEL_FORMAT_RGB24:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_MJPEG:
     case PIXEL_FORMAT_MT21:
     case PIXEL_FORMAT_ABGR:
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 3f9ebba..8a535f43 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -67,8 +67,8 @@
   switch (format) {
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_UYVY:
+    case PIXEL_FORMAT_ABGR:
       DCHECK_EQ(num_textures, 1);
       buffer_formats[0] = gfx::BufferFormat::RGBA_8888;
       switch (target) {
@@ -126,7 +126,6 @@
     case PIXEL_FORMAT_YUV422P12:
     case PIXEL_FORMAT_YUV444P12:
     case PIXEL_FORMAT_Y16:
-    case PIXEL_FORMAT_ABGR:
     case PIXEL_FORMAT_XBGR:
     case PIXEL_FORMAT_P016LE:
     case PIXEL_FORMAT_UNKNOWN:
diff --git a/media/video/gpu_memory_buffer_video_frame_pool.cc b/media/video/gpu_memory_buffer_video_frame_pool.cc
index 666953f..80d81ba 100644
--- a/media/video/gpu_memory_buffer_video_frame_pool.cc
+++ b/media/video/gpu_memory_buffer_video_frame_pool.cc
@@ -293,11 +293,11 @@
     case GpuVideoAcceleratorFactories::OutputFormat::UYVY:
       return PIXEL_FORMAT_UYVY;
     case GpuVideoAcceleratorFactories::OutputFormat::XR30:
+    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
       return PIXEL_FORMAT_ARGB;
     case GpuVideoAcceleratorFactories::OutputFormat::XB30:
     case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
-    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
-      return PIXEL_FORMAT_RGB32;
+      return PIXEL_FORMAT_ABGR;
     case GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED:
       NOTREACHED();
       break;
@@ -633,7 +633,6 @@
     case PIXEL_FORMAT_ARGB:
     case PIXEL_FORMAT_XRGB:
     case PIXEL_FORMAT_RGB24:
-    case PIXEL_FORMAT_RGB32:
     case PIXEL_FORMAT_MJPEG:
     case PIXEL_FORMAT_MT21:
     case PIXEL_FORMAT_YUV422P9:
diff --git a/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc b/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc
index ce68d79..fac4b284 100644
--- a/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc
+++ b/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc
@@ -408,7 +408,7 @@
   RunUntilIdle();
 
   EXPECT_NE(software_frame.get(), frame.get());
-  EXPECT_EQ(PIXEL_FORMAT_RGB32, frame->format());
+  EXPECT_EQ(PIXEL_FORMAT_ABGR, frame->format());
   EXPECT_EQ(1u, frame->NumTextures());
   EXPECT_EQ(1u, sii_->shared_image_count());
   EXPECT_TRUE(frame->metadata()->IsTrue(
@@ -426,7 +426,7 @@
   RunUntilIdle();
 
   EXPECT_NE(software_frame.get(), frame.get());
-  EXPECT_EQ(PIXEL_FORMAT_RGB32, frame->format());
+  EXPECT_EQ(PIXEL_FORMAT_ABGR, frame->format());
   EXPECT_EQ(1u, frame->NumTextures());
   EXPECT_EQ(1u, sii_->shared_image_count());
   EXPECT_TRUE(frame->metadata()->IsTrue(
@@ -653,7 +653,7 @@
   RunUntilIdle();
 
   EXPECT_NE(software_frame_1.get(), frame_1.get());
-  EXPECT_EQ(PIXEL_FORMAT_RGB32, frame_1->format());
+  EXPECT_EQ(PIXEL_FORMAT_ABGR, frame_1->format());
   EXPECT_EQ(1u, frame_1->NumTextures());
   EXPECT_EQ(1u, sii_->shared_image_count());
   EXPECT_TRUE(frame_1->metadata()->IsTrue(
diff --git a/media/video/supported_video_decoder_config.cc b/media/video/supported_video_decoder_config.cc
index 49730eb..b0fbb37a 100644
--- a/media/video/supported_video_decoder_config.cc
+++ b/media/video/supported_video_decoder_config.cc
@@ -52,7 +52,7 @@
 
 // static
 bool IsVideoDecoderConfigSupported(
-    const std::vector<SupportedVideoDecoderConfig>& supported_configs,
+    const SupportedVideoDecoderConfigs& supported_configs,
     const VideoDecoderConfig& config) {
   for (const auto& c : supported_configs) {
     if (c.Matches(config))
diff --git a/media/video/supported_video_decoder_config.h b/media/video/supported_video_decoder_config.h
index 48a0b02..0bd9342 100644
--- a/media/video/supported_video_decoder_config.h
+++ b/media/video/supported_video_decoder_config.h
@@ -5,6 +5,8 @@
 #ifndef MEDIA_VIDEO_SUPPORTED_VIDEO_DECODER_CONFIG_H_
 #define MEDIA_VIDEO_SUPPORTED_VIDEO_DECODER_CONFIG_H_
 
+#include <vector>
+
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "media/base/media_export.h"
@@ -57,15 +59,16 @@
   kMaxValue = kAlternate
 };
 
+using SupportedVideoDecoderConfigs = std::vector<SupportedVideoDecoderConfig>;
+
 // Map of mojo VideoDecoder implementations to the vector of configs that they
 // (probably) support.
 using SupportedVideoDecoderConfigMap =
-    base::flat_map<VideoDecoderImplementation,
-                   std::vector<SupportedVideoDecoderConfig>>;
+    base::flat_map<VideoDecoderImplementation, SupportedVideoDecoderConfigs>;
 
 // Helper method to determine if |config| is supported by |supported_configs|.
 MEDIA_EXPORT bool IsVideoDecoderConfigSupported(
-    const std::vector<SupportedVideoDecoderConfig>& supported_configs,
+    const SupportedVideoDecoderConfigs& supported_configs,
     const VideoDecoderConfig& config);
 
 }  // namespace media
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 99033f1..c73cc91 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -364,6 +364,7 @@
     "ssl/ssl_config_service.cc",
     "ssl/ssl_config_service.h",
     "ssl/ssl_connection_status_flags.h",
+    "ssl/ssl_handshake_details.h",
     "ssl/ssl_info.cc",
     "ssl/ssl_info.h",
     "ssl/ssl_key_logger.h",
@@ -5627,6 +5628,7 @@
     "url_request/url_request_file_dir_job_unittest.cc",
     "url_request/url_request_file_job_unittest.cc",
     "url_request/url_request_filter_unittest.cc",
+    "url_request/url_request_ftp_job_unittest.cc",
     "url_request/url_request_http_job_unittest.cc",
     "url_request/url_request_job_factory_impl_unittest.cc",
     "url_request/url_request_job_unittest.cc",
@@ -5943,6 +5945,7 @@
       "ftp/ftp_directory_listing_parser_windows_unittest.cc",
       "ftp/ftp_network_transaction_unittest.cc",
       "ftp/ftp_util_unittest.cc",
+      "url_request/url_request_ftp_job_unittest.cc",
     ]
   }
 
diff --git a/net/base/features.cc b/net/base/features.cc
index 0e66209..4cc6a52 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -31,6 +31,10 @@
     "PartitionConnectionsByNetworkIsolationKey",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kPartitionSSLSessionsByNetworkIsolationKey{
+    "PartitionSSLSessionsByNetworkIsolationKey",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kTLS13KeyUpdate{"TLS13KeyUpdate",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/net/base/features.h b/net/base/features.h
index 6a9526f..6e387b22 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -44,6 +44,14 @@
 NET_EXPORT extern const base::Feature
     kPartitionConnectionsByNetworkIsolationKey;
 
+// Partitions TLS sessions and QUIC server configs based on the
+// NetworkIsolationKey associated with a request.
+//
+// This feature requires kPartitionConnectionsByNetworkIsolationKey to be
+// enabled to work.
+NET_EXPORT extern const base::Feature
+    kPartitionSSLSessionsByNetworkIsolationKey;
+
 // Enables sending TLS 1.3 Key Update messages on TLS 1.3 connections in order
 // to ensure that this corner of the spec is exercised. This is currently
 // disabled by default because we discovered incompatibilities with some
diff --git a/net/base/network_change_notifier_posix.cc b/net/base/network_change_notifier_posix.cc
index adb09dc..d002c39 100644
--- a/net/base/network_change_notifier_posix.cc
+++ b/net/base/network_change_notifier_posix.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/sequenced_task_runner.h"
@@ -18,29 +19,6 @@
 
 namespace net {
 
-// DNS config services on Chrome OS and Android are signalled by the network
-// state handler rather than relying on watching files in /etc.
-class NetworkChangeNotifierPosix::DnsConfigService
-    : public net::internal::DnsConfigServicePosix {
- public:
-  DnsConfigService() = default;
-  ~DnsConfigService() override = default;
-
-  // net::internal::DnsConfigService() overrides.
-  bool StartWatching() override {
-    CreateReaders();
-    // DNS config changes are handled and notified by the network
-    // state handlers.
-    return true;
-  }
-
-  void OnNetworkChange() {
-    InvalidateConfig();
-    InvalidateHosts();
-    ReadNow();
-  }
-};
-
 NetworkChangeNotifierPosix::NetworkChangeNotifierPosix(
     NetworkChangeNotifier::ConnectionType initial_connection_type,
     NetworkChangeNotifier::ConnectionSubtype initial_connection_subtype)
@@ -48,7 +26,7 @@
       dns_config_service_runner_(
           base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
       dns_config_service_(
-          new DnsConfigService(),
+          nullptr,
           // Ensure DnsConfigService lives on |dns_config_service_runner_|
           // to prevent races where NetworkChangeNotifierPosix outlives
           // ScopedTaskEnvironment. https://crbug.com/938126
@@ -57,12 +35,7 @@
       max_bandwidth_mbps_(
           NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
               initial_connection_subtype)) {
-  dns_config_service_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &NetworkChangeNotifierPosix::DnsConfigService::WatchConfig,
-          base::Unretained(dns_config_service_.get()),
-          base::BindRepeating(&NetworkChangeNotifier::SetDnsConfig)));
+  SetAndStartDnsConfigService(DnsConfigService::CreateSystemService());
   OnDNSChanged();
 }
 
@@ -73,10 +46,8 @@
 void NetworkChangeNotifierPosix::OnDNSChanged() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   dns_config_service_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &NetworkChangeNotifierPosix::DnsConfigService::OnNetworkChange,
-          base::Unretained(dns_config_service_.get())));
+      FROM_HERE, base::BindOnce(&DnsConfigService::RefreshConfig,
+                                base::Unretained(dns_config_service_.get())));
 }
 
 void NetworkChangeNotifierPosix::OnIPAddressChanged() {
@@ -108,6 +79,11 @@
                                                              connection_type);
 }
 
+void NetworkChangeNotifierPosix::SetDnsConfigServiceForTesting(
+    std::unique_ptr<DnsConfigService> dns_config_service) {
+  SetAndStartDnsConfigService(std::move(dns_config_service));
+}
+
 NetworkChangeNotifier::ConnectionType
 NetworkChangeNotifierPosix::GetCurrentConnectionType() const {
   base::AutoLock scoped_lock(lock_);
@@ -122,6 +98,17 @@
   *max_bandwidth_mbps = max_bandwidth_mbps_;
 }
 
+void NetworkChangeNotifierPosix::SetAndStartDnsConfigService(
+    std::unique_ptr<DnsConfigService> dns_config_service) {
+  // Reset/release to use the deleter already set up in |dns_config_service_|.
+  dns_config_service_.reset(dns_config_service.release());
+  dns_config_service_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&DnsConfigService::WatchConfig,
+                                base::Unretained(dns_config_service_.get()),
+                                base::BindRepeating(
+                                    &NetworkChangeNotifier::SetDnsConfig)));
+}
+
 // static
 NetworkChangeNotifier::NetworkChangeCalculatorParams
 NetworkChangeNotifierPosix::NetworkChangeCalculatorParamsPosix() {
diff --git a/net/base/network_change_notifier_posix.h b/net/base/network_change_notifier_posix.h
index eefd15f..95cf9a2 100644
--- a/net/base/network_change_notifier_posix.h
+++ b/net/base/network_change_notifier_posix.h
@@ -25,6 +25,8 @@
 
 namespace net {
 
+class DnsConfigService;
+
 // A NetworkChangeNotifier that needs to be told about network changes by some
 // other object. This class can't directly listen for network changes because on
 // ChromeOS and Android only objects running in the browser process can listen
@@ -46,6 +48,10 @@
       NetworkChangeNotifier::ConnectionType connection_type,
       NetworkChangeNotifier::ConnectionSubtype connection_subtype);
 
+  // |dns_config_service| must support RefreshConfig().
+  void SetDnsConfigServiceForTesting(
+      std::unique_ptr<DnsConfigService> dns_config_service);
+
  protected:
   // NetworkChangeNotifier overrides.
   NetworkChangeNotifier::ConnectionType GetCurrentConnectionType()
@@ -57,7 +63,8 @@
  private:
   friend class NetworkChangeNotifierPosixTest;
 
-  class DnsConfigService;
+  void SetAndStartDnsConfigService(
+      std::unique_ptr<DnsConfigService> dns_config_service);
 
   // |dns_config_service_| will live on this runner.
   scoped_refptr<base::SequencedTaskRunner> dns_config_service_runner_;
diff --git a/net/base/network_change_notifier_posix_unittest.cc b/net/base/network_change_notifier_posix_unittest.cc
index c540877..0f5685ab 100644
--- a/net/base/network_change_notifier_posix_unittest.cc
+++ b/net/base/network_change_notifier_posix_unittest.cc
@@ -4,8 +4,11 @@
 
 #include "net/base/network_change_notifier_posix.h"
 
+#include <utility>
+
 #include "base/test/scoped_task_environment.h"
 #include "net/base/network_change_notifier.h"
+#include "net/dns/test_dns_config_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace net {
@@ -17,18 +20,24 @@
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
         notifier_(new NetworkChangeNotifierPosix(
             NetworkChangeNotifier::CONNECTION_UNKNOWN,
-            NetworkChangeNotifier::SUBTYPE_UNKNOWN)) {}
+            NetworkChangeNotifier::SUBTYPE_UNKNOWN)) {
+    auto dns_config_service = std::make_unique<TestDnsConfigService>();
+    dns_config_service_ = dns_config_service.get();
+    notifier_->SetDnsConfigServiceForTesting(std::move(dns_config_service));
+  }
 
   void FastForwardUntilIdle() {
     scoped_task_environment_.FastForwardUntilNoTasksRemain();
   }
 
   NetworkChangeNotifierPosix* notifier() { return notifier_.get(); }
+  TestDnsConfigService* dns_config_service() { return dns_config_service_; }
 
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   net::NetworkChangeNotifier::DisableForTest mock_notifier_disabler_;
   std::unique_ptr<NetworkChangeNotifierPosix> notifier_;
+  TestDnsConfigService* dns_config_service_;
 };
 
 class MockIPAddressObserver : public NetworkChangeNotifier::IPAddressObserver {
@@ -86,4 +95,17 @@
   NetworkChangeNotifier::RemoveMaxBandwidthObserver(&observer);
 }
 
+TEST_F(NetworkChangeNotifierPosixTest, OnDNSChanged) {
+  DnsConfig expected_config;
+  expected_config.nameservers = {IPEndPoint(IPAddress(1, 2, 3, 4), 233)};
+  dns_config_service()->SetConfigForRefresh(expected_config);
+
+  notifier()->OnDNSChanged();
+  FastForwardUntilIdle();
+
+  DnsConfig actual_config;
+  NetworkChangeNotifier::GetDnsConfig(&actual_config);
+  EXPECT_EQ(expected_config, actual_config);
+}
+
 }  // namespace net
diff --git a/net/base/network_isolation_key.h b/net/base/network_isolation_key.h
index be06053..a25fc1a 100644
--- a/net/base/network_isolation_key.h
+++ b/net/base/network_isolation_key.h
@@ -21,7 +21,7 @@
   // Full constructor.  When a request is initiated by the top frame, it must
   // also populate the initiating frame parameter when calling this constructor.
   explicit NetworkIsolationKey(
-      // TODO(crbug.com/963476): Make the arguments non-optional once all call
+      // TODO(crbug.com/950069): Make the arguments non-optional once all call
       // sites are updated to include the initiating_frame_origin.
       const base::Optional<url::Origin>& top_frame_origin,
       const base::Optional<url::Origin>& initiating_frame_origin =
@@ -76,13 +76,15 @@
   const base::Optional<url::Origin>& GetTopFrameOrigin() const {
     return top_frame_origin_;
   }
+
+  const base::Optional<url::Origin>& GetInitiatingFrameOrigin() const {
+    return initiating_frame_origin_;
+  }
+
   // Returns true if all parts of the key are empty.
   bool IsEmpty() const;
 
  private:
-  // TODO(crbug.com/963476): Add use_initiating_frame_origin_ and
-  // initiating_frame_origin_ to network_isolation_key_mojom_traits.h.
-
   // Whether or not to use the initiating frame origin as part of the key.
   bool use_initiating_frame_origin_;
 
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index 4b2fd725..8f8b6b83 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -492,39 +492,6 @@
   return PartialCookieOrdering(*this, other) < 0;
 }
 
-bool CanonicalCookie::FullCompare(const CanonicalCookie& other) const {
-  // Do the partial comparison first.
-  int diff = PartialCookieOrdering(*this, other);
-  if (diff != 0)
-    return diff < 0;
-
-  DCHECK(IsEquivalent(other));
-
-  // Compare other fields.
-  diff = Value().compare(other.Value());
-  if (diff != 0)
-    return diff < 0;
-
-  if (CreationDate() != other.CreationDate())
-    return CreationDate() < other.CreationDate();
-
-  if (ExpiryDate() != other.ExpiryDate())
-    return ExpiryDate() < other.ExpiryDate();
-
-  if (LastAccessDate() != other.LastAccessDate())
-    return LastAccessDate() < other.LastAccessDate();
-
-  if (IsSecure() != other.IsSecure())
-    return IsSecure();
-
-  if (IsHttpOnly() != other.IsHttpOnly())
-    return IsHttpOnly();
-
-  // TODO(chlily): This should also compare the SameSite attribute.
-
-  return Priority() < other.Priority();
-}
-
 bool CanonicalCookie::IsCanonical() const {
   // Not checking domain or path against ParsedCookie as it may have
   // come purely from the URL.
diff --git a/net/cookies/canonical_cookie.h b/net/cookies/canonical_cookie.h
index 71f61bf..894282176 100644
--- a/net/cookies/canonical_cookie.h
+++ b/net/cookies/canonical_cookie.h
@@ -221,13 +221,6 @@
   // are identical for PartialCompare().
   bool PartialCompare(const CanonicalCookie& other) const;
 
-  // TODO(chlily): Remove this. There should not be multiple cookies for which
-  // PartialCompare disagrees. This is only used in tests.
-  // Returns true if the cookie is less than |other|, considering all fields.
-  // FullCompare() is consistent with PartialCompare(): cookies sorted using
-  // FullCompare() are also sorted with respect to PartialCompare().
-  bool FullCompare(const CanonicalCookie& other) const;
-
   // Return whether this object is a valid CanonicalCookie().  Invalid
   // cookies may be constructed by the detailed constructor.
   // A cookie is considered canonical if-and-only-if:
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index 3f3c57c..d346658f 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -784,42 +784,6 @@
   EXPECT_TRUE(cookie->IsEquivalent(*cookie));
 }
 
-TEST(CanonicalCookieTest, FullCompare) {
-  GURL url("http://www.example.com");
-  base::Time creation_time = base::Time::Now();
-  CookieOptions options;
-  std::unique_ptr<CanonicalCookie> cookie(
-      CanonicalCookie::Create(url, "a=b", creation_time, options));
-  std::unique_ptr<CanonicalCookie> cookie_different_path(
-      CanonicalCookie::Create(url, "a=b; path=/foo", creation_time, options));
-  std::unique_ptr<CanonicalCookie> cookie_different_value(
-      CanonicalCookie::Create(url, "a=c", creation_time, options));
-
-  // Cookie is equivalent to itself.
-  EXPECT_FALSE(cookie->FullCompare(*cookie));
-
-  // Changing the path affects the ordering.
-  EXPECT_TRUE(cookie->FullCompare(*cookie_different_path));
-  EXPECT_FALSE(cookie_different_path->FullCompare(*cookie));
-
-  // Changing the value affects the ordering.
-  EXPECT_TRUE(cookie->FullCompare(*cookie_different_value));
-  EXPECT_FALSE(cookie_different_value->FullCompare(*cookie));
-
-  // FullCompare() implies PartialCompare().
-  auto check_consistency =
-      [](const CanonicalCookie& a, const CanonicalCookie& b) {
-        if (a.FullCompare(b))
-          EXPECT_FALSE(b.PartialCompare(a));
-        else if (b.FullCompare(a))
-          EXPECT_FALSE(a.PartialCompare(b));
-      };
-
-  check_consistency(*cookie, *cookie_different_path);
-  check_consistency(*cookie, *cookie_different_value);
-  check_consistency(*cookie_different_path, *cookie_different_value);
-}
-
 TEST(CanonicalCookieTest, SecureCookiePrefix) {
   GURL https_url("https://www.example.test");
   GURL http_url("http://www.example.test");
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 9b7dbbe..85ca69f 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -126,6 +126,10 @@
   static const size_t kDomainCookiesQuotaMedium;
   static const size_t kDomainCookiesQuotaHigh;
 
+  // The number of days since last access that cookies will not be subject
+  // to global garbage collection.
+  static const int kSafeFromGlobalPurgeDays;
+
   // The store passed in should not have had Init() called on it yet. This
   // class will take care of initializing it. The backing store is NOT owned by
   // this class, but it must remain valid for the duration of the cookie
@@ -202,7 +206,6 @@
  private:
   // For garbage collection constants.
   FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestHostGarbageCollection);
-  FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, GarbageCollectionTriggers);
   FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest,
                            GarbageCollectWithSecureCookiesOnly);
   FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestGCTimes);
@@ -336,10 +339,6 @@
     COOKIE_DELETE_EQUIVALENT_LAST_ENTRY
   };
 
-  // The number of days since last access that cookies will not be subject
-  // to global garbage collection.
-  static const int kSafeFromGlobalPurgeDays;
-
   // Record statistics every kRecordStatisticsIntervalSeconds of uptime.
   static const int kRecordStatisticsIntervalSeconds = 10 * 60;
 
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 88505a3..2d7510c 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -806,8 +806,16 @@
   // no store (and hence no ability to affect access time).
   CookieMonster* CreateMonsterForGC(int num_cookies) {
     CookieMonster* cm(new CookieMonster(nullptr, &net_log_));
+    base::Time creation_time = base::Time::Now();
     for (int i = 0; i < num_cookies; i++) {
-      SetCookie(cm, GURL(base::StringPrintf("http://h%05d.izzle", i)), "a=1");
+      std::unique_ptr<CanonicalCookie> cc(std::make_unique<CanonicalCookie>(
+          "a", "1", base::StringPrintf("h%05d.izzle", i), "/" /* path */,
+          creation_time, base::Time() /* expiration_time */,
+          creation_time /* last_access */, false /* secure */,
+          false /* http_only */, CookieSameSite::NO_RESTRICTION,
+          COOKIE_PRIORITY_DEFAULT));
+      cm->SetCanonicalCookieAsync(std::move(cc), "http", CookieOptions(),
+                                  CookieStore::SetCookiesCallback());
     }
     return cm;
   }
@@ -2084,75 +2092,78 @@
   }
 }
 
-// This test and CookieMonstertest.TestGCTimes (in cookie_monster_perftest.cc)
-// are somewhat complementary twins.  This test is probing for whether
-// garbage collection always happens when it should (i.e. that we actually
-// get rid of cookies when we should).  The perftest is probing for
+// These garbage collection tests and CookieMonstertest.TestGCTimes (in
+// cookie_monster_perftest.cc) are somewhat complementary.  These tests probe
+// for whether garbage collection always happens when it should (i.e. that we
+// actually get rid of cookies when we should).  The perftest is probing for
 // whether garbage collection happens when it shouldn't.  See comments
 // before that test for more details.
 
-// Disabled on Windows, see crbug.com/126095
-#if defined(OS_WIN)
-#define MAYBE_GarbageCollectionTriggers DISABLED_GarbageCollectionTriggers
-#else
-#define MAYBE_GarbageCollectionTriggers GarbageCollectionTriggers
-#endif
+// Check to make sure that a whole lot of recent cookies doesn't get rid of
+// anything after garbage collection is checked for.
+TEST_F(CookieMonsterTest, GarbageCollectionKeepsRecentEphemeralCookies) {
+  std::unique_ptr<CookieMonster> cm(
+      CreateMonsterForGC(CookieMonster::kMaxCookies * 2 /* num_cookies */));
+  EXPECT_EQ(CookieMonster::kMaxCookies * 2, GetAllCookies(cm.get()).size());
+  // Will trigger GC.
+  SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2");
+  EXPECT_EQ(CookieMonster::kMaxCookies * 2 + 1, GetAllCookies(cm.get()).size());
+}
 
-TEST_F(CookieMonsterTest, MAYBE_GarbageCollectionTriggers) {
-  // First we check to make sure that a whole lot of recent cookies
-  // doesn't get rid of anything after garbage collection is checked for.
-  {
-    std::unique_ptr<CookieMonster> cm(
-        CreateMonsterForGC(CookieMonster::kMaxCookies * 2));
-    EXPECT_EQ(CookieMonster::kMaxCookies * 2, GetAllCookies(cm.get()).size());
-    SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2");
-    EXPECT_EQ(CookieMonster::kMaxCookies * 2 + 1,
-              GetAllCookies(cm.get()).size());
-  }
+// A whole lot of recent cookies; GC shouldn't happen.
+TEST_F(CookieMonsterTest, GarbageCollectionKeepsRecentCookies) {
+  std::unique_ptr<CookieMonster> cm = CreateMonsterFromStoreForGC(
+      CookieMonster::kMaxCookies * 2 /* num_cookies */, 0 /* num_old_cookies */,
+      0, 0, CookieMonster::kSafeFromGlobalPurgeDays * 2);
+  EXPECT_EQ(CookieMonster::kMaxCookies * 2, GetAllCookies(cm.get()).size());
+  // Will trigger GC.
+  SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2");
+  EXPECT_EQ(CookieMonster::kMaxCookies * 2 + 1, GetAllCookies(cm.get()).size());
+}
 
-  // Now we explore a series of relationships between cookie last access
-  // time and size of store to make sure we only get rid of cookies when
-  // we really should.
-  const struct TestCase {
-    size_t num_cookies;
-    size_t num_old_cookies;
-    size_t expected_initial_cookies;
-    // Indexed by ExpiryAndKeyScheme
-    size_t expected_cookies_after_set;
-  } test_cases[] = {
-      {// A whole lot of recent cookies; gc shouldn't happen.
-       CookieMonster::kMaxCookies * 2,
-       0,
-       CookieMonster::kMaxCookies * 2,
-       CookieMonster::kMaxCookies * 2 + 1},
-      {// Some old cookies, but still overflowing max.
-       CookieMonster::kMaxCookies * 2,
-       CookieMonster::kMaxCookies / 2,
-       CookieMonster::kMaxCookies * 2,
-       CookieMonster::kMaxCookies * 2 - CookieMonster::kMaxCookies / 2 + 1},
-      {// Old cookies enough to bring us right down to our purge line.
-       CookieMonster::kMaxCookies * 2,
-       CookieMonster::kMaxCookies + CookieMonster::kPurgeCookies + 1,
-       CookieMonster::kMaxCookies * 2,
-       CookieMonster::kMaxCookies - CookieMonster::kPurgeCookies},
-      {// Old cookies enough to bring below our purge line (which we
-       // shouldn't do).
-       CookieMonster::kMaxCookies * 2,
-       CookieMonster::kMaxCookies * 3 / 2,
-       CookieMonster::kMaxCookies * 2,
-       CookieMonster::kMaxCookies - CookieMonster::kPurgeCookies}};
+// Test case where there are more than kMaxCookies - kPurgeCookies recent
+// cookies. All old cookies should be garbage collected, all recent cookies
+// kept.
+TEST_F(CookieMonsterTest, GarbageCollectionKeepsOnlyRecentCookies) {
+  std::unique_ptr<CookieMonster> cm = CreateMonsterFromStoreForGC(
+      CookieMonster::kMaxCookies * 2 /* num_cookies */,
+      CookieMonster::kMaxCookies / 2 /* num_old_cookies */, 0, 0,
+      CookieMonster::kSafeFromGlobalPurgeDays * 2);
+  EXPECT_EQ(CookieMonster::kMaxCookies * 2, GetAllCookies(cm.get()).size());
+  // Will trigger GC.
+  SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2");
+  EXPECT_EQ(CookieMonster::kMaxCookies * 2 - CookieMonster::kMaxCookies / 2 + 1,
+            GetAllCookies(cm.get()).size());
+}
 
-  for (const auto& test_case : test_cases) {
-    std::unique_ptr<CookieMonster> cm = CreateMonsterFromStoreForGC(
-        test_case.num_cookies, test_case.num_old_cookies, 0, 0,
-        CookieMonster::kSafeFromGlobalPurgeDays * 2);
-    EXPECT_EQ(test_case.expected_initial_cookies,
-              GetAllCookies(cm.get()).size());
-    // Will trigger GC
-    SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2");
-    EXPECT_EQ(test_case.expected_cookies_after_set,
-              GetAllCookies(cm.get()).size());
-  }
+// Test case where there are exactly kMaxCookies - kPurgeCookies recent cookies.
+// All old cookies should be deleted.
+TEST_F(CookieMonsterTest, GarbageCollectionExactlyAllOldCookiesDeleted) {
+  std::unique_ptr<CookieMonster> cm = CreateMonsterFromStoreForGC(
+      CookieMonster::kMaxCookies * 2 /* num_cookies */,
+      CookieMonster::kMaxCookies + CookieMonster::kPurgeCookies +
+          1 /* num_old_cookies */,
+      0, 0, CookieMonster::kSafeFromGlobalPurgeDays * 2);
+  EXPECT_EQ(CookieMonster::kMaxCookies * 2, GetAllCookies(cm.get()).size());
+  // Will trigger GC.
+  SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2");
+  EXPECT_EQ(CookieMonster::kMaxCookies - CookieMonster::kPurgeCookies,
+            GetAllCookies(cm.get()).size());
+}
+
+// Test case where there are less than kMaxCookies - kPurgeCookies recent
+// cookies. Enough old cookies should be deleted to reach kMaxCookies -
+// kPurgeCookies total cookies, but no more. Some old cookies should be kept.
+TEST_F(CookieMonsterTest, GarbageCollectionTriggers5) {
+  std::unique_ptr<CookieMonster> cm = CreateMonsterFromStoreForGC(
+      CookieMonster::kMaxCookies * 2 /* num_cookies */,
+      CookieMonster::kMaxCookies * 3 / 2 /* num_old_cookies */, 0, 0,
+      CookieMonster::kSafeFromGlobalPurgeDays * 2);
+  EXPECT_EQ(CookieMonster::kMaxCookies * 2, GetAllCookies(cm.get()).size());
+  // Will trigger GC.
+  SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2");
+  EXPECT_EQ(CookieMonster::kMaxCookies - CookieMonster::kPurgeCookies,
+            GetAllCookies(cm.get()).size());
 }
 
 // Tests garbage collection when there are only secure cookies.
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc
index 2778a89..1d7d0cc5 100644
--- a/net/dns/dns_config_service.cc
+++ b/net/dns/dns_config_service.cc
@@ -41,6 +41,11 @@
   ReadNow();
 }
 
+void DnsConfigService::RefreshConfig() {
+  // Overridden on supported platforms.
+  NOTREACHED();
+}
+
 void DnsConfigService::InvalidateConfig() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::TimeTicks now = base::TimeTicks::Now();
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index 97a211ee..cc5d1bcc 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -45,6 +45,11 @@
   // Might require MessageLoopForIO.
   void WatchConfig(const CallbackType& callback);
 
+  // Triggers invalidation and re-read of the current configuration (followed by
+  // invocation of the callback). For use only on platforms expecting
+  // network-stack-external notifications of DNS config changes.
+  virtual void RefreshConfig();
+
  protected:
   enum WatchStatus {
     DNS_CONFIG_WATCH_STARTED = 0,
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index 48ee925..21852fe 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -412,11 +412,25 @@
   hosts_reader_->Cancel();
 }
 
+void DnsConfigServicePosix::RefreshConfig() {
+  InvalidateConfig();
+  InvalidateHosts();
+  ReadNow();
+}
+
 void DnsConfigServicePosix::ReadNow() {
   config_reader_->WorkNow();
   hosts_reader_->WorkNow();
 }
 
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+bool DnsConfigServicePosix::StartWatching() {
+  CreateReaders();
+  // DNS config changes are handled and notified by the network
+  // state handlers.
+  return true;
+}
+#else   // defined(OS_ANDROID) || defined(OS_CHROMEOS)
 bool DnsConfigServicePosix::StartWatching() {
   CreateReaders();
   // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
@@ -425,6 +439,7 @@
                             DNS_CONFIG_WATCH_MAX);
   return watcher_->Watch();
 }
+#endif  // defined(OS_ANDROID) || defined(OS_CHROMEOS)
 
 void DnsConfigServicePosix::OnConfigChanged(bool succeeded) {
   InvalidateConfig();
diff --git a/net/dns/dns_config_service_posix.h b/net/dns/dns_config_service_posix.h
index 9a8e1ba..342b108d0 100644
--- a/net/dns/dns_config_service_posix.h
+++ b/net/dns/dns_config_service_posix.h
@@ -5,6 +5,8 @@
 #ifndef NET_DNS_DNS_CONFIG_SERVICE_POSIX_H_
 #define NET_DNS_DNS_CONFIG_SERVICE_POSIX_H_
 
+#include <memory>
+
 #if !defined(OS_ANDROID)
 #include <sys/types.h>
 #include <netinet/in.h>
@@ -34,6 +36,8 @@
   DnsConfigServicePosix();
   ~DnsConfigServicePosix() override;
 
+  void RefreshConfig() override;
+
  protected:
   // DnsConfigService:
   void ReadNow() override;
diff --git a/net/dns/dns_config_service_posix_unittest.cc b/net/dns/dns_config_service_posix_unittest.cc
index 346fe53..c8d09d96 100644
--- a/net/dns/dns_config_service_posix_unittest.cc
+++ b/net/dns/dns_config_service_posix_unittest.cc
@@ -256,7 +256,7 @@
   scoped_task_environment_.RunUntilIdle();
 
   for (int i = 0; i < 5; i++) {
-    service_->OnConfigChanged(true);
+    service_->RefreshConfig();
     // Wait for config read after the change. OnConfigChanged() will only be
     // called if the new config is different from the old one, so this can't be
     // ExpectChange().
diff --git a/net/dns/system_dns_config_change_notifier.cc b/net/dns/system_dns_config_change_notifier.cc
index f827573..a75e24c 100644
--- a/net/dns/system_dns_config_change_notifier.cc
+++ b/net/dns/system_dns_config_change_notifier.cc
@@ -118,6 +118,12 @@
     }
   }
 
+  void RefreshConfig() {
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&Core::TriggerRefreshConfig,
+                                          weak_ptr_factory_.GetWeakPtr()));
+  }
+
  private:
   void StartWatching() {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -147,6 +153,11 @@
     }
   }
 
+  void TriggerRefreshConfig() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    dns_config_service_->RefreshConfig();
+  }
+
   // Fields that may be accessed from any sequence. Must protect access using
   // |lock_|.
   mutable base::Lock lock_;
@@ -185,4 +196,8 @@
   core_->RemoveObserver(observer);
 }
 
+void SystemDnsConfigChangeNotifier::RefreshConfig() {
+  core_->RefreshConfig();
+}
+
 }  // namespace net
diff --git a/net/dns/system_dns_config_change_notifier.h b/net/dns/system_dns_config_change_notifier.h
index 01f3d70..ddd0907 100644
--- a/net/dns/system_dns_config_change_notifier.h
+++ b/net/dns/system_dns_config_change_notifier.h
@@ -62,6 +62,11 @@
   // AddObserver() was called.
   void RemoveObserver(Observer* observer);
 
+  // Triggers invalidation and re-read of the current configuration (followed by
+  // notifications to registered Observers). For use only on platforms
+  // expecting network-stack-external notifications of DNS config changes.
+  void RefreshConfig();
+
  private:
   class Core;
 
diff --git a/net/dns/system_dns_config_change_notifier_unittest.cc b/net/dns/system_dns_config_change_notifier_unittest.cc
index e94007a..9c3c705 100644
--- a/net/dns/system_dns_config_change_notifier_unittest.cc
+++ b/net/dns/system_dns_config_change_notifier_unittest.cc
@@ -308,4 +308,20 @@
   notifier_->RemoveObserver(&observer);
 }
 
+TEST_F(SystemDnsConfigChangeNotifierTest, RefreshConfig) {
+  test_config_service_->SetConfigForRefresh(kConfig);
+
+  TestObserver observer;
+  notifier_->AddObserver(&observer);
+
+  notifier_->RefreshConfig();
+  observer.WaitForNotification();
+
+  EXPECT_THAT(observer.configs_received(),
+              testing::ElementsAre(testing::Optional(kConfig)));
+  observer.ExpectNoMoreNotifications();
+
+  notifier_->RemoveObserver(&observer);
+}
+
 }  // namespace net
diff --git a/net/dns/test_dns_config_service.cc b/net/dns/test_dns_config_service.cc
index 1f3e3d6b..af928627 100644
--- a/net/dns/test_dns_config_service.cc
+++ b/net/dns/test_dns_config_service.cc
@@ -6,8 +6,21 @@
 
 namespace net {
 
+TestDnsConfigService::TestDnsConfigService() = default;
+
+TestDnsConfigService::~TestDnsConfigService() = default;
+
 bool TestDnsConfigService::StartWatching() {
   return true;
 }
 
+void TestDnsConfigService::RefreshConfig() {
+  DCHECK(config_for_refresh_);
+  InvalidateConfig();
+  InvalidateHosts();
+  OnConfigRead(config_for_refresh_.value());
+  OnHostsRead(config_for_refresh_.value().hosts);
+  config_for_refresh_ = base::nullopt;
+}
+
 }  // namespace net
diff --git a/net/dns/test_dns_config_service.h b/net/dns/test_dns_config_service.h
index 4f4d32c..3b61c36 100644
--- a/net/dns/test_dns_config_service.h
+++ b/net/dns/test_dns_config_service.h
@@ -2,17 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "net/dns/dns_config_service.h"
-
 #ifndef NET_DNS_TEST_DNS_CONFIG_SERVICE_H_
 #define NET_DNS_TEST_DNS_CONFIG_SERVICE_H_
 
+#include <utility>
+
+#include "base/logging.h"
+#include "base/optional.h"
+#include "net/dns/dns_config_service.h"
+
 namespace net {
 
 // Simple test implementation of DnsConfigService that will trigger
 // notifications only on explicitly calling On...() methods.
 class TestDnsConfigService : public DnsConfigService {
  public:
+  TestDnsConfigService();
+  ~TestDnsConfigService() override;
+
   void ReadNow() override {}
   bool StartWatching() override;
 
@@ -32,6 +39,16 @@
   void set_watch_failed(bool value) {
     DnsConfigService::set_watch_failed(value);
   }
+
+  void RefreshConfig() override;
+
+  void SetConfigForRefresh(DnsConfig config) {
+    DCHECK(!config_for_refresh_);
+    config_for_refresh_ = std::move(config);
+  }
+
+ private:
+  base::Optional<DnsConfig> config_for_refresh_;
 };
 
 }  // namespace net
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc
index ba30a4d..07411b8 100644
--- a/net/ftp/ftp_network_transaction.cc
+++ b/net/ftp/ftp_network_transaction.cc
@@ -10,7 +10,6 @@
 #include "base/bind_helpers.h"
 #include "base/callback_helpers.h"
 #include "base/compiler_specific.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
diff --git a/net/ftp/ftp_response_info.h b/net/ftp/ftp_response_info.h
index d20249a0..576ef25 100644
--- a/net/ftp/ftp_response_info.h
+++ b/net/ftp/ftp_response_info.h
@@ -9,10 +9,11 @@
 
 #include "base/time/time.h"
 #include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
 
 namespace net {
 
-class FtpResponseInfo {
+class NET_EXPORT_PRIVATE FtpResponseInfo {
  public:
   FtpResponseInfo();
   ~FtpResponseInfo();
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 7de86957..388842c8 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -21235,4 +21235,220 @@
   }
 }
 
+// Test that the NetworkIsolationKey is passed down to SSLConfig so the session
+// cache is isolated.
+TEST_F(HttpNetworkTransactionTest, NetworkIsolationSSL) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      {features::kPartitionConnectionsByNetworkIsolationKey,
+       features::kPartitionSSLSessionsByNetworkIsolationKey},
+      {});
+
+  const NetworkIsolationKey kNetworkIsolationKey1(
+      url::Origin::Create(GURL("http://origin1/")));
+  const NetworkIsolationKey kNetworkIsolationKey2(
+      url::Origin::Create(GURL("http://origin2/")));
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  // The server always sends Connection: close, so each request goes over a
+  // distinct socket.
+
+  const MockWrite kWrites1[] = {
+      MockWrite("GET /1 HTTP/1.1\r\n"
+                "Host: foo.test\r\n"
+                "Connection: keep-alive\r\n\r\n")};
+
+  const MockRead kReads1[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Connection: close\r\n"
+               "Content-Length: 1\r\n\r\n"
+               "1")};
+
+  const MockWrite kWrites2[] = {
+      MockWrite("GET /2 HTTP/1.1\r\n"
+                "Host: foo.test\r\n"
+                "Connection: keep-alive\r\n\r\n")};
+
+  const MockRead kReads2[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Connection: close\r\n"
+               "Content-Length: 1\r\n\r\n"
+               "2")};
+
+  const MockWrite kWrites3[] = {
+      MockWrite("GET /3 HTTP/1.1\r\n"
+                "Host: foo.test\r\n"
+                "Connection: keep-alive\r\n\r\n")};
+
+  const MockRead kReads3[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Connection: close\r\n"
+               "Content-Length: 1\r\n\r\n"
+               "3")};
+
+  StaticSocketDataProvider data1(kReads1, kWrites1);
+  StaticSocketDataProvider data2(kReads2, kWrites2);
+  StaticSocketDataProvider data3(kReads3, kWrites3);
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+  session_deps_.socket_factory->AddSocketDataProvider(&data3);
+
+  SSLSocketDataProvider ssl_data1(ASYNC, OK);
+  ssl_data1.expected_host_and_port = HostPortPair("foo.test", 443);
+  ssl_data1.expected_network_isolation_key = kNetworkIsolationKey1;
+  SSLSocketDataProvider ssl_data2(ASYNC, OK);
+  ssl_data2.expected_host_and_port = HostPortPair("foo.test", 443);
+  ssl_data2.expected_network_isolation_key = kNetworkIsolationKey2;
+  SSLSocketDataProvider ssl_data3(ASYNC, OK);
+  ssl_data3.expected_host_and_port = HostPortPair("foo.test", 443);
+  ssl_data3.expected_network_isolation_key = kNetworkIsolationKey1;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data1);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data2);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data3);
+
+  TestCompletionCallback callback;
+  HttpRequestInfo request1;
+  request1.method = "GET";
+  request1.url = GURL("https://foo.test/1");
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  request1.network_isolation_key = kNetworkIsolationKey1;
+  auto trans1 =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  int rv = trans1->Start(&request1, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  std::string response_data1;
+  EXPECT_THAT(ReadTransaction(trans1.get(), &response_data1), IsOk());
+  EXPECT_EQ("1", response_data1);
+  trans1.reset();
+
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL("https://foo.test/2");
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  request2.network_isolation_key = kNetworkIsolationKey2;
+  auto trans2 =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  rv = trans2->Start(&request2, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  std::string response_data2;
+  EXPECT_THAT(ReadTransaction(trans2.get(), &response_data2), IsOk());
+  EXPECT_EQ("2", response_data2);
+  trans2.reset();
+
+  HttpRequestInfo request3;
+  request3.method = "GET";
+  request3.url = GURL("https://foo.test/3");
+  request3.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  request3.network_isolation_key = kNetworkIsolationKey1;
+  auto trans3 =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  rv = trans3->Start(&request3, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  std::string response_data3;
+  EXPECT_THAT(ReadTransaction(trans3.get(), &response_data3), IsOk());
+  EXPECT_EQ("3", response_data3);
+  trans3.reset();
+}
+
+// Test that the NetworkIsolationKey is passed down to SSLConfig so the session
+// cache is isolated, for both origins and proxies.
+TEST_F(HttpNetworkTransactionTest, NetworkIsolationSSLProxy) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      {features::kPartitionConnectionsByNetworkIsolationKey,
+       features::kPartitionSSLSessionsByNetworkIsolationKey},
+      {});
+
+  session_deps_.proxy_resolution_service = ProxyResolutionService::CreateFixed(
+      "https://myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  const NetworkIsolationKey kNetworkIsolationKey1(
+      url::Origin::Create(GURL("http://origin1/")));
+  const NetworkIsolationKey kNetworkIsolationKey2(
+      url::Origin::Create(GURL("http://origin2/")));
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  // Make both a tunneled and non-tunneled request.
+  HttpRequestInfo request1;
+  request1.method = "GET";
+  request1.url = GURL("https://foo.test/1");
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  request1.network_isolation_key = kNetworkIsolationKey1;
+
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL("http://foo.test/2");
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  request2.network_isolation_key = kNetworkIsolationKey2;
+
+  const MockWrite kWrites1[] = {
+      MockWrite("CONNECT foo.test:443 HTTP/1.1\r\n"
+                "Host: foo.test:443\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+      MockWrite("GET /1 HTTP/1.1\r\n"
+                "Host: foo.test\r\n"
+                "Connection: keep-alive\r\n\r\n")};
+
+  const MockRead kReads1[] = {
+      MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Connection: close\r\n"
+               "Content-Length: 1\r\n\r\n"
+               "1")};
+
+  const MockWrite kWrites2[] = {
+      MockWrite("GET http://foo.test/2 HTTP/1.1\r\n"
+                "Host: foo.test\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n")};
+
+  const MockRead kReads2[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Connection: close\r\n"
+               "Content-Length: 1\r\n\r\n"
+               "2")};
+
+  StaticSocketDataProvider data1(kReads1, kWrites1);
+  StaticSocketDataProvider data2(kReads2, kWrites2);
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+
+  SSLSocketDataProvider ssl_proxy1(ASYNC, OK);
+  ssl_proxy1.expected_host_and_port = HostPortPair("myproxy", 70);
+  ssl_proxy1.expected_network_isolation_key = kNetworkIsolationKey1;
+  SSLSocketDataProvider ssl_origin1(ASYNC, OK);
+  ssl_origin1.expected_host_and_port = HostPortPair("foo.test", 443);
+  ssl_origin1.expected_network_isolation_key = kNetworkIsolationKey1;
+  SSLSocketDataProvider ssl_proxy2(ASYNC, OK);
+  ssl_proxy2.expected_host_and_port = HostPortPair("myproxy", 70);
+  ssl_proxy2.expected_network_isolation_key = kNetworkIsolationKey2;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_proxy1);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_origin1);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_proxy2);
+
+  TestCompletionCallback callback;
+  auto trans1 =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  int rv = trans1->Start(&request1, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  std::string response_data1;
+  EXPECT_THAT(ReadTransaction(trans1.get(), &response_data1), IsOk());
+  EXPECT_EQ("1", response_data1);
+  trans1.reset();
+
+  auto trans2 =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  rv = trans2->Start(&request2, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  std::string response_data2;
+  EXPECT_THAT(ReadTransaction(trans2.get(), &response_data2), IsOk());
+  EXPECT_EQ("2", response_data2);
+  trans2.reset();
+}
+
 }  // namespace net
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc
index e436396c..bc41834 100644
--- a/net/http/http_proxy_connect_job.cc
+++ b/net/http/http_proxy_connect_job.cc
@@ -662,7 +662,7 @@
   quic::ParsedQuicVersion quic_version =
       common_connect_job_params()->quic_supported_versions->front();
   return quic_stream_request_->Request(
-      proxy_server, quic_version.transport_version, ssl_params->privacy_mode(),
+      proxy_server, quic_version, ssl_params->privacy_mode(),
       kH2QuicTunnelPriority, socket_tag(), params_->network_isolation_key(),
       ssl_params->ssl_config().GetCertVerifyFlags(),
       GURL("https://" + proxy_server.ToString()), net_log(),
diff --git a/net/http/http_proxy_connect_job_unittest.cc b/net/http/http_proxy_connect_job_unittest.cc
index f555b9f1..560525e 100644
--- a/net/http/http_proxy_connect_job_unittest.cc
+++ b/net/http/http_proxy_connect_job_unittest.cc
@@ -125,7 +125,7 @@
         base::MakeRefCounted<TransportSocketParams>(
             HostPortPair(kHttpsProxyHost, 443), OnHostResolutionCallback()),
         nullptr, nullptr, HostPortPair(kHttpsProxyHost, 443), SSLConfig(),
-        PRIVACY_MODE_DISABLED);
+        PRIVACY_MODE_DISABLED, NetworkIsolationKey());
   }
 
   // Returns a correctly constructed HttpProxyParams for the HTTP or HTTPS
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index 7b32a4934..8fded9b 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -773,10 +773,9 @@
       ssl_config = &server_ssl_config_;
     }
     int rv = quic_request_.Request(
-        destination, quic_version_.transport_version,
-        request_info_.privacy_mode, priority_, request_info_.socket_tag,
-        request_info_.network_isolation_key, ssl_config->GetCertVerifyFlags(),
-        url, net_log_, &net_error_details_,
+        destination, quic_version_, request_info_.privacy_mode, priority_,
+        request_info_.socket_tag, request_info_.network_isolation_key,
+        ssl_config->GetCertVerifyFlags(), url, net_log_, &net_error_details_,
         base::BindOnce(&Job::OnFailedOnDefaultNetwork,
                        ptr_factory_.GetWeakPtr()),
         io_callback_);
diff --git a/net/nqe/socket_watcher.cc b/net/nqe/socket_watcher.cc
index 1c559e7..b0daf8e 100644
--- a/net/nqe/socket_watcher.cc
+++ b/net/nqe/socket_watcher.cc
@@ -106,7 +106,10 @@
 void SocketWatcher::OnUpdatedRTTAvailable(const base::TimeDelta& rtt) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (rtt <= base::TimeDelta())
+  // tcp_socket_posix may sometimes report RTT as 1 microsecond when the RTT was
+  // actually invalid. See:
+  // https://cs.chromium.org/chromium/src/net/socket/tcp_socket_posix.cc?rcl=7ad660e34f2a996e381a85b2a515263003b0c171&l=106.
+  if (rtt <= base::TimeDelta::FromMicroseconds(1))
     return;
 
   if (!first_quic_rtt_notification_received_ &&
diff --git a/net/proxy_resolution/proxy_resolver_v8_tracing.cc b/net/proxy_resolution/proxy_resolver_v8_tracing.cc
index 3f7b5e8..b2f36ba8 100644
--- a/net/proxy_resolution/proxy_resolver_v8_tracing.cc
+++ b/net/proxy_resolution/proxy_resolver_v8_tracing.cc
@@ -14,7 +14,7 @@
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
@@ -220,7 +220,7 @@
   CompletionOnceCallback callback_;
 
   // Flag to indicate whether the request has been cancelled.
-  base::CancellationFlag cancelled_;
+  base::AtomicFlag cancelled_;
 
   // The operation that this Job is running.
   // Initialized on origin thread and then accessed from both threads.
diff --git a/net/quic/bidirectional_stream_quic_impl.cc b/net/quic/bidirectional_stream_quic_impl.cc
index 7878463..06392df0 100644
--- a/net/quic/bidirectional_stream_quic_impl.cc
+++ b/net/quic/bidirectional_stream_quic_impl.cc
@@ -210,15 +210,34 @@
 }
 
 int64_t BidirectionalStreamQuicImpl::GetTotalReceivedBytes() const {
-  if (stream_)
-    return headers_bytes_received_ + stream_->stream_bytes_read();
-  return headers_bytes_received_ + closed_stream_received_bytes_;
+  // When QPACK is enabled, headers are sent and received on the stream, so
+  // the headers bytes do not need to be accounted for independently.
+  int64_t total_received_bytes =
+      quic::VersionUsesQpack(session_->GetQuicVersion())
+          ? 0
+          : headers_bytes_received_;
+  if (stream_) {
+    DCHECK_LE(stream_->NumBytesConsumed(), stream_->stream_bytes_read());
+    // Only count the uniquely received bytes.
+    total_received_bytes += stream_->NumBytesConsumed();
+  } else {
+    total_received_bytes += closed_stream_received_bytes_;
+  }
+  return total_received_bytes;
 }
 
 int64_t BidirectionalStreamQuicImpl::GetTotalSentBytes() const {
-  if (stream_)
-    return headers_bytes_sent_ + stream_->stream_bytes_written();
-  return headers_bytes_sent_ + closed_stream_sent_bytes_;
+  // When QPACK is enabled, headers are sent and received on the stream, so
+  // the headers bytes do not need to be accounted for independently.
+  int64_t total_sent_bytes = quic::VersionUsesQpack(session_->GetQuicVersion())
+                                 ? 0
+                                 : headers_bytes_sent_;
+  if (stream_) {
+    total_sent_bytes += stream_->stream_bytes_written();
+  } else {
+    total_sent_bytes += closed_stream_sent_bytes_;
+  }
+  return total_sent_bytes;
 }
 
 bool BidirectionalStreamQuicImpl::GetLoadTimingInfo(
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc
index 6dd6dcbb..e94c472 100644
--- a/net/quic/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -25,6 +25,7 @@
 #include "net/log/test_net_log_util.h"
 #include "net/quic/address_utils.h"
 #include "net/quic/mock_crypto_client_stream_factory.h"
+#include "net/quic/platform/impl/quic_test_impl.h"
 #include "net/quic/quic_chromium_alarm_factory.h"
 #include "net/quic/quic_chromium_connection_helper.h"
 #include "net/quic/quic_chromium_packet_reader.h"
@@ -33,6 +34,7 @@
 #include "net/quic/quic_server_info.h"
 #include "net/quic/quic_stream_factory.h"
 #include "net/quic/quic_test_packet_maker.h"
+#include "net/quic/quic_test_packet_printer.h"
 #include "net/quic/test_task_runner.h"
 #include "net/socket/socket_test_util.h"
 #include "net/test/gtest_util.h"
@@ -431,7 +433,9 @@
                       quic::Perspective::IS_SERVER,
                       false),
         random_generator_(0),
+        printer_(version_),
         destination_(kDefaultServerHostName, kDefaultServerPort) {
+    SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
     IPAddress ip(192, 0, 2, 33);
     peer_addr_ = IPEndPoint(ip, 443);
     self_addr_ = IPEndPoint(ip, 8435);
@@ -483,6 +487,7 @@
     socket_data_.reset(new StaticSocketDataProvider(
         base::span<MockRead>(),
         base::make_span(mock_writes_.get(), writes_.size())));
+    socket_data_->set_printer(&printer_);
 
     std::unique_ptr<MockUDPClientSocket> socket(new MockUDPClientSocket(
         socket_data_.get(), net_log().bound().net_log()));
@@ -809,6 +814,7 @@
   }
 
  protected:
+  QuicFlagSaver saver_;
   const quic::ParsedQuicVersion version_;
   const bool client_headers_include_h2_stream_dependency_;
   BoundTestNetLog net_log_;
@@ -832,6 +838,7 @@
   IPEndPoint self_addr_;
   IPEndPoint peer_addr_;
   quic::test::MockRandom random_generator_;
+  QuicPacketPrinter printer_;
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
   std::unique_ptr<StaticSocketDataProvider> socket_data_;
   std::vector<PacketToWrite> writes_;
@@ -839,11 +846,23 @@
   HostPortPair destination_;
 };
 
+// TODO(nharper): Make these tests work with TLS.
+quic::ParsedQuicVersionVector AllSupportedVersionsWithQuicCrypto() {
+  quic::ParsedQuicVersionVector versions;
+  for (const auto& version : quic::AllSupportedVersions()) {
+    if (version.handshake_protocol == quic::PROTOCOL_QUIC_CRYPTO) {
+      versions.push_back(version);
+    }
+  }
+  return versions;
+}
+
 INSTANTIATE_TEST_SUITE_P(
     Version,
     BidirectionalStreamQuicImplTest,
-    ::testing::Combine(::testing::ValuesIn(quic::AllVersionsExcept99()),
-                       ::testing::Bool()));
+    ::testing::Combine(
+        ::testing::ValuesIn(AllSupportedVersionsWithQuicCrypto()),
+        ::testing::Bool()));
 
 TEST_P(BidirectionalStreamQuicImplTest, GetRequest) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
@@ -907,8 +926,10 @@
   spdy::SpdyHeaderBlock trailers;
   size_t spdy_trailers_frame_length;
   trailers["foo"] = "bar";
-  trailers[quic::kFinalOffsetHeaderKey] =
-      base::NumberToString(strlen(kResponseBody));
+  if (!quic::VersionUsesQpack(version_.transport_version)) {
+    trailers[quic::kFinalOffsetHeaderKey] =
+        base::NumberToString(strlen(kResponseBody));
+  }
   // Server sends trailers.
   ProcessPacket(ConstructResponseTrailersPacket(4, kFin, trailers.Clone(),
                                                 &spdy_trailers_frame_length));
@@ -1128,8 +1149,10 @@
   size_t spdy_trailers_frame_length;
   spdy::SpdyHeaderBlock trailers;
   trailers["foo"] = "bar";
-  trailers[quic::kFinalOffsetHeaderKey] =
-      base::NumberToString(strlen(kResponseBody));
+  if (!quic::VersionUsesQpack(version_.transport_version)) {
+    trailers[quic::kFinalOffsetHeaderKey] =
+        base::NumberToString(strlen(kResponseBody));
+  }
   // Server sends trailers.
   ProcessPacket(ConstructResponseTrailersPacket(4, kFin, trailers.Clone(),
                                                 &spdy_trailers_frame_length));
@@ -1243,8 +1266,10 @@
   size_t spdy_trailers_frame_length;
   spdy::SpdyHeaderBlock trailers;
   trailers["foo"] = "bar";
-  trailers[quic::kFinalOffsetHeaderKey] =
-      base::NumberToString(strlen(kResponseBody));
+  if (!quic::VersionUsesQpack(version_.transport_version)) {
+    trailers[quic::kFinalOffsetHeaderKey] =
+        base::NumberToString(strlen(kResponseBody));
+  }
   // Server sends trailers.
   ProcessPacket(ConstructResponseTrailersPacket(4, kFin, trailers.Clone(),
                                                 &spdy_trailers_frame_length));
@@ -1372,8 +1397,10 @@
   size_t spdy_trailers_frame_length;
   spdy::SpdyHeaderBlock trailers;
   trailers["foo"] = "bar";
-  trailers[quic::kFinalOffsetHeaderKey] =
-      base::NumberToString(strlen(kResponseBody));
+  if (!quic::VersionUsesQpack(version_.transport_version)) {
+    trailers[quic::kFinalOffsetHeaderKey] =
+        base::NumberToString(strlen(kResponseBody));
+  }
   // Server sends trailers.
   ProcessPacket(ConstructResponseTrailersPacket(4, kFin, trailers.Clone(),
                                                 &spdy_trailers_frame_length));
@@ -1540,8 +1567,10 @@
   size_t spdy_trailers_frame_length;
   spdy::SpdyHeaderBlock trailers;
   trailers["foo"] = "bar";
-  trailers[quic::kFinalOffsetHeaderKey] =
-      base::NumberToString(strlen(kResponseBody));
+  if (!quic::VersionUsesQpack(version_.transport_version)) {
+    trailers[quic::kFinalOffsetHeaderKey] =
+        base::NumberToString(strlen(kResponseBody));
+  }
   // Server sends trailers.
   ProcessPacket(ConstructResponseTrailersPacket(4, kFin, trailers.Clone(),
                                                 &spdy_trailers_frame_length));
@@ -1632,8 +1661,10 @@
   size_t spdy_trailers_frame_length;
   spdy::SpdyHeaderBlock trailers;
   trailers["foo"] = "bar";
-  trailers[quic::kFinalOffsetHeaderKey] =
-      base::NumberToString(strlen(kResponseBody));
+  if (!quic::VersionUsesQpack(version_.transport_version)) {
+    trailers[quic::kFinalOffsetHeaderKey] =
+        base::NumberToString(strlen(kResponseBody));
+  }
   // Server sends trailers.
   ProcessPacket(ConstructResponseTrailersPacket(4, kFin, trailers.Clone(),
                                                 &spdy_trailers_frame_length));
@@ -2324,8 +2355,10 @@
   size_t spdy_trailers_frame_length;
   spdy::SpdyHeaderBlock trailers;
   trailers["foo"] = "bar";
-  trailers[quic::kFinalOffsetHeaderKey] =
-      base::NumberToString(strlen(kResponseBody));
+  if (!quic::VersionUsesQpack(version_.transport_version)) {
+    trailers[quic::kFinalOffsetHeaderKey] =
+        base::NumberToString(strlen(kResponseBody));
+  }
   // Server sends trailers.
   ProcessPacket(ConstructResponseTrailersPacket(4, kFin, trailers.Clone(),
                                                 &spdy_trailers_frame_length));
diff --git a/net/quic/mock_quic_data.cc b/net/quic/mock_quic_data.cc
index 9d4187b..9c71eff9 100644
--- a/net/quic/mock_quic_data.cc
+++ b/net/quic/mock_quic_data.cc
@@ -37,6 +37,13 @@
   writes_.push_back(MockWrite(mode, rv, sequence_number_++));
 }
 
+void MockQuicData::AddWrite(IoMode mode,
+                            int rv,
+                            std::unique_ptr<quic::QuicEncryptedPacket> packet) {
+  writes_.push_back(MockWrite(mode, rv, sequence_number_++));
+  packets_.push_back(std::move(packet));
+}
+
 void MockQuicData::AddSocketDataToFactory(MockClientSocketFactory* factory) {
   factory->AddSocketDataProvider(InitializeAndGetSequencedSocketData());
 }
diff --git a/net/quic/mock_quic_data.h b/net/quic/mock_quic_data.h
index 5ab220e..6c7deff7 100644
--- a/net/quic/mock_quic_data.h
+++ b/net/quic/mock_quic_data.h
@@ -39,6 +39,12 @@
   // synchronously or asynchronously based on |mode|.
   void AddWrite(IoMode mode, int rv);
 
+  // Adds a write at the next sequence number which will write |packet|
+  // synchronously or asynchronously based on |mode| and return |rv|.
+  void AddWrite(IoMode mode,
+                int rv,
+                std::unique_ptr<quic::QuicEncryptedPacket> packet);
+
   // Adds the reads and writes to |factory|.
   void AddSocketDataToFactory(MockClientSocketFactory* factory);
 
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 274682e..639ca0b 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -263,7 +263,7 @@
 // empty or too large headers with FIN.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_avoid_empty_frame_after_empty_headers,
-          false)
+          true)
 
 // If true, disable QUIC version 44.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_44, true)
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index 3fd54d8a..e2bbe9a 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -345,9 +345,12 @@
 }
 
 int64_t QuicHttpStream::GetTotalReceivedBytes() const {
-  // TODO(sclittle): Currently, this only includes headers and response body
-  // bytes. Change this to include QUIC overhead as well.
-  int64_t total_received_bytes = headers_bytes_received_;
+  // When QPACK is enabled, headers are sent and received on the stream, so
+  // the headers bytes do not need to be accounted for independently.
+  int64_t total_received_bytes =
+      quic::VersionUsesQpack(quic_session()->GetQuicVersion())
+          ? 0
+          : headers_bytes_received_;
   if (stream_) {
     DCHECK_LE(stream_->NumBytesConsumed(), stream_->stream_bytes_read());
     // Only count the uniquely received bytes.
@@ -359,9 +362,12 @@
 }
 
 int64_t QuicHttpStream::GetTotalSentBytes() const {
-  // TODO(sclittle): Currently, this only includes request headers and body
-  // bytes. Change this to include QUIC overhead as well.
-  int64_t total_sent_bytes = headers_bytes_sent_;
+  // When QPACK is enabled, headers are sent and received on the stream, so
+  // the headers bytes do not need to be accounted for independently.
+  int64_t total_sent_bytes =
+      quic::VersionUsesQpack(quic_session()->GetQuicVersion())
+          ? 0
+          : headers_bytes_sent_;
   if (stream_) {
     total_sent_bytes += stream_->stream_bytes_written();
   } else {
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 0641806..0d1a74d 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -32,6 +32,7 @@
 #include "net/quic/address_utils.h"
 #include "net/quic/crypto/proof_verifier_chromium.h"
 #include "net/quic/mock_crypto_client_stream_factory.h"
+#include "net/quic/platform/impl/quic_test_impl.h"
 #include "net/quic/quic_chromium_alarm_factory.h"
 #include "net/quic/quic_chromium_connection_helper.h"
 #include "net/quic/quic_chromium_packet_reader.h"
@@ -57,6 +58,8 @@
 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
@@ -215,6 +218,7 @@
                       false),
         random_generator_(0),
         printer_(version_) {
+    SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
     IPAddress ip(192, 0, 2, 33);
     peer_addr_ = IPEndPoint(ip, 443);
     self_addr_ = IPEndPoint(ip, 8435);
@@ -279,6 +283,8 @@
     EXPECT_CALL(*send_algorithm_, InSlowStart()).WillRepeatedly(Return(false));
     EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
         .Times(testing::AtLeast(1));
+    EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _))
+        .Times(AnyNumber());
     EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
         .WillRepeatedly(Return(quic::kMaxOutgoingPacketSize));
     EXPECT_CALL(*send_algorithm_, PacingRate(_))
@@ -649,6 +655,8 @@
         version_.transport_version, n);
   }
 
+  QuicFlagSaver saver_;
+
   const quic::ParsedQuicVersion version_;
   const bool client_headers_include_h2_stream_dependency_;
 
@@ -701,7 +709,7 @@
 INSTANTIATE_TEST_SUITE_P(
     VersionIncludeStreamDependencySequence,
     QuicHttpStreamTest,
-    ::testing::Combine(::testing::ValuesIn(quic::AllVersionsExcept99()),
+    ::testing::Combine(::testing::ValuesIn(quic::AllSupportedVersions()),
                        ::testing::Bool()));
 
 TEST_P(QuicHttpStreamTest, RenewStreamForAuth) {
@@ -920,8 +928,10 @@
   spdy::SpdyHeaderBlock trailers;
   size_t spdy_trailers_frame_length;
   trailers["foo"] = "bar";
-  trailers[quic::kFinalOffsetHeaderKey] =
-      base::NumberToString(strlen(kResponseBody) + header.length());
+  if (!quic::VersionUsesQpack(version_.transport_version)) {
+    trailers[quic::kFinalOffsetHeaderKey] =
+        base::NumberToString(strlen(kResponseBody) + header.length());
+  }
   ProcessPacket(ConstructResponseTrailersPacket(4, kFin, std::move(trailers),
                                                 &spdy_trailers_frame_length));
 
@@ -1127,6 +1137,14 @@
 }
 
 TEST_P(QuicHttpStreamTest, LogGranularQuicErrorIfHandshakeNotConfirmed) {
+  // TODO(nharper): Figure out why this test does not send packets
+  // when TLS is used.
+  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) {
+    Initialize();
+
+    return;
+  }
+
   // By default the test setup defaults handshake to be confirmed. Manually set
   // it to be not confirmed.
   crypto_client_stream_factory_.set_handshake_mode(
@@ -1920,7 +1938,7 @@
 
   // Now sending a matching request will have successful rendezvous
   // with the promised stream.
-  EXPECT_EQ(OK, promised_stream_->SendRequest(headers_, &response_,
+  ASSERT_EQ(OK, promised_stream_->SendRequest(headers_, &response_,
                                               callback_.callback()));
 
   EXPECT_EQ(
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index a4299fc..cebd917 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -329,7 +329,7 @@
 class QuicStreamFactory::Job {
  public:
   Job(QuicStreamFactory* factory,
-      const quic::QuicTransportVersion& quic_version,
+      quic::ParsedQuicVersion quic_version,
       HostResolver* host_resolver,
       const QuicSessionAliasKey& key,
       bool was_alternative_service_recently_broken,
@@ -475,7 +475,7 @@
 
   IoState io_state_;
   QuicStreamFactory* factory_;
-  quic::QuicTransportVersion quic_version_;
+  quic::ParsedQuicVersion quic_version_;
   HostResolver* host_resolver_;
   const QuicSessionAliasKey key_;
   RequestPriority priority_;
@@ -505,7 +505,7 @@
 };
 
 QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
-                            const quic::QuicTransportVersion& quic_version,
+                            quic::ParsedQuicVersion quic_version,
                             HostResolver* host_resolver,
                             const QuicSessionAliasKey& key,
                             bool was_alternative_service_recently_broken,
@@ -768,7 +768,7 @@
       NetLogEventType::QUIC_STREAM_FACTORY_JOB_CONNECT,
       NetLog::BoolCallback("require_confirmation", require_confirmation));
 
-  DCHECK_NE(quic_version_, quic::QUIC_VERSION_UNSUPPORTED);
+  DCHECK_NE(quic_version_.transport_version, quic::QUIC_VERSION_UNSUPPORTED);
   int rv = factory_->CreateSession(
       key_, quic_version_, cert_verify_flags_, require_confirmation,
       resolve_host_request_->GetAddressResults().value(),
@@ -937,7 +937,7 @@
 
 int QuicStreamRequest::Request(
     const HostPortPair& destination,
-    quic::QuicTransportVersion quic_version,
+    quic::ParsedQuicVersion quic_version,
     PrivacyMode privacy_mode,
     RequestPriority priority,
     const SocketTag& socket_tag,
@@ -948,7 +948,7 @@
     NetErrorDetails* net_error_details,
     CompletionOnceCallback failed_on_default_network_callback,
     CompletionOnceCallback callback) {
-  DCHECK_NE(quic_version, quic::QUIC_VERSION_UNSUPPORTED);
+  DCHECK_NE(quic_version.transport_version, quic::QUIC_VERSION_UNSUPPORTED);
   DCHECK(net_error_details);
   DCHECK(callback_.is_null());
   DCHECK(host_resolution_callback_.is_null());
@@ -1289,7 +1289,7 @@
 
 int QuicStreamFactory::Create(const QuicSessionKey& session_key,
                               const HostPortPair& destination,
-                              quic::QuicTransportVersion quic_version,
+                              quic::ParsedQuicVersion quic_version,
                               RequestPriority priority,
                               int cert_verify_flags,
                               const GURL& url,
@@ -1786,7 +1786,7 @@
 
 int QuicStreamFactory::CreateSession(
     const QuicSessionAliasKey& key,
-    const quic::QuicTransportVersion& quic_version,
+    quic::ParsedQuicVersion quic_version,
     int cert_verify_flags,
     bool require_confirmation,
     const AddressList& address_list,
@@ -1844,9 +1844,7 @@
   quic::QuicConnection* connection = new quic::QuicConnection(
       connection_id, ToQuicSocketAddress(addr), helper_.get(),
       alarm_factory_.get(), writer, true /* owns_writer */,
-      quic::Perspective::IS_CLIENT,
-      quic::ParsedQuicVersionVector{
-          quic::ParsedQuicVersion(quic::PROTOCOL_QUIC_CRYPTO, quic_version)});
+      quic::Perspective::IS_CLIENT, {quic_version});
   connection->set_ping_timeout(ping_timeout_);
   connection->SetMaxPacketLength(max_packet_length_);
 
@@ -1857,7 +1855,7 @@
   config.SetInitialStreamFlowControlWindowToSend(kQuicStreamMaxRecvWindowSize);
   config.SetBytesForConnectionIdToSend(0);
   ConfigureInitialRttEstimate(server_id, &config);
-  if (quic_version <= quic::QUIC_VERSION_43 &&
+  if (quic_version.transport_version <= quic::QUIC_VERSION_43 &&
       !config.HasClientSentConnectionOption(quic::kNSTP,
                                             quic::Perspective::IS_CLIENT)) {
     // Enable the no stop waiting frames connection option by default.
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index 207d20b..c3cc96f 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -125,7 +125,7 @@
   // quic::QuicConnection.  This can be different than
   // HostPortPair::FromURL(url).
   int Request(const HostPortPair& destination,
-              quic::QuicTransportVersion quic_version,
+              quic::ParsedQuicVersion quic_version,
               PrivacyMode privacy_mode,
               RequestPriority priority,
               const SocketTag& socket_tag,
@@ -294,7 +294,7 @@
   // OnRequestComplete asynchronously.
   int Create(const QuicSessionKey& session_key,
              const HostPortPair& destination,
-             quic::QuicTransportVersion quic_version,
+             quic::ParsedQuicVersion quic_version,
              RequestPriority priority,
              int cert_verify_flags,
              const GURL& url,
@@ -426,7 +426,7 @@
   bool HasActiveJob(const QuicSessionKey& session_key) const;
   bool HasActiveCertVerifierJob(const quic::QuicServerId& server_id) const;
   int CreateSession(const QuicSessionAliasKey& key,
-                    const quic::QuicTransportVersion& quic_version,
+                    quic::ParsedQuicVersion quic_version,
                     int cert_verify_flags,
                     bool require_confirmation,
                     const AddressList& address_list,
diff --git a/net/quic/quic_stream_factory_fuzzer.cc b/net/quic/quic_stream_factory_fuzzer.cc
index 5c5d5aa..ad8b2c7c 100644
--- a/net/quic/quic_stream_factory_fuzzer.cc
+++ b/net/quic/quic_stream_factory_fuzzer.cc
@@ -156,15 +156,18 @@
           env->connection_options, env->client_connection_options,
           enable_socket_recv_optimization, 0);
 
+  SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
   QuicStreamRequest request(factory.get());
   TestCompletionCallback callback;
   NetErrorDetails net_error_details;
+  quic::ParsedQuicVersionVector versions = quic::AllSupportedVersions();
+  quic::ParsedQuicVersion version =
+      versions[data_provider.ConsumeIntegralInRange<size_t>(
+          0, versions.size() - 1)];
   request.Request(
-      env->host_port_pair,
-      data_provider.PickValueInArray(quic::kSupportedTransportVersions),
-      PRIVACY_MODE_DISABLED, DEFAULT_PRIORITY, SocketTag(),
-      NetworkIsolationKey(), kCertVerifyFlags, GURL(kUrl), env->net_log,
-      &net_error_details,
+      env->host_port_pair, version, PRIVACY_MODE_DISABLED, DEFAULT_PRIORITY,
+      SocketTag(), NetworkIsolationKey(), kCertVerifyFlags, GURL(kUrl),
+      env->net_log, &net_error_details,
       /*failed_on_default_network_callback=*/CompletionOnceCallback(),
       callback.callback());
 
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 5282eec..e9aa621b 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -134,10 +134,13 @@
 std::vector<TestParams> GetTestParams() {
   std::vector<TestParams> params;
   quic::ParsedQuicVersionVector all_supported_versions =
-      quic::AllVersionsExcept99();
+      quic::AllSupportedVersions();
   for (const auto& version : all_supported_versions) {
-    params.push_back(TestParams{version, false});
-    params.push_back(TestParams{version, true});
+    // TODO(rch): crbug.com/978745 - Make this work with TLS
+    if (version.handshake_protocol != quic::PROTOCOL_TLS1_3) {
+      params.push_back(TestParams{version, false});
+      params.push_back(TestParams{version, true});
+    }
   }
   return params;
 }
@@ -174,14 +177,17 @@
 std::vector<PoolingTestParams> GetPoolingTestParams() {
   std::vector<PoolingTestParams> params;
   quic::ParsedQuicVersionVector all_supported_versions =
-      quic::AllVersionsExcept99();
+      quic::AllSupportedVersions();
   for (const quic::ParsedQuicVersion version : all_supported_versions) {
-    params.push_back(PoolingTestParams{version, SAME_AS_FIRST, false});
-    params.push_back(PoolingTestParams{version, SAME_AS_FIRST, true});
-    params.push_back(PoolingTestParams{version, SAME_AS_SECOND, false});
-    params.push_back(PoolingTestParams{version, SAME_AS_SECOND, true});
-    params.push_back(PoolingTestParams{version, DIFFERENT, false});
-    params.push_back(PoolingTestParams{version, DIFFERENT, true});
+    // TODO(rch): crbug.com/978745 - Make this work with TLS
+    if (version.handshake_protocol != quic::PROTOCOL_TLS1_3) {
+      params.push_back(PoolingTestParams{version, SAME_AS_FIRST, false});
+      params.push_back(PoolingTestParams{version, SAME_AS_FIRST, true});
+      params.push_back(PoolingTestParams{version, SAME_AS_SECOND, false});
+      params.push_back(PoolingTestParams{version, SAME_AS_SECOND, true});
+      params.push_back(PoolingTestParams{version, DIFFERENT, false});
+      params.push_back(PoolingTestParams{version, DIFFERENT, true});
+    }
   }
   return params;
 }
@@ -391,8 +397,8 @@
     GURL url("https://" + destination.host() + "/");
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  destination, version_.transport_version, privacy_mode_,
-                  DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                  destination, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  SocketTag(), NetworkIsolationKey(),
                   /*cert_verify_flags=*/0, url, net_log_, &net_error_details_,
                   failed_on_default_network_callback_, callback_.callback()));
 
@@ -520,6 +526,7 @@
     socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
     socket_data1.AddSocketDataToFactory(socket_factory_.get());
 
+    client_maker_.set_coalesce_http_frames(true);
     // Set up second socket data provider that is used after
     // migration.
     MockQuicData socket_data2(version_);
@@ -537,8 +544,8 @@
     QuicStreamRequest request(factory_.get());
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  host_port_pair_, version_.transport_version, privacy_mode_,
-                  DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  SocketTag(), NetworkIsolationKey(),
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                   failed_on_default_network_callback_, callback_.callback()));
     EXPECT_EQ(OK, callback_.WaitForResult());
@@ -725,8 +732,8 @@
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
                   HostPortPair(quic_server_id.host(), quic_server_id.port()),
-                  version_.transport_version, privacy_mode_, DEFAULT_PRIORITY,
-                  SocketTag(), NetworkIsolationKey(),
+                  version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                  NetworkIsolationKey(),
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                   failed_on_default_network_callback_, callback_.callback()));
     EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -761,8 +768,8 @@
     EXPECT_EQ(ERR_IO_PENDING,
               request2.Request(
                   HostPortPair(quic_server_id2.host(), quic_server_id2.port()),
-                  version_.transport_version, privacy_mode_, DEFAULT_PRIORITY,
-                  SocketTag(), NetworkIsolationKey(),
+                  version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                  NetworkIsolationKey(),
                   /*cert_verify_flags=*/0, GURL("https://mail.example.org/"),
                   net_log_, &net_error_details_,
                   failed_on_default_network_callback_, callback_.callback()));
@@ -918,8 +925,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -932,8 +939,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   // Will reset stream 3.
@@ -946,8 +953,8 @@
   QuicStreamRequest request3(factory_.get());
   EXPECT_EQ(OK,
             request3.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   stream = CreateStream(&request3);  // Will reset stream 5.
@@ -976,8 +983,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1000,8 +1007,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1028,8 +1035,8 @@
   auto request = std::make_unique<QuicStreamRequest>(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request->Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   request.reset();
@@ -1058,8 +1065,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1099,8 +1106,8 @@
 
   QuicStreamRequest request(factory_.get());
   EXPECT_THAT(request.Request(
-                  host_port_pair_, version_.transport_version, privacy_mode_,
-                  DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  SocketTag(), NetworkIsolationKey(),
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                   failed_on_default_network_callback_, callback_.callback()),
               IsOk());
@@ -1139,8 +1146,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1172,8 +1179,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1205,8 +1212,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1233,8 +1240,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1265,8 +1272,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1312,8 +1319,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
@@ -1323,8 +1330,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -1352,6 +1359,7 @@
                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
 
   client_maker_.Reset();
+  client_maker_.set_coalesce_http_frames(false);
   // Set up server IP, socket, proof, and config for new session.
   HostPortPair server2(kServer2HostName, kDefaultServerPort);
   host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
@@ -1377,8 +1385,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback.callback()));
   EXPECT_EQ(OK, callback.WaitForResult());
@@ -1415,8 +1423,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
@@ -1426,8 +1434,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -1441,8 +1449,8 @@
   QuicStreamRequest request3(factory_.get());
   EXPECT_EQ(OK,
             request3.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback3.callback()));
   std::unique_ptr<HttpStream> stream3 = CreateStream(&request3);
@@ -1477,8 +1485,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                server1, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server1, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
@@ -1488,8 +1496,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -1527,8 +1535,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                server1, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server1, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
@@ -1538,8 +1546,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -1589,8 +1597,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                server1, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server1, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
@@ -1600,8 +1608,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -1634,8 +1642,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1656,8 +1664,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -1720,8 +1728,8 @@
   for (size_t i = 0; i < quic::kDefaultMaxStreamsPerConnection / 2; i++) {
     QuicStreamRequest request(factory_.get());
     int rv = request.Request(
-        host_port_pair_, version_.transport_version, privacy_mode_,
-        DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+        host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+        NetworkIsolationKey(),
         /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
         failed_on_default_network_callback_, callback_.callback());
     if (i == 0) {
@@ -1741,8 +1749,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, CompletionOnceCallback()));
   std::unique_ptr<HttpStream> stream = CreateStream(&request);
@@ -1780,8 +1788,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1801,8 +1809,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1822,8 +1830,8 @@
     QuicStreamRequest request(factory_.get());
     EXPECT_EQ(ERR_IO_PENDING,
               request.Request(
-                  host_port_pair_, version_.transport_version, privacy_mode_,
-                  DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  SocketTag(), NetworkIsolationKey(),
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                   failed_on_default_network_callback_, callback_.callback()));
   }
@@ -1833,8 +1841,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream = CreateStream(&request2);
@@ -1871,8 +1879,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1896,8 +1904,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -1932,8 +1940,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
@@ -1954,8 +1962,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_FALSE(HasActiveSession(host_port_pair_));
@@ -1999,8 +2007,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   // Check no active session, or active jobs left for this server.
@@ -2021,8 +2029,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_FALSE(HasActiveSession(host_port_pair_));
@@ -2071,8 +2079,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -2106,8 +2114,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -2161,8 +2169,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -2207,8 +2215,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -2246,8 +2254,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -2271,8 +2279,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   stream = CreateStream(&request2);
@@ -2319,7 +2327,6 @@
 
   // Set up the second socket data provider that is used after migration.
   // The response to the earlier request is read on the new socket.
-  client_maker_.Reset();
   MockQuicData quic_data2(version_);
   // Connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS,
@@ -2346,8 +2353,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -2493,7 +2500,6 @@
 
   // Set up the second socket data provider that is used after migration.
   // The response to the earlier request is read on the new socket.
-  client_maker_.Reset();
   MockQuicData quic_data2(version_);
   // First connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS,
@@ -2524,8 +2530,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -2668,8 +2674,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -2785,8 +2791,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -2850,8 +2856,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -2940,8 +2946,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3004,8 +3010,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3093,8 +3099,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3161,8 +3167,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3232,8 +3238,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3349,8 +3355,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3477,7 +3483,6 @@
 
   // Set up the second socket data provider that is used for probing on the
   // alternate network.
-  client_maker_.Reset();
   MockQuicData quic_data2(version_);
   // Connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket(
@@ -3512,8 +3517,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3658,7 +3663,6 @@
 
   // Set up the second socket data provider that is used after migration.
   // The response to the earlier request is read on the new socket.
-  client_maker_.Reset();
   MockQuicData quic_data2(version_);
   // Connectivity probe to be sent on the new path.
   quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket(
@@ -3686,8 +3690,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3821,8 +3825,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cerf_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3862,8 +3866,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -3936,8 +3940,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -4072,8 +4076,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -4227,8 +4231,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -4369,8 +4373,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(OK,
             request1.Request(
-                server1, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server1, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream1 = CreateStream(&request1);
@@ -4380,8 +4384,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -4492,8 +4496,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -4606,8 +4610,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -4672,8 +4676,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -4746,6 +4750,7 @@
       SYNCHRONOUS,
       ConstructGetRequestPacket(
           2, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
+  client_maker_.set_coalesce_http_frames(true);
   socket_data1.AddWrite(
       SYNCHRONOUS,
       ConstructGetRequestPacket(
@@ -4772,8 +4777,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -4794,8 +4799,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -4907,8 +4912,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -5029,8 +5034,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -5090,8 +5095,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -5209,8 +5214,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -5326,8 +5331,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
@@ -5348,8 +5353,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_FALSE(HasActiveSession(host_port_pair_));
@@ -5419,8 +5424,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   // Ensure that the session is alive but not active.
@@ -5487,8 +5492,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -5584,8 +5589,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -5719,8 +5724,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -5741,8 +5746,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -5854,8 +5859,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -5876,8 +5881,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -5999,8 +6004,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -6021,8 +6026,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -6130,8 +6135,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -6217,8 +6222,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -6341,8 +6346,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -6436,8 +6441,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   // Deliver the network notification, which should cause the connection to be
@@ -6473,8 +6478,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -6605,8 +6610,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -6743,8 +6748,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -6864,7 +6869,10 @@
   MockQuicData socket_data(version_);
   socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   socket_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
-  socket_data.AddWrite(ASYNC, ERR_ADDRESS_UNREACHABLE);
+  socket_data.AddWrite(
+      ASYNC, ERR_ADDRESS_UNREACHABLE,
+      ConstructGetRequestPacket(
+          2, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
@@ -6872,8 +6880,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -6974,8 +6982,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -7086,8 +7094,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -7208,26 +7216,15 @@
           3, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
   socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
   // Read two packets so that client will send ACK immedaitely.
-  spdy::SpdyHeaderBlock response_headers =
-      server_maker_.GetResponseHeaders("200 OK");
-  response_headers["key1"] = std::string(2000, 'A');
-  spdy::SpdyHeadersIR headers_frame(
-      GetNthClientInitiatedBidirectionalStreamId(0),
-      std::move(response_headers));
-  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
-  spdy::SpdySerializedFrame spdy_frame =
-      response_framer.SerializeFrame(headers_frame);
-  size_t chunk_size = 1200;
-  unsigned int packet_number = 1;
-  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
-    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
-    socket_data1.AddRead(
-        ASYNC,
-        server_maker_.MakeDataPacket(
-            packet_number++,
-            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
-            false, false, base::StringPiece(spdy_frame.data() + offset, len)));
-  }
+  socket_data1.AddRead(
+      ASYNC,
+      ConstructOkResponsePacket(
+          1, GetNthClientInitiatedBidirectionalStreamId(0), false, false));
+  socket_data1.AddRead(
+      ASYNC, server_maker_.MakeDataPacket(
+                 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+                 base::StringPiece("Hello World")));
+
   // Read an ACK from server which acks all client data.
   socket_data1.AddRead(SYNCHRONOUS,
                        server_maker_.MakeAckPacket(3, 3, 1, 1, false));
@@ -7251,8 +7248,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -7365,26 +7362,14 @@
           3, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
   socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
   // Read two packets so that client will send ACK immedaitely.
-  spdy::SpdyHeaderBlock response_headers =
-      server_maker_.GetResponseHeaders("200 OK");
-  response_headers["key1"] = std::string(2000, 'A');
-  spdy::SpdyHeadersIR headers_frame(
-      GetNthClientInitiatedBidirectionalStreamId(0),
-      std::move(response_headers));
-  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
-  spdy::SpdySerializedFrame spdy_frame =
-      response_framer.SerializeFrame(headers_frame);
-  size_t chunk_size = 1200;
-  unsigned int packet_number = 1;
-  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
-    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
-    socket_data1.AddRead(
-        ASYNC,
-        server_maker_.MakeDataPacket(
-            packet_number++,
-            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
-            false, false, base::StringPiece(spdy_frame.data() + offset, len)));
-  }
+  socket_data1.AddRead(
+      ASYNC,
+      ConstructOkResponsePacket(
+          1, GetNthClientInitiatedBidirectionalStreamId(0), false, false));
+  socket_data1.AddRead(
+      ASYNC, server_maker_.MakeDataPacket(
+                 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+                 base::StringPiece("Hello World")));
   // Read an ACK from server which acks all client data.
   socket_data1.AddRead(SYNCHRONOUS,
                        server_maker_.MakeAckPacket(3, 3, 1, 1, false));
@@ -7408,8 +7393,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -7511,26 +7496,14 @@
           2, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
   socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
   // Read two packets so that client will send ACK immedaitely.
-  spdy::SpdyHeaderBlock response_headers =
-      server_maker_.GetResponseHeaders("200 OK");
-  response_headers["key1"] = std::string(2000, 'A');
-  spdy::SpdyHeadersIR headers_frame(
-      GetNthClientInitiatedBidirectionalStreamId(0),
-      std::move(response_headers));
-  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
-  spdy::SpdySerializedFrame spdy_frame =
-      response_framer.SerializeFrame(headers_frame);
-  size_t chunk_size = 1200;
-  unsigned int packet_number = 1;
-  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
-    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
-    socket_data1.AddRead(
-        ASYNC,
-        server_maker_.MakeDataPacket(
-            packet_number++,
-            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
-            false, false, base::StringPiece(spdy_frame.data() + offset, len)));
-  }
+  socket_data1.AddRead(
+      ASYNC,
+      ConstructOkResponsePacket(
+          1, GetNthClientInitiatedBidirectionalStreamId(0), false, false));
+  socket_data1.AddRead(
+      ASYNC, server_maker_.MakeDataPacket(
+                 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+                 base::StringPiece("Hello World")));
   // Read an ACK from server which acks all client data.
   socket_data1.AddRead(SYNCHRONOUS,
                        server_maker_.MakeAckPacket(3, 2, 1, 1, false));
@@ -7554,8 +7527,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -7646,26 +7619,14 @@
           2, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
   socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
   // Read two packets so that client will send ACK immedaitely.
-  spdy::SpdyHeaderBlock response_headers =
-      server_maker_.GetResponseHeaders("200 OK");
-  response_headers["key1"] = std::string(2000, 'A');
-  spdy::SpdyHeadersIR headers_frame(
-      GetNthClientInitiatedBidirectionalStreamId(0),
-      std::move(response_headers));
-  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
-  spdy::SpdySerializedFrame spdy_frame =
-      response_framer.SerializeFrame(headers_frame);
-  size_t chunk_size = 1200;
-  unsigned int packet_number = 1;
-  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
-    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
-    socket_data1.AddRead(
-        ASYNC,
-        server_maker_.MakeDataPacket(
-            packet_number++,
-            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
-            false, false, base::StringPiece(spdy_frame.data() + offset, len)));
-  }
+  socket_data1.AddRead(
+      ASYNC,
+      ConstructOkResponsePacket(
+          1, GetNthClientInitiatedBidirectionalStreamId(0), false, false));
+  socket_data1.AddRead(
+      ASYNC, server_maker_.MakeDataPacket(
+                 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+                 base::StringPiece("Hello World")));
   // Read an ACK from server which acks all client data.
   socket_data1.AddRead(SYNCHRONOUS,
                        server_maker_.MakeAckPacket(3, 2, 1, 1, false));
@@ -7686,8 +7647,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -7781,26 +7742,14 @@
           2, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
   socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
   // Read two packets so that client will send ACK immedaitely.
-  spdy::SpdyHeaderBlock response_headers =
-      server_maker_.GetResponseHeaders("200 OK");
-  response_headers["key1"] = std::string(2000, 'A');
-  spdy::SpdyHeadersIR headers_frame(
-      GetNthClientInitiatedBidirectionalStreamId(0),
-      std::move(response_headers));
-  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
-  spdy::SpdySerializedFrame spdy_frame =
-      response_framer.SerializeFrame(headers_frame);
-  size_t chunk_size = 1200;
-  unsigned int packet_number = 1;
-  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
-    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
-    socket_data1.AddRead(
-        ASYNC,
-        server_maker_.MakeDataPacket(
-            packet_number++,
-            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
-            false, false, base::StringPiece(spdy_frame.data() + offset, len)));
-  }
+  socket_data1.AddRead(
+      ASYNC,
+      ConstructOkResponsePacket(
+          1, GetNthClientInitiatedBidirectionalStreamId(0), false, false));
+  socket_data1.AddRead(
+      ASYNC, server_maker_.MakeDataPacket(
+                 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+                 base::StringPiece("Hello World")));
   // Read an ACK from server which acks all client data.
   socket_data1.AddRead(SYNCHRONOUS,
                        server_maker_.MakeAckPacket(3, 2, 1, 1, false));
@@ -7824,8 +7773,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -7918,26 +7867,14 @@
           2, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
   socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
   // Read two packets so that client will send ACK immedaitely.
-  spdy::SpdyHeaderBlock response_headers =
-      server_maker_.GetResponseHeaders("200 OK");
-  response_headers["key1"] = std::string(2000, 'A');
-  spdy::SpdyHeadersIR headers_frame(
-      GetNthClientInitiatedBidirectionalStreamId(0),
-      std::move(response_headers));
-  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
-  spdy::SpdySerializedFrame spdy_frame =
-      response_framer.SerializeFrame(headers_frame);
-  size_t chunk_size = 1200;
-  unsigned int packet_number = 1;
-  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
-    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
-    socket_data1.AddRead(
-        ASYNC,
-        server_maker_.MakeDataPacket(
-            packet_number++,
-            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
-            false, false, base::StringPiece(spdy_frame.data() + offset, len)));
-  }
+  socket_data1.AddRead(
+      ASYNC,
+      ConstructOkResponsePacket(
+          1, GetNthClientInitiatedBidirectionalStreamId(0), false, false));
+  socket_data1.AddRead(
+      ASYNC, server_maker_.MakeDataPacket(
+                 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+                 base::StringPiece("Hello World")));
   // Read an ACK from server which acks all client data.
   socket_data1.AddRead(SYNCHRONOUS,
                        server_maker_.MakeAckPacket(3, 2, 1, 1, false));
@@ -7958,8 +7895,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -8049,8 +7986,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -8079,6 +8016,7 @@
       SYNCHRONOUS,
       ConstructGetRequestPacket(
           2, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
+  client_maker_.set_coalesce_http_frames(true);
   socket_data1.AddRead(
       ASYNC,
       ConstructOkResponsePacket(
@@ -8192,8 +8130,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -8239,6 +8177,7 @@
       SYNCHRONOUS,
       ConstructGetRequestPacket(
           2, GetNthClientInitiatedBidirectionalStreamId(0), true, true));
+  client_maker_.set_coalesce_http_frames(true);
   socket_data1.AddRead(
       ASYNC,
       ConstructOkResponsePacket(
@@ -8287,8 +8226,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -8375,8 +8314,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -8493,8 +8432,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -8569,8 +8508,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -8717,8 +8656,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -8775,8 +8714,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -8798,8 +8737,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -8918,8 +8857,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -8969,8 +8908,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9007,8 +8946,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                server2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   QuicChromiumClientSession* session2 = GetActiveSession(server2);
@@ -9079,8 +9018,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9128,8 +9067,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9177,8 +9116,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(OK,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9211,8 +9150,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9234,8 +9173,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9266,8 +9205,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9292,13 +9231,12 @@
   // Doing the request should not use the push stream, but rather
   // cancel it because the privacy modes do not match.
   QuicStreamRequest request2(factory_.get());
-  EXPECT_EQ(
-      ERR_IO_PENDING,
-      request2.Request(
-          host_port_pair_, version_.transport_version, PRIVACY_MODE_ENABLED,
-          DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
-          /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
-          failed_on_default_network_callback_, callback_.callback()));
+  EXPECT_EQ(ERR_IO_PENDING,
+            request2.Request(
+                host_port_pair_, version_, PRIVACY_MODE_ENABLED,
+                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
 
   EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumPushStreamsCreated(factory_.get()));
   EXPECT_EQ(index->GetPromised(kDefaultUrl), nullptr);
@@ -9332,8 +9270,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                destination1, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination1, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -9346,8 +9284,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                destination2, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination2, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -9453,8 +9391,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                destination, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9498,8 +9436,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                destination, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url1, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -9513,8 +9451,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(OK,
             request2.Request(
-                destination, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
@@ -9577,8 +9515,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                destination, version_.transport_version, PRIVACY_MODE_DISABLED,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination, version_, PRIVACY_MODE_DISABLED, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url1, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
@@ -9590,8 +9528,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                destination, version_.transport_version, PRIVACY_MODE_ENABLED,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination, version_, PRIVACY_MODE_ENABLED, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   EXPECT_EQ(OK, callback2.WaitForResult());
@@ -9664,8 +9602,8 @@
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request1.Request(
-                destination, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url1, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -9677,8 +9615,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                destination, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                destination, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback2.callback()));
   EXPECT_THAT(callback2.WaitForResult(), IsOk());
@@ -9788,8 +9726,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                MAXIMUM_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, MAXIMUM_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9816,8 +9754,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                MAXIMUM_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, MAXIMUM_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9827,8 +9765,8 @@
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request2.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_EQ(DEFAULT_PRIORITY, host_resolver_->last_request_priority());
@@ -9872,8 +9810,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9926,8 +9864,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -9977,8 +9915,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_QUIC_PROTOCOL_ERROR,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10015,8 +9953,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10049,8 +9987,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_NAME_NOT_RESOLVED,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10075,8 +10013,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10128,8 +10066,8 @@
 
   QuicStreamRequest request(factory_.get());
   EXPECT_THAT(request.Request(
-                  host_port_pair_, version_.transport_version, privacy_mode_,
-                  DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                  host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                  SocketTag(), NetworkIsolationKey(),
                   /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                   failed_on_default_network_callback_, callback_.callback()),
               IsOk());
@@ -10164,8 +10102,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   TestCompletionCallback host_resolution_callback;
@@ -10222,8 +10160,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10283,8 +10221,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10350,8 +10288,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10422,8 +10360,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10493,8 +10431,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10568,8 +10506,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   // Finish dns resolution, but need to wait for stale connection.
@@ -10610,8 +10548,8 @@
 
   EXPECT_EQ(ERR_NAME_NOT_RESOLVED,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 }
@@ -10635,8 +10573,8 @@
 
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10682,8 +10620,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10738,8 +10676,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   EXPECT_FALSE(HasLiveSession(host_port_pair_));
@@ -10788,8 +10726,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10851,8 +10789,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10905,8 +10843,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10958,8 +10896,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -10993,8 +10931,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
 
@@ -11046,8 +10984,8 @@
   QuicStreamRequest request(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
             request.Request(
-                host_port_pair_, version_.transport_version, privacy_mode_,
-                DEFAULT_PRIORITY, SocketTag(), NetworkIsolationKey(),
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
                 /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
                 failed_on_default_network_callback_, callback_.callback()));
   base::RunLoop().RunUntilIdle();
@@ -11113,8 +11051,8 @@
   // Request a stream with |tag1|.
   QuicStreamRequest request1(factory_.get());
   int rv = request1.Request(
-      host_port_pair_, version_.transport_version, privacy_mode_,
-      DEFAULT_PRIORITY, tag1, NetworkIsolationKey(),
+      host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, tag1,
+      NetworkIsolationKey(),
       /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
       failed_on_default_network_callback_, callback_.callback());
   EXPECT_THAT(callback_.GetResult(rv), IsOk());
@@ -11129,8 +11067,8 @@
   // Request a stream with |tag1| and verify underlying session is reused.
   QuicStreamRequest request2(factory_.get());
   rv = request2.Request(
-      host_port_pair_, version_.transport_version, privacy_mode_,
-      DEFAULT_PRIORITY, tag1, NetworkIsolationKey(),
+      host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, tag1,
+      NetworkIsolationKey(),
       /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
       failed_on_default_network_callback_, callback_.callback());
   EXPECT_THAT(callback_.GetResult(rv), IsOk());
@@ -11143,8 +11081,8 @@
   // Request a stream with |tag2| and verify a new session is created.
   QuicStreamRequest request3(factory_.get());
   rv = request3.Request(
-      host_port_pair_, version_.transport_version, privacy_mode_,
-      DEFAULT_PRIORITY, tag2, NetworkIsolationKey(),
+      host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, tag2,
+      NetworkIsolationKey(),
       /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
       failed_on_default_network_callback_, callback_.callback());
   EXPECT_THAT(callback_.GetResult(rv), IsOk());
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 395a6416eb..612b460 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -28,6 +28,16 @@
 
 }  // namespace
 
+void QuicTestPacketMaker::DecoderStreamErrorDelegate::OnDecoderStreamError(
+    quic::QuicStringPiece error_message) {
+  LOG(FATAL) << error_message;
+}
+
+void QuicTestPacketMaker::EncoderStreamSenderDelegate::WriteStreamData(
+    quic::QuicStringPiece data) {
+  LOG(FATAL) << "data.length: " << data.length();
+}
+
 QuicTestPacketMaker::QuicTestPacketMaker(
     quic::ParsedQuicVersion version,
     quic::QuicConnectionId connection_id,
@@ -41,6 +51,9 @@
       host_(host),
       spdy_request_framer_(spdy::SpdyFramer::ENABLE_COMPRESSION),
       spdy_response_framer_(spdy::SpdyFramer::ENABLE_COMPRESSION),
+      coalesce_http_frames_(false),
+      qpack_encoder_(&decoder_stream_error_delegate_,
+                     &encoder_stream_sender_delegate_),
       perspective_(perspective),
       encryption_level_(quic::ENCRYPTION_FORWARD_SECURE),
       long_header_type_(quic::INVALID_PACKET_TYPE),
@@ -244,16 +257,6 @@
     size_t* spdy_headers_frame_length) {
   InitializeHeader(num, include_version);
   quic::QuicRstStreamFrame rst_frame(1, rst_stream_id, rst_error_code, 0);
-
-  spdy::SpdySerializedFrame spdy_frame = MakeSpdyHeadersFrame(
-      stream_id, fin, priority, std::move(headers), parent_stream_id);
-  if (spdy_headers_frame_length) {
-    *spdy_headers_frame_length = spdy_frame.size();
-  }
-  quic::QuicStreamFrame headers_frame = GenerateNextStreamFrame(
-      quic::QuicUtils::GetHeadersStreamId(version_.transport_version), false,
-      quic::QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
-
   quic::QuicFrames frames;
   frames.push_back(quic::QuicFrame(&rst_frame));
   DVLOG(1) << "Adding frame: " << frames.back();
@@ -265,9 +268,29 @@
     frames.push_back(quic::QuicFrame(&stop));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
-  frames.push_back(quic::QuicFrame(headers_frame));
-  DVLOG(1) << "Adding frame: " << frames.back();
 
+  if (quic::VersionUsesQpack(version_.transport_version)) {
+    std::vector<std::string> data = QpackEncodeHeaders(
+        stream_id, std::move(headers), spdy_headers_frame_length);
+    std::vector<quic::QuicStreamFrame> stream_frames =
+        GenerateNextStreamFrames(stream_id, fin, data);
+    for (const auto& frame : stream_frames)
+      frames.push_back(quic::QuicFrame(frame));
+    InitializeHeader(num, include_version);
+    return MakeMultipleFramesPacket(header_, frames, nullptr);
+  }
+
+  spdy::SpdySerializedFrame spdy_frame = MakeSpdyHeadersFrame(
+      stream_id, fin, priority, std::move(headers), parent_stream_id);
+  if (spdy_headers_frame_length) {
+    *spdy_headers_frame_length = spdy_frame.size();
+  }
+  quic::QuicStreamFrame headers_frame = GenerateNextStreamFrame(
+      quic::QuicUtils::GetHeadersStreamId(version_.transport_version), false,
+      quic::QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
+  frames.push_back(quic::QuicFrame(headers_frame));
+
+  InitializeHeader(num, include_version);
   return MakeMultipleFramesPacket(header_, frames, nullptr);
 }
 
@@ -560,7 +583,6 @@
     quic::QuicStringPiece data) {
   InitializeHeader(packet_number, should_include_version);
   quic::QuicStreamFrame frame = GenerateNextStreamFrame(stream_id, fin, data);
-  DVLOG(1) << "Adding frame: " << frame;
   return MakePacket(header_, quic::QuicFrame(frame));
 }
 
@@ -577,7 +599,6 @@
     bool is_fin = fin && (i == data_writes.size() - 1);
     quic::QuicFrame quic_frame(
         GenerateNextStreamFrame(stream_id, is_fin, data_writes[i]));
-    DVLOG(1) << "Adding frame: " << quic_frame;
     data_frames.push_back(quic_frame);
   }
   return MakeMultipleFramesPacket(header_, data_frames, nullptr);
@@ -610,7 +631,6 @@
 
   frames.push_back(
       quic::QuicFrame(GenerateNextStreamFrame(stream_id, fin, data)));
-  DVLOG(1) << "Adding frame: " << frames.back();
 
   return MakeMultipleFramesPacket(header_, frames, nullptr);
 }
@@ -645,7 +665,6 @@
     bool is_fin = fin && (i == data_writes.size() - 1);
     quic::QuicFrame quic_frame(GenerateNextStreamFrame(
         stream_id, is_fin, quic::QuicStringPiece(data_writes[i])));
-    DVLOG(1) << "Adding frame: " << quic_frame;
     frames.push_back(quic_frame);
   }
   return MakeMultipleFramesPacket(header_, frames, nullptr);
@@ -663,6 +682,26 @@
     size_t* spdy_headers_frame_length,
     const std::vector<std::string>& data_writes) {
   InitializeHeader(packet_number, should_include_version);
+  if (quic::VersionUsesQpack(version_.transport_version)) {
+    // STREAM frames for HEADERS.
+    std::vector<std::string> data = QpackEncodeHeaders(
+        stream_id, std::move(headers), spdy_headers_frame_length);
+    std::vector<quic::QuicStreamFrame> stream_frames =
+        GenerateNextStreamFrames(stream_id, false, data);
+
+    // STREAM frames for DATA.
+    for (size_t i = 0; i < data_writes.size(); ++i) {
+      bool is_fin = fin && (i == data_writes.size() - 1);
+      stream_frames.push_back(GenerateNextStreamFrame(
+          stream_id, is_fin, quic::QuicStringPiece(data_writes[i])));
+    }
+
+    quic::QuicFrames frames;
+    for (const auto& frame : stream_frames)
+      frames.push_back(quic::QuicFrame(frame));
+    return MakeMultipleFramesPacket(header_, frames, nullptr);
+  }
+
   spdy::SpdySerializedFrame spdy_frame =
       MakeSpdyHeadersFrame(stream_id, fin && data_writes.empty(), priority,
                            std::move(headers), parent_stream_id);
@@ -675,7 +714,6 @@
       quic::QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
   quic::QuicFrames frames;
   frames.push_back(quic::QuicFrame(frame));
-  DVLOG(1) << "Adding frame: " << frames.back();
 
   // quic::QuicFrame takes a raw pointer. Use a std::vector here so we keep
   // StreamFrames alive until MakeMultipleFramesPacket is done.
@@ -684,7 +722,6 @@
     bool is_fin = fin && (i == data_writes.size() - 1);
     quic::QuicFrame quic_frame(GenerateNextStreamFrame(
         stream_id, is_fin, quic::QuicStringPiece(data_writes[i])));
-    DVLOG(1) << "Adding frame: " << quic_frame;
     frames.push_back(quic_frame);
   }
   return MakeMultipleFramesPacket(header_, frames, nullptr);
@@ -719,6 +756,19 @@
     size_t* spdy_headers_frame_length,
     std::string* stream_data) {
   InitializeHeader(packet_number, should_include_version);
+
+  if (quic::VersionUsesQpack(version_.transport_version)) {
+    std::vector<std::string> data = QpackEncodeHeaders(
+        stream_id, std::move(headers), spdy_headers_frame_length);
+    std::vector<quic::QuicStreamFrame> stream_frames =
+        GenerateNextStreamFrames(stream_id, fin, data);
+
+    quic::QuicFrames frames;
+    for (const auto& frame : stream_frames)
+      frames.push_back(quic::QuicFrame(frame));
+    return MakeMultipleFramesPacket(header_, frames, nullptr);
+  }
+
   spdy::SpdySerializedFrame spdy_frame = MakeSpdyHeadersFrame(
       stream_id, fin, priority, std::move(headers), parent_stream_id);
   *stream_data = std::string(spdy_frame.data(), spdy_frame.size());
@@ -743,6 +793,29 @@
     quic::QuicStreamId parent_stream_id,
     size_t* spdy_headers_frame_length,
     quic::QuicRstStreamErrorCode error_code) {
+  if (quic::VersionUsesQpack(version_.transport_version)) {
+    // STREAM frames for HEADERS.
+    std::vector<std::string> data = QpackEncodeHeaders(
+        stream_id, std::move(headers), spdy_headers_frame_length);
+    std::vector<quic::QuicStreamFrame> stream_frames =
+        GenerateNextStreamFrames(stream_id, fin, data);
+
+    quic::QuicFrames frames;
+    for (const auto& frame : stream_frames)
+      frames.push_back(quic::QuicFrame(frame));
+
+    quic::QuicRstStreamFrame rst_frame(1, stream_id, error_code,
+                                       stream_offsets_[stream_id]);
+    frames.push_back(quic::QuicFrame(&rst_frame));
+    DVLOG(1) << "Adding frame: " << frames.back();
+
+    quic::QuicStopSendingFrame stop(1, stream_id, error_code);
+    frames.push_back(quic::QuicFrame(&stop));
+    DVLOG(1) << "Adding frame: " << frames.back();
+    InitializeHeader(packet_number, should_include_version);
+    return MakeMultipleFramesPacket(header_, frames, nullptr);
+  }
+
   spdy::SpdySerializedFrame spdy_frame = MakeSpdyHeadersFrame(
       stream_id, fin, priority, std::move(headers), parent_stream_id);
   if (spdy_headers_frame_length) {
@@ -849,6 +922,20 @@
     spdy::SpdyHeaderBlock headers,
     size_t* spdy_headers_frame_length) {
   InitializeHeader(packet_number, should_include_version);
+
+  if (quic::VersionUsesQpack(version_.transport_version)) {
+    // STREAM frames for HEADERS.
+    std::vector<std::string> data = QpackEncodeHeaders(
+        stream_id, std::move(headers), spdy_headers_frame_length);
+    std::vector<quic::QuicStreamFrame> stream_frames =
+        GenerateNextStreamFrames(stream_id, fin, data);
+
+    quic::QuicFrames frames;
+    for (const auto& frame : stream_frames)
+      frames.push_back(quic::QuicFrame(frame));
+    return MakeMultipleFramesPacket(header_, frames, nullptr);
+  }
+
   spdy::SpdySerializedFrame spdy_frame;
   spdy::SpdyHeadersIR headers_frame(stream_id, std::move(headers));
   headers_frame.set_fin(fin);
@@ -1018,26 +1105,33 @@
   quic::QuicFrames frames;
   // A stream frame containing stream type will be written on the control stream
   // first.
-  char type[] = {0x00};
-  quic::QuicStreamFrame type_frame =
-      GenerateNextStreamFrame(quic::QuicUtils::GetFirstUnidirectionalStreamId(
-                                  version_.transport_version, perspective_),
-                              false, quic::QuicStringPiece(type, 1));
-  frames.push_back(quic::QuicFrame(type_frame));
-
+  std::string type(1, 0x00);
   quic::SettingsFrame settings;
   settings.values[quic::kSettingsMaxHeaderListSize] =
       quic::kDefaultMaxUncompressedHeaderSize;
   std::unique_ptr<char[]> buffer;
   quic::QuicByteCount frame_length =
-      encoder_.SerializeSettingsFrame(settings, &buffer);
-  InitializeHeader(packet_number, /*should_include_version*/ true);
-  *stream_data = std::string(type, 1) + std::string(buffer.get(), frame_length);
-  quic::QuicStreamFrame quic_frame = GenerateNextStreamFrame(
+      http_encoder_.SerializeSettingsFrame(settings, &buffer);
+  std::string settings_data = std::string(buffer.get(), frame_length);
+  *stream_data = type + settings_data;
+
+  std::vector<std::string> data;
+  if (coalesce_http_frames_) {
+    data = {type + settings_data};
+  } else {
+    data = {type, settings_data};
+  }
+
+  quic::QuicStreamId stream_id =
       quic::QuicUtils::GetFirstUnidirectionalStreamId(
-          version_.transport_version, perspective_),
-      false, quic::QuicStringPiece(buffer.get(), frame_length));
-  frames.push_back(quic::QuicFrame(quic_frame));
+          version_.transport_version, perspective_);
+
+  std::vector<quic::QuicStreamFrame> stream_frames =
+      GenerateNextStreamFrames(stream_id, false, data);
+  for (const auto& frame : stream_frames)
+    frames.push_back(quic::QuicFrame(frame));
+
+  InitializeHeader(packet_number, /*should_include_version*/ true);
   return MakeMultipleFramesPacket(header_, frames, nullptr);
 }
 
@@ -1059,7 +1153,6 @@
   quic::QuicStreamFrame quic_frame = GenerateNextStreamFrame(
       quic::QuicUtils::GetHeadersStreamId(version_.transport_version), false,
       quic::QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
-  DVLOG(1) << "Adding frame: " << quic::QuicFrame(quic_frame);
   InitializeHeader(packet_number, should_include_version);
   return MakePacket(header_, quic::QuicFrame(quic_frame));
 }
@@ -1103,7 +1196,6 @@
         quic::QuicStringPiece(spdy_frame->data(), spdy_frame->size()));
 
     frames.push_back(quic::QuicFrame(stream_frame));
-    DVLOG(1) << "Adding frame: " << frames.back();
   }
 
   InitializeHeader(packet_number, should_include_version);
@@ -1141,9 +1233,71 @@
     quic::QuicStringPiece data) {
   quic::QuicStreamFrame frame(stream_id, fin, stream_offsets_[stream_id], data);
   stream_offsets_[stream_id] += data.length();
+  DVLOG(1) << "Adding frame: " << frame;
   return frame;
 }
 
+std::vector<std::string> QuicTestPacketMaker::QpackEncodeHeaders(
+    quic::QuicStreamId stream_id,
+    spdy::SpdyHeaderBlock headers,
+    size_t* encoded_data_length) {
+  DCHECK(quic::VersionUsesQpack(version_.transport_version));
+  std::vector<std::string> data;
+
+  std::string encoded_headers =
+      qpack_encoder_.EncodeHeaderList(stream_id, &headers);
+
+  // Generate HEADERS frame header.
+  std::unique_ptr<char[]> headers_frame_header;
+  const size_t headers_frame_header_length =
+      http_encoder_.SerializeHeadersFrameHeader(encoded_headers.size(),
+                                                &headers_frame_header);
+
+  // Possible add a PUSH stream type.
+  if (!quic::QuicUtils::IsBidirectionalStreamId(stream_id) &&
+      stream_offsets_[stream_id] == 0) {
+    // Push stream type header
+    data.push_back("\x01");
+  }
+
+  // Add the HEADERS frame header.
+  data.push_back(
+      std::string(headers_frame_header.get(), headers_frame_header_length));
+  // Add the HEADERS frame payload.
+  data.push_back(encoded_headers);
+
+  if (coalesce_http_frames_) {
+    std::string coalesced;
+    for (const auto& d : data) {
+      coalesced += d;
+    }
+    data = {coalesced};
+  }
+
+  // Compute the total data length.
+  if (encoded_data_length) {
+    *encoded_data_length = 0;
+    for (const auto& d : data)
+      *encoded_data_length += d.length();
+  }
+  return data;
+}
+
+std::vector<quic::QuicStreamFrame>
+QuicTestPacketMaker::GenerateNextStreamFrames(
+    quic::QuicStreamId stream_id,
+    bool fin,
+    const std::vector<std::string>& data) {
+  std::vector<quic::QuicStreamFrame> frames;
+  for (size_t i = 0; i < data.size(); ++i) {
+    const bool frame_fin = i == data.size() - 1 && fin;
+    quic::QuicStreamFrame frame =
+        GenerateNextStreamFrame(stream_id, frame_fin, data[i]);
+    frames.push_back(frame);
+  }
+  return frames;
+}
+
 quic::QuicPacketNumberLength QuicTestPacketMaker::GetPacketNumberLength()
     const {
   if (version_.transport_version > quic::QUIC_VERSION_43 &&
@@ -1194,9 +1348,8 @@
 }
 
 void QuicTestPacketMaker::Reset() {
-  for (const auto& kv : stream_offsets_) {
+  for (const auto& kv : stream_offsets_)
     stream_offsets_[kv.first] = 0;
-  }
 }
 
 }  // namespace test
diff --git a/net/quic/quic_test_packet_maker.h b/net/quic/quic_test_packet_maker.h
index 65c8a57a..26828bd 100644
--- a/net/quic/quic_test_packet_maker.h
+++ b/net/quic/quic_test_packet_maker.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "net/base/request_priority.h"
 #include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
@@ -349,7 +350,28 @@
         version_.transport_version)] = offset;
   }
 
+  void set_coalesce_http_frames(bool coalesce_http_frames) {
+    coalesce_http_frames_ = coalesce_http_frames;
+  }
+
  private:
+  // QpackEncoder::DecoderStreamErrorDelegate implementation that does nothing
+  class DecoderStreamErrorDelegate
+      : public quic::QpackEncoder::DecoderStreamErrorDelegate {
+   public:
+    ~DecoderStreamErrorDelegate() override = default;
+
+    void OnDecoderStreamError(quic::QuicStringPiece error_message) override;
+  };
+
+  // QpackEncoderStreamSender::Delegate implementation that does nothing.
+  class EncoderStreamSenderDelegate : public quic::QpackStreamSenderDelegate {
+   public:
+    ~EncoderStreamSenderDelegate() override = default;
+
+    void WriteStreamData(quic::QuicStringPiece data) override;
+  };
+
   std::unique_ptr<quic::QuicReceivedPacket> MakePacket(
       const quic::QuicPacketHeader& header,
       const quic::QuicFrame& frame);
@@ -373,6 +395,15 @@
                                                 bool fin,
                                                 quic::QuicStringPiece data);
 
+  std::vector<quic::QuicStreamFrame> GenerateNextStreamFrames(
+      quic::QuicStreamId stream_id,
+      bool fin,
+      const std::vector<std::string>& data);
+
+  std::vector<std::string> QpackEncodeHeaders(quic::QuicStreamId stream_id,
+                                              spdy::SpdyHeaderBlock headers,
+                                              size_t* encoded_data_length);
+
   quic::QuicPacketNumberLength GetPacketNumberLength() const;
 
   quic::QuicConnectionId DestinationConnectionId() const;
@@ -387,7 +418,11 @@
   std::string host_;
   spdy::SpdyFramer spdy_request_framer_;
   spdy::SpdyFramer spdy_response_framer_;
-  quic::HttpEncoder encoder_;
+  quic::HttpEncoder http_encoder_;
+  bool coalesce_http_frames_;
+  DecoderStreamErrorDelegate decoder_stream_error_delegate_;
+  EncoderStreamSenderDelegate encoder_stream_sender_delegate_;
+  quic::QpackEncoder qpack_encoder_;
   quic::test::MockRandom random_generator_;
   std::map<quic::QuicStreamId, quic::QuicStreamOffset> stream_offsets_;
   quic::QuicPacketHeader header_;
diff --git a/net/socket/connect_job.cc b/net/socket/connect_job.cc
index 871d0f5..015b416 100644
--- a/net/socket/connect_job.cc
+++ b/net/socket/connect_job.cc
@@ -129,7 +129,7 @@
         ssl_params = base::MakeRefCounted<SSLSocketParams>(
             std::move(proxy_tcp_params), nullptr, nullptr,
             proxy_server.host_port_pair(), *ssl_config_for_proxy,
-            PRIVACY_MODE_DISABLED);
+            PRIVACY_MODE_DISABLED, network_isolation_key);
         proxy_tcp_params = nullptr;
       }
 
@@ -158,7 +158,7 @@
     auto ssl_params = base::MakeRefCounted<SSLSocketParams>(
         std::move(ssl_tcp_params), std::move(socks_params),
         std::move(http_proxy_params), endpoint, *ssl_config_for_origin,
-        privacy_mode);
+        privacy_mode, network_isolation_key);
     return std::make_unique<SSLConnectJob>(
         request_priority, socket_tag, common_connect_job_params,
         std::move(ssl_params), delegate, nullptr /* net_log */);
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index c371d50..69c1f95 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -824,6 +824,13 @@
     EXPECT_EQ(*next_ssl_data->expected_false_start_enabled,
               ssl_config.false_start_enabled);
   }
+  if (next_ssl_data->expected_host_and_port) {
+    EXPECT_EQ(*next_ssl_data->expected_host_and_port, host_and_port);
+  }
+  if (next_ssl_data->expected_network_isolation_key) {
+    EXPECT_EQ(*next_ssl_data->expected_network_isolation_key,
+              ssl_config.network_isolation_key);
+  }
   return std::unique_ptr<SSLClientSocket>(new MockSSLClientSocket(
       std::move(stream_socket), host_and_port, ssl_config, next_ssl_data));
 }
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index d4ae682..9a53e1c 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -495,6 +495,8 @@
   base::Optional<bool> expected_send_client_cert;
   scoped_refptr<X509Certificate> expected_client_cert;
   base::Optional<bool> expected_false_start_enabled;
+  base::Optional<HostPortPair> expected_host_and_port;
+  base::Optional<NetworkIsolationKey> expected_network_isolation_key;
 
   bool is_connect_data_consumed = false;
   bool is_confirm_data_consumed = false;
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 857dc8a0..30804e3 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -14,6 +14,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/containers/span.h"
+#include "base/feature_list.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
 #include "base/memory/singleton.h"
@@ -49,6 +50,7 @@
 #include "net/ssl/ssl_cipher_suite_names.h"
 #include "net/ssl/ssl_client_session_cache.h"
 #include "net/ssl/ssl_connection_status_flags.h"
+#include "net/ssl/ssl_handshake_details.h"
 #include "net/ssl/ssl_info.h"
 #include "net/ssl/ssl_key_logger.h"
 #include "net/ssl/ssl_private_key.h"
@@ -871,9 +873,7 @@
   if (!ssl_config_.alpn_protos.empty()) {
     std::vector<uint8_t> wire_protos =
         SerializeNextProtos(ssl_config_.alpn_protos);
-    SSL_set_alpn_protos(ssl_.get(),
-                        wire_protos.empty() ? nullptr : &wire_protos[0],
-                        wire_protos.size());
+    SSL_set_alpn_protos(ssl_.get(), wire_protos.data(), wire_protos.size());
   }
 
   SSL_enable_signed_cert_timestamps(ssl_.get());
@@ -960,10 +960,6 @@
     return OK;
   }
 
-  if (IsCachingEnabled()) {
-    ssl_client_session_cache_->ResetLookupCount(GetSessionCacheKey());
-  }
-
   const uint8_t* alpn_proto = nullptr;
   unsigned alpn_len = 0;
   SSL_get0_alpn_selected(ssl_.get(), &alpn_proto, &alpn_len);
@@ -1074,6 +1070,26 @@
     }
   }
 
+  SSLHandshakeDetails details;
+  if (SSL_version(ssl_.get()) < TLS1_3_VERSION) {
+    if (SSL_session_reused(ssl_.get())) {
+      details = SSLHandshakeDetails::kTLS12Resume;
+    } else if (SSL_in_false_start(ssl_.get())) {
+      details = SSLHandshakeDetails::kTLS12FalseStart;
+    } else {
+      details = SSLHandshakeDetails::kTLS12Full;
+    }
+  } else {
+    if (SSL_in_early_data(ssl_.get())) {
+      details = SSLHandshakeDetails::kTLS13Early;
+    } else if (SSL_session_reused(ssl_.get())) {
+      details = SSLHandshakeDetails::kTLS13Resume;
+    } else {
+      details = SSLHandshakeDetails::kTLS13Full;
+    }
+  }
+  UMA_HISTOGRAM_ENUMERATION("Net.SSLHandshakeDetails", details);
+
   completed_connect_ = true;
   next_handshake_state_ = STATE_NONE;
   return OK;
@@ -1622,6 +1638,11 @@
 }
 
 std::string SSLClientSocketImpl::GetSessionCacheKey() const {
+  if (base::FeatureList::IsEnabled(
+          features::kPartitionSSLSessionsByNetworkIsolationKey)) {
+    return host_and_port_.ToString() + '/' +
+           ssl_config_.network_isolation_key.ToString();
+  }
   return host_and_port_.ToString();
 }
 
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index f07bdcd..4a96fff 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -34,6 +34,7 @@
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/test_completion_callback.h"
 #include "net/cert/asn1_util.h"
 #include "net/cert/ct_policy_enforcer.h"
@@ -66,6 +67,7 @@
 #include "net/ssl/ssl_client_session_cache.h"
 #include "net/ssl/ssl_config_service.h"
 #include "net/ssl/ssl_connection_status_flags.h"
+#include "net/ssl/ssl_handshake_details.h"
 #include "net/ssl/ssl_info.h"
 #include "net/ssl/ssl_server_config.h"
 #include "net/ssl/test_ssl_private_key.h"
@@ -85,6 +87,8 @@
 #include "third_party/boringssl/src/include/openssl/bio.h"
 #include "third_party/boringssl/src/include/openssl/evp.h"
 #include "third_party/boringssl/src/include/openssl/pem.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -3170,6 +3174,120 @@
   EXPECT_EQ(kProtoHTTP11, sock_->GetNegotiatedProtocol());
 }
 
+// Tests that the session cache is not sharded by NetworkIsolationKey if the
+// feature is disabled.
+TEST_F(SSLClientSocketTest, SessionResumptionNetworkIsolationKeyDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      features::kPartitionSSLSessionsByNetworkIsolationKey);
+
+  SpawnedTestServer::SSLOptions ssl_options;
+  ASSERT_TRUE(StartTestServer(ssl_options));
+
+  // First, perform a full handshake.
+  SSLConfig ssl_config;
+  int rv;
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  SSLInfo ssl_info;
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+
+  // The next connection should resume.
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+  sock_.reset();
+
+  // Using a different NetworkIsolationKey shares session cache key because
+  // sharding is disabled.
+  ssl_config.network_isolation_key =
+      NetworkIsolationKey(url::Origin::Create(GURL("https://a.test")));
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+  sock_.reset();
+
+  ssl_config.network_isolation_key =
+      NetworkIsolationKey(url::Origin::Create(GURL("https://b.test")));
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+  sock_.reset();
+}
+
+// Tests that the session cache is sharded by NetworkIsolationKey if the
+// feature is enabled.
+TEST_F(SSLClientSocketTest, SessionResumptionNetworkIsolationKeyEnabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kPartitionSSLSessionsByNetworkIsolationKey);
+
+  const NetworkIsolationKey kNetworkIsolationKeyA(
+      url::Origin::Create(GURL("https://a.test")));
+  const NetworkIsolationKey kNetworkIsolationKeyB(
+      url::Origin::Create(GURL("https://b.test")));
+
+  SpawnedTestServer::SSLOptions ssl_options;
+  ASSERT_TRUE(StartTestServer(ssl_options));
+
+  // First, perform a full handshake.
+  SSLConfig ssl_config;
+  int rv;
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  SSLInfo ssl_info;
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+
+  // The next connection should resume.
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+  sock_.reset();
+
+  // Using a different NetworkIsolationKey uses a different session cache key.
+  ssl_config.network_isolation_key = kNetworkIsolationKeyA;
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+  sock_.reset();
+
+  // We, however, can resume under that newly-established session.
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+  sock_.reset();
+
+  // Repeat with another non-null key.
+  ssl_config.network_isolation_key = kNetworkIsolationKeyB;
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+  sock_.reset();
+
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+  sock_.reset();
+
+  // b.test does not evict a.test's session.
+  ssl_config.network_isolation_key = kNetworkIsolationKeyA;
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+  sock_.reset();
+}
+
 // Tests that connections with certificate errors do not add entries to the
 // session cache.
 TEST_F(SSLClientSocketTest, CertificateErrorNoResume) {
@@ -5395,4 +5513,134 @@
   }
 }
 
+struct SSLHandshakeDetailsParams {
+  bool alpn;
+  bool early_data;
+  uint16_t version;
+  SSLHandshakeDetails expected_initial;
+  SSLHandshakeDetails expected_resume;
+};
+
+const SSLHandshakeDetailsParams kSSLHandshakeDetailsParams[] = {
+    // TLS 1.0 and 1.1 never do False Start.
+    {false /* no ALPN */, false /* no early data */, SSL_PROTOCOL_VERSION_TLS1,
+     SSLHandshakeDetails::kTLS12Full, SSLHandshakeDetails::kTLS12Resume},
+    {false /* no ALPN */, false /* no early data */,
+     SSL_PROTOCOL_VERSION_TLS1_1, SSLHandshakeDetails::kTLS12Full,
+     SSLHandshakeDetails::kTLS12Resume},
+
+    // TLS 1.2 does False Start if ALPN is enabled.
+    {false /* no ALPN */, false /* no early data */,
+     SSL_PROTOCOL_VERSION_TLS1_2, SSLHandshakeDetails::kTLS12Full,
+     SSLHandshakeDetails::kTLS12Resume},
+    {true /* ALPN */, false /* no early data */, SSL_PROTOCOL_VERSION_TLS1_2,
+     SSLHandshakeDetails::kTLS12FalseStart, SSLHandshakeDetails::kTLS12Resume},
+
+    // TLS 1.3 supports full handshakes, resumption, and 0-RTT.
+    {false /* no ALPN */, false /* no early data */,
+     SSL_PROTOCOL_VERSION_TLS1_3, SSLHandshakeDetails::kTLS13Full,
+     SSLHandshakeDetails::kTLS13Resume},
+    {false /* no ALPN */, true /* early data */, SSL_PROTOCOL_VERSION_TLS1_3,
+     SSLHandshakeDetails::kTLS13Full, SSLHandshakeDetails::kTLS13Early},
+};
+
+class SSLHandshakeDetailsTest
+    : public SSLClientSocketTest,
+      public ::testing::WithParamInterface<SSLHandshakeDetailsParams> {};
+
+INSTANTIATE_TEST_SUITE_P(/* no prefix */,
+                         SSLHandshakeDetailsTest,
+                         ::testing::ValuesIn(kSSLHandshakeDetailsParams));
+
+TEST_P(SSLHandshakeDetailsTest, Metrics) {
+  // Enable all test features in the server.
+  SSLServerConfig server_config;
+  server_config.version_max = SSL_PROTOCOL_VERSION_TLS1;
+  server_config.version_max = SSL_PROTOCOL_VERSION_TLS1_3;
+  server_config.early_data_enabled = true;
+  server_config.alpn_protos = {kProtoHTTP11};
+  ASSERT_TRUE(
+      StartEmbeddedTestServer(EmbeddedTestServer::CERT_OK, server_config));
+
+  SSLConfig client_config;
+  client_config.version_min = GetParam().version;
+  client_config.version_max = GetParam().version;
+  client_config.early_data_enabled = GetParam().early_data;
+  if (GetParam().alpn) {
+    client_config.alpn_protos = {kProtoHTTP11};
+  }
+
+  SSLVersion version;
+  switch (GetParam().version) {
+    case SSL_PROTOCOL_VERSION_TLS1:
+      version = SSL_CONNECTION_VERSION_TLS1;
+      break;
+    case SSL_PROTOCOL_VERSION_TLS1_1:
+      version = SSL_CONNECTION_VERSION_TLS1_1;
+      break;
+    case SSL_PROTOCOL_VERSION_TLS1_2:
+      version = SSL_CONNECTION_VERSION_TLS1_2;
+      break;
+    case SSL_PROTOCOL_VERSION_TLS1_3:
+      version = SSL_CONNECTION_VERSION_TLS1_3;
+      break;
+    default:
+      FAIL() << GetParam().version;
+  }
+
+  // Make the initial connection.
+  {
+    base::HistogramTester histograms;
+    int rv;
+    ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv));
+    EXPECT_THAT(rv, IsOk());
+
+    // Sanity-check the socket matches the test parameters.
+    SSLInfo info;
+    ASSERT_TRUE(sock_->GetSSLInfo(&info));
+    EXPECT_EQ(version, SSLConnectionStatusToVersion(info.connection_status));
+    EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, info.handshake_type);
+    EXPECT_EQ(GetParam().alpn, sock_->WasAlpnNegotiated());
+
+    histograms.ExpectUniqueSample("Net.SSLHandshakeDetails",
+                                  GetParam().expected_initial, 1);
+
+    // Use the socket to ensure the session ticket has been picked up.
+    base::StringPiece request = "GET / HTTP/1.0\r\n\r\n";
+    TestCompletionCallback callback;
+    while (!request.empty()) {
+      auto request_buffer =
+          base::MakeRefCounted<StringIOBuffer>(request.as_string());
+      rv = callback.GetResult(
+          sock_->Write(request_buffer.get(), request_buffer->size(),
+                       callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
+      ASSERT_GT(rv, 0);
+      request = request.substr(rv);
+    }
+
+    auto response_buffer = base::MakeRefCounted<IOBuffer>(1024);
+    rv = callback.GetResult(
+        sock_->Read(response_buffer.get(), 1024, callback.callback()));
+    ASSERT_GT(rv, 0);
+  }
+
+  // Make a resumption connection.
+  {
+    base::HistogramTester histograms;
+    int rv;
+    ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv));
+    EXPECT_THAT(rv, IsOk());
+
+    // Sanity-check the socket matches the test parameters.
+    SSLInfo info;
+    ASSERT_TRUE(sock_->GetSSLInfo(&info));
+    EXPECT_EQ(version, SSLConnectionStatusToVersion(info.connection_status));
+    EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, info.handshake_type);
+    EXPECT_EQ(GetParam().alpn, sock_->WasAlpnNegotiated());
+
+    histograms.ExpectUniqueSample("Net.SSLHandshakeDetails",
+                                  GetParam().expected_resume, 1);
+  }
+}
+
 }  // namespace net
diff --git a/net/socket/ssl_connect_job.cc b/net/socket/ssl_connect_job.cc
index 3c512cd..c5c936b 100644
--- a/net/socket/ssl_connect_job.cc
+++ b/net/socket/ssl_connect_job.cc
@@ -46,13 +46,15 @@
     scoped_refptr<HttpProxySocketParams> http_proxy_params,
     const HostPortPair& host_and_port,
     const SSLConfig& ssl_config,
-    PrivacyMode privacy_mode)
+    PrivacyMode privacy_mode,
+    NetworkIsolationKey network_isolation_key)
     : direct_params_(std::move(direct_params)),
       socks_proxy_params_(std::move(socks_proxy_params)),
       http_proxy_params_(std::move(http_proxy_params)),
       host_and_port_(host_and_port),
       ssl_config_(ssl_config),
-      privacy_mode_(privacy_mode) {
+      privacy_mode_(privacy_mode),
+      network_isolation_key_(network_isolation_key) {
   // Only one set of lower level ConnectJob params should be non-NULL.
   DCHECK((direct_params_ && !socks_proxy_params_ && !http_proxy_params_) ||
          (!direct_params_ && socks_proxy_params_ && !http_proxy_params_) ||
@@ -355,9 +357,10 @@
           ? ssl_client_socket_context_privacy_mode()
           : ssl_client_socket_context();
 
+  SSLConfig ssl_config = params_->ssl_config();
+  ssl_config.network_isolation_key = params_->network_isolation_key();
   ssl_socket_ = client_socket_factory()->CreateSSLClientSocket(
-      std::move(nested_socket_), params_->host_and_port(),
-      params_->ssl_config(), context);
+      std::move(nested_socket_), params_->host_and_port(), ssl_config, context);
   nested_connect_job_.reset();
   return ssl_socket_->Connect(callback_);
 }
diff --git a/net/socket/ssl_connect_job.h b/net/socket/ssl_connect_job.h
index bc19886..575f4293 100644
--- a/net/socket/ssl_connect_job.h
+++ b/net/socket/ssl_connect_job.h
@@ -14,6 +14,7 @@
 #include "net/base/completion_once_callback.h"
 #include "net/base/completion_repeating_callback.h"
 #include "net/base/net_export.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/privacy_mode.h"
 #include "net/socket/connect_job.h"
 #include "net/socket/connection_attempts.h"
@@ -41,7 +42,8 @@
                   scoped_refptr<HttpProxySocketParams> http_proxy_params,
                   const HostPortPair& host_and_port,
                   const SSLConfig& ssl_config,
-                  PrivacyMode privacy_mode);
+                  PrivacyMode privacy_mode,
+                  NetworkIsolationKey network_isolation_key);
 
   // Returns the type of the underlying connection.
   ConnectionType GetConnectionType() const;
@@ -59,6 +61,9 @@
   const HostPortPair& host_and_port() const { return host_and_port_; }
   const SSLConfig& ssl_config() const { return ssl_config_; }
   PrivacyMode privacy_mode() const { return privacy_mode_; }
+  const NetworkIsolationKey& network_isolation_key() const {
+    return network_isolation_key_;
+  }
 
  private:
   friend class base::RefCounted<SSLSocketParams>;
@@ -70,6 +75,7 @@
   const HostPortPair host_and_port_;
   const SSLConfig ssl_config_;
   const PrivacyMode privacy_mode_;
+  const NetworkIsolationKey network_isolation_key_;
 
   DISALLOW_COPY_AND_ASSIGN(SSLSocketParams);
 };
diff --git a/net/socket/ssl_connect_job_unittest.cc b/net/socket/ssl_connect_job_unittest.cc
index 799d232..06f4d2e 100644
--- a/net/socket/ssl_connect_job_unittest.cc
+++ b/net/socket/ssl_connect_job_unittest.cc
@@ -135,7 +135,8 @@
                                             : nullptr,
         proxy == ProxyServer::SCHEME_SOCKS5 ? socks_socket_params_ : nullptr,
         proxy == ProxyServer::SCHEME_HTTP ? http_proxy_socket_params_ : nullptr,
-        HostPortPair("host", 443), ssl_config_, PRIVACY_MODE_DISABLED);
+        HostPortPair("host", 443), ssl_config_, PRIVACY_MODE_DISABLED,
+        NetworkIsolationKey());
   }
 
   void AddAuthToCache() {
diff --git a/net/socket/ssl_server_socket_impl.cc b/net/socket/ssl_server_socket_impl.cc
index bb3e4fc8..dac7d7e 100644
--- a/net/socket/ssl_server_socket_impl.cc
+++ b/net/socket/ssl_server_socket_impl.cc
@@ -26,6 +26,7 @@
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/ssl/ssl_info.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
 #include "third_party/boringssl/src/include/openssl/err.h"
 #include "third_party/boringssl/src/include/openssl/pool.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
@@ -142,6 +143,13 @@
                                                       size_t max_out);
   void OnPrivateKeyComplete(Error error, const std::vector<uint8_t>& signature);
 
+  static int ALPNSelectCallback(SSL* ssl,
+                                const uint8_t** out,
+                                uint8_t* out_len,
+                                const uint8_t* in,
+                                unsigned in_len,
+                                void* arg);
+
   // SocketBIOAdapter::Delegate implementation.
   void OnReadReady() override;
   void OnWriteReady() override;
@@ -202,6 +210,8 @@
   State next_handshake_state_;
   bool completed_handshake_;
 
+  NextProto negotiated_protocol_;
+
   base::WeakPtrFactory<SocketImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SocketImpl);
@@ -218,6 +228,7 @@
       transport_socket_(std::move(transport_socket)),
       next_handshake_state_(STATE_NONE),
       completed_handshake_(false),
+      negotiated_protocol_(kProtoUnknown),
       weak_factory_(this) {
   ssl_.reset(SSL_new(context_->ssl_ctx_.get()));
   SSL_set_app_data(ssl_.get(), this);
@@ -335,6 +346,41 @@
   DoHandshakeLoop(ERR_IO_PENDING);
 }
 
+int SSLServerContextImpl::SocketImpl::ALPNSelectCallback(SSL* ssl,
+                                                         const uint8_t** out,
+                                                         uint8_t* out_len,
+                                                         const uint8_t* in,
+                                                         unsigned in_len,
+                                                         void* arg) {
+  SSLServerContextImpl::SocketImpl* socket =
+      static_cast<SSLServerContextImpl::SocketImpl*>(SSL_get_ex_data(
+          ssl, SocketDataIndex::GetInstance()->ssl_socket_data_index_));
+
+  // Iterate over the server protocols in preference order.
+  for (NextProto server_proto :
+       socket->context_->ssl_server_config_.alpn_protos) {
+    const char* server_proto_str = NextProtoToString(server_proto);
+
+    // See if the client advertised the corresponding protocol.
+    CBS cbs;
+    CBS_init(&cbs, in, in_len);
+    while (CBS_len(&cbs) != 0) {
+      CBS client_proto;
+      if (!CBS_get_u8_length_prefixed(&cbs, &client_proto)) {
+        return SSL_TLSEXT_ERR_NOACK;
+      }
+      if (base::StringPiece(
+              reinterpret_cast<const char*>(CBS_data(&client_proto)),
+              CBS_len(&client_proto)) == server_proto_str) {
+        *out = CBS_data(&client_proto);
+        *out_len = CBS_len(&client_proto);
+        return SSL_TLSEXT_ERR_OK;
+      }
+    }
+  }
+  return SSL_TLSEXT_ERR_NOACK;
+}
+
 int SSLServerContextImpl::SocketImpl::Handshake(
     CompletionOnceCallback callback) {
   net_log_.BeginEvent(NetLogEventType::SSL_SERVER_HANDSHAKE);
@@ -486,13 +532,11 @@
 }
 
 bool SSLServerContextImpl::SocketImpl::WasAlpnNegotiated() const {
-  NOTIMPLEMENTED();
-  return false;
+  return negotiated_protocol_ != kProtoUnknown;
 }
 
 NextProto SSLServerContextImpl::SocketImpl::GetNegotiatedProtocol() const {
-  // ALPN is not supported by this class.
-  return kProtoUnknown;
+  return negotiated_protocol_;
 }
 
 bool SSLServerContextImpl::SocketImpl::GetSSLInfo(SSLInfo* ssl_info) {
@@ -659,6 +703,15 @@
       if (!client_cert_)
         return ERR_SSL_CLIENT_AUTH_CERT_BAD_FORMAT;
     }
+
+    const uint8_t* alpn_proto = nullptr;
+    unsigned alpn_len = 0;
+    SSL_get0_alpn_selected(ssl_.get(), &alpn_proto, &alpn_len);
+    if (alpn_len > 0) {
+      base::StringPiece proto(reinterpret_cast<const char*>(alpn_proto),
+                              alpn_len);
+      negotiated_protocol_ = NextProtoFromString(proto);
+    }
   } else {
     int ssl_error = SSL_get_error(ssl_.get(), rv);
 
@@ -915,6 +968,9 @@
     }
     SSL_CTX_set0_client_CAs(ssl_ctx_.get(), stack.release());
   }
+
+  SSL_CTX_set_alpn_select_cb(ssl_ctx_.get(), &SocketImpl::ALPNSelectCallback,
+                             nullptr);
 }
 
 SSLServerContextImpl::~SSLServerContextImpl() = default;
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index 3570796..743eb6b7 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -98,7 +98,7 @@
   SSLConfig ssl_config;
   auto ssl_params = base::MakeRefCounted<SSLSocketParams>(
       transport_params, nullptr, nullptr, key.host_port_pair(), ssl_config,
-      key.privacy_mode());
+      key.privacy_mode(), key.network_isolation_key());
   TestConnectJobDelegate connect_job_delegate;
   SSLConnectJob connect_job(MEDIUM, SocketTag(), common_connect_job_params,
                             ssl_params, &connect_job_delegate,
diff --git a/net/ssl/ssl_client_session_cache.cc b/net/ssl/ssl_client_session_cache.cc
index 8729b84..24cb13f 100644
--- a/net/ssl/ssl_client_session_cache.cc
+++ b/net/ssl/ssl_client_session_cache.cc
@@ -79,14 +79,6 @@
   return session;
 }
 
-void SSLClientSessionCache::ResetLookupCount(const std::string& cache_key) {
-  // It's possible that the cached session for this key was deleted after the
-  // Lookup. If that's the case, don't do anything.
-  auto iter = cache_.Get(cache_key);
-  if (iter == cache_.end())
-    return;
-}
-
 void SSLClientSessionCache::Insert(const std::string& cache_key,
                                    bssl::UniquePtr<SSL_SESSION> session) {
   if (IsTLS13(session.get())) {
diff --git a/net/ssl/ssl_client_session_cache.h b/net/ssl/ssl_client_session_cache.h
index 2a37293..858dd61c 100644
--- a/net/ssl/ssl_client_session_cache.h
+++ b/net/ssl/ssl_client_session_cache.h
@@ -53,10 +53,6 @@
   // of the MRU list. Returns nullptr if there is none.
   bssl::UniquePtr<SSL_SESSION> Lookup(const std::string& cache_key);
 
-  // Resets the count returned by Lookup to 0 for the session associated with
-  // |cache_key|.
-  void ResetLookupCount(const std::string& cache_key);
-
   // Inserts |session| into the cache at |cache_key|. If there is an existing
   // one, it is released. Every |expiration_check_count| calls, the cache is
   // checked for stale entries.
diff --git a/net/ssl/ssl_config.h b/net/ssl/ssl_config.h
index 7a23fc45..88e4376 100644
--- a/net/ssl/ssl_config.h
+++ b/net/ssl/ssl_config.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "net/base/net_export.h"
+#include "net/base/network_isolation_key.h"
 #include "net/cert/x509_certificate.h"
 #include "net/socket/next_proto.h"
 #include "net/ssl/ssl_private_key.h"
@@ -122,7 +123,7 @@
   bool send_client_cert;
 
   // The list of application level protocols supported with ALPN (Application
-  // Layer Protocol Negotation), in decreasing order of preference.  Protocols
+  // Layer Protocol Negotiation), in decreasing order of preference.  Protocols
   // will be advertised in this order during TLS handshake.
   NextProtoVector alpn_protos;
 
@@ -135,6 +136,10 @@
 
   scoped_refptr<X509Certificate> client_cert;
   scoped_refptr<SSLPrivateKey> client_private_key;
+
+  // If the PartitionSSLSessionsByNetworkIsolationKey feature is enabled, the
+  // session cache is partitioned by this value.
+  NetworkIsolationKey network_isolation_key;
 };
 
 }  // namespace net
diff --git a/net/ssl/ssl_handshake_details.h b/net/ssl/ssl_handshake_details.h
new file mode 100644
index 0000000..a9a024f
--- /dev/null
+++ b/net/ssl/ssl_handshake_details.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SSL_SSL_HANDSHAKE_DETAILS_H_
+#define NET_SSL_SSL_HANDSHAKE_DETAILS_H_
+
+namespace net {
+
+// This enum is persisted into histograms. Values may not be renumbered.
+enum class SSLHandshakeDetails {
+  // TLS 1.2 (or earlier) full handshake (2-RTT)
+  kTLS12Full = 0,
+  // TLS 1.2 (or earlier) resumption (1-RTT)
+  kTLS12Resume = 1,
+  // TLS 1.2 full handshake with False Start (1-RTT)
+  kTLS12FalseStart = 2,
+  // TLS 1.3 full handshake (1-RTT, usually)
+  kTLS13Full = 3,
+  // TLS 1.3 resumption handshake (1-RTT, usually)
+  kTLS13Resume = 4,
+  // TLS 1.3 0-RTT handshake (0-RTT)
+  kTLS13Early = 5,
+  kMaxValue = kTLS13Early,
+};
+
+}  // namespace net
+
+#endif  // NET_SSL_SSL_HANDSHAKE_DETAILS_H_
diff --git a/net/ssl/ssl_server_config.h b/net/ssl/ssl_server_config.h
index 5ae81d46..01bd0390 100644
--- a/net/ssl/ssl_server_config.h
+++ b/net/ssl/ssl_server_config.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "net/base/net_export.h"
+#include "net/socket/next_proto.h"
 #include "net/ssl/ssl_config.h"
 
 namespace net {
@@ -81,6 +82,11 @@
   // This field is meaningful only if client certificates are requested.
   // If a verifier is not provided then all certificates are accepted.
   ClientCertVerifier* client_cert_verifier;
+
+  // The list of application level protocols supported with ALPN (Application
+  // Layer Protocol Negotiation), in decreasing order of preference.  Protocols
+  // will be advertised in this order during TLS handshake.
+  NextProtoVector alpn_protos;
 };
 
 }  // namespace net
diff --git a/net/test/embedded_test_server/embedded_test_server_unittest.cc b/net/test/embedded_test_server/embedded_test_server_unittest.cc
index 23a695c9..18bd4c7 100644
--- a/net/test/embedded_test_server/embedded_test_server_unittest.cc
+++ b/net/test/embedded_test_server/embedded_test_server_unittest.cc
@@ -84,7 +84,7 @@
   void AcceptedSocket(const net::StreamSocket& connection) override {
     base::AutoLock lock(lock_);
     ++socket_accepted_count_;
-    task_runner_->PostTask(FROM_HERE, accept_loop_.QuitClosure());
+    accept_loop_.Quit();
   }
 
   // Get called from the EmbeddedTestServer thread to be notified that
diff --git a/net/test/embedded_test_server/simple_connection_listener.cc b/net/test/embedded_test_server/simple_connection_listener.cc
index cb36d760..d428d79 100644
--- a/net/test/embedded_test_server/simple_connection_listener.cc
+++ b/net/test/embedded_test_server/simple_connection_listener.cc
@@ -5,8 +5,6 @@
 #include "net/test/embedded_test_server/simple_connection_listener.h"
 
 #include "base/location.h"
-#include "base/sequenced_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -16,8 +14,7 @@
     int expected_connections,
     AllowAdditionalConnections allow_additional_connections)
     : expected_connections_(expected_connections),
-      allow_additional_connections_(allow_additional_connections),
-      run_loop_task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+      allow_additional_connections_(allow_additional_connections) {}
 
 SimpleConnectionListener::~SimpleConnectionListener() = default;
 
@@ -26,14 +23,13 @@
   if (allow_additional_connections_ != ALLOW_ADDITIONAL_CONNECTIONS)
     EXPECT_LE(seen_connections_, expected_connections_);
   if (seen_connections_ == expected_connections_)
-    run_loop_task_runner_->PostTask(FROM_HERE, run_loop_.QuitClosure());
+    run_loop_.Quit();
 }
 
 void SimpleConnectionListener::ReadFromSocket(const StreamSocket& socket,
                                               int rv) {}
 
 void SimpleConnectionListener::WaitForConnections() {
-  EXPECT_TRUE(run_loop_task_runner_->RunsTasksInCurrentSequence());
   run_loop_.Run();
 }
 
diff --git a/net/test/embedded_test_server/simple_connection_listener.h b/net/test/embedded_test_server/simple_connection_listener.h
index 3370cf3..440a3a1 100644
--- a/net/test/embedded_test_server/simple_connection_listener.h
+++ b/net/test/embedded_test_server/simple_connection_listener.h
@@ -6,14 +6,9 @@
 #define NET_TEST_EMBEDDED_TEST_SERVER_SIMPLE_CONNECTION_LISTENER_H_
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
 
-namespace base {
-class SequencedTaskRunner;
-}
-
 namespace net {
 
 class StreamSocket;
@@ -54,8 +49,6 @@
   const int expected_connections_;
   const AllowAdditionalConnections allow_additional_connections_;
 
-  const scoped_refptr<base::SequencedTaskRunner> run_loop_task_runner_;
-
   base::RunLoop run_loop_;
 
   DISALLOW_COPY_AND_ASSIGN(SimpleConnectionListener);
diff --git a/net/url_request/url_request_ftp_job.cc b/net/url_request/url_request_ftp_job.cc
index 0e8c186..26f44a86 100644
--- a/net/url_request/url_request_ftp_job.cc
+++ b/net/url_request/url_request_ftp_job.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/location.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_task_runner_handle.h"
@@ -15,6 +16,7 @@
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/ftp/ftp_auth_cache.h"
+#include "net/ftp/ftp_network_transaction.h"
 #include "net/ftp/ftp_response_info.h"
 #include "net/ftp/ftp_transaction_factory.h"
 #include "net/http/http_response_headers.h"
@@ -65,6 +67,13 @@
 }
 
 bool URLRequestFtpJob::GetMimeType(std::string* mime_type) const {
+  // When auth has been cancelled, return a blank text/plain page instead of
+  // triggering a download.
+  if (auth_data_ && auth_data_->state == AUTH_STATE_CANCELED) {
+    *mime_type = "text/plain";
+    return true;
+  }
+
   if (ftp_transaction_->GetResponseInfo()->is_directory_listing) {
     *mime_type = "text/vnd.chromium.ftp-dir";
     return true;
@@ -115,6 +124,11 @@
 }
 
 void URLRequestFtpJob::GetResponseInfo(HttpResponseInfo* info) {
+  // Don't expose the challenge if it has already been successfully
+  // authenticated.
+  if (!auth_data_ || auth_data_->state == AUTH_STATE_HAVE_AUTH)
+    return;
+
   std::unique_ptr<AuthChallengeInfo> challenge = GetAuthChallengeInfo();
   if (challenge)
     info->auth_challenge = *challenge;
@@ -170,14 +184,20 @@
     set_expected_content_size(
         ftp_transaction_->GetResponseInfo()->expected_content_size);
 
+    if (auth_data_ && auth_data_->state == AUTH_STATE_HAVE_AUTH) {
+      LogFtpStartResult(FTPStartResult::kSuccessAuth);
+    } else {
+      LogFtpStartResult(FTPStartResult::kSuccessNoAuth);
+    }
+
     NotifyHeadersComplete();
   } else if (ftp_transaction_ /* May be null if creation fails. */ &&
-             ftp_transaction_->GetResponseInfo()->needs_auth &&
-             // If auth is cancelled, cancel the request with an error below.
-             (!auth_data_ || auth_data_->state != AUTH_STATE_CANCELED)) {
+             ftp_transaction_->GetResponseInfo()->needs_auth) {
     HandleAuthNeededResponse();
   } else {
     NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, result));
+
+    LogFtpStartResult(FTPStartResult::kFailed);
   }
 }
 
@@ -246,16 +266,17 @@
 
   auth_data_->state = AUTH_STATE_CANCELED;
 
-  // Once the auth is cancelled, end the request with an error. Schedule this
-  // for later so that we don't cause any recursing into the caller as a result
-  // of this call.
-  OnStartCompletedAsync(ERR_FTP_FAILED);
+  ftp_transaction_.reset();
+  NotifyHeadersComplete();
 }
 
 int URLRequestFtpJob::ReadRawData(IOBuffer* buf, int buf_size) {
   DCHECK_NE(buf_size, 0);
   DCHECK(!read_in_progress_);
 
+  if (!ftp_transaction_)
+    return 0;
+
   int rv =
       ftp_transaction_->Read(buf, buf_size,
                              base::BindOnce(&URLRequestFtpJob::OnReadCompleted,
@@ -275,8 +296,12 @@
       return;
     }
 
-    if (ftp_transaction_ && auth_data_->state == AUTH_STATE_HAVE_AUTH)
+    if (ftp_transaction_ && auth_data_->state == AUTH_STATE_HAVE_AUTH) {
       ftp_auth_cache_->Remove(origin, auth_data_->credentials);
+
+      // The user entered invalid auth
+      LogFtpStartResult(FTPStartResult::kFailed);
+    }
   } else {
     auth_data_ = std::make_unique<AuthData>();
   }
@@ -294,4 +319,8 @@
   }
 }
 
+void URLRequestFtpJob::LogFtpStartResult(FTPStartResult result) {
+  UMA_HISTOGRAM_ENUMERATION("Net.FTP.StartResult", result);
+}
+
 }  // namespace net
diff --git a/net/url_request/url_request_ftp_job.h b/net/url_request/url_request_ftp_job.h
index 9d6885a..7d0648e 100644
--- a/net/url_request/url_request_ftp_job.h
+++ b/net/url_request/url_request_ftp_job.h
@@ -21,6 +21,15 @@
 
 namespace net {
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class FTPStartResult : int {
+  kSuccessNoAuth = 0,
+  kSuccessAuth = 1,
+  kFailed = 2,
+  kMaxValue = kFailed
+};
+
 class NetworkDelegate;
 class FtpTransactionFactory;
 class FtpAuthCache;
@@ -33,15 +42,14 @@
                    NetworkDelegate* network_delegate,
                    FtpTransactionFactory* ftp_transaction_factory,
                    FtpAuthCache* ftp_auth_cache);
+  ~URLRequestFtpJob() override;
+  void Start() override;
 
  protected:
-  ~URLRequestFtpJob() override;
-
   // Overridden from URLRequestJob:
   bool IsSafeRedirect(const GURL& location) override;
   bool GetMimeType(std::string* mime_type) const override;
   IPEndPoint GetResponseRemoteEndpoint() const override;
-  void Start() override;
   void Kill() override;
   void GetResponseInfo(HttpResponseInfo* info) override;
 
@@ -69,6 +77,8 @@
 
   void HandleAuthNeededResponse();
 
+  void LogFtpStartResult(FTPStartResult result);
+
   ProxyResolutionService* proxy_resolution_service_;
   ProxyInfo proxy_info_;
   std::unique_ptr<ProxyResolutionService::Request> proxy_resolve_request_;
diff --git a/net/url_request/url_request_ftp_job_unittest.cc b/net/url_request/url_request_ftp_job_unittest.cc
new file mode 100644
index 0000000..f342822
--- /dev/null
+++ b/net/url_request/url_request_ftp_job_unittest.cc
@@ -0,0 +1,265 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/url_request/url_request_ftp_job.h"
+
+#include <memory>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "net/base/auth.h"
+#include "net/base/load_states.h"
+#include "net/base/net_errors.h"
+#include "net/base/request_priority.h"
+#include "net/ftp/ftp_auth_cache.h"
+#include "net/ftp/ftp_response_info.h"
+#include "net/ftp/ftp_transaction.h"
+#include "net/ftp/ftp_transaction_factory.h"
+#include "net/test/test_with_scoped_task_environment.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+class MockFtpTransaction : public FtpTransaction {
+ public:
+  MockFtpTransaction(int start_return_value,
+                     int read_return_value,
+                     bool needs_auth,
+                     std::vector<int> restart_return_values)
+      : start_return_value_(start_return_value),
+        read_return_value_(read_return_value),
+        restart_return_values_(restart_return_values),
+        restart_index_(0) {
+    response_.needs_auth = needs_auth;
+  }
+  ~MockFtpTransaction() override {}
+
+  int Start(const FtpRequestInfo* request_info,
+            CompletionOnceCallback callback,
+            const NetLogWithSource& net_log,
+            const NetworkTrafficAnnotationTag& traffic_annotation) override {
+    return start_return_value_;
+  }
+
+  int RestartWithAuth(const AuthCredentials& credentials,
+                      CompletionOnceCallback callback) override {
+    CHECK(restart_index_ < restart_return_values_.size());
+    return restart_return_values_[restart_index_++];
+  }
+
+  int Read(IOBuffer* buf,
+           int buf_len,
+           CompletionOnceCallback callback) override {
+    return read_return_value_;
+  }
+
+  const FtpResponseInfo* GetResponseInfo() const override { return &response_; }
+
+  LoadState GetLoadState() const override { return LOAD_STATE_IDLE; }
+
+  uint64_t GetUploadProgress() const override { return 0; }
+
+ private:
+  FtpResponseInfo response_;
+  int start_return_value_;
+  int read_return_value_;
+  std::vector<int> restart_return_values_;
+  unsigned int restart_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockFtpTransaction);
+};
+
+class MockFtpTransactionFactory : public FtpTransactionFactory {
+ public:
+  MockFtpTransactionFactory(int start_return_value,
+                            int read_return_value,
+                            bool needs_auth,
+                            std::vector<int> restart_return_values)
+      : start_return_value_(start_return_value),
+        read_return_value_(read_return_value),
+        needs_auth_(needs_auth),
+        restart_return_values_(restart_return_values) {}
+
+  ~MockFtpTransactionFactory() override {}
+
+  std::unique_ptr<FtpTransaction> CreateTransaction() override {
+    return std::make_unique<MockFtpTransaction>(start_return_value_,
+                                                read_return_value_, needs_auth_,
+                                                restart_return_values_);
+  }
+
+  void Suspend(bool suspend) override {}
+
+ private:
+  int start_return_value_;
+  int read_return_value_;
+  bool needs_auth_;
+  std::vector<int> restart_return_values_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockFtpTransactionFactory);
+};
+
+class MockURLRequestFtpJobFactory : public URLRequestJobFactory {
+ public:
+  MockURLRequestFtpJobFactory(int start_return_value,
+                              int read_return_value,
+                              bool needs_auth,
+                              std::vector<int> restart_return_values)
+      : auth_cache(new FtpAuthCache()),
+        factory(new MockFtpTransactionFactory(start_return_value,
+                                              read_return_value,
+                                              needs_auth,
+                                              restart_return_values)) {}
+
+  ~MockURLRequestFtpJobFactory() override {
+    delete auth_cache;
+    delete factory;
+  }
+
+  URLRequestJob* MaybeCreateJobWithProtocolHandler(
+      const std::string& scheme,
+      URLRequest* request,
+      NetworkDelegate* network_delegate) const override {
+    return new URLRequestFtpJob(request, network_delegate, factory, auth_cache);
+  }
+
+  URLRequestJob* MaybeInterceptRedirect(URLRequest* request,
+                                        NetworkDelegate* network_delegate,
+                                        const GURL& location) const override {
+    return nullptr;
+  }
+
+  URLRequestJob* MaybeInterceptResponse(
+      URLRequest* request,
+      NetworkDelegate* network_delegate) const override {
+    return nullptr;
+  }
+
+  bool IsHandledProtocol(const std::string& scheme) const override {
+    return scheme == "ftp";
+  }
+
+  bool IsSafeRedirectTarget(const GURL& location) const override {
+    return true;
+  }
+
+ private:
+  FtpAuthCache* auth_cache;
+  MockFtpTransactionFactory* factory;
+
+  DISALLOW_COPY_AND_ASSIGN(MockURLRequestFtpJobFactory);
+};
+
+using UrlRequestFtpJobTest = TestWithScopedTaskEnvironment;
+
+TEST_F(UrlRequestFtpJobTest, HistogramLogSuccessNoAuth) {
+  base::HistogramTester histograms;
+  MockURLRequestFtpJobFactory url_request_ftp_job_factory(OK, OK, false, {OK});
+  TestNetworkDelegate network_delegate;
+  TestURLRequestContext context(true);
+  context.set_network_delegate(&network_delegate);
+  context.set_job_factory(&url_request_ftp_job_factory);
+  context.Init();
+
+  TestDelegate test_delegate;
+  std::unique_ptr<URLRequest> r(context.CreateRequest(
+      GURL("ftp://example.test/"), RequestPriority::DEFAULT_PRIORITY,
+      &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  r->Start();
+  test_delegate.RunUntilComplete();
+
+  histograms.ExpectBucketCount("Net.FTP.StartResult",
+                               FTPStartResult::kSuccessNoAuth, 1);
+  histograms.ExpectBucketCount("Net.FTP.StartResult",
+                               FTPStartResult::kSuccessAuth, 0);
+  histograms.ExpectBucketCount("Net.FTP.StartResult", FTPStartResult::kFailed,
+                               0);
+}
+
+TEST_F(UrlRequestFtpJobTest, HistogramLogSuccessAuth) {
+  base::HistogramTester histograms;
+  MockURLRequestFtpJobFactory url_request_ftp_job_factory(
+      ERR_FAILED, ERR_FAILED, true, {OK});
+  TestNetworkDelegate network_delegate;
+  TestURLRequestContext context(true);
+  context.set_network_delegate(&network_delegate);
+  context.set_job_factory(&url_request_ftp_job_factory);
+  context.Init();
+
+  TestDelegate test_delegate;
+  test_delegate.set_credentials(
+      AuthCredentials(base::ASCIIToUTF16("user"), base::ASCIIToUTF16("pass")));
+  std::unique_ptr<URLRequest> r(context.CreateRequest(
+      GURL("ftp://example.test/"), RequestPriority::DEFAULT_PRIORITY,
+      &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  r->Start();
+  test_delegate.RunUntilComplete();
+
+  histograms.ExpectBucketCount("Net.FTP.StartResult",
+                               FTPStartResult::kSuccessNoAuth, 0);
+  histograms.ExpectBucketCount("Net.FTP.StartResult",
+                               FTPStartResult::kSuccessAuth, 1);
+  histograms.ExpectBucketCount("Net.FTP.StartResult", FTPStartResult::kFailed,
+                               0);
+}
+
+TEST_F(UrlRequestFtpJobTest, HistogramLogFailed) {
+  base::HistogramTester histograms;
+  MockURLRequestFtpJobFactory url_request_ftp_job_factory(
+      ERR_FAILED, ERR_FAILED, false, {ERR_FAILED});
+  TestNetworkDelegate network_delegate;
+  TestURLRequestContext context(true);
+  context.set_network_delegate(&network_delegate);
+  context.set_job_factory(&url_request_ftp_job_factory);
+  context.Init();
+
+  TestDelegate test_delegate;
+  std::unique_ptr<URLRequest> r(context.CreateRequest(
+      GURL("ftp://example.test/"), RequestPriority::DEFAULT_PRIORITY,
+      &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  r->Start();
+  test_delegate.RunUntilComplete();
+
+  histograms.ExpectBucketCount("Net.FTP.StartResult",
+                               FTPStartResult::kSuccessNoAuth, 0);
+  histograms.ExpectBucketCount("Net.FTP.StartResult",
+                               FTPStartResult::kSuccessAuth, 0);
+  histograms.ExpectBucketCount("Net.FTP.StartResult", FTPStartResult::kFailed,
+                               1);
+}
+
+TEST_F(UrlRequestFtpJobTest, HistogramLogFailedInvalidAuthThenSucceed) {
+  base::HistogramTester histograms;
+  MockURLRequestFtpJobFactory url_request_ftp_job_factory(
+      ERR_FAILED, ERR_FAILED, true, {ERR_ACCESS_DENIED, OK});
+  TestNetworkDelegate network_delegate;
+  TestURLRequestContext context(true);
+  context.set_network_delegate(&network_delegate);
+  context.set_job_factory(&url_request_ftp_job_factory);
+  context.Init();
+
+  TestDelegate test_delegate;
+  test_delegate.set_credentials(
+      AuthCredentials(base::ASCIIToUTF16("user"), base::ASCIIToUTF16("pass")));
+  std::unique_ptr<URLRequest> r(context.CreateRequest(
+      GURL("ftp://example.test/"), RequestPriority::DEFAULT_PRIORITY,
+      &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  r->Start();
+  test_delegate.RunUntilComplete();
+
+  histograms.ExpectBucketCount("Net.FTP.StartResult",
+                               FTPStartResult::kSuccessNoAuth, 0);
+  histograms.ExpectBucketCount("Net.FTP.StartResult",
+                               FTPStartResult::kSuccessAuth, 1);
+  histograms.ExpectBucketCount("Net.FTP.StartResult", FTPStartResult::kFailed,
+                               1);
+}
+}  // namespace
+}  // namespace net
\ No newline at end of file
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index cdaf059..552b216 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -736,13 +736,15 @@
  public:
   // Does not own |delegate|.
   TestURLRequestContextWithProxy(const std::string& proxy,
-                                 NetworkDelegate* delegate)
+                                 NetworkDelegate* delegate,
+                                 bool delay_initialization = false)
       : TestURLRequestContext(true) {
     context_storage_.set_proxy_resolution_service(
         ProxyResolutionService::CreateFixed(proxy,
                                             TRAFFIC_ANNOTATION_FOR_TESTS));
     set_network_delegate(delegate);
-    Init();
+    if (!delay_initialization)
+      Init();
   }
   ~TestURLRequestContextWithProxy() override = default;
 };
@@ -2395,19 +2397,15 @@
   ASSERT_NE(req->identifier(), other_req->identifier());
 }
 
-#if defined(OS_IOS)
-// TODO(droger): Check that a failure to connect to the proxy is reported to
-// the network delegate. crbug.com/496743
-#define MAYBE_NetworkDelegateProxyError DISABLED_NetworkDelegateProxyError
-#else
-#define MAYBE_NetworkDelegateProxyError NetworkDelegateProxyError
-#endif
-TEST_F(URLRequestTest, MAYBE_NetworkDelegateProxyError) {
+TEST_F(URLRequestTest, NetworkDelegateProxyError) {
   MockHostResolver host_resolver;
   host_resolver.rules()->AddSimulatedFailure("*");
 
   TestNetworkDelegate network_delegate;  // Must outlive URLRequests.
-  TestURLRequestContextWithProxy context("myproxy:70", &network_delegate);
+  TestURLRequestContextWithProxy context("myproxy:70", &network_delegate,
+                                         true /* delay_initialization */);
+  context.set_host_resolver(&host_resolver);
+  context.Init();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
@@ -12842,7 +12840,7 @@
   EXPECT_EQ(6, req->GetRawBodyBytes());
 }
 
-TEST_F(URLRequestTestFTP, AuthCancellation) {
+TEST_F(URLRequestTestFTP, FtpAuthCancellation) {
   ftp_test_server_.set_no_anonymous_ftp_user(true);
   ASSERT_TRUE(ftp_test_server_.Start());
   TestDelegate d;
@@ -12853,8 +12851,13 @@
   d.RunUntilComplete();
 
   ASSERT_TRUE(d.auth_required_called());
-  EXPECT_EQ(ERR_FTP_FAILED, d.request_status());
+  EXPECT_EQ(OK, d.request_status());
   EXPECT_TRUE(req->auth_challenge_info());
+  std::string mime_type;
+  req->GetMimeType(&mime_type);
+  EXPECT_EQ("text/plain", mime_type);
+  EXPECT_EQ("", d.data_received());
+  EXPECT_EQ(-1, req->GetExpectedContentSize());
 }
 
 class URLRequestTestFTPOverHttpProxy : public URLRequestTestFTP {
diff --git a/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java b/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java
index 6e4ac21..1e8e49cc 100644
--- a/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java
+++ b/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java
@@ -213,7 +213,13 @@
 
     @Override
     public void pdfWritingDone(int pageCount) {
-        if (mPrintingState == PRINTING_STATE_FINISHED) return;
+        if (mPrintingState == PRINTING_STATE_READY) {
+            assert pageCount
+                    == 0 : "There is no pending printing task, should only be a failure report";
+        }
+
+        if (mPrintingState != PRINTING_STATE_STARTED_FROM_ONWRITE) return;
+
         mPrintingState = PRINTING_STATE_READY;
         closeFileDescriptor();
         if (pageCount > 0) {
diff --git a/printing/backend/cups_printer.cc b/printing/backend/cups_printer.cc
index 2fe9da8..88c5bf9 100644
--- a/printing/backend/cups_printer.cc
+++ b/printing/backend/cups_printer.cc
@@ -147,14 +147,14 @@
 
 ipp_status_t CupsPrinter::CreateJob(int* job_id,
                                     const std::string& title,
-                                    const base::Optional<std::string>& username,
+                                    base::StringPiece username,
                                     const std::vector<cups_option_t>& options) {
   DCHECK(dest_info_) << "Verify availability before starting a print job";
 
   cups_option_t* data = const_cast<cups_option_t*>(
       options.data());  // createDestJob will not modify the data
-  if (username)
-    cupsSetUser(username->c_str());
+  if (!username.empty())
+    cupsSetUser(username.data());
 
   ipp_status_t create_status =
       cupsCreateDestJob(cups_http_, destination_.get(), dest_info_.get(),
@@ -166,9 +166,12 @@
 bool CupsPrinter::StartDocument(int job_id,
                                 const std::string& document_name,
                                 bool last_document,
+                                base::StringPiece username,
                                 const std::vector<cups_option_t>& options) {
   DCHECK(dest_info_);
   DCHECK(job_id);
+  if (!username.empty())
+    cupsSetUser(username.data());
 
   cups_option_t* data = const_cast<cups_option_t*>(
       options.data());  // createStartDestDocument will not modify the data
@@ -177,6 +180,7 @@
                             job_id, document_name.c_str(), CUPS_FORMAT_PDF,
                             options.size(), data, last_document ? 0 : 1);
 
+  cupsSetUser(nullptr);  // reset to default username ("anonymous")
   return start_doc_status == HTTP_CONTINUE;
 }
 
@@ -195,12 +199,16 @@
   return status == IPP_STATUS_OK;
 }
 
-ipp_status_t CupsPrinter::CloseJob(int job_id) {
+ipp_status_t CupsPrinter::CloseJob(int job_id, base::StringPiece username) {
   DCHECK(dest_info_);
   DCHECK(job_id);
+  if (!username.empty())
+    cupsSetUser(username.data());
 
-  return cupsCloseDestJob(cups_http_, destination_.get(), dest_info_.get(),
-                          job_id);
+  ipp_status_t result = cupsCloseDestJob(cups_http_, destination_.get(),
+                                         dest_info_.get(), job_id);
+  cupsSetUser(nullptr);  // reset to default username ("anonymous")
+  return result;
 }
 
 bool CupsPrinter::CancelJob(int job_id) {
diff --git a/printing/backend/cups_printer.h b/printing/backend/cups_printer.h
index d1b666d..52a1595 100644
--- a/printing/backend/cups_printer.h
+++ b/printing/backend/cups_printer.h
@@ -87,21 +87,23 @@
   bool ToPrinterInfo(PrinterBasicInfo* basic_info) const;
 
   // Start a print job.  Writes the id of the started job to |job_id|.  |job_id|
-  // is 0 if there is an error.  Check availability before using this operation.
-  // Usage on an unavailable printer is undefined.
+  // is 0 if there is an error.  If |username| is empty no username is sent.
+  // Check availability before using this operation. Usage on an unavailable
+  // printer is undefined.
   ipp_status_t CreateJob(int* job_id,
                          const std::string& title,
-                         const base::Optional<std::string>& username,
+                         base::StringPiece username,
                          const std::vector<cups_option_t>& options);
 
   // Add a document to a print job.  |job_id| must be non-zero and refer to a
   // job started with CreateJob.  |document_name| will be displayed in print
   // status.  |last_doc| should be true if this is the last document for this
-  // print job.  |options| should be IPP key value pairs for the Send-Document
-  // operation.
+  // print job.  If |username| is empty no username is sent.  |options| should
+  // be IPP key value pairs for the Send-Document operation.
   bool StartDocument(int job_id,
                      const std::string& document_name,
                      bool last_doc,
+                     base::StringPiece username,
                      const std::vector<cups_option_t>& options);
 
   // Add data to the current document started by StartDocument.  Calling this
@@ -113,8 +115,9 @@
   bool FinishDocument();
 
   // Close the job.  If the job is not closed, the documents will not be
-  // printed.  |job_id| should match the id from CreateJob.
-  ipp_status_t CloseJob(int job_id);
+  // printed.  |job_id| should match the id from CreateJob.  If |username| is
+  // empty no username is sent.
+  ipp_status_t CloseJob(int job_id, base::StringPiece username);
 
   // Cancel the print job |job_id|.  Returns true if the operation succeeded.
   // Returns false if it failed for any reason.
diff --git a/printing/printing_context_chromeos.cc b/printing/printing_context_chromeos.cc
index db0972f..594adf9 100644
--- a/printing/printing_context_chromeos.cc
+++ b/printing/printing_context_chromeos.cc
@@ -297,7 +297,7 @@
   std::vector<ScopedCupsOption> cups_options = SettingsToCupsOptions(settings_);
 
   std::vector<cups_option_t> options;
-  base::Optional<std::string> username;
+  base::StringPiece username;
   const base::StringPiece requestingUserName(kIppRequestingUserName);
   for (const ScopedCupsOption& option : cups_options) {
     if (option->name == requestingUserName) {
@@ -320,7 +320,8 @@
   }
 
   // we only send one document, so it's always the last one
-  if (!printer_->StartDocument(job_id_, converted_name, true, options)) {
+  if (!printer_->StartDocument(job_id_, converted_name, true, username,
+                               options)) {
     LOG(ERROR) << "Starting document failed";
     return OnError();
   }
@@ -361,7 +362,10 @@
     return OnError();
   }
 
-  ipp_status_t job_status = printer_->CloseJob(job_id_);
+  ipp_status_t job_status =
+      printer_->CloseJob(job_id_, settings_.send_user_info()
+                                      ? base::StringPiece(settings_.username())
+                                      : base::StringPiece());
   job_id_ = 0;
 
   if (job_status != IPP_STATUS_OK) {
diff --git a/remoting/BUILD.gn b/remoting/BUILD.gn
index 080f2e0..0aa3140 100644
--- a/remoting/BUILD.gn
+++ b/remoting/BUILD.gn
@@ -193,9 +193,7 @@
   }
 
   if (!is_chromeos) {
-    deps += [
-      "//remoting/client/display:unit_tests",
-    ]
+    deps += [ "//remoting/client/display:unit_tests" ]
   }
 }
 
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn
index 89d605e..dc7b908f 100644
--- a/remoting/base/BUILD.gn
+++ b/remoting/base/BUILD.gn
@@ -174,6 +174,7 @@
     "buffered_socket_writer_unittest.cc",
     "capabilities_unittest.cc",
     "compound_buffer_unittest.cc",
+    "leaky_bucket_unittest.cc",
     "oauth_token_getter_proxy_unittest.cc",
     "rate_counter_unittest.cc",
     "result_unittest.cc",
diff --git a/remoting/base/leaky_bucket.cc b/remoting/base/leaky_bucket.cc
index d9236b4..a029781 100644
--- a/remoting/base/leaky_bucket.cc
+++ b/remoting/base/leaky_bucket.cc
@@ -46,10 +46,14 @@
 }
 
 void LeakyBucket::UpdateLevel(base::TimeTicks now) {
-  current_level_ -= rate_ * (now - level_updated_time_).InMicroseconds() /
-            base::TimeTicks::kMicrosecondsPerSecond;
-  if (current_level_ < 0)
+  int64_t drainage_amount = rate_ *
+                            (now - level_updated_time_).InMicroseconds() /
+                            base::TimeTicks::kMicrosecondsPerSecond;
+  if (current_level_ < drainage_amount) {
     current_level_ = 0;
+  } else {
+    current_level_ -= drainage_amount;
+  }
   level_updated_time_ = now;
 }
 
diff --git a/remoting/base/leaky_bucket_unittest.cc b/remoting/base/leaky_bucket_unittest.cc
new file mode 100644
index 0000000..1131c25fe
--- /dev/null
+++ b/remoting/base/leaky_bucket_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/base/leaky_bucket.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+TEST(LeakyBucketTest, LargeTimeDelta_BucketIsEmpty) {
+  // This test triggers an integer underflow bug in the previous implementation.
+  // The large (but reasonable) time skip should result in an empty bucket.
+  // A bug (due to int64->int conversion) caused the bucket-level to be a large
+  // positive value, resulting in a bucket-empty time in the far future.
+  auto now = base::TimeTicks::Now();
+  int rate = 1e6;
+  LeakyBucket bucket(LeakyBucket::kUnlimitedDepth, rate);
+
+  // Small random amount, should empty very quickly.
+  bucket.RefillOrSpill(123, now);
+
+  // The amount of drainage is 3000 * rate = 3e9, which overflows a signed
+  // 32-bit int. This would be subtracted from the current level, under the
+  // old implementation.
+  now += base::TimeDelta::FromSeconds(3000);
+  bucket.UpdateRate(rate, now);
+
+  auto empty_time = bucket.GetEmptyTime();
+  EXPECT_LE(empty_time, now);
+}
+
+}  // namespace remoting
diff --git a/remoting/client/display/BUILD.gn b/remoting/client/display/BUILD.gn
index 193e4c6..6f581ec 100644
--- a/remoting/client/display/BUILD.gn
+++ b/remoting/client/display/BUILD.gn
@@ -58,6 +58,9 @@
       "GLKit.framework",
       "OpenGLES.framework",
     ]
+
+    # TODO(crbug.com/875022) fix for OpenGLES deprecation.
+    defines = [ "GLES_SILENCE_DEPRECATION" ]
   }
 
   if (is_win) {
diff --git a/remoting/host/host_exit_codes.cc b/remoting/host/host_exit_codes.cc
index eec8a6b..0809a8a 100644
--- a/remoting/host/host_exit_codes.cc
+++ b/remoting/host/host_exit_codes.cc
@@ -9,15 +9,16 @@
 namespace remoting {
 
 const NameMapElement<HostExitCodes> kHostExitCodeStrings[] = {
-  { kSuccessExitCode, "SUCCESS_EXIT" },
-  { kInitializationFailed, "INITIALIZATION_FAILED" },
-  { kInvalidCommandLineExitCode, "INVALID_COMMAND_LINE" },
-  { kInvalidHostConfigurationExitCode, "INVALID_HOST_CONFIGURATION" },
-  { kInvalidHostIdExitCode, "INVALID_HOST_ID" },
-  { kInvalidOauthCredentialsExitCode, "INVALID_OAUTH_CREDENTIALS" },
-  { kInvalidHostDomainExitCode, "INVALID_HOST_DOMAIN" },
-  { kLoginScreenNotSupportedExitCode, "LOGIN_SCREEN_NOT_SUPPORTED" },
-  { kUsernameMismatchExitCode, "USERNAME_MISMATCH" },
+    {kSuccessExitCode, "SUCCESS_EXIT"},
+    {kInitializationFailed, "INITIALIZATION_FAILED"},
+    {kInvalidCommandLineExitCode, "INVALID_COMMAND_LINE"},
+    {kInvalidHostConfigurationExitCode, "INVALID_HOST_CONFIGURATION"},
+    {kInvalidHostIdExitCode, "INVALID_HOST_ID"},
+    {kInvalidOauthCredentialsExitCode, "INVALID_OAUTH_CREDENTIALS"},
+    {kInvalidHostDomainExitCode, "INVALID_HOST_DOMAIN"},
+    {kLoginScreenNotSupportedExitCode, "LOGIN_SCREEN_NOT_SUPPORTED"},
+    {kUsernameMismatchExitCode, "USERNAME_MISMATCH"},
+    {kHostDeletedExitCode, "HOST_DELETED"},
 };
 
 const char* ExitCodeToString(HostExitCodes exit_code) {
diff --git a/remoting/host/host_exit_codes.h b/remoting/host/host_exit_codes.h
index becefae..5a31757 100644
--- a/remoting/host/host_exit_codes.h
+++ b/remoting/host/host_exit_codes.h
@@ -26,11 +26,12 @@
   kInvalidHostDomainExitCode = 103,
   kLoginScreenNotSupportedExitCode = 104,
   kUsernameMismatchExitCode = 105,
+  kHostDeletedExitCode = 106,
 
   // The range of the exit codes that should be interpreted as a permanent error
   // condition.
   kMinPermanentErrorExitCode = kInvalidHostConfigurationExitCode,
-  kMaxPermanentErrorExitCode = kUsernameMismatchExitCode
+  kMaxPermanentErrorExitCode = kHostDeletedExitCode
 };
 
 const char* ExitCodeToString(HostExitCodes exit_code);
diff --git a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
index f9911907..283cbc97 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
@@ -570,8 +570,7 @@
   pipe_->Start(std::move(it2me_host), std::move(channel));
 
   // Notify the test that the host has finished starting up.
-  test_message_loop_->task_runner()->PostTask(
-      FROM_HERE, test_run_loop_->QuitClosure());
+  test_run_loop_->Quit();
 }
 
 void It2MeNativeMessagingHostTest::ExitTest() {
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index f916cb2..7804fe51 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -967,7 +967,7 @@
 
 void HostProcess::OnHostDeleted() {
   LOG(ERROR) << "Host was deleted from the directory.";
-  ShutdownHost(kInvalidHostIdExitCode);
+  ShutdownHost(kHostDeletedExitCode);
 }
 
 void HostProcess::OnInitializePairingRegistry(
@@ -1724,7 +1724,12 @@
 
   // Before shutting down HostSignalingManager, send the |host_offline_reason|
   // if possible (i.e. if we have the config).
-  if (!serialized_config_.empty()) {
+  if (host_offline_reason == ExitCodeToString(kHostDeletedExitCode)) {
+    // Host is deleted. There is no need to report the host offline reason back
+    // to directory.
+    OnHostOfflineReasonAck(true);
+    return;
+  } else if (!serialized_config_.empty()) {
     if (!signal_strategy_)
       InitializeSignaling();
 
diff --git a/remoting/host/setup/me2me_native_messaging_host_unittest.cc b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
index 88375e0..9ce0809 100644
--- a/remoting/host/setup/me2me_native_messaging_host_unittest.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
@@ -360,8 +360,7 @@
   native_messaging_pipe_->Start(std::move(host), std::move(channel));
 
   // Notify the test that the host has finished starting up.
-  test_message_loop_->task_runner()->PostTask(
-      FROM_HERE, test_run_loop_->QuitClosure());
+  test_run_loop_->Quit();
 }
 
 void Me2MeNativeMessagingHostTest::StopHost() {
diff --git a/remoting/ios/app/BUILD.gn b/remoting/ios/app/BUILD.gn
index 65993b1..ba42a57 100644
--- a/remoting/ios/app/BUILD.gn
+++ b/remoting/ios/app/BUILD.gn
@@ -94,6 +94,8 @@
     "//ui/resources",
   ]
 
+  # TODO(crbug.com/875022) fix for OpenGLES deprecation.
+  defines = [ "GLES_SILENCE_DEPRECATION" ]
   foreach(locale, remoting_locales_with_underscores) {
     deps += [ "//remoting/ios/app/resources:locale_${locale}_bundle_data" ]
   }
@@ -119,6 +121,9 @@
     ":common_source_set",
     "//base",
   ]
+
+  # TODO(crbug.com/875022) fix for OpenGLES deprecation.
+  defines = [ "GLES_SILENCE_DEPRECATION" ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
diff --git a/remoting/ios/display/BUILD.gn b/remoting/ios/display/BUILD.gn
index 5379574..183669b8 100644
--- a/remoting/ios/display/BUILD.gn
+++ b/remoting/ios/display/BUILD.gn
@@ -45,5 +45,8 @@
     "OpenGLES.framework",
   ]
 
+  # TODO(crbug.com/875022) fix for OpenGLES deprecation.
+  defines = [ "GLES_SILENCE_DEPRECATION" ]
+
   configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/remoting/ios/persistence/remoting_preferences.mm b/remoting/ios/persistence/remoting_preferences.mm
index aa4874d..b9ffe8d 100644
--- a/remoting/ios/persistence/remoting_preferences.mm
+++ b/remoting/ios/persistence/remoting_preferences.mm
@@ -53,8 +53,12 @@
 - (HostSettings*)settingsForHost:(NSString*)hostId {
   NSData* encodedSettings =
       [_defaults objectForKey:KeyWithPrefix(kHostSettingsKey, hostId)];
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingFromData:encodedSettings
+                                                  error:nil];
+  unarchiver.requiresSecureCoding = NO;
   HostSettings* settings =
-      [NSKeyedUnarchiver unarchiveObjectWithData:encodedSettings];
+      [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
   if (settings == nil) {
     settings = [[HostSettings alloc] init];
     settings.hostId = hostId;
@@ -67,7 +71,9 @@
   NSString* key = KeyWithPrefix(kHostSettingsKey, hostId);
   if (settings) {
     NSData* encodedSettings =
-        [NSKeyedArchiver archivedDataWithRootObject:settings];
+        [NSKeyedArchiver archivedDataWithRootObject:settings
+                              requiringSecureCoding:NO
+                                              error:nil];
     [_defaults setObject:encodedSettings forKey:key];
   } else {
     return [_defaults removeObjectForKey:key];
diff --git a/remoting/resources/remoting_strings_nl.xtb b/remoting/resources/remoting_strings_nl.xtb
index 944c78b..b5efb8cc 100644
--- a/remoting/resources/remoting_strings_nl.xtb
+++ b/remoting/resources/remoting_strings_nl.xtb
@@ -294,7 +294,7 @@
 <translation id="7526139040829362392">Account wijzigen</translation>
 <translation id="7606912958770842224">Externe verbindingen inschakelen</translation>
 <translation id="7628469622942688817">Mijn pincode onthouden op dit apparaat.</translation>
-<translation id="7649070708921625228">Help</translation>
+<translation id="7649070708921625228">Hulp</translation>
 <translation id="7658239707568436148">Annuleren</translation>
 <translation id="7665369617277396874">Account toevoegen</translation>
 <translation id="7672203038394118626">Externe verbindingen voor deze computer zijn uitgeschakeld.</translation>
diff --git a/services/data_decoder/bundled_exchanges_parser_fuzzer.cc b/services/data_decoder/bundled_exchanges_parser_fuzzer.cc
index 71dd858..70e29b3 100644
--- a/services/data_decoder/bundled_exchanges_parser_fuzzer.cc
+++ b/services/data_decoder/bundled_exchanges_parser_fuzzer.cc
@@ -7,7 +7,9 @@
 
 #include <string>
 
+#include "base/at_exit.h"
 #include "base/bind.h"
+#include "base/i18n/icu_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "mojo/core/embedder/embedder.h"
@@ -106,8 +108,13 @@
 };
 
 struct Environment {
-  Environment() { mojo::core::Init(); }
+  Environment() {
+    mojo::core::Init();
+    CHECK(base::i18n::InitializeICU());
+  }
 
+  // Used by ICU integration.
+  base::AtExitManager at_exit_manager;
   base::MessageLoop message_loop;
 };
 
diff --git a/services/identity/public/cpp/identity_test_environment.cc b/services/identity/public/cpp/identity_test_environment.cc
index cad9b36..4a0a60e 100644
--- a/services/identity/public/cpp/identity_test_environment.cc
+++ b/services/identity/public/cpp/identity_test_environment.cc
@@ -48,6 +48,16 @@
 
 namespace identity {
 
+IdentityTestEnvironment::ExtraParams::ExtraParams() = default;
+
+IdentityTestEnvironment::ExtraParams::~ExtraParams() = default;
+
+IdentityTestEnvironment::ExtraParams::ExtraParams(
+    IdentityTestEnvironment::ExtraParams&& other) = default;
+
+IdentityTestEnvironment::ExtraParams& IdentityTestEnvironment::ExtraParams::
+operator=(ExtraParams&& other) = default;
+
 class IdentityManagerDependenciesOwner {
  public:
   IdentityManagerDependenciesOwner(
@@ -109,13 +119,15 @@
     network::TestURLLoaderFactory* test_url_loader_factory,
     sync_preferences::TestingPrefServiceSyncable* pref_service,
     signin::AccountConsistencyMethod account_consistency,
-    TestSigninClient* test_signin_client)
+    TestSigninClient* test_signin_client,
+    ExtraParams extra_params)
     : IdentityTestEnvironment(
           std::make_unique<IdentityManagerDependenciesOwner>(
               pref_service,
               test_signin_client),
           test_url_loader_factory,
-          account_consistency) {
+          account_consistency,
+          std::move(extra_params)) {
   DCHECK(!test_url_loader_factory || !test_signin_client);
 }
 
@@ -142,7 +154,8 @@
 IdentityTestEnvironment::IdentityTestEnvironment(
     std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner,
     network::TestURLLoaderFactory* test_url_loader_factory,
-    signin::AccountConsistencyMethod account_consistency)
+    signin::AccountConsistencyMethod account_consistency,
+    ExtraParams extra_params)
     : weak_ptr_factory_(this) {
   dependencies_owner_ = std::move(dependencies_owner);
   TestSigninClient* test_signin_client = dependencies_owner_->signin_client();
@@ -155,20 +168,13 @@
   IdentityManager::RegisterProfilePrefs(test_pref_service->registry());
   IdentityManager::RegisterLocalStatePrefs(test_pref_service->registry());
 
-  owned_identity_manager_ =
-      BuildIdentityManagerForTests(test_signin_client, test_pref_service,
-                                   base::FilePath(), account_consistency);
+  owned_identity_manager_ = BuildIdentityManagerForTests(
+      test_signin_client, test_pref_service, base::FilePath(),
+      account_consistency, std::move(extra_params));
 
   Initialize();
 }
 
-IdentityTestEnvironment::ExtraParams::ExtraParams() = default;
-IdentityTestEnvironment::ExtraParams::~ExtraParams() = default;
-IdentityTestEnvironment::ExtraParams::ExtraParams(
-    IdentityTestEnvironment::ExtraParams&& other) = default;
-IdentityTestEnvironment::ExtraParams& IdentityTestEnvironment::ExtraParams::
-operator=(ExtraParams&& other) = default;
-
 // static
 std::unique_ptr<IdentityManagerWrapper>
 IdentityTestEnvironment::BuildIdentityManagerForTests(
diff --git a/services/identity/public/cpp/identity_test_environment.h b/services/identity/public/cpp/identity_test_environment.h
index 8c49998..e970e0f1 100644
--- a/services/identity/public/cpp/identity_test_environment.h
+++ b/services/identity/public/cpp/identity_test_environment.h
@@ -50,6 +50,22 @@
 // requirement.
 class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver {
  public:
+  // Allow passing platform specific parameters to create the IdentityManager.
+  // Need to be default constructible and moveable.
+  struct ExtraParams {
+    ExtraParams();
+    ~ExtraParams();
+    ExtraParams(ExtraParams&& other);
+    ExtraParams& operator=(ExtraParams&& other);
+
+#if defined(OS_IOS)
+    // If non-null, an iOS delegate instance will be constructed for the
+    // token service as opposed to the default fake delegate.
+    std::unique_ptr<ProfileOAuth2TokenServiceIOSProvider>
+        token_service_provider;
+#endif
+  };
+
   // Preferred constructor: constructs an IdentityManager object and its
   // dependencies internally. Cannot be used if the client of this class
   // is still interacting directly with those dependencies (e.g., if
@@ -80,7 +96,8 @@
       sync_preferences::TestingPrefServiceSyncable* pref_service = nullptr,
       signin::AccountConsistencyMethod account_consistency =
           signin::AccountConsistencyMethod::kDisabled,
-      TestSigninClient* test_signin_client = nullptr);
+      TestSigninClient* test_signin_client = nullptr,
+      ExtraParams extra_params = {});
 
   ~IdentityTestEnvironment() override;
 
@@ -304,7 +321,8 @@
   IdentityTestEnvironment(
       std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner,
       network::TestURLLoaderFactory* test_url_loader_factory,
-      signin::AccountConsistencyMethod account_consistency);
+      signin::AccountConsistencyMethod account_consistency,
+      ExtraParams extra_params = {});
 
   // Constructs an IdentityTestEnvironment that uses the supplied
   // |identity_manager|.
@@ -356,23 +374,6 @@
   base::OnceClosure on_access_token_requested_callback_;
   std::vector<AccessTokenRequestState> requesters_;
 
-  // Allow passing platform specific parameters to
-  // BuildIdentityManagerForTests. Need to be default
-  // constructible and moveable.
-  struct ExtraParams {
-    ExtraParams();
-    ~ExtraParams();
-    ExtraParams(ExtraParams&& other);
-    ExtraParams& operator=(ExtraParams&& other);
-
-#if defined(OS_IOS)
-    // If non-null, an iOS delegate instance will be constructed for the
-    // token service as opposed to the default fake delegate.
-    std::unique_ptr<ProfileOAuth2TokenServiceIOSProvider>
-        token_service_provider;
-#endif
-  };
-
   // Create an IdentityManager instance for tests.
   static std::unique_ptr<IdentityManagerWrapper> BuildIdentityManagerForTests(
       SigninClient* signin_client,
diff --git a/services/media_session/public/cpp/media_image_manager.cc b/services/media_session/public/cpp/media_image_manager.cc
index 54abae8..fbd2122 100644
--- a/services/media_session/public/cpp/media_image_manager.cc
+++ b/services/media_session/public/cpp/media_image_manager.cc
@@ -50,7 +50,7 @@
   if (dominant_size < min_size)
     return 0;
 
-  if (dominant_size <= ideal_size)
+  if (dominant_size < ideal_size)
     return 0.8 * (dominant_size - min_size) / (ideal_size - min_size) + 0.2;
 
   return 1.0 * ideal_size / dominant_size;
diff --git a/services/media_session/public/cpp/media_image_manager_unittest.cc b/services/media_session/public/cpp/media_image_manager_unittest.cc
index 3ba2b69..9e607f86 100644
--- a/services/media_session/public/cpp/media_image_manager_unittest.cc
+++ b/services/media_session/public/cpp/media_image_manager_unittest.cc
@@ -170,4 +170,16 @@
   EXPECT_EQ(image1, manager()->SelectImage(images));
 }
 
+TEST_F(MediaImageManagerTest, MinAndIdealAndImageSizeAreSame) {
+  MediaImageManager manager(10, 10);
+
+  std::vector<MediaImage> images;
+
+  MediaImage image;
+  image.sizes.push_back(gfx::Size(10, 10));
+  images.push_back(image);
+
+  EXPECT_TRUE(manager.SelectImage(images));
+}
+
 }  // namespace media_session
diff --git a/services/metrics/ukm_api.md b/services/metrics/ukm_api.md
index e9949c6..5b12fa33 100644
--- a/services/metrics/ukm_api.md
+++ b/services/metrics/ukm_api.md
@@ -1,18 +1,21 @@
 # URL-Keyed Metrics API
 
-This describes how to write client code to collect UKM data. Before you add new metrics, you should file a proposal.  See go/ukm for more information.
+This describes how to write client code to collect UKM data. Before you add new metrics, you should file a proposal.  See [go/ukm](http://go/ukm) for more information.
+
+[TOC]
 
 ## Document your metrics in ukm.xml
 
 Any events and metrics you collect need to be documented in [//tools/metrics/ukm/ukm.xml](https://cs.chromium.org/chromium/src/tools/metrics/ukm/ukm.xml)
 
-Important information to include:
+### Required Details
 
-* Metric owners: This the email of someone who can answer questions about how this metric is recorded, what it means, and how it should be used. Can include multiple people.
-* A description of the event about which you are recording details, including when the event will be recorded.
-* For each metric in the event: a description of the data and what it means.
-* The unit should be included in the description, along with possible output values.
-* If an event will only happen once per Navigation, it can be marked singular="true".
+* Metric `owner`: This the email of someone who can answer questions about how this metric is recorded, what it means, and how it should be used. Can include multiple people.
+* A `summary` of the event about which you are recording details, including a description of when the event will be recorded.
+* For each metric in the event: a `summary` of the data and what it means.
+* The `enum` type if the metric is enumerated. The enum uses the [//tools/metrics/histograms/enums.xml](https://cs.chromium.org/chromium/src/tools/metrics/histograms/enums.xml) file for definitions. Note this is the same file for UMA histogram definitions so these can ideally be reused.
+* If the metric is numeric then a `unit` should be included.
+* If an event will only happen once per Navigation, it can be marked `singular="true"`.
 
 ### Example
 ```xml
@@ -21,14 +24,14 @@
   <summary>
     Recorded when a page teleports a goat.
   </summary>
-  <metric name="Duration">
+  <metric name="Duration" unit="ms">
     <summary>
-      How long it took to teleport, in ns.
+      How long it took to teleport.
     </summary>
   </metric>
-  <metric name="Mass">
+  <metric name="GoatType" enum="GoatType">
     <summary>
-      The mass of the teleported goat, in kg, rounded to the nearest multiple of 10.
+      The type of goat that was teleported.
     </summary>
   </metric>
 </event>
@@ -82,7 +85,7 @@
 *   `profile.form_factor`
 *   `profile.system_ram`
 
-#### Aggregation by Metrics in the Same Event
+### Aggregation by Metrics in the Same Event
 
 In addition to the standard "profile" keys, aggregation can be done against
 another metric in the same event. This is accomplished with the same `<index>`
@@ -96,7 +99,7 @@
 
 ```xml
 <event name="Memory.Experimental">
-  <metric name="ProcessType">
+  <metric name="ProcessType" enum="MemoryProcessType">
     <aggregation>
       <history>
         <statistics export="False">
@@ -105,7 +108,7 @@
       </history>
     </aggregation>
   </metric>
-  <metric name="PrivateMemoryFootprint">
+  <metric name="PrivateMemoryFootprint" unit="MB">
     <aggregation>
       <history>
         <index fields="metrics.ProcessType"/>
@@ -118,9 +121,9 @@
 </event>
 ```
 
-## Enumeration Proportions
+### Enumeration Proportions
 
-Porportions are calculated against the number of "page loads" (meaning per
+Proportions are calculated against the number of "page loads" (meaning per
 "source" which is usually but not always the same as a browser page load) that
 emitted one or more values for the enumeration.  The proportions will sum to 1.0
 for an enumeration that emits only one result per page-load if it emits anything
@@ -165,7 +168,9 @@
 The numerator for each enum value is the count of how many times the value was
 emitted.
 
-## Get UkmRecorder instance
+## Client API
+
+### Get UkmRecorder Instance
 
 In order to record UKM events, your code needs a UkmRecorder object, defined by [//services/metrics/public/cpp/ukm_recorder.h](https://cs.chromium.org/chromium/src/services/metrics/public/cpp/ukm_recorder.h)
 
@@ -183,7 +188,7 @@
     .Record(ukm_recorder.get());
 ```
 
-## Get a ukm::SourceId
+### Get A ukm::SourceId
 
 UKM identifies navigations by their source ID and you'll need to associate and ID with your event in order to tie it to a main frame URL.  Preferrably, get an existing ID for the navigation from another object.
 
@@ -228,7 +233,7 @@
 
 You will also need to add your class as a friend of UkmRecorder in order to use this private API.
 
-## Create some events
+### Create Events
 
 Helper objects for recording your event are generated from the descriptions in ukm.xml.  You can use them like so:
 
@@ -238,18 +243,18 @@
 void OnGoatTeleported() {
   ...
   ukm::builders::Goat_Teleported(source_id)
-      .SetDuration(duration.InNanoseconds())
-      .SetMass(RoundedToMultiple(mass_kg, 10))
+      .SetDuration(duration.InMilliseconds())
+      .SetType(goat_type)
       .Record(ukm_recorder);
 }
 ```
 
 If the event name in the XML contains a period (`.`), it is replaced with an underscore (`_`) in the method name.
 
-## Check that it works
+### Local Testing
 
-Build chromium and run it with '--force-enable-metrics-reporting'.  Trigger your event and check chrome://ukm to make sure the data was recorded correctly.
+Build Chromium and run it with '--force-enable-metrics-reporting'. Trigger your event locally and check chrome://ukm to make sure the data was recorded correctly.
 
-## Unit testing
+## Unit Testing
 
 You can pass your code a TestUkmRecorder (see [//components/ukm/test_ukm_recorder.h](https://cs.chromium.org/chromium/src/components/ukm/test_ukm_recorder.h)) and then use the methods it provides to test that your data records correctly.
diff --git a/services/network/cookie_manager_unittest.cc b/services/network/cookie_manager_unittest.cc
index a2711a46..ead50df 100644
--- a/services/network/cookie_manager_unittest.cc
+++ b/services/network/cookie_manager_unittest.cc
@@ -333,7 +333,7 @@
 
 bool CompareCanonicalCookies(const net::CanonicalCookie& c1,
                              const net::CanonicalCookie& c2) {
-  return c1.FullCompare(c2);
+  return c1.PartialCompare(c2);
 }
 
 // Test the GetAllCookies accessor.  Also tests that canonical
diff --git a/services/network/cross_origin_read_blocking.cc b/services/network/cross_origin_read_blocking.cc
index 48b5e38a..244dfeda 100644
--- a/services/network/cross_origin_read_blocking.cc
+++ b/services/network/cross_origin_read_blocking.cc
@@ -531,9 +531,6 @@
   // Returns true if the data has been confirmed to be of the CORB-protected
   // content type that this sniffer is intended to detect.
   virtual bool IsConfirmedContentType() const = 0;
-
-  // Helper for reporting the right UMA.
-  virtual bool IsParserBreakerSniffer() const = 0;
 };
 
 // A ConfirmationSniffer that wraps one of the sniffing functions from
@@ -579,8 +576,6 @@
     return last_sniff_result_ == SniffingResult::kYes;
   }
 
-  bool IsParserBreakerSniffer() const override { return false; }
-
  private:
   // The function that actually knows how to sniff for a content type.
   SnifferFunction sniffer_function_;
@@ -591,22 +586,6 @@
   DISALLOW_COPY_AND_ASSIGN(SimpleConfirmationSniffer);
 };
 
-// A ConfirmationSniffer for parser breakers (fetch-only resources). This logs
-// to an UMA histogram whenever it is the reason for a response being blocked.
-class CrossOriginReadBlocking::ResponseAnalyzer::FetchOnlyResourceSniffer
-    : public CrossOriginReadBlocking::ResponseAnalyzer::
-          SimpleConfirmationSniffer {
- public:
-  FetchOnlyResourceSniffer()
-      : SimpleConfirmationSniffer(
-            &network::CrossOriginReadBlocking::SniffForFetchOnlyResource) {}
-
-  bool IsParserBreakerSniffer() const override { return true; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FetchOnlyResourceSniffer);
-};
-
 CrossOriginReadBlocking::ResponseAnalyzer::ResponseAnalyzer(
     const GURL& request_url,
     const base::Optional<url::Origin>& request_initiator,
@@ -616,7 +595,6 @@
   content_length_ = response.content_length;
   http_response_code_ =
       response.headers ? response.headers->response_code() : 0;
-  request_initiator_site_lock_ = request_initiator_site_lock;
 
   // CORB should look directly at the Content-Type header if one has been
   // received from the network. Ignoring |response.mime_type| helps avoid
@@ -895,6 +873,19 @@
 }
 
 // static
+bool CrossOriginReadBlocking::ResponseAnalyzer::SupportsRangeRequests(
+    const ResourceResponseInfo& response) {
+  if (response.headers) {
+    std::string value;
+    response.headers->GetNormalizedHeader("accept-ranges", &value);
+    if (!value.empty() && !base::LowerCaseEqualsASCII(value, "none")) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// static
 CrossOriginReadBlocking::ResponseAnalyzer::MimeTypeBucket
 CrossOriginReadBlocking::ResponseAnalyzer::GetMimeTypeBucket(
     const ResourceResponseInfo& response) {
@@ -956,22 +947,22 @@
 
   // When the MIME type is "text/plain", create sniffers for HTML, XML and
   // JSON. If any of these sniffers match, the response will be blocked.
-  const bool use_all = canonical_mime_type() == MimeType::kPlain;
+  const bool use_all = canonical_mime_type_ == MimeType::kPlain;
 
   // HTML sniffer.
-  if (use_all || canonical_mime_type() == MimeType::kHtml) {
+  if (use_all || canonical_mime_type_ == MimeType::kHtml) {
     sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>(
         &network::CrossOriginReadBlocking::SniffForHTML));
   }
 
   // XML sniffer.
-  if (use_all || canonical_mime_type() == MimeType::kXml) {
+  if (use_all || canonical_mime_type_ == MimeType::kXml) {
     sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>(
         &network::CrossOriginReadBlocking::SniffForXML));
   }
 
   // JSON sniffer.
-  if (use_all || canonical_mime_type() == MimeType::kJson) {
+  if (use_all || canonical_mime_type_ == MimeType::kJson) {
     sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>(
         &network::CrossOriginReadBlocking::SniffForJSON));
   }
@@ -983,7 +974,8 @@
   // header. So this sniffer is created unconditionally.
   //
   // For MimeType::kOthers, this will be the only sniffer that's active.
-  sniffers_.push_back(std::make_unique<FetchOnlyResourceSniffer>());
+  sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>(
+      &network::CrossOriginReadBlocking::SniffForFetchOnlyResource));
 }
 
 void CrossOriginReadBlocking::ResponseAnalyzer::SniffResponseBody(
@@ -993,9 +985,6 @@
   DCHECK(!sniffers_.empty());
   DCHECK(!found_blockable_content_);
 
-  DCHECK_LE(bytes_read_for_sniffing_, static_cast<int>(data.size()));
-  bytes_read_for_sniffing_ = static_cast<int>(data.size());
-
   DCHECK_LE(data.size(), static_cast<size_t>(net::kMaxBytesToSniff));
   DCHECK_LE(new_data_offset, data.size());
   bool has_new_data = (new_data_offset < data.size());
@@ -1010,9 +999,6 @@
     }
 
     if (sniffers_[i]->IsConfirmedContentType()) {
-      if (sniffers_[i]->IsParserBreakerSniffer())
-        found_parser_breaker_ = true;
-
       found_blockable_content_ = true;
       sniffers_.clear();
       break;
@@ -1053,15 +1039,15 @@
 
   // Don't bother showing a warning message when blocking responses that are
   // already empty.
-  if (content_length() == 0)
+  if (content_length_ == 0)
     return false;
-  if (http_response_code() == 204)
+  if (http_response_code_ == 204)
     return false;
 
   // Don't bother showing a warning message when blocking responses that are
   // associated with error responses (e.g. it is quite common to serve a
   // text/html 404 error page for an <img> tag pointing to a wrong URL).
-  if (400 <= http_response_code() && http_response_code() <= 599)
+  if (400 <= http_response_code_ && http_response_code_ <= 599)
     return false;
 
   return true;
@@ -1158,6 +1144,11 @@
             protection_decision);
     }
   }
+  // Also log if the server supports range requests, since these may allow
+  // bypassing CORB.
+  UMA_HISTOGRAM_BOOLEAN(
+      "SiteIsolation.CORBProtection.SensitiveWithRangeSupport",
+      SupportsRangeRequests(response));
 }
 
 // static
diff --git a/services/network/cross_origin_read_blocking.h b/services/network/cross_origin_read_blocking.h
index 409c07d..39fa71b 100644
--- a/services/network/cross_origin_read_blocking.h
+++ b/services/network/cross_origin_read_blocking.h
@@ -136,26 +136,16 @@
     }
 
     // The MIME type determined by ShouldBlockBasedOnHeaders.
-    const CrossOriginReadBlocking::MimeType& canonical_mime_type() const {
+    const CrossOriginReadBlocking::MimeType& canonical_mime_type_for_testing()
+        const {
       return canonical_mime_type_;
     }
 
-    // Value of the content-length response header if available. -1 if not
-    // available.
-    int64_t content_length() const { return content_length_; }
-
-    // The HTTP response code (e.g. 200 or 404) received in response to this
-    // resource request.
-    int http_response_code() const { return http_response_code_; }
-
     // Allows ResponseAnalyzer to sniff the response body.
     void SniffResponseBody(base::StringPiece data, size_t new_data_offset);
 
-    bool found_parser_breaker() const { return found_parser_breaker_; }
-
     class ConfirmationSniffer;
     class SimpleConfirmationSniffer;
-    class FetchOnlyResourceSniffer;
 
     void LogAllowedResponse();
     void LogBlockedResponse();
@@ -167,6 +157,8 @@
                              SeemsSensitiveFromCacheHeuristic);
     FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest,
                              SeemsSensitiveWithBothHeuristics);
+    FRIEND_TEST_ALL_PREFIXES(CrossOriginReadBlockingTest,
+                             SupportsRangeRequests);
     FRIEND_TEST_ALL_PREFIXES(content::CrossSiteDocumentResourceHandlerTest,
                              CORBProtectionLogging);
 
@@ -202,6 +194,12 @@
     static bool SeemsSensitiveFromCacheHeuristic(
         const ResourceResponseInfo& response);
 
+    // Checks if a response has an Accept-Ranges header. This indicates the
+    // server supports range requests which may allow bypassing CORB due to
+    // their multipart content type.
+    static bool SupportsRangeRequests(
+        const ResourceResponseInfo& response_headers);
+
     // Determines the MIME type bucket for CORB protection logging.
     static MimeTypeBucket GetMimeTypeBucket(
         const ResourceResponseInfo& response);
@@ -211,7 +209,8 @@
     void CreateSniffers();
 
     // Reports potentially sensitive responses and whether CORB would have
-    // protected them, were they made cross origin.
+    // protected them, were they made cross origin. Also reports if the server
+    // supports range requests.
     static void LogSensitiveResponseProtection(
         const ResourceResponseInfo& response,
         BlockingDecision would_protect_based_on_headers);
@@ -231,16 +230,11 @@
     // resource request.
     int http_response_code_ = 0;
 
-    // Propagated from URLLoaderFactoryParams::request_initiator_site_lock;
-    base::Optional<url::Origin> request_initiator_site_lock_;
-
     // The sniffers to be used.
     std::vector<std::unique_ptr<ConfirmationSniffer>> sniffers_;
 
     // Sniffing results.
     bool found_blockable_content_ = false;
-    bool found_parser_breaker_ = false;
-    int bytes_read_for_sniffing_ = -1;
 
     DISALLOW_COPY_AND_ASSIGN(ResponseAnalyzer);
   };
diff --git a/services/network/cross_origin_read_blocking_unittest.cc b/services/network/cross_origin_read_blocking_unittest.cc
index aa0fab5..0b9dd5cd5 100644
--- a/services/network/cross_origin_read_blocking_unittest.cc
+++ b/services/network/cross_origin_read_blocking_unittest.cc
@@ -414,4 +414,26 @@
                   SeemsSensitiveFromCacheHeuristic(both_response));
 }
 
+TEST(CrossOriginReadBlockingTest, SupportsRangeRequests) {
+  // Response with no Accept-Ranges header. Should return false.
+  network::ResourceResponseInfo no_accept_ranges =
+      CreateResponse("HTTP/1.1 200 OK");
+  EXPECT_FALSE(CrossOriginReadBlocking::ResponseAnalyzer::SupportsRangeRequests(
+      no_accept_ranges));
+
+  // Response with an Accept-Ranges header. Should return true.
+  network::ResourceResponseInfo bytes_accept_ranges = CreateResponse(
+      "HTTP/1.1 200 OK\n"
+      "Accept-Ranges: bytes");
+  EXPECT_TRUE(CrossOriginReadBlocking::ResponseAnalyzer::SupportsRangeRequests(
+      bytes_accept_ranges));
+
+  // Response with an Accept-Ranges header value of |none|. Should return false.
+  network::ResourceResponseInfo none_accept_ranges = CreateResponse(
+      "HTTP/1.1 200 OK\n"
+      "Accept-Ranges: none");
+  EXPECT_FALSE(CrossOriginReadBlocking::ResponseAnalyzer::SupportsRangeRequests(
+      none_accept_ranges));
+}
+
 }  // namespace network
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index f71067d..492cda2 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -1553,7 +1553,7 @@
     const GURL& original_url,
     int32_t load_flags,
     bool privacy_mode_enabled,
-    const base::Optional<net::NetworkIsolationKey>& network_isolation_key) {
+    const net::NetworkIsolationKey& network_isolation_key) {
   GURL url = GetHSTSRedirect(original_url);
 
   // |PreconnectSockets| may receive arguments from the renderer, which is not
@@ -1575,8 +1575,7 @@
   request_info.load_flags = load_flags;
   request_info.privacy_mode = privacy_mode_enabled ? net::PRIVACY_MODE_ENABLED
                                                    : net::PRIVACY_MODE_DISABLED;
-  if (network_isolation_key)
-    request_info.network_isolation_key = *network_isolation_key;
+  request_info.network_isolation_key = network_isolation_key;
 
   net::HttpTransactionFactory* factory =
       url_request_context_->http_transaction_factory();
diff --git a/services/network/network_context.h b/services/network/network_context.h
index c6e9cf4..9376edd 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -318,12 +318,12 @@
       const std::string& ocsp_response,
       const std::string& sct_list,
       VerifyCertificateForTestingCallback callback) override;
-  void PreconnectSockets(uint32_t num_streams,
-                         const GURL& url,
-                         int32_t load_flags,
-                         bool privacy_mode_enabled,
-                         const base::Optional<net::NetworkIsolationKey>&
-                             network_isolation_key) override;
+  void PreconnectSockets(
+      uint32_t num_streams,
+      const GURL& url,
+      int32_t load_flags,
+      bool privacy_mode_enabled,
+      const net::NetworkIsolationKey& network_isolation_key) override;
   void CreateP2PSocketManager(
       mojom::P2PTrustedSocketManagerClientPtr client,
       mojom::P2PTrustedSocketManagerRequest trusted_socket_manager,
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index efc26e5..f8cb6e1 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -3368,14 +3368,9 @@
 class ConnectionListener
     : public net::test_server::EmbeddedTestServerConnectionListener {
  public:
-  ConnectionListener()
-      : total_sockets_seen_(0),
-        total_sockets_waited_for_(0),
-        task_runner_(base::ThreadTaskRunnerHandle::Get()),
-        num_accepted_connections_needed_(0),
-        num_accepted_connections_loop_(nullptr) {}
+  ConnectionListener() = default;
 
-  ~ConnectionListener() override {}
+  ~ConnectionListener() override = default;
 
   // Get called from the EmbeddedTestServer thread to be notified that
   // a connection was accepted.
@@ -3397,13 +3392,17 @@
 
   // Wait for exactly |n| items in |sockets_|. |n| must be greater than 0.
   void WaitForAcceptedConnections(size_t num_connections) {
-    DCHECK(!num_accepted_connections_loop_);
+    DCHECK(on_done_accepting_connections_.is_null());
     DCHECK_GT(num_connections, 0u);
     base::RunLoop run_loop;
     {
       base::AutoLock lock(lock_);
       EXPECT_GE(num_connections, sockets_.size() - total_sockets_waited_for_);
-      num_accepted_connections_loop_ = &run_loop;
+      // QuitWhenIdle() instead of regular Quit() because in Preconnect tests we
+      // count "idle_socket_count" but tasks posted synchronously after
+      // AcceptedSocket() need to resolve before the new sockets are considered
+      // idle.
+      on_done_accepting_connections_ = run_loop.QuitWhenIdleClosure();
       num_accepted_connections_needed_ = num_connections;
       CheckAccepted();
     }
@@ -3426,18 +3425,16 @@
     lock_.AssertAcquired();
     // |num_accepted_connections_loop_| null implies
     // |num_accepted_connections_needed_| == 0.
-    DCHECK(num_accepted_connections_loop_ ||
+    DCHECK(!on_done_accepting_connections_.is_null() ||
            num_accepted_connections_needed_ == 0);
-    if (!num_accepted_connections_loop_ ||
+    if (on_done_accepting_connections_.is_null() ||
         num_accepted_connections_needed_ !=
             sockets_.size() - total_sockets_waited_for_) {
       return;
     }
 
-    task_runner_->PostTask(FROM_HERE,
-                           num_accepted_connections_loop_->QuitClosure());
     num_accepted_connections_needed_ = 0;
-    num_accepted_connections_loop_ = nullptr;
+    std::move(on_done_accepting_connections_).Run();
   }
 
  private:
@@ -3455,13 +3452,11 @@
     return address.port();
   }
 
-  int total_sockets_seen_;
-  int total_sockets_waited_for_;
+  int total_sockets_seen_ = 0;
+  int total_sockets_waited_for_ = 0;
 
   enum SocketStatus { SOCKET_ACCEPTED, SOCKET_READ_FROM };
 
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
   // This lock protects all the members below, which each are used on both the
   // IO and UI thread. Members declared after the lock are protected by it.
   mutable base::Lock lock_;
@@ -3470,9 +3465,9 @@
 
   // If |num_accepted_connections_needed_| is non zero, then the object is
   // waiting for |num_accepted_connections_needed_| sockets to be accepted
-  // before quitting the |num_accepted_connections_loop_|.
-  size_t num_accepted_connections_needed_;
-  base::RunLoop* num_accepted_connections_loop_;
+  // before invoking |on_done_accepting_connections_|.
+  size_t num_accepted_connections_needed_ = 0;
+  base::OnceClosure on_done_accepting_connections_;
 
   DISALLOW_COPY_AND_ASSIGN(ConnectionListener);
 };
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index be38a727..92a9a7b 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -117,6 +117,7 @@
     "net_ipc_param_traits.h",
     "network_ipc_param_traits.cc",
     "network_ipc_param_traits.h",
+    "network_isolation_key_mojom_traits.cc",
     "network_isolation_key_mojom_traits.h",
     "p2p_param_traits.cc",
     "p2p_param_traits.h",
diff --git a/services/network/public/cpp/network_isolation_key_mojom_traits.cc b/services/network/public/cpp/network_isolation_key_mojom_traits.cc
new file mode 100644
index 0000000..c268c19a
--- /dev/null
+++ b/services/network/public/cpp/network_isolation_key_mojom_traits.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 "services/network/public/cpp/network_isolation_key_mojom_traits.h"
+
+namespace mojo {
+
+bool StructTraits<network::mojom::NetworkIsolationKeyDataView,
+                  net::NetworkIsolationKey>::
+    Read(network::mojom::NetworkIsolationKeyDataView data,
+         net::NetworkIsolationKey* out) {
+  base::Optional<url::Origin> top_frame_origin, initiating_frame_origin;
+  if (!data.ReadTopFrameOrigin(&top_frame_origin))
+    return false;
+  if (!data.ReadInitiatingFrameOrigin(&initiating_frame_origin))
+    return false;
+  // A key is either fully empty or fully populated (for all fields relevant
+  // given the flags set).  The constructor verifies this, so if the top-frame
+  // origin is populated, we call the full constructor, otherwise, the empty.
+  if (top_frame_origin.has_value()) {
+    *out = net::NetworkIsolationKey(top_frame_origin, initiating_frame_origin);
+  } else {
+    *out = net::NetworkIsolationKey();
+  }
+  return true;
+}
+
+}  // namespace mojo
diff --git a/services/network/public/cpp/network_isolation_key_mojom_traits.h b/services/network/public/cpp/network_isolation_key_mojom_traits.h
index 8a2dcfeb..f3f9bda1 100644
--- a/services/network/public/cpp/network_isolation_key_mojom_traits.h
+++ b/services/network/public/cpp/network_isolation_key_mojom_traits.h
@@ -14,29 +14,21 @@
 namespace mojo {
 
 template <>
-struct StructTraits<network::mojom::NetworkIsolationKeyDataView,
-                    net::NetworkIsolationKey> {
-  static bool IsNull(const net::NetworkIsolationKey& input) {
-    return input.IsEmpty();
-  }
-
-  static void SetToNull(net::NetworkIsolationKey* out) {
-    *out = net::NetworkIsolationKey();
-  }
-
+struct COMPONENT_EXPORT(NETWORK_CPP_BASE)
+    StructTraits<network::mojom::NetworkIsolationKeyDataView,
+                 net::NetworkIsolationKey> {
   static const base::Optional<url::Origin>& top_frame_origin(
       const net::NetworkIsolationKey& input) {
     return input.GetTopFrameOrigin();
   }
 
-  static bool Read(network::mojom::NetworkIsolationKeyDataView data,
-                   net::NetworkIsolationKey* out) {
-    base::Optional<url::Origin> top_frame_origin;
-    if (!data.ReadTopFrameOrigin(&top_frame_origin))
-      return false;
-    *out = net::NetworkIsolationKey(top_frame_origin);
-    return true;
+  static const base::Optional<url::Origin>& initiating_frame_origin(
+      const net::NetworkIsolationKey& input) {
+    return input.GetInitiatingFrameOrigin();
   }
+
+  static bool Read(network::mojom::NetworkIsolationKeyDataView data,
+                   net::NetworkIsolationKey* out);
 };
 
 }  // namespace mojo
diff --git a/services/network/public/cpp/network_isolation_key_mojom_traits_unittest.cc b/services/network/public/cpp/network_isolation_key_mojom_traits_unittest.cc
index c5a0f8fa..928ac97e 100644
--- a/services/network/public/cpp/network_isolation_key_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/network_isolation_key_mojom_traits_unittest.cc
@@ -5,7 +5,9 @@
 #include "services/network/public/cpp/network_isolation_key_mojom_traits.h"
 
 #include "base/stl_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
+#include "net/base/features.h"
 #include "services/network/public/mojom/network_isolation_key.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -16,7 +18,35 @@
 TEST(NetworkIsolationKeyMojomTraitsTest, SerializeAndDeserialize) {
   std::vector<net::NetworkIsolationKey> keys = {
       net::NetworkIsolationKey(),
-      net::NetworkIsolationKey(url::Origin::Create(GURL("http://a.test/")))};
+      net::NetworkIsolationKey(url::Origin::Create(GURL("http://a.test/")),
+                               url::Origin::Create(GURL("http://b.test/")))};
+
+  for (auto original : keys) {
+    net::NetworkIsolationKey copied;
+    EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+                network::mojom::NetworkIsolationKey>(&original, &copied));
+    EXPECT_EQ(original, copied);
+  }
+}
+
+class NetworkIsolationKeyMojomTraitsWithInitiatingFrameOriginTest
+    : public testing::Test {
+ public:
+  NetworkIsolationKeyMojomTraitsWithInitiatingFrameOriginTest() {
+    feature_list_.InitAndEnableFeature(
+        net::features::kAppendInitiatingFrameOriginToNetworkIsolationKey);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(NetworkIsolationKeyMojomTraitsWithInitiatingFrameOriginTest,
+       SerializeAndDeserialize) {
+  std::vector<net::NetworkIsolationKey> keys = {
+      net::NetworkIsolationKey(),
+      net::NetworkIsolationKey(url::Origin::Create(GURL("http://a.test/")),
+                               url::Origin::Create(GURL("http://b.test/")))};
 
   for (auto original : keys) {
     net::NetworkIsolationKey copied;
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index b09c5d2e..d41870d7 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -863,7 +863,7 @@
                     url.mojom.Url url,
                     int32 load_flags,
                     bool privacy_mode_enabled,
-                    NetworkIsolationKey? network_isolation_key);
+                    NetworkIsolationKey network_isolation_key);
 
   // Creates a P2PSocketManager instance, used for WebRTC.
   CreateP2PSocketManager(P2PTrustedSocketManagerClient client,
diff --git a/services/network/public/mojom/network_isolation_key.mojom b/services/network/public/mojom/network_isolation_key.mojom
index 2964809..3734e8fb 100644
--- a/services/network/public/mojom/network_isolation_key.mojom
+++ b/services/network/public/mojom/network_isolation_key.mojom
@@ -11,4 +11,5 @@
   // Keeping optional to allow clients that do not populate top frame origin.
   // TODO(crbug.com/910721): This will eventually always be populated.
   url.mojom.Origin? top_frame_origin;
+  url.mojom.Origin? initiating_frame_origin;
 };
diff --git a/services/network/public/mojom/websocket.mojom b/services/network/public/mojom/websocket.mojom
index fb9f553..6dd2579 100644
--- a/services/network/public/mojom/websocket.mojom
+++ b/services/network/public/mojom/websocket.mojom
@@ -52,24 +52,24 @@
 
 interface WebSocketHandshakeClient {
   // Notify the renderer that the browser has started an opening handshake.
-  OnStartOpeningHandshake(WebSocketHandshakeRequest request);
+  OnOpeningHandshakeStarted(WebSocketHandshakeRequest request);
 
-  // Notify the renderer that the browser has finished an opening handshake.
-  // This message precedes AddChannelResponse.
-  // |response| may contains cookie-related headers when the client has
+  // Called when the HTTP response is received. This doesn't mean the connection
+  // is (or will be) established. This message precedes OnConnectionEstablished.
+  // |response| may contain cookie-related headers when the client has
   // an access to raw cookie information.
-  OnFinishOpeningHandshake(WebSocketHandshakeResponse response);
+  OnResponseReceived(WebSocketHandshakeResponse response);
 
-  // Response to an AddChannelRequest. |selected_protocol| is the sub-protocol
-  // the server selected, or empty if no sub-protocol was selected.
+  // Called when the connection is established. |selected_protocol| is the
+  // sub-protocol the server selected, or empty if no sub-protocol was selected.
   // |extensions| is the list of extensions negotiated for the connection.
   // default threshold value
   // |receive_quota_threshold| is the value that the renderer calls
   // AddReceiveFlowControlQuota() to the browser per receiving this value
   // so that the browser can continue sending remaining data to the renderer.
-  OnAddChannelResponse(string selected_protocol,
-                       string extensions,
-                       uint64 receive_quota_threshold);
+  OnConnectionEstablished(string selected_protocol,
+                          string extensions,
+                          uint64 receive_quota_threshold);
 };
 
 interface WebSocketClient {
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc
index e87db33..dfa63bc 100644
--- a/services/network/restricted_cookie_manager.cc
+++ b/services/network/restricted_cookie_manager.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/compiler_specific.h"  // for FALLTHROUGH;
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_util.h"
@@ -25,6 +26,8 @@
 
 namespace network {
 
+using CookieInclusionStatus = net::CanonicalCookie::CookieInclusionStatus;
+
 class RestrictedCookieManager::Listener : public base::LinkNode<Listener> {
  public:
   Listener(net::CookieStore* cookie_store,
@@ -64,7 +67,7 @@
                       net::CookieChangeCause cause) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     if (cookie.IncludeForRequestURL(url_, options_) !=
-        net::CanonicalCookie::CookieInclusionStatus::INCLUDE)
+        CookieInclusionStatus::INCLUDE)
       return;
 
     // When a user blocks a site's access to cookies, the existing cookies are
@@ -149,6 +152,10 @@
 
   // TODO(https://crbug.com/925311): Wire initiator here.
   net::CookieOptions net_options;
+
+  // TODO(https://crbug.com/977040): remove set_return_excluded_cookies() once
+  //                                 removing deprecation warnings.
+  net_options.set_return_excluded_cookies();
   net_options.set_same_site_cookie_context(
       net::cookie_util::ComputeSameSiteContextForScriptGet(
           url, site_for_cookies, base::nullopt /*initiator*/));
@@ -157,12 +164,13 @@
       url, net_options,
       base::BindOnce(&RestrictedCookieManager::CookieListToGetAllForUrlCallback,
                      weak_ptr_factory_.GetWeakPtr(), url, site_for_cookies,
-                     std::move(options), std::move(callback)));
+                     net_options, std::move(options), std::move(callback)));
 }
 
 void RestrictedCookieManager::CookieListToGetAllForUrlCallback(
     const GURL& url,
     const GURL& site_for_cookies,
+    const net::CookieOptions& net_options,
     mojom::CookieManagerGetOptionsPtr options,
     GetAllForUrlCallback callback,
     const net::CookieList& cookie_list,
@@ -175,6 +183,19 @@
   std::vector<net::CanonicalCookie> result;
   std::vector<net::CookieWithStatus> result_with_status;
 
+  // TODO(https://crbug.com/977040): Remove once samesite tightening up is
+  // rolled out.
+  for (const auto& cookie_and_status : excluded_cookies) {
+    if (cookie_and_status.status ==
+            CookieInclusionStatus::
+                EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX ||
+        cookie_and_status.status ==
+            CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE) {
+      result_with_status.push_back(
+          {cookie_and_status.cookie, cookie_and_status.status});
+    }
+  }
+
   if (!blocked)
     result.reserve(cookie_list.size());
   mojom::CookieMatchType match_type = options->match_type;
@@ -197,12 +218,20 @@
 
     if (blocked) {
       result_with_status.push_back(
-          {cookie, net::CanonicalCookie::CookieInclusionStatus::
-                       EXCLUDE_USER_PREFERENCES});
+          {cookie, CookieInclusionStatus::EXCLUDE_USER_PREFERENCES});
     } else {
       result.push_back(cookie);
-      result_with_status.push_back(
-          {cookie, net::CanonicalCookie::CookieInclusionStatus::INCLUDE});
+      result_with_status.push_back({cookie, CookieInclusionStatus::INCLUDE});
+
+      // Warn about upcoming deprecations.
+      // TODO(https://crbug.com/977040): Remove once samesite tightening up is
+      // rolled out.
+      CookieInclusionStatus eventual_status =
+          net::cookie_util::CookieWouldBeExcludedDueToSameSite(cookie,
+                                                               net_options);
+      if (eventual_status != CookieInclusionStatus::INCLUDE) {
+        result_with_status.push_back({cookie, eventual_status});
+      }
     }
   }
 
@@ -233,17 +262,14 @@
   bool blocked =
       !cookie_settings_->IsCookieAccessAllowed(url, site_for_cookies);
 
-  std::vector<net::CookieWithStatus> result_with_status;
-  net::CanonicalCookie::CookieInclusionStatus status_for_cookie_list =
-      blocked ? net::CanonicalCookie::CookieInclusionStatus::
-                    EXCLUDE_USER_PREFERENCES
-              : net::CanonicalCookie::CookieInclusionStatus::INCLUDE;
-  result_with_status.push_back({cookie, status_for_cookie_list});
-
-  network_context_client_->OnCookiesChanged(is_service_worker_, process_id_,
-                                            frame_id_, url, site_for_cookies,
-                                            result_with_status);
   if (blocked) {
+    std::vector<net::CookieWithStatus> result_with_status;
+    result_with_status.push_back(
+        {cookie, CookieInclusionStatus::EXCLUDE_USER_PREFERENCES});
+    network_context_client_->OnCookiesChanged(is_service_worker_, process_id_,
+                                              frame_id_, url, site_for_cookies,
+                                              result_with_status);
+
     std::move(callback).Run(false);
     return;
   }
@@ -255,6 +281,7 @@
       cookie.Name(), cookie.Value(), cookie.Domain(), cookie.Path(), now,
       cookie.ExpiryDate(), now, cookie.IsSecure(), cookie.IsHttpOnly(),
       cookie.SameSite(), cookie.Priority());
+  net::CanonicalCookie cookie_copy = *sanitized_cookie;
 
   // TODO(pwnall): source_scheme might depend on the renderer.
   net::CookieOptions options;
@@ -264,7 +291,45 @@
   options.set_exclude_httponly();  // Default, but make it explicit here.
   cookie_store_->SetCanonicalCookieAsync(
       std::move(sanitized_cookie), origin_.scheme(), options,
-      net::cookie_util::AdaptCookieInclusionStatusToBool(std::move(callback)));
+      base::BindOnce(&RestrictedCookieManager::SetCanonicalCookieResult,
+                     weak_ptr_factory_.GetWeakPtr(), url, site_for_cookies,
+                     cookie_copy, options, std::move(callback)));
+}
+
+void RestrictedCookieManager::SetCanonicalCookieResult(
+    const GURL& url,
+    const GURL& site_for_cookies,
+    const net::CanonicalCookie& cookie,
+    const net::CookieOptions& net_options,
+    SetCanonicalCookieCallback user_callback,
+    net::CanonicalCookie::CookieInclusionStatus status) {
+  std::vector<net::CookieWithStatus> notify;
+  // TODO(https://crbug.com/977040): Only report pure INCLUDE once samesite
+  // tightening up is rolled out.
+  DCHECK_NE(status, CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
+
+  switch (status) {
+    case CookieInclusionStatus::INCLUDE: {
+      CookieInclusionStatus eventual_status =
+          net::cookie_util::CookieWouldBeExcludedDueToSameSite(cookie,
+                                                               net_options);
+      if (eventual_status != CookieInclusionStatus::INCLUDE) {
+        notify.push_back({cookie, eventual_status});
+      }
+      FALLTHROUGH;
+    }
+    case CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX:
+    case CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE:
+      notify.push_back({cookie, status});
+      network_context_client_->OnCookiesChanged(
+          is_service_worker_, process_id_, frame_id_, url, site_for_cookies,
+          std::move(notify));
+      break;
+
+    default:
+      break;
+  }
+  std::move(user_callback).Run(status == CookieInclusionStatus::INCLUDE);
 }
 
 void RestrictedCookieManager::AddChangeListener(
diff --git a/services/network/restricted_cookie_manager.h b/services/network/restricted_cookie_manager.h
index 34ea0fe..114aa21 100644
--- a/services/network/restricted_cookie_manager.h
+++ b/services/network/restricted_cookie_manager.h
@@ -80,11 +80,22 @@
   void CookieListToGetAllForUrlCallback(
       const GURL& url,
       const GURL& site_for_cookies,
+      const net::CookieOptions& net_options,
       mojom::CookieManagerGetOptionsPtr options,
       GetAllForUrlCallback callback,
       const net::CookieList& cookie_list,
       const net::CookieStatusList& excluded_cookies);
 
+  // Reports the result of setting the cookie to |network_context_client_|, and
+  // invokes the user callback.
+  void SetCanonicalCookieResult(
+      const GURL& url,
+      const GURL& site_for_cookies,
+      const net::CanonicalCookie& cookie,
+      const net::CookieOptions& net_options,
+      SetCanonicalCookieCallback user_callback,
+      net::CanonicalCookie::CookieInclusionStatus status);
+
   // Called when the Mojo pipe associated with a listener is closed.
   void RemoveChangeListener(Listener* listener);
 
diff --git a/services/network/restricted_cookie_manager_unittest.cc b/services/network/restricted_cookie_manager_unittest.cc
index 47c6a98..74475fa 100644
--- a/services/network/restricted_cookie_manager_unittest.cc
+++ b/services/network/restricted_cookie_manager_unittest.cc
@@ -10,8 +10,10 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "mojo/core/embedder/embedder.h"
+#include "net/base/features.h"
 #include "net/cookies/canonical_cookie_test_helpers.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_monster.h"
@@ -246,7 +248,7 @@
 
 bool CompareCanonicalCookies(const net::CanonicalCookie& c1,
                              const net::CanonicalCookie& c2) {
-  return c1.FullCompare(c2);
+  return c1.PartialCompare(c2);
 }
 
 }  // anonymous namespace
@@ -362,6 +364,27 @@
     EXPECT_EQ("cookie-value", cookies[0].Value());
   }
 
+  ASSERT_EQ(2u, recorded_activity().size());
+  EXPECT_EQ(recorded_activity()[0].get, true);
+  EXPECT_EQ(recorded_activity()[0].url, "http://example.com/test/");
+  EXPECT_EQ(recorded_activity()[0].site_for_cookies, "http://notexample.com/");
+  EXPECT_THAT(recorded_activity()[0].cookie,
+              net::MatchesCookieLine("cookie-name=cookie-value"));
+  EXPECT_EQ(recorded_activity()[0].status,
+            net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
+
+  // Duplicated for exclusion warning.
+  EXPECT_EQ(recorded_activity()[1].get, true);
+  EXPECT_EQ(recorded_activity()[1].url, "http://example.com/test/");
+  EXPECT_EQ(recorded_activity()[1].site_for_cookies, "http://notexample.com/");
+  EXPECT_THAT(recorded_activity()[1].cookie,
+              net::MatchesCookieLine("cookie-name=cookie-value"));
+  // SetSessionCookie uses net::CookieSameSite::NO_RESTRICTION, but this is an
+  // insecure cookie.
+  EXPECT_EQ(recorded_activity()[1].status,
+            net::CanonicalCookie::CookieInclusionStatus::
+                EXCLUDE_SAMESITE_NONE_INSECURE);
+
   // Disabing getting third-party cookies works correctly.
   cookie_settings_.set_block_third_party_cookies(true);
   {
@@ -375,23 +398,47 @@
     ASSERT_THAT(cookies, testing::SizeIs(0));
   }
 
-  ASSERT_EQ(2u, recorded_activity().size());
+  ASSERT_EQ(3u, recorded_activity().size());
+  EXPECT_EQ(recorded_activity()[2].get, true);
+  EXPECT_EQ(recorded_activity()[2].url, "http://example.com/test/");
+  EXPECT_EQ(recorded_activity()[2].site_for_cookies, "http://notexample.com/");
+  EXPECT_THAT(recorded_activity()[2].cookie,
+              net::MatchesCookieLine("cookie-name=cookie-value"));
+  EXPECT_EQ(
+      recorded_activity()[2].status,
+      net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
+}
+
+TEST_F(RestrictedCookieManagerTest, GetAllForUrlPolicyWarnActual) {
+  SetSessionCookie("cookie-name", "cookie-value", "example.com", "/");
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      {net::features::kSameSiteByDefaultCookies,
+       net::features::
+           kCookiesWithoutSameSiteMustBeSecure} /* enabled_features */,
+      {} /* disabled_features */);
+
+  {
+    auto options = mojom::CookieManagerGetOptions::New();
+    options->name = "cookie-name";
+    options->match_type = mojom::CookieMatchType::STARTS_WITH;
+
+    std::vector<net::CanonicalCookie> cookies = sync_service_->GetAllForUrl(
+        GURL("http://example.com/test/"), GURL("http://notexample.com"),
+        std::move(options));
+    EXPECT_TRUE(cookies.empty());
+  }
+
+  ASSERT_EQ(1u, recorded_activity().size());
+
   EXPECT_EQ(recorded_activity()[0].get, true);
   EXPECT_EQ(recorded_activity()[0].url, "http://example.com/test/");
   EXPECT_EQ(recorded_activity()[0].site_for_cookies, "http://notexample.com/");
   EXPECT_THAT(recorded_activity()[0].cookie,
               net::MatchesCookieLine("cookie-name=cookie-value"));
   EXPECT_EQ(recorded_activity()[0].status,
-            net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
-
-  EXPECT_EQ(recorded_activity()[1].get, true);
-  EXPECT_EQ(recorded_activity()[1].url, "http://example.com/test/");
-  EXPECT_EQ(recorded_activity()[1].site_for_cookies, "http://notexample.com/");
-  EXPECT_THAT(recorded_activity()[1].cookie,
-              net::MatchesCookieLine("cookie-name=cookie-value"));
-  EXPECT_EQ(
-      recorded_activity()[1].status,
-      net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
+            net::CanonicalCookie::CookieInclusionStatus::
+                EXCLUDE_SAMESITE_NONE_INSECURE);
 }
 
 TEST_F(RestrictedCookieManagerTest, SetCanonicalCookie) {
@@ -438,6 +485,22 @@
         *cookie, GURL("http://example.com"), GURL("http://notexample.com")));
   }
 
+  ASSERT_EQ(2u, recorded_activity().size());
+  EXPECT_EQ(recorded_activity()[0].get, false);
+  EXPECT_EQ(recorded_activity()[0].url, "http://example.com/");
+  EXPECT_EQ(recorded_activity()[0].site_for_cookies, "http://notexample.com/");
+  EXPECT_THAT(recorded_activity()[0].cookie, net::MatchesCookieLine("A=B"));
+  EXPECT_EQ(recorded_activity()[0].status,
+            net::CanonicalCookie::CookieInclusionStatus::
+                EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
+
+  EXPECT_EQ(recorded_activity()[1].get, false);
+  EXPECT_EQ(recorded_activity()[1].url, "http://example.com/");
+  EXPECT_EQ(recorded_activity()[1].site_for_cookies, "http://notexample.com/");
+  EXPECT_THAT(recorded_activity()[1].cookie, net::MatchesCookieLine("A=B"));
+  EXPECT_EQ(recorded_activity()[1].status,
+            net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
+
   {
     // Not if third-party cookies are disabled, though.
     cookie_settings_.set_block_third_party_cookies(true);
@@ -448,6 +511,16 @@
         *cookie, GURL("http://example.com"), GURL("http://otherexample.com")));
   }
 
+  ASSERT_EQ(3u, recorded_activity().size());
+  EXPECT_EQ(recorded_activity()[2].get, false);
+  EXPECT_EQ(recorded_activity()[2].url, "http://example.com/");
+  EXPECT_EQ(recorded_activity()[2].site_for_cookies,
+            "http://otherexample.com/");
+  EXPECT_THAT(recorded_activity()[2].cookie, net::MatchesCookieLine("A2=B2"));
+  EXPECT_EQ(
+      recorded_activity()[2].status,
+      net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
+
   // Read back, in first-party context
   auto options = mojom::CookieManagerGetOptions::New();
   options->name = "A";
@@ -460,29 +533,38 @@
   EXPECT_EQ("A", cookies[0].Name());
   EXPECT_EQ("B", cookies[0].Value());
 
-  ASSERT_EQ(3u, recorded_activity().size());
+  ASSERT_EQ(4u, recorded_activity().size());
+  EXPECT_EQ(recorded_activity()[3].get, true);
+  EXPECT_EQ(recorded_activity()[3].url, "http://example.com/test/");
+  EXPECT_EQ(recorded_activity()[3].site_for_cookies, "http://example.com/");
+  EXPECT_THAT(recorded_activity()[3].cookie, net::MatchesCookieLine("A=B"));
+  EXPECT_EQ(recorded_activity()[3].status,
+            net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
+}
+
+TEST_F(RestrictedCookieManagerTest, SetCanonicalCookiePolicyWarnActual) {
+  // Make sure the deprecation warnings are also produced when the feature
+  // to enable the new behavior is on.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(net::features::kSameSiteByDefaultCookies);
+
+  // Uses different options between create/set here for failure to be at set.
+  net::CookieOptions create_options;
+  create_options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
+  auto cookie = net::CanonicalCookie::Create(GURL("http://example.com"), "A=B",
+                                             base::Time::Now(), create_options);
+  EXPECT_FALSE(sync_service_->SetCanonicalCookie(
+      *cookie, GURL("http://example.com"), GURL("http://notexample.com")));
+
+  ASSERT_EQ(1u, recorded_activity().size());
   EXPECT_EQ(recorded_activity()[0].get, false);
   EXPECT_EQ(recorded_activity()[0].url, "http://example.com/");
   EXPECT_EQ(recorded_activity()[0].site_for_cookies, "http://notexample.com/");
   EXPECT_THAT(recorded_activity()[0].cookie, net::MatchesCookieLine("A=B"));
   EXPECT_EQ(recorded_activity()[0].status,
-            net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
-
-  EXPECT_EQ(recorded_activity()[1].get, false);
-  EXPECT_EQ(recorded_activity()[1].url, "http://example.com/");
-  EXPECT_EQ(recorded_activity()[1].site_for_cookies,
-            "http://otherexample.com/");
-  EXPECT_THAT(recorded_activity()[1].cookie, net::MatchesCookieLine("A2=B2"));
-  EXPECT_EQ(
-      recorded_activity()[1].status,
-      net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
-
-  EXPECT_EQ(recorded_activity()[2].get, true);
-  EXPECT_EQ(recorded_activity()[2].url, "http://example.com/test/");
-  EXPECT_EQ(recorded_activity()[2].site_for_cookies, "http://example.com/");
-  EXPECT_THAT(recorded_activity()[2].cookie, net::MatchesCookieLine("A=B"));
-  EXPECT_EQ(recorded_activity()[2].status,
-            net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
+            net::CanonicalCookie::CookieInclusionStatus::
+                EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
 }
 
 namespace {
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index 30aa49d..768a21c 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -187,12 +187,12 @@
       const std::string& ocsp_response,
       const std::string& sct_list,
       VerifyCertificateForTestingCallback callback) override {}
-  void PreconnectSockets(uint32_t num_streams,
-                         const GURL& url,
-                         int32_t load_flags,
-                         bool privacy_mode_enabled,
-                         const base::Optional<net::NetworkIsolationKey>&
-                             network_isolation_key) override {}
+  void PreconnectSockets(
+      uint32_t num_streams,
+      const GURL& url,
+      int32_t load_flags,
+      bool privacy_mode_enabled,
+      const net::NetworkIsolationKey& network_isolation_key) override {}
   void CreateP2PSocketManager(
       mojom::P2PTrustedSocketManagerClientPtr client,
       mojom::P2PTrustedSocketManagerRequest trusted_socket_manager,
diff --git a/services/network/websocket.cc b/services/network/websocket.cc
index b4b67d3..ede3fc7 100644
--- a/services/network/websocket.cc
+++ b/services/network/websocket.cc
@@ -161,8 +161,8 @@
       receive_quota_threshold = net::WebSocketChannel::kReceiveQuotaThreshold;
   }
   DVLOG(3) << "receive_quota_threshold is " << receive_quota_threshold;
-  impl_->handshake_client_->OnAddChannelResponse(selected_protocol, extensions,
-                                                 receive_quota_threshold);
+  impl_->handshake_client_->OnConnectionEstablished(
+      selected_protocol, extensions, receive_quota_threshold);
 }
 
 void WebSocket::WebSocketEventHandler::OnDataFrame(
@@ -251,7 +251,8 @@
   headers_text.append("\r\n");
   request_to_pass->headers_text = std::move(headers_text);
 
-  impl_->handshake_client_->OnStartOpeningHandshake(std::move(request_to_pass));
+  impl_->handshake_client_->OnOpeningHandshakeStarted(
+      std::move(request_to_pass));
 }
 
 void WebSocket::WebSocketEventHandler::OnFinishOpeningHandshake(
@@ -285,8 +286,7 @@
   headers_text.append("\r\n");
   response_to_pass->headers_text = headers_text;
 
-  impl_->handshake_client_->OnFinishOpeningHandshake(
-      std::move(response_to_pass));
+  impl_->handshake_client_->OnResponseReceived(std::move(response_to_pass));
 }
 
 void WebSocket::WebSocketEventHandler::OnSSLCertificateError(
diff --git a/services/test/echo/echo_service.cc b/services/test/echo/echo_service.cc
index fc22e2fc7..cf0f2da5 100644
--- a/services/test/echo/echo_service.cc
+++ b/services/test/echo/echo_service.cc
@@ -3,36 +3,20 @@
 // found in the LICENSE file.
 
 #include "services/test/echo/echo_service.h"
+
 #include "base/bind.h"
 
 namespace echo {
 
-EchoService::EchoService(service_manager::mojom::ServiceRequest request)
-    : service_binding_(this, std::move(request)) {
-  registry_.AddInterface<mojom::Echo>(base::BindRepeating(
-      &EchoService::BindEchoRequest, base::Unretained(this)));
-}
+EchoService::EchoService() = default;
 
 EchoService::~EchoService() = default;
 
-void EchoService::OnBindInterface(
-    const service_manager::BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  registry_.BindInterface(interface_name, std::move(interface_pipe));
-}
-
-void EchoService::BindEchoRequest(mojom::EchoRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
 void EchoService::EchoString(const std::string& input,
                              EchoStringCallback callback) {
   std::move(callback).Run(input);
 }
 
-void EchoService::Quit() {
-  service_binding_.RequestClose();
-}
+void EchoService::Quit() {}
 
 }  // namespace echo
diff --git a/services/test/echo/echo_service.h b/services/test/echo/echo_service.h
index d73462ae..21c488b 100644
--- a/services/test/echo/echo_service.h
+++ b/services/test/echo/echo_service.h
@@ -5,37 +5,22 @@
 #ifndef SERVICES_ECHO_ECHO_SERVICE_H_
 #define SERVICES_ECHO_ECHO_SERVICE_H_
 
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
+#include "base/macros.h"
 #include "services/test/echo/public/mojom/echo.mojom.h"
 
 namespace echo {
 
-class EchoService : public service_manager::Service, public mojom::Echo {
+class EchoService : public mojom::EchoService {
  public:
-  explicit EchoService(service_manager::mojom::ServiceRequest request);
+  EchoService();
   ~EchoService() override;
 
  private:
-  // service_manager::Service:
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override;
-
-  // mojom::Echo:
+  // mojom::EchoService:
   void EchoString(const std::string& input,
                   EchoStringCallback callback) override;
   void Quit() override;
 
-  void BindEchoRequest(mojom::EchoRequest request);
-
-  service_manager::ServiceBinding service_binding_;
-  service_manager::BinderRegistry registry_;
-  mojo::BindingSet<mojom::Echo> bindings_;
-
   DISALLOW_COPY_AND_ASSIGN(EchoService);
 };
 
diff --git a/services/test/echo/public/cpp/BUILD.gn b/services/test/echo/public/cpp/BUILD.gn
deleted file mode 100644
index fc2d595..0000000
--- a/services/test/echo/public/cpp/BUILD.gn
+++ /dev/null
@@ -1,16 +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.
-
-source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
-
-  deps = [
-    "//base",
-    "//services/service_manager/public/cpp",
-    "//services/test/echo/public/mojom",
-  ]
-}
diff --git a/services/test/echo/public/cpp/OWNERS b/services/test/echo/public/cpp/OWNERS
deleted file mode 100644
index 6faeaa47..0000000
--- a/services/test/echo/public/cpp/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-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/services/test/echo/public/cpp/manifest.cc b/services/test/echo/public/cpp/manifest.cc
deleted file mode 100644
index ddb5a0d..0000000
--- a/services/test/echo/public/cpp/manifest.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 "services/test/echo/public/cpp/manifest.h"
-
-#include "base/no_destructor.h"
-#include "services/service_manager/public/cpp/manifest_builder.h"
-#include "services/test/echo/public/mojom/echo.mojom.h"
-
-namespace echo {
-
-service_manager::Manifest GetManifest(
-    service_manager::Manifest::ExecutionMode execution_mode) {
-  return service_manager::ManifestBuilder()
-      .WithServiceName(mojom::kServiceName)
-      .WithDisplayName("Echo Service")
-      .WithOptions(service_manager::ManifestOptionsBuilder()
-                       .WithInstanceSharingPolicy(
-                           service_manager::Manifest::InstanceSharingPolicy::
-                               kSharedAcrossGroups)
-                       .WithExecutionMode(execution_mode)
-                       .Build())
-      .ExposeCapability("echo",
-                        service_manager::Manifest::InterfaceList<mojom::Echo>())
-      .Build();
-}
-
-}  // namespace echo
diff --git a/services/test/echo/public/cpp/manifest.h b/services/test/echo/public/cpp/manifest.h
deleted file mode 100644
index 1c7dfa9..0000000
--- a/services/test/echo/public/cpp/manifest.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_TEST_ECHO_PUBLIC_CPP_MANIFEST_H_
-#define SERVICES_TEST_ECHO_PUBLIC_CPP_MANIFEST_H_
-
-#include "services/service_manager/public/cpp/manifest.h"
-
-namespace echo {
-
-service_manager::Manifest GetManifest(
-    service_manager::Manifest::ExecutionMode execution_mode);
-
-}  // namespace echo
-
-#endif  // SERVICES_TEST_ECHO_PUBLIC_CPP_MANIFEST_H_
diff --git a/services/test/echo/public/mojom/echo.mojom b/services/test/echo/public/mojom/echo.mojom
index 412364e..ffded09 100644
--- a/services/test/echo/public/mojom/echo.mojom
+++ b/services/test/echo/public/mojom/echo.mojom
@@ -4,10 +4,8 @@
 
 module echo.mojom;
 
-const string kServiceName = "echo";
-
 // Echos its input.
-interface Echo {
+interface EchoService {
   // Echos the passed-in string.
   EchoString(string input) => (string echoed_input);
 
diff --git a/services/viz/privileged/interfaces/gl/gpu_host.mojom b/services/viz/privileged/interfaces/gl/gpu_host.mojom
index 2e081bb..cf88012 100644
--- a/services/viz/privileged/interfaces/gl/gpu_host.mojom
+++ b/services/viz/privileged/interfaces/gl/gpu_host.mojom
@@ -6,6 +6,7 @@
 
 import "gpu/ipc/common/gpu_feature_info.mojom";
 import "gpu/ipc/common/gpu_info.mojom";
+import "gpu/ipc/common/gpu_extra_info.mojom";
 import "gpu/ipc/common/surface_handle.mojom";
 import "services/viz/privileged/interfaces/gl/context_lost_reason.mojom";
 import "url/mojom/url.mojom";
@@ -19,7 +20,8 @@
   DidInitialize(gpu.mojom.GpuInfo gpu_info,
                 gpu.mojom.GpuFeatureInfo gpu_feature_info,
                 gpu.mojom.GpuInfo? gpu_info_for_hardware_gpu,
-                gpu.mojom.GpuFeatureInfo? gpu_feature_info_for_hardware_gpu);
+                gpu.mojom.GpuFeatureInfo? gpu_feature_info_for_hardware_gpu,
+                gpu.mojom.GpuExtraInfo gpu_extra_info);
   DidFailInitialize();
 
   DidCreateContextSuccessfully();
diff --git a/testing/android/native_test/BUILD.gn b/testing/android/native_test/BUILD.gn
index 17dcf61..e938a4d9 100644
--- a/testing/android/native_test/BUILD.gn
+++ b/testing/android/native_test/BUILD.gn
@@ -86,5 +86,4 @@
   sources = [
     "java/src/org/chromium/native_test/NativeBrowserTest.java",
   ]
-  jni_package = "native_browser_test"
 }
diff --git a/testing/android/native_test/native_browser_test_support.cc b/testing/android/native_test/native_browser_test_support.cc
index dba4764..a1ae805 100644
--- a/testing/android/native_test/native_browser_test_support.cc
+++ b/testing/android/native_test/native_browser_test_support.cc
@@ -9,7 +9,7 @@
 // These markers are read by the test runner script to generate test results.
 // It installs signal handlers to detect crashes.
 
-#include "jni/NativeBrowserTest_jni.h"
+#include "testing/android/native_test/native_browser_test_jni_headers/NativeBrowserTest_jni.h"
 
 namespace testing {
 namespace android {
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
new file mode 100644
index 0000000..81fceb6
--- /dev/null
+++ b/testing/buildbot/chrome.json
@@ -0,0 +1,2225 @@
+{
+  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "chromeos-amd64-generic-google-rel": {
+    "additional_compile_targets": [
+      "chrome"
+    ]
+  },
+  "chromeos-betty-google-rel": {
+    "additional_compile_targets": [
+      "chromiumos_preflight"
+    ],
+    "gtest_tests": [
+      {
+        "args": [
+          "--ozone-platform=headless"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "aura_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "base_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-jobs=1",
+          "--gtest_filter=-*UsingRealWebcam_CaptureMjpeg*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ],
+          "idempotent": false
+        },
+        "test": "chrome_all_tast_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "cros_vm_sanity_test"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "display_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "args": [
+          "--stop-ui",
+          "--dbus-stub",
+          "--gtest_filter=SplitViewTest.SplitViewResize"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "latency_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.media_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "media_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "args": [
+          "--vpython-dir=../../vpython_dir_linux_amd64"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/vpython/linux-amd64",
+              "location": "vpython_dir_linux_amd64",
+              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "net_unittests"
+      },
+      {
+        "args": [
+          "--stop-ui"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "ozone_gl_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.ozone_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "ozone_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ]
+        },
+        "test": "url_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--browser=cros-chrome",
+          "--remote=127.0.0.1",
+          "--remote-ssh-port=9222",
+          "--xvfb"
+        ],
+        "isolate_name": "telemetry_perf_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "telemetry_perf_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ],
+          "idempotent": false,
+          "shards": 6
+        }
+      },
+      {
+        "args": [
+          "--jobs=1",
+          "--browser=cros-chrome",
+          "--remote=127.0.0.1",
+          "--remote-ssh-port=9222"
+        ],
+        "isolate_name": "telemetry_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "telemetry_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests.cros-vm"
+            }
+          ],
+          "idempotent": false,
+          "shards": 24
+        }
+      }
+    ]
+  },
+  "linux-chromeos-google-rel": {
+    "additional_compile_targets": [
+      "chrome",
+      "chrome_sandbox",
+      "linux_symbols",
+      "symupload"
+    ],
+    "gtest_tests": [
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "accessibility_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "app_list_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "app_shell_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ash_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "aura_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "base_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "base_util_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "blink_common_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "blink_fuzzer_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "blink_platform_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "boringssl_crypto_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "boringssl_ssl_tests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.browser_tests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.browser_tests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "viz_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "webui_html_imports_polyfill_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=-*UsingRealWebcam*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "chrome_app_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "chromedriver_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "chromeos_components_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "chromeos_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "components_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "compositor_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ],
+          "shards": 6
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--disable-perfetto",
+          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "nonperfetto_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "viz_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "viz_content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "dbus_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "device_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "display_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "events_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "exo_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "filesystem_service_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "gin_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "gl_unittests_ozone"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=-SadTabViewInteractiveUITest.ReloadMultipleSadTabs"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
+          "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_interactive_ui_tests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "webui_html_imports_polyfill_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "keyboard_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "latency_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "leveldb_service_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "libjingle_xmpp_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "media_service_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "media_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "message_center_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "mojo_core_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "nacl_helper_nonsfi_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "nacl_loader_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "net_unittests"
+      },
+      {
+        "args": [
+          "--ozone-platform=headless"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ozone_gl_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.ozone_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ozone_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ozone_x11_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "perfetto_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "service_manager_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "shell_dialogs_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "snapshot_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "sync_integration_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "traffic_annotation_auditor_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ui_chromeos_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.unit_tests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "unit_tests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "url_unittests"
+      },
+      {
+        "experiment_percentage": 100,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "usage_time_limit_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "views_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "viz_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "wayland_client_perftests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "wm_unittests"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04",
+              "pool": "chrome.tests"
+            }
+          ]
+        },
+        "test": "wtf_unittests"
+      }
+    ]
+  },
+  "linux-google-rel": {
+    "additional_compile_targets": [
+      "chrome",
+      "chrome/installer/linux"
+    ],
+    "isolated_scripts": [
+      {
+        "isolate_name": "chrome_sizes",
+        "merge": {
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "chrome_sizes",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "pool": "chrome.tests"
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "mac-google-rel": {
+    "additional_compile_targets": [
+      "chrome"
+    ],
+    "isolated_scripts": [
+      {
+        "isolate_name": "chrome_sizes",
+        "merge": {
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "chrome_sizes",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Mac-10.14.4",
+              "pool": "chrome.tests"
+            }
+          ],
+          "optional_dimensions": {
+            "300": [
+              {
+                "os": "Mac-10.14.3"
+              }
+            ],
+            "600": [
+              {
+                "os": "Mac-10.14.5"
+              }
+            ]
+          }
+        }
+      }
+    ]
+  },
+  "win-google-rel": {
+    "additional_compile_targets": [
+      "chrome",
+      "chrome_official_builder"
+    ],
+    "isolated_scripts": [
+      {
+        "isolate_name": "chrome_sizes",
+        "merge": {
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "chrome_sizes",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10-15063",
+              "pool": "chrome.tests"
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index d5bc429..15f75ad 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -30,6 +30,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -153,6 +176,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu-14.04",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -276,6 +322,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -324,6 +393,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu-14.04",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -375,6 +467,30 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -500,6 +616,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -622,6 +761,30 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -671,6 +834,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -721,6 +907,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -844,6 +1053,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -967,6 +1199,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -1015,6 +1270,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -1065,6 +1343,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -1186,6 +1487,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -1307,6 +1631,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -1355,6 +1702,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index b90e2b6..3a51337 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -21555,6 +21555,26 @@
     "gtest_tests": [
       {
         "args": [
+          "--enable-features=BackForwardCache"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "bf_cache_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
           "--disable-field-trial-config"
         ],
         "merge": {
@@ -21572,6 +21592,64 @@
           "shards": 10
         },
         "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=BackForwardCache"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "bf_cache_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=BackForwardCache"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "bf_cache_content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "args": [
+          "--enable-features=BackForwardCache"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "bf_cache_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "unit_tests"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index d8c9aa8..93551b8 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -6971,6 +6971,30 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -7021,6 +7045,30 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu-14.04",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -7070,6 +7118,29 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.13.6"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -7120,6 +7191,31 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -7172,6 +7268,31 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -7223,6 +7344,30 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -7273,6 +7418,30 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
@@ -29103,6 +29272,31 @@
         "args": [
           "--use-gpu-in-tests",
           "--test-launcher-retry-limit=0",
+          "--enable-backend-validation"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:2184",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "expiration": 21600
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
           "--use-wire"
         ],
         "merge": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 285fea3..030484b 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -14098,9 +14098,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14142,9 +14142,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14186,9 +14186,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14230,9 +14230,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14274,9 +14274,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14318,9 +14318,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14362,9 +14362,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14407,9 +14407,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14452,9 +14452,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14496,9 +14496,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14518,7 +14518,8 @@
       {
         "args": [
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
+          "--recover-devices",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.breakpad_unittests.filter"
         ],
         "merge": {
           "args": [
@@ -14540,9 +14541,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14584,9 +14585,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14629,9 +14630,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14673,9 +14674,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14717,9 +14718,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14761,9 +14762,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14805,9 +14806,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14851,9 +14852,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14896,9 +14897,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14941,9 +14942,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -14985,9 +14986,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15029,9 +15030,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15073,9 +15074,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15117,9 +15118,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15161,9 +15162,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15205,9 +15206,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15249,9 +15250,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15293,9 +15294,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15337,9 +15338,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15381,9 +15382,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15425,9 +15426,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15470,9 +15471,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15514,9 +15515,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15558,9 +15559,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15602,9 +15603,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15646,9 +15647,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15690,9 +15691,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15734,9 +15735,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15778,9 +15779,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15822,9 +15823,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15867,9 +15868,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15912,9 +15913,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -15956,9 +15957,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16000,9 +16001,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16044,9 +16045,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16088,9 +16089,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16132,9 +16133,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16176,9 +16177,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16220,9 +16221,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16265,9 +16266,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16310,9 +16311,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16354,9 +16355,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16398,9 +16399,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16442,9 +16443,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16486,9 +16487,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
@@ -16530,9 +16531,9 @@
           ],
           "dimension_sets": [
             {
-              "device_os": "MMB29Q",
+              "device_os": "KTU84P",
               "device_os_type": "userdebug",
-              "device_type": "bullhead",
+              "device_type": "hammerhead",
               "os": "Android"
             }
           ],
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 30b0efc0..36736bd 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -81,7 +81,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 14400,
-          "shards": 7
+          "shards": 4
         },
         "trigger_script": {
           "args": [
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index d1beb34..d2cbbef 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -172,7 +172,8 @@
           "${got_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold"
+          "--use-skia-gold",
+          "--stream-goldctl-output"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
@@ -541,7 +542,8 @@
           "${got_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold"
+          "--use-skia-gold",
+          "--stream-goldctl-output"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
@@ -904,7 +906,8 @@
           "${got_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold"
+          "--use-skia-gold",
+          "--stream-goldctl-output"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
@@ -1262,7 +1265,8 @@
           "${got_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold"
+          "--use-skia-gold",
+          "--stream-goldctl-output"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
@@ -1724,7 +1728,8 @@
           "${got_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold"
+          "--use-skia-gold",
+          "--stream-goldctl-output"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 0b40195..33a9701 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -24,6 +24,14 @@
   ]
 }
 
+source_set("breakpad_unittests_filters") {
+  testonly = true
+
+  data = [
+    "//testing/buildbot/filters/android.asan.breakpad_unittests.filter",
+  ]
+}
+
 source_set("browser_tests_filters") {
   testonly = true
 
diff --git a/testing/buildbot/filters/android.asan.breakpad_unittests.filter b/testing/buildbot/filters/android.asan.breakpad_unittests.filter
new file mode 100644
index 0000000..cac82b6
--- /dev/null
+++ b/testing/buildbot/filters/android.asan.breakpad_unittests.filter
@@ -0,0 +1,9 @@
+-ExceptionHandlerTest.AdditionalMemory
+-ExceptionHandlerTest.AdditionalMemoryRemove
+-ExceptionHandlerTest.GenerateMultipleDumpsWithFD
+-ExceptionHandlerTest.GenerateMultipleDumpsWithPath
+-ExceptionHandlerTest.InstructionPointerMemory
+-ExceptionHandlerTest.InstructionPointerMemoryMaxBound
+-ExceptionHandlerTest.InstructionPointerMemoryMinBound
+-ExceptionHandlerTest.ModuleInfo
+-ExceptionHandlerTest.WriteMinidumpExceptionStream
diff --git a/testing/buildbot/filters/chromeos.browser_tests.filter b/testing/buildbot/filters/chromeos.browser_tests.filter
index f5c30da..dea7db1f0 100644
--- a/testing/buildbot/filters/chromeos.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.browser_tests.filter
@@ -1,12 +1,12 @@
 # TODO(crbug.com/977736): Enable this.
 -ActiveDirectoryWebUILoginTest.PasswordChange_ReopenClearErrors
 
+# TODO(crbug.com/979355): Enable this.
+-AutotestPrivateWithPolicyApiTest.PolicyAPITest
+
 # TODO(crbug.com/977738): Enable this.
 -ChromePluginTest.InstalledPlugins
 
-# TODO(crbug.com/977739): Enable this.
--CrSettingsPeoplePageAccountManagerTest.All
-
 # TODO(crbug.com/977740): Enable this.
 -LoginUIKeyboardTest.CheckPODScreenDefault
 -LoginUIKeyboardTestWithUsersAndOwner.CheckPODScreenKeyboard
@@ -20,6 +20,9 @@
 # TODO(crbug.com/977743): Enable this.
 -ManagementApiKioskTest.ManagementApi
 
+# TODO(crbug.com/979226): Enable this.
+-MultiActionAPITest.DynamicSetIcon/*
+
 # TODO(crbug.com/977744): Enable this.
 -ProcessMemoryMetricsEmitterTest.RendererBuildId
 
diff --git a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
index f475fd7..e24de01 100644
--- a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
+++ b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
@@ -11,6 +11,7 @@
 -Http/MediaTest.VideoBearMp4/0
 -Http/MediaTest.VideoBearSilentMp4/0
 -Http/MediaTest.VideoTulipWebm/0
+-MediaCanPlayTypeTest.CodecSupportTest_NewVp9Variants
 -MediaColorTest.Yuv420pTheora
 -MediaColorTest.Yuv420pVp8
 -MediaSessionImplBrowserTest.MetadataWhenFileUrlScheme
@@ -32,10 +33,15 @@
 -NavigationControllerBrowserTest.ReloadWithUrlAnchorAndScroll
 -OOPBrowserTest.Basic
 -P/CompositorImplBrowserTestRefreshRate.VideoPreference/0
+-SitePerProcessBrowserTest.ChildFrameCrashMetrics_ScrolledIntoViewAfterTabIsShown
 -SitePerProcessBrowserTest.HiddenOOPIFWillNotGenerateCompositorFrames
+-SitePerProcessBrowserTest.HiddenOOPIFWillNotGenerateCompositorFramesAfterNavigation
+-SitePerProcessBrowserTest.PageScaleFactorPropagatesToOOPIFs
+-SitePerProcessBrowserTest.ReloadHiddenTabWithCrashedSubframe
 -SitePerProcessBrowserTest.ScaledIframeRasterSize
 -SitePerProcessBrowserTest.ScrollOopifInPinchZoomedPage
 -SitePerProcessBrowserTest.SubframeVisibleAfterRenderViewBecomesSwappedOut
+-SitePerProcessBrowserTest.UnloadNestedPendingDeletion
 -SitePerProcessEmulatedTouchBrowserTest.EmulatedGestureScrollBubbles/0
 -SitePerProcessEmulatedTouchBrowserTest.EmulatedTouchScrollBubbles/0
 -SitePerProcessEmulatedTouchBrowserTest.EmulatedTouchShowPressHasTouchID/0
@@ -57,6 +63,7 @@
 -SitePerProcessHitTestBrowserTest.TouchpadPinchWhenMissingHitTestDataDoesNotCrash/1
 -SnapshotBrowserTest.SyncMultiWindowTest
 -SRC_ClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
+-SRC_ExternalClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
 -TouchActionBrowserTest.DefaultAuto/1
 -TouchActionBrowserTest.PanXMainThreadJanky/1
 -TouchActionBrowserTest.PanXYMainThreadJanky/1
diff --git a/testing/buildbot/filters/navigation_loader_on_ui_browser_tests.filter b/testing/buildbot/filters/navigation_loader_on_ui_browser_tests.filter
index 62aefa5..428cf6b 100644
--- a/testing/buildbot/filters/navigation_loader_on_ui_browser_tests.filter
+++ b/testing/buildbot/filters/navigation_loader_on_ui_browser_tests.filter
@@ -1,29 +1,5 @@
 # These tests currently fail when run with --enable-features=NavigationLoaderOnUI
 
-# http://crbug.com/978556
--CaptivePortalBrowserTest.AbortLoad
--CaptivePortalBrowserTest.CloseLoginTab
--CaptivePortalBrowserTest.Disabled
--CaptivePortalBrowserTest.GoBack
--CaptivePortalBrowserTest.GoBackToTimeout
--CaptivePortalBrowserTest.HttpToHttpsRedirectLogin
--CaptivePortalBrowserTest.InternetConnected
--CaptivePortalBrowserTest.InterstitialTimerCertErrorAfterSlowLoad
--CaptivePortalBrowserTest.Login
--CaptivePortalBrowserTest.LoginExtraNavigations
--CaptivePortalBrowserTest.LoginFastTimeout
--CaptivePortalBrowserTest.LoginIncognito
--CaptivePortalBrowserTest.LoginSlow
--CaptivePortalBrowserTest.NavigateBrokenTab
--CaptivePortalBrowserTest.NavigateLoadingTabToTimeoutSingleSite
--CaptivePortalBrowserTest.NavigateLoadingTabToTimeoutThreeSites
--CaptivePortalBrowserTest.NavigateLoadingTabToTimeoutTwoSites
--CaptivePortalBrowserTest.RedirectSSLCertError
--CaptivePortalBrowserTest.ReloadTimeout
--CaptivePortalBrowserTest.RequestFails
--CaptivePortalBrowserTest.Status511
--CaptivePortalBrowserTest.TwoBrokenTabs
-
 # http://crbug.com/824858
 -AppBannerManagerBrowserTest.ListedRelatedChromeAppInstalled
 -AppBannerManagerBrowserTest.PreferRelatedAppUnknown
@@ -150,6 +126,7 @@
 -PaymentRequestShowPromiseTest.SingleOptionShippingWithUpdate
 -PaymentRequestShowPromiseTest.SkipUI
 -PendingBookmarkAppManagerBrowserTest.AlwaysUpdate
+-PendingBookmarkAppManagerBrowserTest.ForceReinstall
 -PushMessagingBrowserTest.AppHandlerOnlyIfSubscribed
 -PushMessagingBrowserTest.AutomaticUnsubscriptionFollowsContentSettingRules
 -PushMessagingBrowserTest.DenyNotificationsPermissionUnsubscribes
@@ -261,23 +238,6 @@
 -WorkerDevToolsSanityTest.PauseInSharedWorkerInitialization
 -WorkerTest.WorkerInBackgroundPage
 
-# http://crbug.com/978614
--DataReductionProxyBrowsertest.BlockOnceWorksAfterUpdateConfig
--DataReductionProxyBrowsertest.ChromeProxyHeaderSet
--DataReductionProxyBrowsertest.PingbackSent
--DataReductionProxyBrowsertest.UMAMetricsRecorded
--DataReductionProxyFallbackBrowsertest.BadProxiesResetWhenDisabled
--DataReductionProxyFallbackBrowsertest.FallbackProxyUsedOn500Status
--DataReductionProxyFallbackBrowsertest.FallbackProxyUsedOnNetError
--DataReductionProxyFallbackBrowsertest.FallbackProxyUsedWhenBlockForLargeDurationSent
--DataReductionProxyFallbackBrowsertest.FallbackProxyUsedWhenBlockHeaderSent
--DataReductionProxyFallbackBrowsertest.FallbackProxyUsedWhenBlockZeroHeaderSent
--DataReductionProxyFallbackBrowsertest.FallbackProxyUsedWhenBypassHeaderSent
--DataReductionProxyFallbackBrowsertest.NoProxyUsedWhenBlockOnceHeaderSent
--DataReductionProxyFallbackBrowsertest.ProxyBlockedOnAuthError
--DataReductionProxyFallbackBrowsertest.ProxyBypassedOn502Error
--DataReductionProxyFallbackBrowsertest.ProxyShortBypassedOn502ErrorWithFeature
-
 # http://crbug.com/978617
 -DiceBrowserTest.EnableSyncAfterToken
 -DiceBrowserTest.EnableSyncBeforeToken
@@ -287,36 +247,6 @@
 -DiceBrowserTest.SignoutMainAccount
 -DiceBrowserTest.SignoutSecondaryAccount
 
-# http://crbug.com/931786
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.BothPreviewsAllowedWantLPR_CoinFlipEnabled_Allowed/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.BothPreviewsAllowedWantLPR_NoCoinFlip/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.BothPreviewsAllowedWantLPR_WithRedirect_CoinFlipEnabled_Allowed/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.BothPreviewsAllowedWantLPR_WithRedirect_NoCoinFlip/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.LPRAllowed_CoinFlipEnabled_Allowed/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.LPRAllowed_NoCoinFlip/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.LPRAllowed_WithRedirect_CoinFlipEnabled_Allowed/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.LPRAllowed_WithRedirect_NoCoinFlip/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.RLHAllowed_WithRedirect_CoinFlipEnabled_Allowed/1
--URLLoaderImplementation/CoinFlipHoldbackExperimentBrowserTest.RLHAllowed_WithRedirect_NoCoinFlip/1
--URLLoaderImplementation/PreviewsLitePageAndPageHintsBrowserTest.PreviewsServerIsInBloomFilter/1
--URLLoaderImplementation/PreviewsLitePageServerBadServerBrowserTest.LitePagePreviewsBadServer/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePageCreatesPingback/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsClientRedirect/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsLoadOriginal/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsLoadshed/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsNavigation/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsNoChromeProxyHeader/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsRedirect/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsReloadSoftOptOut/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsReportSavings/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsResponse/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePagePreviewsTriggering/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePageSendsInterventionReport/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.LitePageURLNotReportedToHistory/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.PingbackContent/1
--URLLoaderImplementation/PreviewsLitePageServerBrowserTest.PreresolverShownAndHidden/1
--URLLoaderImplementation/PreviewsLitePageServerTimeoutBrowserTest.LitePagePreviewsTimeout/1
-
 # http://crbug.com/824843
 -MaybeSetMetadata/V4SafeBrowsingServiceMetadataTest.MalwareIFrame/0
 -MaybeSetMetadata/V4SafeBrowsingServiceMetadataTest.MalwareIFrame/1
diff --git a/testing/buildbot/filters/navigation_loader_on_ui_content_browsertests.filter b/testing/buildbot/filters/navigation_loader_on_ui_content_browsertests.filter
index efe743c..fed92303 100644
--- a/testing/buildbot/filters/navigation_loader_on_ui_content_browsertests.filter
+++ b/testing/buildbot/filters/navigation_loader_on_ui_content_browsertests.filter
@@ -1,21 +1,5 @@
 # These tests currently fail when run with --enable-features=NavigationLoaderOnUI
 
-# http://crbug.com/978556
--WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.BlockHeaders/0
--WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.BlockImages/0
--WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.CrossOriginResourcePolicy/0
--WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.HtmlImports_CompatibleLock1/0
--WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.HtmlImports_CompatibleLock2/0
--WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.HtmlImports_IncorrectLock/0
--WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.SharedWorker/0
--WithoutOutOfBlinkCors/CrossSiteDocumentBlockingTest.BlockHeaders/0
--WithoutOutOfBlinkCors/CrossSiteDocumentBlockingTest.BlockImages/0
--WithoutOutOfBlinkCors/CrossSiteDocumentBlockingTest.CrossOriginResourcePolicy/0
--WithoutOutOfBlinkCors/CrossSiteDocumentBlockingTest.HtmlImports_CompatibleLock1/0
--WithoutOutOfBlinkCors/CrossSiteDocumentBlockingTest.HtmlImports_CompatibleLock2/0
--WithoutOutOfBlinkCors/CrossSiteDocumentBlockingTest.HtmlImports_IncorrectLock/0
--WithoutOutOfBlinkCors/CrossSiteDocumentBlockingTest.SharedWorker/0
-
 # http://crbug.com/824856
 -AppCacheNetworkServiceBrowserTest.CacheableResourcesReuse
 -WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.AppCache_InitiatorEnforcement/0
@@ -80,19 +64,51 @@
 -ServiceWorkerBrowserTest.RequestOrigin
 -ServiceWorkerBrowserTest.ResponseFromHTTPSServiceWorkerIsMarkedAsSecure
 -ServiceWorkerBrowserTest.ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.FetchPageWithSaveData/0
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.FetchPageWithSaveData/1
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.GetAllServiceWorkerRunningInfos/0
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.GetAllServiceWorkerRunningInfos/1
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.GetServiceWorkerRunningInfo/0
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.GetServiceWorkerRunningInfo/1
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.ImportsBustMemcache/0
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.ImportsBustMemcache/1
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.Reload/0
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.Reload/1
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.RequestOrigin/0
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.RequestOrigin/1
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.ResponseFromHTTPSServiceWorkerIsMarkedAsSecure/0
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.ResponseFromHTTPSServiceWorkerIsMarkedAsSecure/1
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure/0
+-ServiceWorkerBrowserTestOnMainThreadFetch/ServiceWorkerBrowserTest.ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure/1
 -ServiceWorkerClientsApiBrowserTest.Navigate
 -ServiceWorkerClientsApiBrowserTest.NavigateDisallowedUrl
 -ServiceWorkerClientsApiBrowserTest.NavigateDuringBrowserNavigation
 -ServiceWorkerClientsApiBrowserTest.OpenWindow
 -ServiceWorkerClientsApiBrowserTest.OpenWindowDisallowedUrl
 -ServiceWorkerCodeCacheStrategyDontGenerateTest.DontGenerate
+-ServiceWorkerCodeCacheStrategyDontGenerateTestOnMainThreadFetch/ServiceWorkerCodeCacheStrategyDontGenerateTest.DontGenerate/0
+-ServiceWorkerCodeCacheStrategyDontGenerateTestOnMainThreadFetch/ServiceWorkerCodeCacheStrategyDontGenerateTest.DontGenerate/1
 -ServiceWorkerCodeCacheStrategyIdleTaskTest.CacheScriptTwice
 -ServiceWorkerCodeCacheStrategyIdleTaskTest.GenerateInIdleTask
+-ServiceWorkerCodeCacheStrategyIdleTaskTestOnMainThreadFetch/ServiceWorkerCodeCacheStrategyIdleTaskTest.CacheScriptTwice/0
+-ServiceWorkerCodeCacheStrategyIdleTaskTestOnMainThreadFetch/ServiceWorkerCodeCacheStrategyIdleTaskTest.CacheScriptTwice/1
+-ServiceWorkerCodeCacheStrategyIdleTaskTestOnMainThreadFetch/ServiceWorkerCodeCacheStrategyIdleTaskTest.GenerateInIdleTask/0
+-ServiceWorkerCodeCacheStrategyIdleTaskTestOnMainThreadFetch/ServiceWorkerCodeCacheStrategyIdleTaskTest.GenerateInIdleTask/1
 -ServiceWorkerCodeCacheStrategyInstallEventTest.GenerateInInstallEvent
+-ServiceWorkerCodeCacheStrategyInstallEventTestOnMainThreadFetch/ServiceWorkerCodeCacheStrategyInstallEventTest.GenerateInInstallEvent/0
+-ServiceWorkerCodeCacheStrategyInstallEventTestOnMainThreadFetch/ServiceWorkerCodeCacheStrategyInstallEventTest.GenerateInInstallEvent/1
 -ServiceWorkerDisableWebSecurityTest.GetRegistrationNoCrash
 -ServiceWorkerDisableWebSecurityTest.RegisterNoCrash
 -ServiceWorkerDisableWebSecurityTest.UnregisterNoCrash
 -ServiceWorkerDisableWebSecurityTest.UpdateNoCrash
+-ServiceWorkerDisableWebSecurityTestOnMainThreadFetch/ServiceWorkerDisableWebSecurityTest.GetRegistrationNoCrash/0
+-ServiceWorkerDisableWebSecurityTestOnMainThreadFetch/ServiceWorkerDisableWebSecurityTest.GetRegistrationNoCrash/1
+-ServiceWorkerDisableWebSecurityTestOnMainThreadFetch/ServiceWorkerDisableWebSecurityTest.RegisterNoCrash/0
+-ServiceWorkerDisableWebSecurityTestOnMainThreadFetch/ServiceWorkerDisableWebSecurityTest.RegisterNoCrash/1
+-ServiceWorkerDisableWebSecurityTestOnMainThreadFetch/ServiceWorkerDisableWebSecurityTest.UnregisterNoCrash/0
+-ServiceWorkerDisableWebSecurityTestOnMainThreadFetch/ServiceWorkerDisableWebSecurityTest.UnregisterNoCrash/1
+-ServiceWorkerDisableWebSecurityTestOnMainThreadFetch/ServiceWorkerDisableWebSecurityTest.UpdateNoCrash/0
+-ServiceWorkerDisableWebSecurityTestOnMainThreadFetch/ServiceWorkerDisableWebSecurityTest.UpdateNoCrash/1
 -ServiceWorkerFileUploadTest.AsBlob
 -ServiceWorkerFileUploadTest.AsFormData
 -ServiceWorkerFileUploadTest.AsFormData_CrossOrigin
@@ -107,6 +123,22 @@
 -ServiceWorkerNavigationPreloadTest.PreloadHeadersSimple
 -ServiceWorkerNavigationPreloadTest.RespondWithNavigationPreload
 -ServiceWorkerNavigationPreloadTest.SetHeaderValue
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.InvalidRedirect_InvalidLocation/0
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.InvalidRedirect_InvalidLocation/1
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.InvalidRedirect_MultiLocation/0
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.InvalidRedirect_MultiLocation/1
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.NetworkError/0
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.NetworkError/1
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.NotEnabled/0
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.NotEnabled/1
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.PreloadHeadersCustom/0
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.PreloadHeadersCustom/1
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.PreloadHeadersSimple/0
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.PreloadHeadersSimple/1
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.RespondWithNavigationPreload/0
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.RespondWithNavigationPreload/1
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.SetHeaderValue/0
+-ServiceWorkerNavigationPreloadTestOnMainThreadFetch/ServiceWorkerNavigationPreloadTest.SetHeaderValue/1
 -ServiceWorkerNoBestEffortTasksTest.RegisterServiceWorker
 -ServiceWorkerTlsTest.ClientAuthFetchMainResource
 -ServiceWorkerTlsTest.ClientAuthFetchSubResource
@@ -114,7 +146,17 @@
 -ServiceWorkerURLLoaderThrottleTest.NavigationHasThrottledRequestHeadersAfterNetworkFallback
 -ServiceWorkerURLLoaderThrottleTest.NavigationPreloadHasThrottledRequestHeaders
 -ServiceWorkerURLLoaderThrottleTest.RedirectOccursBeforeFetchEvent
+-ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch/ServiceWorkerURLLoaderThrottleTest.FetchEventForNavigationHasThrottledRequest/0
+-ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch/ServiceWorkerURLLoaderThrottleTest.FetchEventForNavigationHasThrottledRequest/1
+-ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch/ServiceWorkerURLLoaderThrottleTest.NavigationHasThrottledRequestHeadersAfterNetworkFallback/0
+-ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch/ServiceWorkerURLLoaderThrottleTest.NavigationHasThrottledRequestHeadersAfterNetworkFallback/1
+-ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch/ServiceWorkerURLLoaderThrottleTest.NavigationPreloadHasThrottledRequestHeaders/0
+-ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch/ServiceWorkerURLLoaderThrottleTest.NavigationPreloadHasThrottledRequestHeaders/1
+-ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch/ServiceWorkerURLLoaderThrottleTest.RedirectOccursBeforeFetchEvent/0
+-ServiceWorkerURLLoaderThrottleTestOnMainThreadFetch/ServiceWorkerURLLoaderThrottleTest.RedirectOccursBeforeFetchEvent/1
 -ServiceWorkerV8CodeCacheForCacheStorageTest.V8CacheOnCacheStorage
+-ServiceWorkerV8CodeCacheForCacheStorageTestOnMainThreadFetch/ServiceWorkerV8CodeCacheForCacheStorageTest.V8CacheOnCacheStorage/0
+-ServiceWorkerV8CodeCacheForCacheStorageTestOnMainThreadFetch/ServiceWorkerV8CodeCacheForCacheStorageTest.V8CacheOnCacheStorage/1
 
 # http://crbug.com/824854
 -PrefetchBrowserTest/PrefetchBrowserTest.CrossOriginSignedExchangeWithPreload/3
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 5502f32..25cdb29 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1203,7 +1203,7 @@
     "type": "console_test_launcher",
   },
   "http_structured_header_fuzzer": {
-    "label": "//content/test/fuzzer:http_structured_header_fuzzer",
+    "label": "//third_party/blink/common:http_structured_header_fuzzer",
     "type": "fuzzer",
   },
   "hunspell_fuzzer": {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 9b979f09d..0fc96da 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -69,6 +69,15 @@
       'Linux FYI Experimental Release (Intel HD 630)',
     ],
   },
+  'breakpad_unittests': {
+    'modifications': {
+      'android-asan': {
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.breakpad_unittests.filter',
+        ],
+      },
+    },
+  },
   'browser_tests': {
     'remove_from': [
       # chromium.clang
@@ -1219,7 +1228,28 @@
       },
     },
   },
- 'pixel_test':{
+  # TODO(https://crbug.com/977620): Remove this once the V8 bots are fixed and
+  # no longer need additional logging.
+  'pixel_skia_gold_test': {
+    'modifications': {
+      'Android V8 FYI Release (Nexus 5X)': {
+        'args': [ '--stream-goldctl-output' ]
+      },
+      'Linux V8 FYI Release (NVIDIA)': {
+        'args': [ '--stream-goldctl-output' ]
+      },
+      'Linux V8 FYI Release - pointer compression (NVIDIA)': {
+        'args': [ '--stream-goldctl-output' ]
+      },
+      'Mac V8 FYI Release (Intel)': {
+        'args': [ '--stream-goldctl-output' ]
+      },
+      'Win V8 FYI Release (NVIDIA)': {
+        'args': [ '--stream-goldctl-output' ]
+      },
+    },
+  },
+  'pixel_test': {
     'modifications': {
       'Android Release (Nexus 5X)': {
         'swarming': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 09a064d..b5e14fc 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -2669,7 +2669,7 @@
       'blink_python_tests': {},
     },
 
-    'fieldtrial_browser_tests': {
+    'fieldtrial_gtests': {
       'no_fieldtrial_browser_tests': {
         'args': [
           '--disable-field-trial-config',
@@ -2679,6 +2679,36 @@
         },
         'test': 'browser_tests',
       },
+      'bf_cache_content_unittests': {
+        'args': [
+          '--enable-features=BackForwardCache',
+        ],
+        'test': 'content_unittests',
+      },
+      'bf_cache_unit_tests': {
+        'args': [
+          '--enable-features=BackForwardCache',
+        ],
+        'test': 'unit_tests',
+      },
+      'bf_cache_content_browsertests': {
+        'args': [
+          '--enable-features=BackForwardCache',
+        ],
+        'swarming': {
+          'shards': 10,
+        },
+        'test': 'content_browsertests',
+      },
+      'bf_cache_browser_tests': {
+        'args': [
+          '--enable-features=BackForwardCache',
+        ],
+        'swarming': {
+          'shards': 10,
+        },
+       'test': 'browser_tests',
+      },
     },
 
     'fuchsia_gtests': {
@@ -3234,6 +3264,15 @@
           '--test-launcher-retry-limit=0'
         ],
       },
+      'dawn_end2end_validation_layers_tests': {
+        'desktop_args': [
+          '--use-gpu-in-tests',
+          # Dawn test retries deliberately disabled to prevent flakiness.
+          '--test-launcher-retry-limit=0',
+          '--enable-backend-validation',
+        ],
+        'test': 'dawn_end2end_tests',
+      },
       'dawn_end2end_wire_tests': {
         'desktop_args': [
           '--use-gpu-in-tests',
@@ -3255,6 +3294,15 @@
           '--test-launcher-retry-limit=0'
         ],
       },
+      'dawn_end2end_validation_layers_tests': {
+        'desktop_args': [
+          '--use-gpu-in-tests',
+          # Dawn test retries deliberately disabled to prevent flakiness.
+          '--test-launcher-retry-limit=0',
+          '--enable-backend-validation',
+        ],
+        'test': 'dawn_end2end_tests',
+      },
       'dawn_end2end_wire_tests': {
         'desktop_args': [
           '--use-gpu-in-tests',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 02ffe87..5d70421 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -10,6 +10,86 @@
 
 [
   {
+    'name': 'chrome',
+    'machines': {
+      'chromeos-amd64-generic-google-rel': {
+        'additional_compile_targets': [
+          'chrome',
+        ],
+      },
+      'chromeos-betty-google-rel': {
+        'additional_compile_targets': [
+          'chromiumos_preflight',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromeos_device_friendly_gtests',
+          'isolated_scripts': 'chromeos_isolated_scripts',
+        },
+        'swarming': {
+          'dimension_sets': [
+            {
+              'kvm': '1',
+              'os': 'Ubuntu-16.04',
+              'pool': 'chrome.tests.cros-vm',
+            },
+          ],
+        },
+      },
+      'linux-chromeos-google-rel': {
+        'additional_compile_targets': [
+          'chrome',
+          'chrome_sandbox',
+          'linux_symbols',
+          'symupload'
+        ],
+        'mixins': [
+          'chrome-swarming-pool',
+          'linux-trusty',
+        ],
+        'test_suites': {
+          'gtest_tests': 'linux_chromeos_gtests',
+        },
+      },
+      'linux-google-rel': {
+        'additional_compile_targets': [
+          'chrome',
+          'chrome/installer/linux',
+        ],
+        'mixins': [
+          'chrome-swarming-pool',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'chrome_sizes',
+        },
+      },
+      'mac-google-rel': {
+        'additional_compile_targets': [
+          'chrome',
+        ],
+        'mixins': [
+          'chrome-swarming-pool',
+          'mac_10.14',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'chrome_sizes',
+        },
+      },
+      'win-google-rel': {
+        'additional_compile_targets': [
+          'chrome',
+          'chrome_official_builder',
+        ],
+        'mixins': [
+          'chrome-swarming-pool',
+          'win10',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'chrome_sizes',
+        },
+      },
+    },
+  },
+  {
     'name': 'chromium',
     'machines': {
       'android-archive-dbg': {
@@ -542,6 +622,7 @@
       },
     },
   },
+  # TODO(bpastene): Remove 'chromium.chrome' when it's been switched to 'chrome'
   {
     'name': 'chromium.chrome',
     'machines': {
@@ -1828,7 +1909,7 @@
           'linux-xenial',
         ],
         'test_suites': {
-          'gtest_tests': 'fieldtrial_browser_tests',
+          'gtest_tests': 'fieldtrial_gtests',
         },
       },
       'linux-tcmalloc-rel': {
@@ -3756,15 +3837,10 @@
         'test_suites': {
           'gtest_tests': 'chromium_android_gtests',
         },
-        'swarming': {
-          'dimension_sets': [
-            {
-              'device_os': 'MMB29Q',
-              'device_type': 'bullhead',
-              'os': 'Android',
-            },
-          ],
-        },
+        'mixins': [
+          'hammerhead',
+          'kitkat',
+        ],
         'os_type': 'android',
       },
       'win-asan': {
diff --git a/testing/merge_scripts/code_coverage/merge_lib.py b/testing/merge_scripts/code_coverage/merge_lib.py
index 0eeaf0c..f5ccbd9 100644
--- a/testing/merge_scripts/code_coverage/merge_lib.py
+++ b/testing/merge_scripts/code_coverage/merge_lib.py
@@ -6,6 +6,7 @@
 import logging
 import multiprocessing
 import os
+import shutil
 import subprocess
 
 logging.basicConfig(
@@ -162,6 +163,17 @@
     invalid_profraw_files.append(profraw_file)
 
 
+def move_java_exec_files(input_dir, output_dir):
+  """Moves generated .exec files to output_dir.
+
+  Args:
+    input_dir (str): The path to traverse to find input files.
+    output_dir (str): Where to move input files to.
+  """
+  exec_input_file_paths = _get_profile_paths(input_dir, '.exec')
+  for input_file in exec_input_file_paths:
+    shutil.move(input_file, output_dir)
+
 def merge_profiles(input_dir, output_file, input_extension, profdata_tool_path):
   """Merges the profiles produced by the shards using llvm-profdata.
 
diff --git a/testing/merge_scripts/code_coverage/merge_results.py b/testing/merge_scripts/code_coverage/merge_results.py
index afc2803..3a2414bf 100755
--- a/testing/merge_scripts/code_coverage/merge_results.py
+++ b/testing/merge_scripts/code_coverage/merge_results.py
@@ -43,6 +43,8 @@
       '--profdata-dir', required=True, help='where to store the merged data')
   parser.add_argument(
       '--llvm-profdata', required=True, help='path to llvm-profdata executable')
+  parser.add_argument(
+      '--java-coverage-dir', help='directory for Java coverage data')
   return parser
 
 
@@ -51,6 +53,11 @@
   parser = _MergeAPIArgumentParser(description=desc)
   params = parser.parse_args()
 
+  if params.java_coverage_dir:
+    logging.info('Moving JaCoCo .exec files to %s', params.java_coverage_dir)
+    coverage_merger.move_java_exec_files(
+        params.task_output_dir, params.java_coverage_dir)
+
   # NOTE: The coverage data merge script must make sure that the profraw files
   # are deleted from the task output directory after merging, otherwise, other
   # test results merge script such as layout tests will treat them as json test
diff --git a/testing/merge_scripts/code_coverage/merge_results_test.py b/testing/merge_scripts/code_coverage/merge_results_test.py
index 7de53ecb5..21fa8190 100755
--- a/testing/merge_scripts/code_coverage/merge_results_test.py
+++ b/testing/merge_scripts/code_coverage/merge_results_test.py
@@ -5,6 +5,7 @@
 
 import json
 import os
+import shutil
 import subprocess
 import sys
 import unittest
@@ -225,6 +226,31 @@
     self.assertIn('missing_shards', written)
     self.assertEqual(written['missing_shards'], [0])
 
+  def test_move_java_exec_files(self):
+    mock_input_dir_walk = [
+        ('/b/some/path', ['0', '1', '2', '3'], ['summary.json']),
+        ('/b/some/path/0', [],
+         ['output.json', 'default-1.exec', 'default-2.exec']),
+        ('/b/some/path/1', [],
+         ['output.json', 'default-3.exec', 'default-4.exec']),
+    ]
+
+    with mock.patch.object(os, 'walk') as mock_walk:
+      mock_walk.return_value = mock_input_dir_walk
+      with mock.patch.object(shutil, 'move') as mock_exec_cmd:
+        merger.move_java_exec_files('/b/some/path', 'output/dir')
+        self.assertEqual(4, mock_exec_cmd.call_count)
+
+  def test_move_java_exec_files_if_there_is_no_file(self):
+    mock_input_dir_walk = [
+        ('/b/some/path', ['0', '1', '2', '3'], ['summary.json']),
+    ]
+
+    with mock.patch.object(os, 'walk') as mock_walk:
+      mock_walk.return_value = mock_input_dir_walk
+      with mock.patch.object(shutil, 'move') as mock_exec_cmd:
+        merger.move_java_exec_files('/b/some/path', 'output/dir')
+        self.assertFalse(mock_exec_cmd.called)
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index e1fd1a0..3f5c5e2 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2829,25 +2829,6 @@
             ]
         }
     ],
-    "LookalikeUrlNavigationSuggestionsUI": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows",
-                "android",
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "LookalikeUrlNavigationSuggestionsUI"
-                    ]
-                }
-            ]
-        }
-    ],
     "LowPriorityIframes2": [
         {
             "platforms": [
@@ -3062,37 +3043,6 @@
             ]
         }
     ],
-    "MojoChannel": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled"
-                }
-            ]
-        }
-    ],
-    "MojoChannelMac": [
-        {
-            "platforms": [
-                "mac"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MojoChannelMac"
-                    ]
-                }
-            ]
-        }
-    ],
     "MostLikelyDeprecation": [
         {
             "platforms": [
@@ -4728,6 +4678,25 @@
             ]
         }
     ],
+    "SecurityInterstitialsDarkMode": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SecurityInterstitialsDarkMode"
+                    ]
+                }
+            ]
+        }
+    ],
     "ServiceWorkerAggressiveCodeCache": [
         {
             "platforms": [
diff --git a/third_party/android_build_tools/aapt2/README.chromium b/third_party/android_build_tools/aapt2/README.chromium
index 789326e842..c9f7740b 100644
--- a/third_party/android_build_tools/aapt2/README.chromium
+++ b/third_party/android_build_tools/aapt2/README.chromium
@@ -1,6 +1,6 @@
 Name: Android SDK tool aapt2
 Short name: aapt2
-Version: 3.3.0-beta01-5013011
+Version: 3.6.0-alpha03-5516695
 URL:  https://dl.google.com/dl/android/maven2/com/android/tools/build/aapt2/${Version}/aapt2-${Version}-linux.jar
 Security Critical: no
 License: Apache Version 2.0
diff --git a/third_party/android_build_tools/aapt2/cipd.yaml b/third_party/android_build_tools/aapt2/cipd.yaml
index c4ab5df0..39fd55a 100644
--- a/third_party/android_build_tools/aapt2/cipd.yaml
+++ b/third_party/android_build_tools/aapt2/cipd.yaml
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:3.3.0-beta01-5013011-cr0
+# cipd create --pkg-def cipd.yaml -tag version:3.6.0-alpha03-5516695-cr0
 package: chromium/third_party/android_build_tools/aapt2
 description: Android SDK tool to build App Bundles
 # TODO(https://crbug.com/950727): Remove this and go back to symlinks once
@@ -11,4 +11,3 @@
 install_mode: copy
 data:
   - file: aapt2
-  - file: lib64/libc++.so
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index aa5ca9e..e42a2314 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/jumbo.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
 
 jumbo_source_set("common") {
@@ -10,6 +11,7 @@
   # source set rather than the actual component that can be linked to.
   # Dependencies instead should be to //third_party/blink/public/common:common.
   visibility = [
+    ":*",
     "//third_party/blink/public/common",
     "//third_party/blink/public:all_blink",
   ]
@@ -72,7 +74,9 @@
     "service_worker/service_worker_status_code.cc",
     "service_worker/service_worker_type_converters.cc",
     "service_worker/service_worker_utils.cc",
+    "web_package/http_structured_header.cc",
     "web_package/signed_exchange_consts.cc",
+    "web_package/signed_exchange_request_matcher.cc",
   ]
 
   public_deps = [
@@ -95,6 +99,7 @@
   if (is_android || is_win) {
     sources += [
       "font_unique_name_lookup/font_table_matcher.cc",
+      "font_unique_name_lookup/font_table_persistence.cc",
       "font_unique_name_lookup/icu_fold_case_util.cc",
     ]
 
@@ -107,6 +112,17 @@
   }
 }
 
+fuzzer_test("http_structured_header_fuzzer") {
+  sources = [
+    "web_package/http_structured_header_fuzzer.cc",
+  ]
+  deps = [
+    ":common",
+    "//third_party/blink/renderer/platform:blink_fuzzer_test_support",
+  ]
+  seed_corpus = "web_package/http_structured_header_corpus"
+}
+
 test("blink_common_unittests") {
   visibility = [ "*" ]
   deps = [
@@ -140,6 +156,8 @@
     "origin_trials/trial_token_unittest.cc",
     "origin_trials/trial_token_validator_unittest.cc",
     "test/run_all_unittests.cc",
+    "web_package/http_structured_header_unittest.cc",
+    "web_package/signed_exchange_request_matcher_unittest.cc",
   ]
 
   deps = [
diff --git a/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc b/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc
new file mode 100644
index 0000000..5203200b
--- /dev/null
+++ b/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc
@@ -0,0 +1,107 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/common/font_unique_name_lookup/font_table_persistence.h"
+
+#include "base/hash/hash.h"
+#include "base/pickle.h"
+#include "base/threading/scoped_blocking_call.h"
+
+namespace blink {
+
+namespace font_table_persistence {
+
+bool LoadFromFile(base::FilePath file_path,
+                  base::MappedReadOnlyRegion* name_table_region) {
+  DCHECK(!file_path.empty());
+
+  // Reset to empty to ensure IsValid() is false if reading fails.
+  *name_table_region = base::MappedReadOnlyRegion();
+  std::vector<char> file_contents;
+  {
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+
+    base::File table_cache_file(
+        file_path, base::File::FLAG_OPEN | base::File::Flags::FLAG_READ);
+    if (!table_cache_file.IsValid() || table_cache_file.GetLength() <= 0) {
+      return false;
+    }
+
+    file_contents.resize(table_cache_file.GetLength());
+
+    if (table_cache_file.Read(0, file_contents.data(), file_contents.size()) <=
+        0) {
+      return false;
+    }
+  }
+
+  base::PickleIterator pickle_iterator(
+      base::Pickle(file_contents.data(), file_contents.size()));
+
+  uint32_t checksum = 0;
+  if (!pickle_iterator.ReadUInt32(&checksum)) {
+    return false;
+  }
+
+  const char* proto_data = nullptr;
+  int proto_length = 0;
+
+  if (!pickle_iterator.ReadData(&proto_data, &proto_length)) {
+    return false;
+  }
+
+  if (checksum != base::PersistentHash(proto_data, proto_length)) {
+    return false;
+  }
+
+  blink::FontUniqueNameTable font_table;
+  if (!font_table.ParseFromArray(proto_data, proto_length)) {
+    return false;
+  }
+
+  *name_table_region = base::ReadOnlySharedMemoryRegion::Create(proto_length);
+  if (!name_table_region->IsValid() || !name_table_region->mapping.size()) {
+    return false;
+  }
+
+  memcpy(name_table_region->mapping.memory(), proto_data, proto_length);
+
+  return true;
+}
+
+bool PersistToFile(const base::MappedReadOnlyRegion& name_table_region,
+                   base::FilePath file_path) {
+  DCHECK(name_table_region.mapping.IsValid() &&
+         name_table_region.mapping.size() && !file_path.empty());
+
+  base::File table_cache_file(file_path, base::File::FLAG_CREATE_ALWAYS |
+                                             base::File::Flags::FLAG_WRITE);
+  if (!table_cache_file.IsValid()) {
+    return false;
+  }
+
+  base::Pickle pickle;
+  uint32_t checksum = base::PersistentHash(name_table_region.mapping.memory(),
+                                           name_table_region.mapping.size());
+  pickle.WriteUInt32(checksum);
+  pickle.WriteData(static_cast<char*>(name_table_region.mapping.memory()),
+                   name_table_region.mapping.size());
+  DCHECK(pickle.size());
+  {
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+
+    if (table_cache_file.Write(0, static_cast<const char*>(pickle.data()),
+                               pickle.size()) == -1) {
+      table_cache_file.SetLength(0);
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace font_table_persistence
+
+}  // namespace blink
diff --git a/content/browser/web_package/http_structured_header.cc b/third_party/blink/common/web_package/http_structured_header.cc
similarity index 98%
rename from content/browser/web_package/http_structured_header.cc
rename to third_party/blink/common/web_package/http_structured_header.cc
index b3284e56..c3984f15 100644
--- a/content/browser/web_package/http_structured_header.cc
+++ b/third_party/blink/common/web_package/http_structured_header.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/web_package/http_structured_header.h"
+#include "third_party/blink/public/common/web_package/http_structured_header.h"
 
 #include <string>
 #include <utility>
@@ -11,7 +11,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 
-namespace content {
+namespace blink {
 namespace http_structured_header {
 
 namespace {
@@ -320,4 +320,4 @@
 }
 
 }  // namespace http_structured_header
-}  // namespace content
+}  // namespace blink
diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/1.txt b/third_party/blink/common/web_package/http_structured_header_corpus/1.txt
similarity index 100%
rename from content/test/data/fuzzer_corpus/http_structured_header_data/1.txt
rename to third_party/blink/common/web_package/http_structured_header_corpus/1.txt
diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/2.txt b/third_party/blink/common/web_package/http_structured_header_corpus/2.txt
similarity index 100%
rename from content/test/data/fuzzer_corpus/http_structured_header_data/2.txt
rename to third_party/blink/common/web_package/http_structured_header_corpus/2.txt
diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/3.txt b/third_party/blink/common/web_package/http_structured_header_corpus/3.txt
similarity index 100%
rename from content/test/data/fuzzer_corpus/http_structured_header_data/3.txt
rename to third_party/blink/common/web_package/http_structured_header_corpus/3.txt
diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/4.txt b/third_party/blink/common/web_package/http_structured_header_corpus/4.txt
similarity index 100%
rename from content/test/data/fuzzer_corpus/http_structured_header_data/4.txt
rename to third_party/blink/common/web_package/http_structured_header_corpus/4.txt
diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/5.txt b/third_party/blink/common/web_package/http_structured_header_corpus/5.txt
similarity index 100%
rename from content/test/data/fuzzer_corpus/http_structured_header_data/5.txt
rename to third_party/blink/common/web_package/http_structured_header_corpus/5.txt
diff --git a/content/test/fuzzer/http_structured_header_fuzzer.cc b/third_party/blink/common/web_package/http_structured_header_fuzzer.cc
similarity index 77%
rename from content/test/fuzzer/http_structured_header_fuzzer.cc
rename to third_party/blink/common/web_package/http_structured_header_fuzzer.cc
index 5779a33..f6723f3 100644
--- a/content/test/fuzzer/http_structured_header_fuzzer.cc
+++ b/third_party/blink/common/web_package/http_structured_header_fuzzer.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/web_package/http_structured_header.h"  // nogncheck
+#include "third_party/blink/public/common/web_package/http_structured_header.h"  // nogncheck
 
-namespace content {
+namespace blink {
 namespace http_structured_header {
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
@@ -16,4 +16,4 @@
 }
 
 }  // namespace http_structured_header
-}  // namespace content
+}  // namespace blink
diff --git a/content/browser/web_package/http_structured_header_unittest.cc b/third_party/blink/common/web_package/http_structured_header_unittest.cc
similarity index 97%
rename from content/browser/web_package/http_structured_header_unittest.cc
rename to third_party/blink/common/web_package/http_structured_header_unittest.cc
index 14d8a21c..da212eb 100644
--- a/content/browser/web_package/http_structured_header_unittest.cc
+++ b/third_party/blink/common/web_package/http_structured_header_unittest.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/web_package/http_structured_header.h"
+#include "third_party/blink/public/common/web_package/http_structured_header.h"
 
 #include <string>
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace content {
+namespace blink {
 namespace http_structured_header {
 
 // Test cases are taken from https://github.com/httpwg/structured-header-tests.
@@ -170,4 +170,4 @@
 }
 
 }  // namespace http_structured_header
-}  // namespace content
+}  // namespace blink
diff --git a/content/browser/web_package/signed_exchange_request_matcher.cc b/third_party/blink/common/web_package/signed_exchange_request_matcher.cc
similarity index 98%
rename from content/browser/web_package/signed_exchange_request_matcher.cc
rename to third_party/blink/common/web_package/signed_exchange_request_matcher.cc
index 350f679..522d578 100644
--- a/content/browser/web_package/signed_exchange_request_matcher.cc
+++ b/third_party/blink/common/web_package/signed_exchange_request_matcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/web_package/signed_exchange_request_matcher.h"
+#include "third_party/blink/public/common/web_package/signed_exchange_request_matcher.h"
 
 #include <algorithm>
 #include <memory>
@@ -17,7 +17,7 @@
 #include "net/http/http_util.h"
 #include "third_party/blink/public/common/web_package/signed_exchange_consts.h"
 
-namespace content {
+namespace blink {
 
 namespace {
 
@@ -452,4 +452,4 @@
   return false;
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/browser/web_package/signed_exchange_request_matcher_unittest.cc b/third_party/blink/common/web_package/signed_exchange_request_matcher_unittest.cc
similarity index 98%
rename from content/browser/web_package/signed_exchange_request_matcher_unittest.cc
rename to third_party/blink/common/web_package/signed_exchange_request_matcher_unittest.cc
index 61cf6e0..15e1732 100644
--- a/content/browser/web_package/signed_exchange_request_matcher_unittest.cc
+++ b/third_party/blink/common/web_package/signed_exchange_request_matcher_unittest.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/web_package/signed_exchange_request_matcher.h"
+#include "third_party/blink/public/common/web_package/signed_exchange_request_matcher.h"
 
 #include "net/http/http_request_headers.h"
 #include "net/http/http_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace content {
+namespace blink {
 
 constexpr char kVariantsHeader[] = "variants-04";
 constexpr char kVariantKeyHeader[] = "variant-key-04";
@@ -218,4 +218,4 @@
   }
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 57745d0..a7f701e 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -120,6 +120,7 @@
 # the browser- and renderer-side components.
 source_set("blink_headers") {
   sources = [
+    "platform/audio/web_audio_device_source_type.h",
     "platform/blame_context.h",
     "platform/code_cache_loader.h",
     "platform/file_path_conversion.h",
@@ -164,6 +165,7 @@
     "platform/modules/webrtc/peer_connection_remote_audio_source.h",
     "platform/modules/webrtc/track_observer.h",
     "platform/modules/webrtc/webrtc_logging.h",
+    "platform/modules/webrtc/webrtc_source.h",
     "platform/modules/webrtc/webrtc_video_frame_adapter.h",
     "platform/modules/webrtc/webrtc_video_utils.h",
     "platform/platform.h",
@@ -211,7 +213,6 @@
     "platform/web_crypto_key_algorithm_params.h",
     "platform/web_cursor_info.h",
     "platform/web_data.h",
-    "platform/web_database_observer.h",
     "platform/web_dedicated_worker.h",
     "platform/web_dedicated_worker_host_factory_client.h",
     "platform/web_distillability.h",
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 63d781f..27742e69 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -108,7 +108,9 @@
     "service_worker/service_worker_types.h",
     "service_worker/service_worker_utils.h",
     "user_agent/user_agent_metadata.h",
+    "web_package/http_structured_header.h",
     "web_package/signed_exchange_consts.h",
+    "web_package/signed_exchange_request_matcher.h",
   ]
 
   public_deps = [
@@ -139,6 +141,7 @@
   if (is_android || is_win) {
     sources += [
       "font_unique_name_lookup/font_table_matcher.h",
+      "font_unique_name_lookup/font_table_persistence.h",
       "font_unique_name_lookup/icu_fold_case_util.h",
     ]
     deps += [ ":font_unique_name_table_proto" ]
diff --git a/third_party/blink/public/common/font_unique_name_lookup/font_table_persistence.h b/third_party/blink/public/common/font_unique_name_lookup/font_table_persistence.h
new file mode 100644
index 0000000..73e14785
--- /dev/null
+++ b/third_party/blink/public/common/font_unique_name_lookup/font_table_persistence.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_FONT_UNIQUE_NAME_LOOKUP_FONT_TABLE_PERSISTENCE_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_FONT_UNIQUE_NAME_LOOKUP_FONT_TABLE_PERSISTENCE_H_
+
+#include "base/files/file.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/common/font_unique_name_lookup/font_unique_name_table.pb.h"
+
+namespace blink {
+
+// Persistence functions for storing to and reading from disk the font table
+// lookup structure. The storage format is a pickle of a PersistenceHash prefix
+// computed over the protobuf, followed by a dump of the FontUniqueNameTable
+// protobuf. The persistence functions ensure that the format is followed, that
+// persisting stores the checksum and reading validates the checksum.
+namespace font_table_persistence {
+
+// Load a FontUniqueNameTable protobuf from a persistence file, and if the
+// checksum validates correctly, allocate the MappedReadOnlyRegion and copy the
+// read ProtoBuf into it. Returns true on success, false on failures such as
+// inabilitiy to access the file, empty file, incorrect checksum, or failing to
+// parse the read dump as a FontUniqueNameTable protobuf.
+bool BLINK_COMMON_EXPORT
+LoadFromFile(base::FilePath file_path,
+             base::MappedReadOnlyRegion* name_table_region);
+
+// Store a FontUniqueNameTable protobuf to a dump file, prefixing the protobuf
+// dump with a PersistenceHash checksum. Returns true on success, false on
+// failures such as failing to open the file or failing to write the dump into
+// it.
+bool BLINK_COMMON_EXPORT
+PersistToFile(const base::MappedReadOnlyRegion& name_table_region,
+              base::FilePath file_path);
+
+}  // namespace font_table_persistence
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_FONT_UNIQUE_NAME_LOOKUP_FONT_TABLE_PERSISTENCE_H_x
diff --git a/third_party/blink/public/common/web_package/http_structured_header.h b/third_party/blink/public/common/web_package/http_structured_header.h
new file mode 100644
index 0000000..b430307
--- /dev/null
+++ b/third_party/blink/public/common/web_package/http_structured_header.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PACKAGE_HTTP_STRUCTURED_HEADER_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PACKAGE_HTTP_STRUCTURED_HEADER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+#include "third_party/blink/public/common/common_export.h"
+
+namespace blink {
+namespace http_structured_header {
+
+struct BLINK_COMMON_EXPORT ParameterisedIdentifier {
+  using Parameters = std::map<std::string, std::string>;
+
+  std::string identifier;
+  Parameters params;
+
+  ParameterisedIdentifier(const ParameterisedIdentifier&);
+  ParameterisedIdentifier(const std::string&, const Parameters&);
+  ~ParameterisedIdentifier();
+};
+
+using ParameterisedList = std::vector<ParameterisedIdentifier>;
+using ListOfLists = std::vector<std::vector<std::string>>;
+
+BLINK_COMMON_EXPORT base::Optional<std::string> ParseItem(
+    const base::StringPiece& str);
+BLINK_COMMON_EXPORT base::Optional<ParameterisedList> ParseParameterisedList(
+    const base::StringPiece& str);
+BLINK_COMMON_EXPORT base::Optional<ListOfLists> ParseListOfLists(
+    const base::StringPiece& str);
+
+}  // namespace http_structured_header
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PACKAGE_HTTP_STRUCTURED_HEADER_H_
diff --git a/content/browser/web_package/signed_exchange_request_matcher.h b/third_party/blink/public/common/web_package/signed_exchange_request_matcher.h
similarity index 75%
rename from content/browser/web_package/signed_exchange_request_matcher.h
rename to third_party/blink/public/common/web_package/signed_exchange_request_matcher.h
index 0a94e435..234cda8f 100644
--- a/content/browser/web_package/signed_exchange_request_matcher.h
+++ b/third_party/blink/public/common/web_package/signed_exchange_request_matcher.h
@@ -2,23 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_REQUEST_MATCHER_H_
-#define CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_REQUEST_MATCHER_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PACKAGE_SIGNED_EXCHANGE_REQUEST_MATCHER_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PACKAGE_SIGNED_EXCHANGE_REQUEST_MATCHER_H_
 
 #include <map>
 #include <string>
 #include <vector>
 
 #include "base/gtest_prod_util.h"
-#include "content/browser/web_package/http_structured_header.h"
-#include "content/common/content_export.h"
 #include "net/http/http_request_headers.h"
+#include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/common/web_package/http_structured_header.h"
 
-namespace content {
+namespace blink {
 
 // SignedExchangeRequestMatcher implements the Request Matching algorithm [1].
 // [1] https://wicg.github.io/webpackage/loading.html#request-matching
-class CONTENT_EXPORT SignedExchangeRequestMatcher {
+class BLINK_COMMON_EXPORT SignedExchangeRequestMatcher {
  public:
   // Keys must be lower-cased.
   using HeaderMap = std::map<std::string, std::string>;
@@ -43,6 +43,6 @@
   FRIEND_TEST_ALL_PREFIXES(SignedExchangeRequestMatcherTest, CacheBehavior);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_REQUEST_MATCHER_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PACKAGE_SIGNED_EXCHANGE_REQUEST_MATCHER_H_
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index a7c3724..9dc26628 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -56,10 +56,12 @@
     "hid/hid.mojom",
     "hyphenation/hyphenation.mojom",
     "idle/idle_manager.mojom",
+    "image_downloader/image_downloader.mojom",
     "insecure_input/insecure_input_service.mojom",
     "keyboard_lock/keyboard_lock.mojom",
     "leak_detector/leak_detector.mojom",
     "loader/code_cache.mojom",
+    "loader/fetch_client_settings_object.mojom",
     "loader/navigation_predictor.mojom",
     "loader/pause_subresource_loading_handle.mojom",
     "loader/previews_resource_loading_hints.mojom",
diff --git a/third_party/blink/public/mojom/content_index/content_index.mojom b/third_party/blink/public/mojom/content_index/content_index.mojom
index 120c4a4..14b64139 100644
--- a/third_party/blink/public/mojom/content_index/content_index.mojom
+++ b/third_party/blink/public/mojom/content_index/content_index.mojom
@@ -5,7 +5,6 @@
 module blink.mojom;
 
 import "skia/public/interfaces/bitmap.mojom";
-import "url/mojom/url.mojom";
 
 // As per https://github.com/rknoll/content-index.
 
@@ -15,6 +14,7 @@
   STORAGE_ERROR,
 };
 
+// These values are persisted, do not change or create gaps.
 enum ContentCategory {
   // "homepage"
   HOME_PAGE = 1,
@@ -34,10 +34,16 @@
   string title;
   string description;
   ContentCategory category;
-  url.mojom.Url icon_url;
-  url.mojom.Url launch_url;
+
+  // Passed as a string so the same description can be returned when queried.
+  string icon_url;
+  string launch_url;
 };
 
+// The |service_worker_registration_id| comes from an untrusted renderer but
+// the origin it's validated against is trusted.
+// TODO(crbug.com/976962): Register interfaces that are exposed only on
+// Service Workers.
 interface ContentIndexService {
   Add(int64 service_worker_registration_id,
       ContentDescription description,
diff --git a/chrome/browser/chromeos/kiosk_next_home/mojom/OWNERS b/third_party/blink/public/mojom/image_downloader/OWNERS
similarity index 100%
rename from chrome/browser/chromeos/kiosk_next_home/mojom/OWNERS
rename to third_party/blink/public/mojom/image_downloader/OWNERS
diff --git a/content/common/image_downloader/image_downloader.mojom b/third_party/blink/public/mojom/image_downloader/image_downloader.mojom
similarity index 97%
rename from content/common/image_downloader/image_downloader.mojom
rename to third_party/blink/public/mojom/image_downloader/image_downloader.mojom
index 7a6b3a74..6358894f 100644
--- a/content/common/image_downloader/image_downloader.mojom
+++ b/third_party/blink/public/mojom/image_downloader/image_downloader.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 content.mojom;
+module blink.mojom;
 
 import "skia/public/interfaces/bitmap.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
diff --git a/third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom b/third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom
new file mode 100644
index 0000000..f636edb
--- /dev/null
+++ b/third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom
@@ -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.
+
+module blink.mojom;
+
+import "url/mojom/url.mojom";
+import "services/network/public/mojom/referrer_policy.mojom";
+
+// This is equivalent to blink::FetchClientSettingsObject. This struct is always
+// created by the renderer process, and forwarded to the worker's renderer
+// process or used in the browser process.
+//
+// See comments on blink::FetchClientSettingsObject for details of this struct
+// and each member.
+struct FetchClientSettingsObject {
+  network.mojom.ReferrerPolicy referrer_policy;
+  url.mojom.Url outgoing_referrer;
+};
diff --git a/third_party/blink/public/mojom/payments/payment_app.mojom b/third_party/blink/public/mojom/payments/payment_app.mojom
index 4e7c72f..567f572 100644
--- a/third_party/blink/public/mojom/payments/payment_app.mojom
+++ b/third_party/blink/public/mojom/payments/payment_app.mojom
@@ -20,6 +20,21 @@
   FETCH_PAYMENT_APP_INFO_FAILED,
 };
 
+enum PaymentEventResponseType {
+  PAYMENT_EVENT_SUCCESS,
+  PAYMENT_EVENT_REJECT,
+  PAYMENT_EVENT_SERVICE_WORKER_ERROR,
+  PAYMENT_HANDLER_WINDOW_CLOSING,
+  PAYMENT_EVENT_INTERNAL_ERROR,
+  PAYMENT_EVENT_NO_RESPONSE,
+  PAYMENT_DETAILS_STRINGIFY_ERROR,
+  PAYMENT_METHOD_NAME_EMPTY,
+  PAYMENT_DETAILS_ABSENT,
+  PAYMENT_DETAILS_NOT_OBJECT,
+  PAYMENT_EVENT_BROWSER_ERROR,
+  PAYMENT_EVENT_TIMEOUT,
+};
+
 // This struct is provided to hold a payment instrument from render
 // side (PaymentInstrument.idl).
 struct PaymentInstrument {
@@ -88,6 +103,7 @@
 struct PaymentHandlerResponse {
   string method_name;
   string stringified_details;
+  PaymentEventResponseType response_type;
 };
 
 // This interface is provided to pass a payment handler response from payment
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_client.mojom b/third_party/blink/public/mojom/service_worker/service_worker_client.mojom
index dc0e35c..14331e7 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_client.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_client.mojom
@@ -13,6 +13,7 @@
 // and https://w3c.github.io/ServiceWorker/#dom-clientqueryoptions-type.
 enum ServiceWorkerClientType {
   kWindow,
+  kDedicatedWorker,
   kSharedWorker,
   kAll,
 };
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom b/third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom
index 2ea7c067..d7c94300 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom
@@ -14,6 +14,7 @@
 
   // For service worker clients.
   kForWindow,
+  kForDedicatedWorker,
   kForSharedWorker,
 
   // For service workers.
diff --git a/third_party/blink/public/mojom/use_counter/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
index 4fb0082d7..c2b391ec 100644
--- a/third_party/blink/public/mojom/use_counter/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
@@ -4,7 +4,7 @@
 
 module blink.mojom;
 
-const int32 kMaximumCSSSampleId = 640;
+const int32 kMaximumCSSSampleId = 643;
 
 // This CSSSampleId represents page load for CSS histograms. It is recorded once
 // per page visit for each CSS histogram being logged on the blink side and the
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 223f596..cb59d4e 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2325,6 +2325,8 @@
   kLazyLoadImageMissingDimensionsForLazy = 2936,
   kPeriodicBackgroundSyncGetTags = 2937,
   kPeriodicBackgroundSyncUnregister = 2938,
+  kCreateObjectURLMediaSourceFromWorker = 2939,
+  kCSSAtRuleProperty = 2940,
 
   // 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/mojom/worker/dedicated_worker_host_factory.mojom b/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
index 067dd9f..134bacd 100644
--- a/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
+++ b/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
@@ -7,6 +7,7 @@
 import "services/network/public/mojom/fetch_api.mojom";
 import "services/service_manager/public/mojom/interface_provider.mojom";
 import "third_party/blink/public/mojom/blob/blob_url_store.mojom";
+import "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom";
 import "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom";
 import "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom";
 import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
@@ -75,15 +76,19 @@
   // PlzDedicatedWorker is enabled.
   //
   // Creates a new DedicatedWorkerHost, and requests to start top-level worker
-  // script loading for |script_url|. |origin| must either be unique or match
-  // the origin of the creating context (Document or
-  // DedicatedWorkerGlobalScope). |blob_url_token| should be non-null when
-  // |script_url| is a blob URL. |client| is used for notifying the renderer
-  // process of results of worker host creation and script loading.
+  // script loading for |script_url| using |credentials_mode| and
+  // |outside_fetch_client_settings_object|.
+  // |origin| must either be unique or match the origin of the creating context
+  // (Document or DedicatedWorkerGlobalScope).
+  // |blob_url_token| should be non-null when |script_url| is a blob URL.
+  // |client| is used for notifying the renderer process of results of worker
+  // host creation and script loading.
   CreateWorkerHostAndStartScriptLoad(
       url.mojom.Url script_url,
       url.mojom.Origin origin,
       network.mojom.CredentialsMode credentials_mode,
+      blink.mojom.FetchClientSettingsObject
+          outside_fetch_client_settings_object,
       blink.mojom.BlobURLToken? blob_url_token,
       DedicatedWorkerHostFactoryClient client);
 };
diff --git a/third_party/blink/public/mojom/worker/shared_worker_connector.mojom b/third_party/blink/public/mojom/worker/shared_worker_connector.mojom
index 8feca4f..d78248f2 100644
--- a/third_party/blink/public/mojom/worker/shared_worker_connector.mojom
+++ b/third_party/blink/public/mojom/worker/shared_worker_connector.mojom
@@ -5,6 +5,7 @@
 module blink.mojom;
 
 import "third_party/blink/public/mojom/blob/blob_url_store.mojom";
+import "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom";
 import "third_party/blink/public/mojom/worker/shared_worker_client.mojom";
 import "third_party/blink/public/mojom/worker/shared_worker_creation_context_type.mojom";
 import "third_party/blink/public/mojom/worker/shared_worker_info.mojom";
@@ -19,6 +20,8 @@
   // blob URL. Without this by the time the worker code starts fetching
   // the URL the blob URL might no longer be valid.
   Connect(SharedWorkerInfo info,
+          blink.mojom.FetchClientSettingsObject
+              outside_fetch_client_settings_object,
           SharedWorkerClient client,
           SharedWorkerCreationContextType creation_context_type,
           handle<message_pipe> message_port,
diff --git a/third_party/blink/public/platform/audio/OWNERS b/third_party/blink/public/platform/audio/OWNERS
new file mode 100644
index 0000000..ca9ce93
--- /dev/null
+++ b/third_party/blink/public/platform/audio/OWNERS
@@ -0,0 +1,3 @@
+file://content/renderer/media/OWNERS
+
+# COMPONENT: Internals>Media
diff --git a/third_party/blink/public/platform/audio/web_audio_device_source_type.h b/third_party/blink/public/platform/audio/web_audio_device_source_type.h
new file mode 100644
index 0000000..1208e4b0
--- /dev/null
+++ b/third_party/blink/public/platform/audio/web_audio_device_source_type.h
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_AUDIO_WEB_AUDIO_DEVICE_SOURCE_TYPE_H_
+#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_AUDIO_WEB_AUDIO_DEVICE_SOURCE_TYPE_H_
+
+namespace blink {
+
+// Types of audio sources. Each source can have individual mixing and/or
+// latency requirements for output. The source is specified by the client when
+// requesting output sink from the factory, and the factory creates the output
+// sink basing on those requirements.
+//
+// TODO(crbug.com/704136): This enum originally belongs to AudioDeviceFactory
+// class (currently in //content). Move it back to the aforementiened class,
+// when audio_device_factory.cc|h gets Onion souped.
+enum class WebAudioDeviceSourceType {
+  kNone = 0,
+  kMediaElement,
+  kWebRtc,
+  kNonRtcAudioTrack,
+  kWebAudioInteractive,
+  kWebAudioBalanced,
+  kWebAudioPlayback,
+  kWebAudioExact,
+  kLast = kWebAudioExact  // Only used for validation of format.
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_AUDIO_WEB_AUDIO_DEVICE_SOURCE_TYPE_H_
diff --git a/third_party/blink/public/platform/modules/webrtc/webrtc_source.h b/third_party/blink/public/platform/modules/webrtc/webrtc_source.h
new file mode 100644
index 0000000..4af07f2d
--- /dev/null
+++ b/third_party/blink/public/platform/modules/webrtc/webrtc_source.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_WEBRTC_WEBRTC_SOURCE_H_
+#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_WEBRTC_WEBRTC_SOURCE_H_
+
+#include "third_party/blink/public/platform/web_common.h"
+
+namespace media {
+class AudioBus;
+}
+
+namespace blink {
+
+// TODO(xians): Move the following interface to webrtc so that
+// libjingle can own references to the renderer and capturer.
+//
+// TODO(xians): Merge this interface with WebRtcAudioRendererSource.
+// The reason why we could not do it today is that WebRtcAudioRendererSource
+// gets the data by pulling, while the data is pushed into
+// WebRtcPlayoutDataSource::Sink.
+//
+// TODO(crbug.com/704136): Move the class below out of the Blink exposed
+// API when all users of it have been Onion souped.
+class BLINK_PLATFORM_EXPORT WebRtcPlayoutDataSource {
+ public:
+  class Sink {
+   public:
+    // Callback to get the playout data.
+    // Called on the audio render thread.
+    // |audio_bus| must have buffer size |sample_rate/100| and 1-2 channels.
+    virtual void OnPlayoutData(media::AudioBus* audio_bus,
+                               int sample_rate,
+                               int audio_delay_milliseconds) = 0;
+
+    // Callback to notify the sink that the source has changed.
+    // Called on the main render thread.
+    virtual void OnPlayoutDataSourceChanged() = 0;
+
+    // Called to notify that the audio render thread has changed, and
+    // OnPlayoutData() will from now on be called on the new thread.
+    // Called on the new audio render thread.
+    virtual void OnRenderThreadChanged() = 0;
+
+   protected:
+    virtual ~Sink() {}
+  };
+
+  // Adds/Removes the sink of WebRtcAudioRendererSource to the ADM.
+  // These methods are used by the MediaStreamAudioProcesssor to get the
+  // rendered data for AEC.
+  virtual void AddPlayoutSink(Sink* sink) = 0;
+  virtual void RemovePlayoutSink(Sink* sink) = 0;
+
+ protected:
+  virtual ~WebRtcPlayoutDataSource() {}
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_WEBRTC_WEBRTC_SOURCE_H_
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index e7116b7..e5fdbad 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -41,12 +41,14 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
+#include "media/base/audio_renderer_sink.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-shared.h"
+#include "third_party/blink/public/platform/audio/web_audio_device_source_type.h"
 #include "third_party/blink/public/platform/blame_context.h"
 #include "third_party/blink/public/platform/code_cache_loader.h"
 #include "third_party/blink/public/platform/user_metrics_action.h"
@@ -77,6 +79,7 @@
 }
 
 namespace media {
+struct AudioSinkParameters;
 class GpuVideoAcceleratorFactories;
 }
 
@@ -109,7 +112,6 @@
 class WebAudioLatencyHint;
 class WebBlobRegistry;
 class WebCrypto;
-class WebDatabaseObserver;
 class WebDedicatedWorker;
 class WebGraphicsContext3DProvider;
 class WebLocalFrame;
@@ -216,40 +218,6 @@
 
   // Database (WebSQL) ---------------------------------------------------
 
-  // Opens a database file.
-  virtual base::File DatabaseOpenFile(const WebString& vfs_file_name,
-                                      int desired_flags) {
-    return base::File();
-  }
-
-  // Deletes a database file and returns the error code.
-  virtual int DatabaseDeleteFile(const WebString& vfs_file_name,
-                                 bool sync_dir) {
-    return 0;
-  }
-
-  // Returns the attributes of the given database file.
-  virtual int32_t DatabaseGetFileAttributes(const WebString& vfs_file_name) {
-    return 0;
-  }
-
-  // Returns the size of the given database file.
-  virtual int64_t DatabaseGetFileSize(const WebString& vfs_file_name) {
-    return 0;
-  }
-
-  // Returns the space available for the given origin.
-  virtual int64_t DatabaseGetSpaceAvailableForOrigin(
-      const WebSecurityOrigin& origin) {
-    return 0;
-  }
-
-  // Set the size of the given database file.
-  virtual bool DatabaseSetFileSize(const WebString& vfs_file_name,
-                                   int64_t size) {
-    return false;
-  }
-
   // Return a filename-friendly identifier for an origin.
   virtual WebString DatabaseCreateOriginIdentifier(
       const WebSecurityOrigin& origin) {
@@ -628,6 +596,17 @@
     return base::nullopt;
   }
 
+  virtual scoped_refptr<media::AudioRendererSink> NewAudioRendererSink(
+      blink::WebAudioDeviceSourceType source_type,
+      blink::WebLocalFrame* web_frame,
+      const media::AudioSinkParameters& params) {
+    return nullptr;
+  }
+  virtual media::AudioLatency::LatencyType GetAudioSourceLatencyType(
+      blink::WebAudioDeviceSourceType source_type) {
+    return media::AudioLatency::LATENCY_PLAYBACK;
+  }
+
   // WebWorker ----------------------------------------------------------
 
   virtual std::unique_ptr<WebDedicatedWorkerHostFactoryClient>
@@ -659,10 +638,6 @@
 
   virtual const char* GetBrowserServiceName() const { return ""; }
 
-  // WebDatabase --------------------------------------------------------
-
-  virtual WebDatabaseObserver* DatabaseObserver() { return nullptr; }
-
   // Media Capabilities --------------------------------------------------
 
   virtual WebMediaCapabilitiesClient* MediaCapabilitiesClient() {
diff --git a/third_party/blink/public/platform/web_database_observer.h b/third_party/blink/public/platform/web_database_observer.h
deleted file mode 100644
index 607d637..0000000
--- a/third_party/blink/public/platform/web_database_observer.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_DATABASE_OBSERVER_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_DATABASE_OBSERVER_H_
-
-#include "base/time/time.h"
-
-namespace blink {
-
-class WebString;
-class WebSecurityOrigin;
-
-class WebDatabaseObserver {
- public:
-  virtual void DatabaseOpened(const WebSecurityOrigin&,
-                              const WebString& database_name,
-                              const WebString& database_display_name,
-                              uint32_t estimated_size) = 0;
-  virtual void DatabaseModified(const WebSecurityOrigin&,
-                                const WebString& database_name) = 0;
-  virtual void DatabaseClosed(const WebSecurityOrigin&,
-                              const WebString& database_name) = 0;
-  virtual void ReportSqliteError(const blink::WebSecurityOrigin& origin,
-                                 const blink::WebString& database_name,
-                                 int error) = 0;
-
- protected:
-  ~WebDatabaseObserver() = default;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_DATABASE_OBSERVER_H_
diff --git a/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h b/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
index ed3b9302..f6b8eb2 100644
--- a/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
+++ b/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
@@ -8,6 +8,7 @@
 #include "base/memory/ref_counted.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -19,7 +20,6 @@
 class WebURL;
 class WebWorkerFetchContext;
 
-// PlzDedicatedWorker:
 // WebDedicatedWorkerHostFactoryClient is the interface to access
 // content::DedicatedWorkerHostFactoryClient from blink::DedicatedWorker.
 class WebDedicatedWorkerHostFactoryClient {
@@ -32,10 +32,15 @@
   virtual void CreateWorkerHostDeprecated(
       const blink::WebSecurityOrigin& script_origin) = 0;
   // For PlzDedicatedWorker.
+  // TODO(nhiroki): Pack |fetch_client_*| into some struct like
+  // WebFetchClientSettingsObject.
   virtual void CreateWorkerHost(
       const blink::WebURL& script_url,
       const blink::WebSecurityOrigin& script_origin,
       network::mojom::CredentialsMode credentials_mode,
+      const blink::WebSecurityOrigin& fetch_client_security_origin,
+      network::mojom::ReferrerPolicy fetch_client_referrer_policy,
+      const blink::WebURL& fetch_client_outgoing_referrer,
       mojo::ScopedMessagePipeHandle blob_url_token) = 0;
 
   // Clones the given WebWorkerFetchContext for nested workers.
diff --git a/third_party/blink/public/platform/web_media_player.h b/third_party/blink/public/platform/web_media_player.h
index fd7cd75..61869a8 100644
--- a/third_party/blink/public/platform/web_media_player.h
+++ b/third_party/blink/public/platform/web_media_player.h
@@ -214,6 +214,10 @@
   virtual uint64_t AudioDecodedByteCount() const = 0;
   virtual uint64_t VideoDecodedByteCount() const = 0;
 
+  // Returns true if the player has a frame available for presentation. Usually
+  // this just means the first frame has been delivered.
+  virtual bool HasAvailableVideoFrame() const = 0;
+
   // |already_uploaded_id| indicates the unique_id of the frame last uploaded
   //   to this destination. It should only be set by the caller if the contents
   //   of the destination are known not to have changed since that upload.
diff --git a/third_party/blink/public/platform/web_rtc_peer_connection_handler.h b/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
index 0d042ea..ae882dc 100644
--- a/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
+++ b/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
@@ -87,10 +87,10 @@
   // create new transceivers or update the direction of existing transceivers.
   // https://w3c.github.io/webrtc-pc/#legacy-configuration-extensions
   // Plan B: Returns an empty list.
-  virtual std::vector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
+  virtual WebVector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
       const WebRTCSessionDescriptionRequest&,
       const WebMediaConstraints&) = 0;
-  virtual std::vector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
+  virtual WebVector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
       const WebRTCSessionDescriptionRequest&,
       const WebRTCOfferOptions&) = 0;
   virtual void CreateAnswer(const WebRTCSessionDescriptionRequest&,
@@ -126,7 +126,7 @@
   // third_party/webrtc/api/stats/.  These will replace the old stats collection
   // API when the new API has matured enough.
   virtual void GetStats(WebRTCStatsReportCallback,
-                        const std::vector<webrtc::NonStandardGroupId>&) = 0;
+                        const WebVector<webrtc::NonStandardGroupId>&) = 0;
   virtual scoped_refptr<webrtc::DataChannelInterface> CreateDataChannel(
       const WebString& label,
       const WebRTCDataChannelInit&) = 0;
diff --git a/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h b/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
index 65bbb1e..a789ec3 100644
--- a/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
+++ b/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
@@ -32,10 +32,10 @@
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_
 
 #include <memory>
-#include <vector>
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
 #include "third_party/webrtc/api/sctp_transport_interface.h"
 
@@ -70,7 +70,7 @@
   virtual void DidAddReceiverPlanB(std::unique_ptr<WebRTCRtpReceiver>) = 0;
   virtual void DidRemoveReceiverPlanB(std::unique_ptr<WebRTCRtpReceiver>) = 0;
   virtual void DidModifyTransceivers(
-      std::vector<std::unique_ptr<WebRTCRtpTransceiver>>,
+      WebVector<std::unique_ptr<WebRTCRtpTransceiver>>,
       bool is_remote_description) = 0;
   virtual void DidModifySctpTransport(WebRTCSctpTransportSnapshot) = 0;
   virtual void DidAddRemoteDataChannel(
diff --git a/third_party/blink/public/platform/web_rtc_rtp_receiver.h b/third_party/blink/public/platform/web_rtc_rtp_receiver.h
index b9deeb2..c4913a1 100644
--- a/third_party/blink/public/platform/web_rtc_rtp_receiver.h
+++ b/third_party/blink/public/platform/web_rtc_rtp_receiver.h
@@ -43,7 +43,7 @@
   virtual WebVector<WebString> StreamIds() const = 0;
   virtual WebVector<std::unique_ptr<WebRTCRtpSource>> GetSources() = 0;
   virtual void GetStats(blink::WebRTCStatsReportCallback,
-                        const std::vector<webrtc::NonStandardGroupId>&) = 0;
+                        const WebVector<webrtc::NonStandardGroupId>&) = 0;
   virtual std::unique_ptr<webrtc::RtpParameters> GetParameters() const = 0;
   virtual void SetJitterBufferMinimumDelay(
       base::Optional<double> delay_seconds) = 0;
diff --git a/third_party/blink/public/platform/web_rtc_rtp_sender.h b/third_party/blink/public/platform/web_rtc_rtp_sender.h
index 072bd0c..379ed9b1 100644
--- a/third_party/blink/public/platform/web_rtc_rtp_sender.h
+++ b/third_party/blink/public/platform/web_rtc_rtp_sender.h
@@ -51,7 +51,7 @@
                              webrtc::DegradationPreference,
                              WebRTCVoidRequest) = 0;
   virtual void GetStats(blink::WebRTCStatsReportCallback,
-                        const std::vector<webrtc::NonStandardGroupId>&) = 0;
+                        const WebVector<webrtc::NonStandardGroupId>&) = 0;
   virtual void SetStreams(const WebVector<WebString>& stream_ids) = 0;
 };
 
diff --git a/third_party/blink/public/platform/web_url_loader_client.h b/third_party/blink/public/platform/web_url_loader_client.h
index 09612a7..facfbd5 100644
--- a/third_party/blink/public/platform/web_url_loader_client.h
+++ b/third_party/blink/public/platform/web_url_loader_client.h
@@ -39,6 +39,7 @@
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/platform/web_vector.h"
 
 namespace blink {
 
@@ -108,7 +109,7 @@
       int64_t total_encoded_body_length,
       int64_t total_decoded_body_length,
       bool should_report_corb_blocking,
-      const std::vector<network::cors::PreflightTimingInfo>&) {}
+      const WebVector<network::cors::PreflightTimingInfo>&) {}
 
   // Called when the load completes with an error.
   // |total_encoded_data_length| may be equal to kUnknownEncodedDataLength.
diff --git a/third_party/blink/renderer/bindings/IDLExtendedAttributes.md b/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
index 72838b3..35024b81 100644
--- a/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
+++ b/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
@@ -47,7 +47,7 @@
 
 ### Constants
 
-Only the following (Blink-only) extended attributes apply to constants: `[DeprecateAs]`, `[MeasureAs]`, `[OriginTrialEnabled]`, `[Reflect]`, and `[RuntimeEnabled]`, and the interface extended attribute `[DoNotCheckConstants]` affects constants.
+Only the following (Blink-only) extended attributes apply to constants: `[DeprecateAs]`, `[MeasureAs]`, `[Reflect]`, and `[RuntimeEnabled]`, and the interface extended attribute `[DoNotCheckConstants]` affects constants.
 
 ### Overloaded methods
 
@@ -100,9 +100,9 @@
 
 Extended attributes on partial interface members work as normal. However, only the following 4 extended attributes can be used on the partial interface itself; otherwise extended attributes should appear on the main interface definition:
 
-`[Conditional]`, `[ImplementedAs]`, `[OriginTrialEnabled]` and `[RuntimeEnabled]`
+`[Conditional]`, `[ImplementedAs]` and `[RuntimeEnabled]`
 
-3 of these are used to allow the entire partial interface to be selectively enabled or disabled: `[Conditional]`, `[OriginTrialEnabled]` and `[RuntimeEnabled]`, and function as if the extended attribute were applied to each _member_ (methods, attributes, and constants). Style-wise, if the entire partial interface should be enabled or disabled, these extended attributes should be used on the partial interface, not on each individual member; this clarifies intent and simplifies editing. However:
+2 of these are used to allow the entire partial interface to be selectively enabled or disabled: `[Conditional]` and `[RuntimeEnabled]`, and function as if the extended attribute were applied to each _member_ (methods, attributes, and constants). Style-wise, if the entire partial interface should be enabled or disabled, these extended attributes should be used on the partial interface, not on each individual member; this clarifies intent and simplifies editing. However:
 
 * If some members should not be disabled, this cannot be used on the partial interface; this is often the case for constants.
 * If different members should be controlled by different flags, this must be specified individually.
@@ -120,8 +120,6 @@
 
 * `[ImplementedAs]` is only necessary for these legacy files: otherwise the class (C++) implementing (IDL) interface mixins does not need to be specified, as this is handled in Blink C++.
 
-* `[OriginTrialEnabled]` behaves as for partial interfaces.
-
 * `[RuntimeEnabled]` behaves as for partial interfaces.
 
 ### Inheritance
@@ -1002,24 +1000,6 @@
 
 `[NotEnumerable]` indicates that the method or attribute is not enumerable.
 
-### [OriginTrialEnabled] _(i, m, a, c)_
-
-Summary: Like `[RuntimeEnabled]`, it controls at runtime whether bindings are exposed, but uses a different mechanism for enabling experimental features.
-
-Usage: `[OriginTrialEnabled=FeatureName]`. FeatureName must be included in [runtime\_enabled\_features.json5](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/blink/renderer/platform/runtime_enabled_features.json5), and is the same value that would be used with `[RuntimeEnabled]`.
-
-```webidl
-[
-    OriginTrialEnabled=MediaSession
-] interface MediaSession { ... };
-```
-
-When there is an active origin trial for the current execution context, the feature is enabled at runtime, and the binding would be exposed to the web. `[OriginTrialEnabled]` also includes a check for the associated runtime flag, so features can be enabled in that fashion, even without an origin trial.
-
-`[OriginTrialEnabled]` has similar semantics to `[RuntimeEnabled]`, and is intended as a drop-in replacement. For example, `[OriginTrialEnabled]` _cannot_ be applied to arguments, see `[RuntimeEnabled]` for reasoning. The key implementation difference is that `[OriginTrialEnabled]` wraps the generated code with `if (origin_trials::FeatureNameEnabled(...)) { ...code... }`.
-
-For more information, see [RuntimeEnabledFeatures](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/blink/renderer/platform/runtime_enabled_features.json5) and [OriginTrialContext](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/blink/renderer/core/origin_trials/origin_trial_context.h).
-
 ### [RaisesException] _(i, m, a)_
 
 Summary: Tells the code generator to append an `ExceptionState&` argument when calling the Blink implementation.
diff --git a/third_party/blink/renderer/bindings/IDLExtendedAttributes.txt b/third_party/blink/renderer/bindings/IDLExtendedAttributes.txt
index 4451ae09..20ddc71 100644
--- a/third_party/blink/renderer/bindings/IDLExtendedAttributes.txt
+++ b/third_party/blink/renderer/bindings/IDLExtendedAttributes.txt
@@ -74,7 +74,6 @@
 NamedConstructor=*
 NoInterfaceObject
 NotEnumerable
-OriginTrialEnabled=*
 OverrideBuiltins
 PartialInterfaceImplementedAs=*
 PermissiveDictionaryConversion
diff --git a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.h b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.h
index 6d001b6..84ed3667 100644
--- a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.h
+++ b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_REFERRER_SCRIPT_INFO_H_
 
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
index af3a05cd..751033f 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
@@ -25,7 +25,7 @@
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.h"
 
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-shared.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer.h"
diff --git a/third_party/blink/renderer/bindings/scripts/scripts.gni b/third_party/blink/renderer/bindings/scripts/scripts.gni
index 3eb7eba1..2006df91 100644
--- a/third_party/blink/renderer/bindings/scripts/scripts.gni
+++ b/third_party/blink/renderer/bindings/scripts/scripts.gni
@@ -61,13 +61,16 @@
                                   "web_idl/argument.py",
                                   "web_idl/attribute.py",
                                   "web_idl/callback_function.py",
+                                  "web_idl/common.py",
                                   "web_idl/constant.py",
                                   "web_idl/dictionary.py",
                                   "web_idl/ecma_script_types.py",
                                   "web_idl/enumeration.py",
                                   "web_idl/extended_attribute.py",
+                                  "web_idl/identifier_ir_map.py",
+                                  "web_idl/idl_reference_proxy.py",
                                   "web_idl/idl_types.py",
-                                  "web_idl/implements.py",
+                                  "web_idl/includes.py",
                                   "web_idl/interface.py",
                                   "web_idl/literal_token.py",
                                   "web_idl/namespace.py",
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/callback_function.py b/third_party/blink/renderer/bindings/scripts/web_idl/callback_function.py
index 78001a2..2e3a741 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/callback_function.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/callback_function.py
@@ -3,12 +3,34 @@
 # found in the LICENSE file.
 
 import exceptions
+from .common import WithCodeGeneratorInfo
+from .common import WithComponent
+from .common import WithDebugInfo
+from .common import WithExtendedAttributes
+from .identifier_ir_map import IdentifierIRMap
 from .idl_definition import IdlDefinition
 
 
 class CallbackFunction(IdlDefinition):
     """https://heycam.github.io/webidl/#idl-callback-functions"""
 
+    class IR(IdentifierIRMap.IR, WithExtendedAttributes, WithCodeGeneratorInfo,
+             WithComponent, WithDebugInfo):
+        def __init__(self,
+                     identifier,
+                     extended_attributes=None,
+                     code_generator_info=None,
+                     component=None,
+                     debug_info=None):
+            IdentifierIRMap.IR.__init__(
+                self,
+                identifier=identifier,
+                kind=IdentifierIRMap.IR.Kind.CALLBACK_FUNCTION)
+            WithExtendedAttributes.__init__(self, extended_attributes)
+            WithCodeGeneratorInfo.__init__(self, code_generator_info)
+            WithComponent.__init__(self, component)
+            WithDebugInfo.__init__(self, debug_info)
+
     @property
     def return_type(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/callback_interface.py b/third_party/blink/renderer/bindings/scripts/web_idl/callback_interface.py
index 95b5114..82bb35e8 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/callback_interface.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/callback_interface.py
@@ -3,12 +3,34 @@
 # found in the LICENSE file.
 
 import exceptions
+from .common import WithCodeGeneratorInfo
+from .common import WithComponent
+from .common import WithDebugInfo
+from .common import WithExtendedAttributes
+from .identifier_ir_map import IdentifierIRMap
 from .idl_definition import IdlDefinition
 
 
 class CallbackInterface(IdlDefinition):
     """https://heycam.github.io/webidl/#idl-interfaces"""
 
+    class IR(IdentifierIRMap.IR, WithExtendedAttributes, WithCodeGeneratorInfo,
+             WithComponent, WithDebugInfo):
+        def __init__(self,
+                     identifier,
+                     extended_attributes=None,
+                     code_generator_info=None,
+                     component=None,
+                     debug_info=None):
+            IdentifierIRMap.IR.__init__(
+                self,
+                identifier=identifier,
+                kind=IdentifierIRMap.IR.Kind.CALLBACK_INTERFACE)
+            WithExtendedAttributes.__init__(self, extended_attributes)
+            WithCodeGeneratorInfo.__init__(self, code_generator_info)
+            WithComponent.__init__(self, component)
+            WithDebugInfo.__init__(self, debug_info)
+
     @property
     def inherited_callback_interface(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/dictionary.py b/third_party/blink/renderer/bindings/scripts/web_idl/dictionary.py
index f70a0dc..2f46e87d4 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/dictionary.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/dictionary.py
@@ -3,6 +3,11 @@
 # found in the LICENSE file.
 
 import exceptions
+from .common import WithCodeGeneratorInfo
+from .common import WithComponent
+from .common import WithDebugInfo
+from .common import WithExtendedAttributes
+from .identifier_ir_map import IdentifierIRMap
 from .idl_definition import IdlDefinition
 from .idl_member import IdlMember
 
@@ -10,6 +15,23 @@
 class Dictionary(IdlDefinition):
     """https://heycam.github.io/webidl/#idl-dictionaries"""
 
+    class IR(IdentifierIRMap.IR, WithExtendedAttributes, WithCodeGeneratorInfo,
+             WithComponent, WithDebugInfo):
+        def __init__(self,
+                     identifier,
+                     is_partial,
+                     extended_attributes=None,
+                     code_generator_info=None,
+                     component=None,
+                     debug_info=None):
+            kind = (IdentifierIRMap.IR.Kind.PARTIAL_DICTIONARY
+                    if is_partial else IdentifierIRMap.IR.Kind.DICTIONARY)
+            IdentifierIRMap.IR.__init__(self, identifier=identifier, kind=kind)
+            WithExtendedAttributes.__init__(self, extended_attributes)
+            WithCodeGeneratorInfo.__init__(self, code_generator_info)
+            WithComponent.__init__(self, component)
+            WithDebugInfo.__init__(self, debug_info)
+
     @property
     def inherited_dictionary(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/enumeration.py b/third_party/blink/renderer/bindings/scripts/web_idl/enumeration.py
index 07fe0b1..88cbd87 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/enumeration.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/enumeration.py
@@ -3,12 +3,34 @@
 # found in the LICENSE file.
 
 import exceptions
+from .common import WithCodeGeneratorInfo
+from .common import WithComponent
+from .common import WithDebugInfo
+from .common import WithExtendedAttributes
+from .identifier_ir_map import IdentifierIRMap
 from .idl_definition import IdlDefinition
 
 
 class Enumeration(IdlDefinition):
     """https://heycam.github.io/webidl/#idl-enums"""
 
+    class IR(IdentifierIRMap.IR, WithExtendedAttributes, WithCodeGeneratorInfo,
+             WithComponent, WithDebugInfo):
+        def __init__(self,
+                     identifier,
+                     extended_attributes=None,
+                     code_generator_info=None,
+                     component=None,
+                     debug_info=None):
+            IdentifierIRMap.IR.__init__(
+                self,
+                identifier=identifier,
+                kind=IdentifierIRMap.IR.Kind.ENUMERATION)
+            WithExtendedAttributes.__init__(self, extended_attributes)
+            WithCodeGeneratorInfo.__init__(self, code_generator_info)
+            WithComponent.__init__(self, component)
+            WithDebugInfo.__init__(self, debug_info)
+
     @property
     def values(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/identifier_ir_map.py b/third_party/blink/renderer/bindings/scripts/web_idl/identifier_ir_map.py
new file mode 100644
index 0000000..03a20db9
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/identifier_ir_map.py
@@ -0,0 +1,189 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from .common import WithIdentifier
+
+
+class IdentifierIRMap(object):
+    """
+    Manages an identifier-IR map, where IR is IdIRMap.IR.  This class is
+    designed to work together with IdlCompiler closely.  See also
+    IdlCompiler, especially how IdlCompiler uses compilation phases.
+
+    This class manages IDL definitions' IRs in following style.
+
+        [ # phase 0
+          {
+            kind : { identifier : definition(s) },
+            ...
+          },
+          # phase 1
+          ...
+        ]
+
+    The outermost list represents compilation phases.  Each phase stores the
+    IRs that are processed in that compilation phase.  IRs at the initial
+    phase (= phase 0) is a direct translation from an AST.
+    The innermost dict maps an identifier to an IR at a certain phase(*1).
+
+    A certain phase is responsible to process certain group(s) of IR.Kind,
+    so not all phases have all the kinds, there may be missing kinds in a
+    certain phase.
+
+    (*1) In case of partial definitions and includes, the dict maps
+         an identifier to a list of IRs, as there may be multiple
+         definitions with the same identifier.
+    """
+
+    class IR(WithIdentifier):
+        """
+        Represents an intermediate representation of IDL definitions used in
+        IdlCompiler.
+
+        IR is used only by IdlCompiler. See also IdlCompiler and its compilation
+        strategy.  IR supports identifier-IR maps grouped by Kind (see below).
+        """
+
+        class Kind(object):
+            """
+            Enumerates supported kinds of IDL definitions in IR, such as
+            "interface", "typedef", or "partial dictionary".
+            """
+
+            CALLBACK_FUNCTION = 'callback function'
+            CALLBACK_INTERFACE = 'callback interface'
+            DICTIONARY = 'dictionary'
+            ENUMERATION = 'enumeration'
+            INCLUDES = 'includes'
+            INTERFACE = 'interface'
+            INTERFACE_MIXIN = 'interface mixin'
+            NAMESPACE = 'namespace'
+            PARTIAL_DICTIONARY = 'partial dictionary'
+            PARTIAL_INTERFACE = 'partial interface'
+            PARTIAL_INTERFACE_MIXIN = 'partial interface mixin'
+            PARTIAL_NAMESPACE = 'partial namespace'
+            TYPEDEF = 'typedef'
+
+            _MULTI_VALUE_KINDS = (
+                INCLUDES,
+                PARTIAL_DICTIONARY,
+                PARTIAL_INTERFACE,
+                PARTIAL_INTERFACE_MIXIN,
+                PARTIAL_NAMESPACE,
+            )
+
+            @classmethod
+            def does_support_multiple_defs(cls, kind):
+                return kind in cls._MULTI_VALUE_KINDS
+
+        def __init__(self, identifier, kind):
+            WithIdentifier.__init__(self, identifier)
+            self._kind = kind
+
+        @property
+        def kind(self):
+            return self._kind
+
+        @property
+        def does_support_multiple_defs(self):
+            """
+            Returns True if multiple IRs may have a same identifier.
+
+            For most kinds, an identifier points to a single IR.  However, it's
+            reasonable and convenient to allow multiple IRs to have the same
+            identifier for some kinds, e.g. partial interface and includes.
+            This function returns True for such kinds.
+            """
+            return IdentifierIRMap.IR.Kind.does_support_multiple_defs(
+                self.kind)
+
+    def __init__(self):
+        # IRs whose does_support_multiple_defs is False
+        self._single_value_irs = [dict()]
+        # IRs whose does_support_multiple_defs is True
+        self._multiple_value_irs = [dict()]
+
+        self._current_phase = 0
+
+    def move_to_new_phase(self):
+        assert len(self._single_value_irs) == self._current_phase + 1
+        assert len(self._multiple_value_irs) == self._current_phase + 1
+
+        self._current_phase += 1
+        self._single_value_irs.append(dict())
+        self._multiple_value_irs.append(dict())
+
+    def register(self, ir):
+        """
+        Registers the given IR to this map.
+
+        Duplicated registration is not allowed.  The registration must be for
+        the first time.
+        """
+        assert isinstance(ir, IdentifierIRMap.IR), ir
+        # Assert |ir| doesn't yet exist in this map.
+        try:
+            if ir.does_support_multiple_defs:
+                irs = self.find_by_kind(ir.kind)
+                assert ir not in irs[ir.identifier]
+            else:
+                self.find_by_identifier(ir.identifier)
+                assert False
+        except KeyError:
+            pass
+        self.add(ir)
+
+    def add(self, ir):
+        assert isinstance(ir, IdentifierIRMap.IR)
+
+        ir_map = (self._multiple_value_irs
+                  if ir.does_support_multiple_defs else self._single_value_irs)
+        current_irs = ir_map[self._current_phase]
+        kind = ir.kind
+        if kind not in current_irs:
+            current_irs[kind] = dict()
+        irs_per_kind = current_irs[kind]
+
+        identifier = ir.identifier
+        if ir.does_support_multiple_defs:
+            if identifier not in irs_per_kind:
+                irs_per_kind[identifier] = []
+            irs_per_kind[identifier].append(ir)
+        else:
+            assert identifier not in irs_per_kind, (
+                'Duplicated definition: {}\n  {}\n  {}'.format(
+                    identifier, ir.debug_info.filepaths,
+                    irs_per_kind[identifier].debug_info.filepaths))
+            irs_per_kind[identifier] = ir
+
+    def find_by_identifier(self, identifier, skip_current_phase=False):
+        """
+        Returns the latest IR whose identifier is |identifier| and
+        does_support_multiple_defs is False.  Raises KeyError, if not found.
+
+        If |skip_current_phase| is True, skips the current phase when looking
+        for the identifier.
+        """
+        start_phase = self._current_phase - (1 if skip_current_phase else 0)
+        for irs_per_phase in self._single_value_irs[start_phase::-1]:
+            for irs_per_kind in irs_per_phase.values():
+                if identifier in irs_per_kind:
+                    return irs_per_kind[identifier]
+        raise KeyError
+
+    def find_by_kind(self, kind, skip_current_phase=False):
+        """
+        Returns a map of identifier to IR(s) for the latest IRs of |kind|.
+
+        If |skip_current_phase| is True, skips the current phase when looking
+        for the kind.
+        """
+        start_phase = self._current_phase - (1 if skip_current_phase else 0)
+        ir_map = (self._multiple_value_irs
+                  if IdentifierIRMap.IR.Kind.does_support_multiple_defs(kind)
+                  else self._single_value_irs)
+        for irs_per_phase in ir_map[start_phase::-1]:
+            if kind in irs_per_phase:
+                return irs_per_phase[kind]
+        return dict()
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_reference_proxy.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_reference_proxy.py
new file mode 100644
index 0000000..387ad5d
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_reference_proxy.py
@@ -0,0 +1,79 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from .common import WithIdentifier
+
+
+class Proxy(object):
+    """
+    Proxies attributes of the target object.  The target object can be set
+    after the construction, but its attributes to proxy must be known in the
+    construction.
+    """
+
+    def __init__(self, target_object=None, target_attributes=None):
+        self._target_object = target_object
+        self._target_attributes = target_attributes
+
+    def __getattr__(self, attribute):
+        assert self._target_object
+        assert self._target_attributes
+        if attribute in self._target_attributes:
+            return getattr(self._target_object, attribute)
+        raise AttributeError
+
+    def set_target_object(self, target_object):
+        assert self._target_object is None
+        self._target_object = target_object
+
+    def set_target_attributes(self, target_attributes):
+        assert self._target_attrributes is None
+        self._target_attributes = target_attributes
+
+    @property
+    def target_object(self):
+        assert self._target_object
+        return self._target_object
+
+
+class IdlReferenceFactory(object):
+    """
+    Creates reference proxies to refer an IDL definition.
+
+    Because we cannot have direct references to IDL definitions until we
+    actually make the definitions, but we need the references to point to
+    definitions when compiling IDLs.
+    """
+
+    class _IdlReference(Proxy, WithIdentifier):
+        """
+        Proxies attributes of target object, that can be specified by an
+        identifier.
+        """
+
+        def __init__(self, identifier):
+            Proxy.__init__(self)
+            WithIdentifier.__init__(self, identifier)
+
+    def __init__(self):
+        self._idl_references = set()
+        self._resolved = False
+
+    def create(self, identifier):
+        assert not self._resolved
+        ref = IdlReferenceFactory._IdlReference(identifier)
+        self._idl_references.add(ref)
+        return ref
+
+    def resolve(self, resolver):
+        """
+        Updates stored references' targets.
+
+        |resolver| is a callback function that takes a Proxy
+        instance and updates its target object.
+        """
+        assert not self._resolved
+        self._resolved = True
+        for ref in self._idl_references:
+            resolver(ref)
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/includes.py b/third_party/blink/renderer/bindings/scripts/web_idl/includes.py
index ab433fa..09ed9384 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/includes.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/includes.py
@@ -3,13 +3,36 @@
 # found in the LICENSE file.
 
 import exceptions
+from .common import WithCodeGeneratorInfo
 from .common import WithComponent
 from .common import WithDebugInfo
+from .identifier_ir_map import IdentifierIRMap
 
 
 class Includes(WithComponent, WithDebugInfo):
     """https://heycam.github.io/webidl/#include"""
 
+    class IR(IdentifierIRMap.IR, WithCodeGeneratorInfo, WithComponent,
+             WithDebugInfo):
+        def __init__(self,
+                     interface_identifier,
+                     code_generator_info=None,
+                     component=None,
+                     debug_info=None):
+            # Includes statements are treated similarly to partial
+            # definitions, and it's convenient for IdlCompiler that
+            # 'includes' are grouped by interface's identifier, i.e.
+            # a group of mixins are merged into the interface.
+            # So, we take the interface's identifier as this IR's
+            # identifier.
+            IdentifierIRMap.IR.__init__(
+                self,
+                identifier=interface_identifier,
+                kind=IdentifierIRMap.IR.Kind.INCLUDES)
+            WithCodeGeneratorInfo.__init__(self, code_generator_info)
+            WithComponent.__init__(self, component)
+            WithDebugInfo.__init__(self, debug_info)
+
     @property
     def interface(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/interface.py b/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
index 90e031f..7e17eef 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
@@ -3,6 +3,12 @@
 # found in the LICENSE file.
 
 import exceptions
+from .common import WithCodeGeneratorInfo
+from .common import WithComponent
+from .common import WithDebugInfo
+from .common import WithExposure
+from .common import WithExtendedAttributes
+from .identifier_ir_map import IdentifierIRMap
 from .idl_definition import IdlDefinition
 from .idl_member import IdlMember
 
@@ -16,6 +22,35 @@
     https://heycam.github.io/webidl/#idl-interfaces
     """
 
+    class IR(IdentifierIRMap.IR, WithExtendedAttributes, WithExposure,
+             WithCodeGeneratorInfo, WithComponent, WithDebugInfo):
+        def __init__(self,
+                     identifier,
+                     is_partial,
+                     is_mixin,
+                     extended_attributes=None,
+                     exposures=None,
+                     code_generator_info=None,
+                     component=None,
+                     debug_info=None):
+            kind = None
+            if is_partial:
+                if is_mixin:
+                    kind = IdentifierIRMap.IR.Kind.PARTIAL_INTERFACE_MIXIN
+                else:
+                    kind = IdentifierIRMap.IR.Kind.PARTIAL_INTERFACE
+            else:
+                if is_mixin:
+                    kind = IdentifierIRMap.IR.Kind.INTERFACE_MIXIN
+                else:
+                    kind = IdentifierIRMap.IR.Kind.INTERFACE
+            IdentifierIRMap.IR.__init__(self, identifier=identifier, kind=kind)
+            WithExtendedAttributes.__init__(self, extended_attributes)
+            WithExposure.__init__(self, exposures)
+            WithCodeGeneratorInfo.__init__(self, code_generator_info)
+            WithComponent.__init__(self, component)
+            WithDebugInfo.__init__(self, debug_info)
+
     @property
     def inherited_interface(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/namespace.py b/third_party/blink/renderer/bindings/scripts/web_idl/namespace.py
index d339663..b8b14aea 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/namespace.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/namespace.py
@@ -3,12 +3,37 @@
 # found in the LICENSE file.
 
 import exceptions
+from .common import WithCodeGeneratorInfo
+from .common import WithComponent
+from .common import WithDebugInfo
+from .common import WithExposure
+from .common import WithExtendedAttributes
+from .identifier_ir_map import IdentifierIRMap
 from .idl_definition import IdlDefinition
 
 
 class Namespace(IdlDefinition):
     """https://heycam.github.io/webidl/#idl-namespaces"""
 
+    class IR(IdentifierIRMap.IR, WithExtendedAttributes, WithExposure,
+             WithCodeGeneratorInfo, WithComponent, WithDebugInfo):
+        def __init__(self,
+                     identifier,
+                     is_partial,
+                     extended_attributes=None,
+                     exposures=None,
+                     code_generator_info=None,
+                     component=None,
+                     debug_info=None):
+            kind = (IdentifierIRMap.IR.Kind.PARTIAL_NAMESPACE
+                    if is_partial else IdentifierIRMap.IR.Kind.NAMESPACE)
+            IdentifierIRMap.IR.__init__(self, identifier=identifier, kind=kind)
+            WithExtendedAttributes.__init__(self, extended_attributes)
+            WithExposure.__init__(self, exposures)
+            WithCodeGeneratorInfo.__init__(self, code_generator_info)
+            WithComponent.__init__(self, component)
+            WithDebugInfo.__init__(self, debug_info)
+
     @property
     def attributes(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/typedef.py b/third_party/blink/renderer/bindings/scripts/web_idl/typedef.py
index 912d153..cd0b7c3 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/typedef.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/typedef.py
@@ -7,12 +7,28 @@
 from .common import WithComponent
 from .common import WithDebugInfo
 from .common import WithIdentifier
+from .identifier_ir_map import IdentifierIRMap
 
 
 class Typedef(WithIdentifier, WithCodeGeneratorInfo, WithComponent,
               WithDebugInfo):
     """https://heycam.github.io/webidl/#idl-typedefs"""
 
+    class IR(IdentifierIRMap.IR, WithCodeGeneratorInfo, WithComponent,
+             WithDebugInfo):
+        def __init__(self,
+                     identifier,
+                     code_generator_info=None,
+                     component=None,
+                     debug_info=None):
+            IdentifierIRMap.IR.__init__(
+                self,
+                identifier=identifier,
+                kind=IdentifierIRMap.IR.Kind.TYPEDEF)
+            WithCodeGeneratorInfo.__init__(self, code_generator_info)
+            WithComponent.__init__(self, component)
+            WithDebugInfo.__init__(self, debug_info)
+
     @property
     def idl_type(self):
         """
diff --git a/third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl b/third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl
index a0bf43f..180c7eef 100644
--- a/third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl
@@ -132,29 +132,6 @@
 }
 {% endif %}
 
-{% for method in methods %}
-v8::Maybe<{{method.cpp_type}}> V8PersistentCallbackInterface<{{v8_class}}>::{{method.name}}({{method.argument_declarations | join(', ')}}) {
-  return Proxy()->{{method.name}}(
-      {{
-         (['callback_this_value'] +
-          (method.arguments|map(attribute='name')|list)
-         )|join(', ')
-      }});
-}
-
-{% endfor %}
-
-{% if methods|length == 1 and methods[0].idl_type == 'void' %}
-void V8PersistentCallbackInterface<{{v8_class}}>::InvokeAndReportException({{methods[0].argument_declarations | join(', ')}}) {
-  Proxy()->InvokeAndReportException(
-      {{
-         (['callback_this_value'] +
-          (methods[0].arguments|map(attribute='name')|list)
-         )|join(', ')
-      }});
-}
-{% endif %}
-
 }  // namespace blink
 
 {% endfilter %}{# format_blink_cpp_source_code #}
diff --git a/third_party/blink/renderer/bindings/templates/callback_interface.h.tmpl b/third_party/blink/renderer/bindings/templates/callback_interface.h.tmpl
index 0b4c445..dd35f4922 100644
--- a/third_party/blink/renderer/bindings/templates/callback_interface.h.tmpl
+++ b/third_party/blink/renderer/bindings/templates/callback_interface.h.tmpl
@@ -77,32 +77,6 @@
 {% endif %}
 };
 
-template <>
-class V8PersistentCallbackInterface<{{v8_class}}> final : public V8PersistentCallbackInterfaceBase {
-  using V8CallbackInterface = {{v8_class}};
-
- public:
-  explicit V8PersistentCallbackInterface(V8CallbackInterface* callback_interface)
-      : V8PersistentCallbackInterfaceBase(callback_interface) {}
-  ~V8PersistentCallbackInterface() override = default;
-
-{% for method in methods %}
-  {{exported}}v8::Maybe<{{method.cpp_type}}> {{method.name}}({{method.argument_declarations | join(', ')}}) WARN_UNUSED_RESULT;
-{% endfor %}
-{% if methods|length == 1 and methods[0].idl_type == 'void' %}
-  {{exported}}void InvokeAndReportException({{methods[0].argument_declarations | join(', ')}});
-{% endif %}
-
- private:
-  V8CallbackInterface* Proxy() {
-    return As<V8CallbackInterface>();
-  }
-
-  template <typename V8CallbackInterface>
-  friend V8PersistentCallbackInterface<V8CallbackInterface>*
-  ToV8PersistentCallbackInterface(V8CallbackInterface*);
-};
-
 }  // namespace blink
 
 #endif  // {{header_guard}}
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_constants.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_constants.idl
index d19752f..9c3014c 100644
--- a/third_party/blink/renderer/bindings/tests/idls/core/test_constants.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_constants.idl
@@ -31,10 +31,10 @@
     [RuntimeEnabled=RuntimeFeature1] const short FEATURE1_ENABLED_CONST2 = 2;
     [RuntimeEnabled=RuntimeFeature2] const short FEATURE2_ENABLED_CONST1 = 3;
     [RuntimeEnabled=RuntimeFeature2] const short FEATURE2_ENABLED_CONST2 = 4;
-    [OriginTrialEnabled=FeatureName1] const short FEATURE1_ORIGIN_TRIAL_ENABLED_CONST1 = 6;
-    [OriginTrialEnabled=FeatureName1] const short FEATURE1_ORIGIN_TRIAL_ENABLED_CONST2 = 7;
-    [OriginTrialEnabled=FeatureName2] const short FEATURE2_ORIGIN_TRIAL_ENABLED_CONST1 = 8;
-    [OriginTrialEnabled=FeatureName2] const short FEATURE2_ORIGIN_TRIAL_ENABLED_CONST2 = 9;
+    [RuntimeEnabled=FeatureName1] const short FEATURE1_ORIGIN_TRIAL_ENABLED_CONST1 = 6;
+    [RuntimeEnabled=FeatureName1] const short FEATURE1_ORIGIN_TRIAL_ENABLED_CONST2 = 7;
+    [RuntimeEnabled=FeatureName2] const short FEATURE2_ORIGIN_TRIAL_ENABLED_CONST1 = 8;
+    [RuntimeEnabled=FeatureName2] const short FEATURE2_ORIGIN_TRIAL_ENABLED_CONST2 = 9;
     [RuntimeEnabled=OriginTrialFeature1] const short FEATURE3_ORIGIN_TRIAL_ENABLED_CONST1 = 10;
     [RuntimeEnabled=OriginTrialFeature1] const short FEATURE3_ORIGIN_TRIAL_ENABLED_CONST2 = 11;
     [RuntimeEnabled=OriginTrialFeature2] const short FEATURE4_ORIGIN_TRIAL_ENABLED_CONST1 = 12;
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_dictionary.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_dictionary.idl
index d597473f..ab862c4b 100644
--- a/third_party/blink/renderer/bindings/tests/idls/core/test_dictionary.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_dictionary.idl
@@ -39,8 +39,8 @@
     Dictionary dictionaryMember;
     [RuntimeEnabled=RuntimeFeature] boolean runtimeMember;
     [RuntimeEnabled=RuntimeFeature] boolean runtimeSecondMember;
-    [OriginTrialEnabled=FeatureName] boolean originTrialMember;
-    [OriginTrialEnabled=FeatureName1] boolean originTrialSecondMember;
+    [RuntimeEnabled=FeatureName] boolean originTrialMember;
+    [RuntimeEnabled=FeatureName1] boolean originTrialSecondMember;
     [RuntimeEnabled=OriginTrialFeature] boolean originTrialThirdMember;
     [RuntimeEnabled=OriginTrialFeature1] boolean originTrialFourthMember;
     record<ByteString, byte> recordMember;
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl
index 9e30358..91081ea 100644
--- a/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl
@@ -90,7 +90,7 @@
     void alwaysExposedMethod();
     [Exposed=Worker] void workerExposedMethod();
     [Exposed=Window] void windowExposedMethod();
-    [Exposed=Window,OriginTrialEnabled=TestFeature] void originTrialWindowExposedMethod();
+    [Exposed=Window,RuntimeEnabled=TestFeature] void originTrialWindowExposedMethod();
     [Exposed=Window,RuntimeEnabled=OriginTrialFeature] void originTrialWindowExposedMethod2();
 
     static void alwaysExposedStaticMethod();
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_interface_origin_trial_enabled.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_interface_origin_trial_enabled.idl
index 6b1f656..cb5f4b6 100644
--- a/third_party/blink/renderer/bindings/tests/idls/core/test_interface_origin_trial_enabled.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_interface_origin_trial_enabled.idl
@@ -7,7 +7,7 @@
 // [OriginTrialEnabled] attribute takes precedence over [RuntimeEnabled].
 
 [
-    OriginTrialEnabled=InterfaceFeatureName
+    RuntimeEnabled=InterfaceFeatureName
 ] interface TestInterfaceOriginTrialEnabled {
     const unsigned long UNSIGNED_LONG = 0;
     const short CONST_JAVASCRIPT = 1;
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_object.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_object.idl
index cffc076..a7cd992 100644
--- a/third_party/blink/renderer/bindings/tests/idls/core/test_object.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_object.idl
@@ -156,7 +156,7 @@
     [Custom=Setter, ImplementedAs=implementedAsNameWithCustomGetter] attribute long customSetterImplementedAsLongAttribute;
     [MeasureAs=TestFeature] attribute long measureAsLongAttribute;
     [NotEnumerable] attribute long notEnumerableLongAttribute;
-    [OriginTrialEnabled=FeatureName] attribute long originTrialEnabledLongAttribute;
+    [RuntimeEnabled=FeatureName] attribute long originTrialEnabledLongAttribute;
     [RuntimeEnabled=OriginTrialFeature] attribute long originTrialEnabledLongAttribute2;
     [PerWorldBindings] readonly attribute TestInterfaceEmpty perWorldBindingsReadonlyTestInterfaceEmptyAttribute;
     [LogActivity, LogAllWorlds, PerWorldBindings] attribute long activityLoggingAccessPerWorldBindingsLongAttribute;
@@ -212,7 +212,7 @@
     [SameObject, SaveSameObject] readonly attribute TestInterface saveSameObjectAttribute;
     [SameObject, SaveSameObject] static readonly attribute TestInterface staticSaveSameObjectAttribute;
     [Unscopable] attribute long unscopableLongAttribute;
-    [Unscopable, OriginTrialEnabled=FeatureName] attribute long unscopableOriginTrialEnabledLongAttribute;
+    [Unscopable, RuntimeEnabled=FeatureName] attribute long unscopableOriginTrialEnabledLongAttribute;
     [Unscopable, RuntimeEnabled=RuntimeFeature] attribute long unscopableRuntimeEnabledLongAttribute;
     [Unscopable] void unscopableVoidMethod();
     [Unscopable, RuntimeEnabled=RuntimeFeature] void unscopableRuntimeEnabledVoidMethod();
@@ -500,16 +500,16 @@
     [DeprecateAs=TestFeatureA, MeasureAs=TestFeatureB] void deprecateAsSameValueMeasureAsSameValueOverloadedMethod();
     [DeprecateAs=TestFeatureA, MeasureAs=TestFeatureB] void deprecateAsSameValueMeasureAsSameValueOverloadedMethod(long arg);
     [NotEnumerable] void notEnumerableVoidMethod();
-    [OriginTrialEnabled=FeatureName] void originTrialEnabledVoidMethod();
-    [PerWorldBindings, OriginTrialEnabled=FeatureName] void perWorldBindingsOriginTrialEnabledVoidMethod();
+    [RuntimeEnabled=FeatureName] void originTrialEnabledVoidMethod();
+    [PerWorldBindings, RuntimeEnabled=FeatureName] void perWorldBindingsOriginTrialEnabledVoidMethod();
     // TODO(iclelland): Re-enable origin trials on overloaded methods
     // (crbug.com/621641)
-    // [OriginTrialEnabled=FeatureName] void originTrialEnabledOverloadedVoidMethod(DOMString stringArg);
-    // [OriginTrialEnabled=FeatureName] void originTrialEnabledOverloadedVoidMethod(long longArg);
-    // [OriginTrialEnabled=FeatureName1] void partiallyOriginTrialEnabledOverloadedVoidMethod(DOMString stringArg);
-    // [OriginTrialEnabled=FeatureName2] void partiallyOriginTrialEnabledOverloadedVoidMethod(TestInterface testInterfaceArg);
+    // [RuntimeEnabled=FeatureName] void originTrialEnabledOverloadedVoidMethod(DOMString stringArg);
+    // [RuntimeEnabled=FeatureName] void originTrialEnabledOverloadedVoidMethod(long longArg);
+    // [RuntimeEnabled=FeatureName1] void partiallyOriginTrialEnabledOverloadedVoidMethod(DOMString stringArg);
+    // [RuntimeEnabled=FeatureName2] void partiallyOriginTrialEnabledOverloadedVoidMethod(TestInterface testInterfaceArg);
     // void partiallyOriginTrialEnabledOverloadedVoidMethod(long longArg, DOMString stringArg);
-    // [OriginTrialEnabled=FeatureName3] void partiallyOriginTrialEnabledOverloadedVoidMethod(long longArg, DOMString stringArg, TestInterface testInterfaceArg);
+    // [RuntimeEnabled=FeatureName3] void partiallyOriginTrialEnabledOverloadedVoidMethod(long longArg, DOMString stringArg, TestInterface testInterfaceArg);
     [PerWorldBindings] void perWorldBindingsVoidMethod();
     [PerWorldBindings] void perWorldBindingsVoidMethodTestInterfaceEmptyArg(TestInterfaceEmpty testInterfaceEmptyArg);
     [LogActivity, LogAllWorlds, PerWorldBindings] void activityLoggingForAllWorldsPerWorldBindingsVoidMethod();
diff --git a/third_party/blink/renderer/bindings/tests/idls/modules/test_interface_partial_4.idl b/third_party/blink/renderer/bindings/tests/idls/modules/test_interface_partial_4.idl
index 06150fa5..acb9be7 100644
--- a/third_party/blink/renderer/bindings/tests/idls/modules/test_interface_partial_4.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/modules/test_interface_partial_4.idl
@@ -8,7 +8,7 @@
 
 [
     ImplementedAs=TestInterfacePartial4,
-    OriginTrialEnabled=OriginTrialPartialFeature, // Conflicts with RuntimeEnabled
+    RuntimeEnabled=OriginTrialPartialFeature, // Conflicts with RuntimeEnabled
     SecureContext
 ] partial interface TestInterface {
     const unsigned short PARTIAL4_UNSIGNED_SHORT = 4;
diff --git a/third_party/blink/renderer/bindings/tests/idls/runtime_enabled_features.json5 b/third_party/blink/renderer/bindings/tests/idls/runtime_enabled_features.json5
index 1f962be6..522f002 100644
--- a/third_party/blink/renderer/bindings/tests/idls/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/bindings/tests/idls/runtime_enabled_features.json5
@@ -15,6 +15,26 @@
 
   data: [
     {
+      name: "FeatureName",
+      in_origin_trial: true,
+    },
+    {
+      name: "FeatureName1",
+      in_origin_trial: true,
+    },
+    {
+      name: "FeatureName2",
+      in_origin_trial: true,
+    },
+    {
+      name: "FeatureName3",
+      in_origin_trial: true,
+    },
+    {
+      name: "InterfaceFeatureName",
+      in_origin_trial: true,
+    },
+    {
       name: "Interface2PartialRuntimeFeature",
     },
     {
@@ -40,6 +60,10 @@
       in_origin_trial: true,
     },
     {
+      name: "OriginTrialPartialFeature",
+      in_origin_trial: true,
+    },
+    {
       name: "PartialRuntimeFeature",
     },
     {
@@ -57,5 +81,9 @@
     {
       name: "SecureFeature",
     },
+    {
+      name: "TestFeature",
+      in_origin_trial: true,
+    },
   ],
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
index 084ad2dc..3d3cc9c2 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
@@ -1045,49 +1045,4 @@
   return v8::JustVoid();
 }
 
-v8::Maybe<void> V8PersistentCallbackInterface<V8TestCallbackInterface>::voidMethod(bindings::V8ValueOrScriptWrappableAdapter callback_this_value) {
-  return Proxy()->voidMethod(
-      callback_this_value);
-}
-
-v8::Maybe<bool> V8PersistentCallbackInterface<V8TestCallbackInterface>::booleanMethod(bindings::V8ValueOrScriptWrappableAdapter callback_this_value) {
-  return Proxy()->booleanMethod(
-      callback_this_value);
-}
-
-v8::Maybe<void> V8PersistentCallbackInterface<V8TestCallbackInterface>::voidMethodBooleanArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, bool boolArg) {
-  return Proxy()->voidMethodBooleanArg(
-      callback_this_value, boolArg);
-}
-
-v8::Maybe<void> V8PersistentCallbackInterface<V8TestCallbackInterface>::voidMethodSequenceArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, const HeapVector<Member<TestInterfaceEmpty>>& sequenceArg) {
-  return Proxy()->voidMethodSequenceArg(
-      callback_this_value, sequenceArg);
-}
-
-v8::Maybe<void> V8PersistentCallbackInterface<V8TestCallbackInterface>::voidMethodFloatArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, float floatArg) {
-  return Proxy()->voidMethodFloatArg(
-      callback_this_value, floatArg);
-}
-
-v8::Maybe<void> V8PersistentCallbackInterface<V8TestCallbackInterface>::voidMethodTestInterfaceEmptyArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg) {
-  return Proxy()->voidMethodTestInterfaceEmptyArg(
-      callback_this_value, testInterfaceEmptyArg);
-}
-
-v8::Maybe<void> V8PersistentCallbackInterface<V8TestCallbackInterface>::voidMethodTestInterfaceEmptyStringArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg, const String& stringArg) {
-  return Proxy()->voidMethodTestInterfaceEmptyStringArg(
-      callback_this_value, testInterfaceEmptyArg, stringArg);
-}
-
-v8::Maybe<void> V8PersistentCallbackInterface<V8TestCallbackInterface>::callbackWithThisValueVoidMethodStringArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, const String& stringArg) {
-  return Proxy()->callbackWithThisValueVoidMethodStringArg(
-      callback_this_value, stringArg);
-}
-
-v8::Maybe<void> V8PersistentCallbackInterface<V8TestCallbackInterface>::customVoidMethodTestInterfaceEmptyArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg) {
-  return Proxy()->customVoidMethodTestInterfaceEmptyArg(
-      callback_this_value, testInterfaceEmptyArg);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.h b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.h
index 56a8b5d..e2f1303 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.h
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.h
@@ -70,35 +70,6 @@
   v8::Maybe<void> customVoidMethodTestInterfaceEmptyArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg) WARN_UNUSED_RESULT;
 };
 
-template <>
-class V8PersistentCallbackInterface<V8TestCallbackInterface> final : public V8PersistentCallbackInterfaceBase {
-  using V8CallbackInterface = V8TestCallbackInterface;
-
- public:
-  explicit V8PersistentCallbackInterface(V8CallbackInterface* callback_interface)
-      : V8PersistentCallbackInterfaceBase(callback_interface) {}
-  ~V8PersistentCallbackInterface() override = default;
-
-  CORE_EXPORT v8::Maybe<void> voidMethod(bindings::V8ValueOrScriptWrappableAdapter callback_this_value) WARN_UNUSED_RESULT;
-  CORE_EXPORT v8::Maybe<bool> booleanMethod(bindings::V8ValueOrScriptWrappableAdapter callback_this_value) WARN_UNUSED_RESULT;
-  CORE_EXPORT v8::Maybe<void> voidMethodBooleanArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, bool boolArg) WARN_UNUSED_RESULT;
-  CORE_EXPORT v8::Maybe<void> voidMethodSequenceArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, const HeapVector<Member<TestInterfaceEmpty>>& sequenceArg) WARN_UNUSED_RESULT;
-  CORE_EXPORT v8::Maybe<void> voidMethodFloatArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, float floatArg) WARN_UNUSED_RESULT;
-  CORE_EXPORT v8::Maybe<void> voidMethodTestInterfaceEmptyArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg) WARN_UNUSED_RESULT;
-  CORE_EXPORT v8::Maybe<void> voidMethodTestInterfaceEmptyStringArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg, const String& stringArg) WARN_UNUSED_RESULT;
-  CORE_EXPORT v8::Maybe<void> callbackWithThisValueVoidMethodStringArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, const String& stringArg) WARN_UNUSED_RESULT;
-  CORE_EXPORT v8::Maybe<void> customVoidMethodTestInterfaceEmptyArg(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg) WARN_UNUSED_RESULT;
-
- private:
-  V8CallbackInterface* Proxy() {
-    return As<V8CallbackInterface>();
-  }
-
-  template <typename V8CallbackInterface>
-  friend V8PersistentCallbackInterface<V8CallbackInterface>*
-  ToV8PersistentCallbackInterface(V8CallbackInterface*);
-};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_TESTS_RESULTS_CORE_V8_TEST_CALLBACK_INTERFACE_H_
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
index 24a71bdd..ec9f318 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
@@ -208,9 +208,4 @@
   }
 }
 
-v8::Maybe<uint16_t> V8PersistentCallbackInterface<V8TestLegacyCallbackInterface>::acceptNode(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, Node* node) {
-  return Proxy()->acceptNode(
-      callback_this_value, node);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.h b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.h
index 9fa3cca..876161cf 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.h
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.h
@@ -51,27 +51,6 @@
   v8::Maybe<uint16_t> acceptNode(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, Node* node) WARN_UNUSED_RESULT;
 };
 
-template <>
-class V8PersistentCallbackInterface<V8TestLegacyCallbackInterface> final : public V8PersistentCallbackInterfaceBase {
-  using V8CallbackInterface = V8TestLegacyCallbackInterface;
-
- public:
-  explicit V8PersistentCallbackInterface(V8CallbackInterface* callback_interface)
-      : V8PersistentCallbackInterfaceBase(callback_interface) {}
-  ~V8PersistentCallbackInterface() override = default;
-
-  CORE_EXPORT v8::Maybe<uint16_t> acceptNode(bindings::V8ValueOrScriptWrappableAdapter callback_this_value, Node* node) WARN_UNUSED_RESULT;
-
- private:
-  V8CallbackInterface* Proxy() {
-    return As<V8CallbackInterface>();
-  }
-
-  template <typename V8CallbackInterface>
-  friend V8PersistentCallbackInterface<V8CallbackInterface>*
-  ToV8PersistentCallbackInterface(V8CallbackInterface*);
-};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_TESTS_RESULTS_CORE_V8_TEST_LEGACY_CALLBACK_INTERFACE_H_
diff --git a/third_party/blink/renderer/build/scripts/core/css/parser/templates/at_rule_descriptors.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/parser/templates/at_rule_descriptors.cc.tmpl
index e005213..3603d157 100644
--- a/third_party/blink/renderer/build/scripts/core/css/parser/templates/at_rule_descriptors.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/parser/templates/at_rule_descriptors.cc.tmpl
@@ -100,7 +100,6 @@
     return AtRuleDescriptorID::{{descriptor.name.to_upper_camel_case()}};
 {% endfor %}
   default:
-    NOTREACHED();
     return AtRuleDescriptorID::Invalid;
   }
 }
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.cc.tmpl
index a5a4939..56d58053 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.cc.tmpl
@@ -33,17 +33,15 @@
 {% for property in properties %}
 
 const StylePropertyShorthand& {{property.name.to_lower_camel_case()}}Shorthand() {
-  static const CSSProperty* {{property.name.to_lower_camel_case()}}Properties[] = {
+  static const CSSProperty* longhands[] = {
     {% for longhand_id in property.longhand_property_ids %}
     &Get{{longhand_id}}(),
     {% endfor %}
   };
 
-  static StylePropertyShorthand {{property.name.to_lower_camel_case()}}Longhands(
-    CSSPropertyID::{{property.enum_key}},
-    {{property.name.to_lower_camel_case()}}Properties,
-    base::size({{property.name.to_lower_camel_case()}}Properties));
-  return {{property.name.to_lower_camel_case()}}Longhands;
+  DEFINE_STATIC_LOCAL(const StylePropertyShorthand, shorthand,
+        (CSSPropertyID::{{property.enum_key}}, longhands, base::size(longhands)));
+  return shorthand;
 }
 {% endfor %}
 
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 34ed58f..20a1a9a 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1354,6 +1354,7 @@
     "layout/multi_column_fragmentainer_group_test.cc",
     "layout/ng/exclusions/ng_exclusion_space_test.cc",
     "layout/ng/geometry/ng_box_strut_test.cc",
+    "layout/ng/geometry/ng_static_position_test.cc",
     "layout/ng/inline/ng_baseline_test.cc",
     "layout/ng/inline/ng_caret_position_test.cc",
     "layout/ng/inline/ng_inline_fragment_traversal_test.cc",
@@ -1422,6 +1423,7 @@
     "loader/resource_load_observer_for_frame_test.cc",
     "loader/text_resource_decoder_builder_test.cc",
     "loader/threadable_loader_test.cc",
+    "loader/threaded_icon_loader_test.cc",
     "messaging/blink_transferable_message_struct_traits_test.cc",
     "origin_trials/origin_trial_context_test.cc",
     "page/autoscroll_controller_test.cc",
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index cd89e5b..3317554 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -76,7 +76,7 @@
     "+third_party/blink/renderer/core",
     "-third_party/blink/renderer/modules",
     "+third_party/skia/include",
-    "+ui/base/ime/mojo/ime_types.mojom-shared.h",
+    "+ui/base/ime/mojo/ime_types.mojom-blink.h",
     "+ui/gfx/geometry",
     "+ui/gfx/skia_util.h",
     "-web",
diff --git a/third_party/blink/renderer/core/animation/animation_effect.cc b/third_party/blink/renderer/core/animation/animation_effect.cc
index bc9cbfe..00a3c7e 100644
--- a/third_party/blink/renderer/core/animation/animation_effect.cc
+++ b/third_party/blink/renderer/core/animation/animation_effect.cc
@@ -70,7 +70,7 @@
   return result;
 }
 
-double AnimationEffect::RepeatedDuration() const {
+double AnimationEffect::ActiveDuration() const {
   const double result =
       MultiplyZeroAlwaysGivesZero(IterationDuration(), timing_.iteration_count);
   DCHECK_GE(result, 0);
@@ -80,7 +80,7 @@
 double AnimationEffect::EndTimeInternal() const {
   // Per the spec, the end time has a lower bound of 0.0:
   // https://drafts.csswg.org/web-animations-1/#end-time
-  return std::max(timing_.start_delay + RepeatedDuration() + timing_.end_delay,
+  return std::max(timing_.start_delay + ActiveDuration() + timing_.end_delay,
                   0.0);
 }
 
@@ -99,7 +99,7 @@
 
   // ComputedEffectTiming members.
   computed_timing->setEndTime(EndTimeInternal() * 1000);
-  computed_timing->setActiveDuration(RepeatedDuration() * 1000);
+  computed_timing->setActiveDuration(ActiveDuration() * 1000);
 
   if (IsNull(EnsureCalculated().local_time)) {
     computed_timing->setLocalTimeToNull();
@@ -159,7 +159,7 @@
   const double local_time = inherited_time;
   double time_to_next_iteration = std::numeric_limits<double>::infinity();
   if (needs_update) {
-    const double active_duration = RepeatedDuration();
+    const double active_duration = ActiveDuration();
     // TODO(yigu): Direction of WorkletAnimation is always forwards based on
     // the calculation. Need to unify the logic to handle it correctly.
     // https://crbug.com/896249.
diff --git a/third_party/blink/renderer/core/animation/animation_effect.h b/third_party/blink/renderer/core/animation/animation_effect.h
index 5e64dc17..d63d42d 100644
--- a/third_party/blink/renderer/core/animation/animation_effect.h
+++ b/third_party/blink/renderer/core/animation/animation_effect.h
@@ -120,8 +120,12 @@
   }
   double LocalTime() const { return EnsureCalculated().local_time; }
 
+  // https://drafts.csswg.org/web-animations-1/#iteration-duration
   AnimationTimeDelta IterationDuration() const;
-  double RepeatedDuration() const;
+
+  // https://drafts.csswg.org/web-animations-1/#active-duration
+  double ActiveDuration() const;
+
   double EndTimeInternal() const;
 
   const Timing& SpecifiedTiming() const { return timing_; }
@@ -159,6 +163,11 @@
   void ClearEventDelegate() { event_delegate_ = nullptr; }
 
   virtual void UpdateChildrenAndEffects() const = 0;
+
+  // This is the value of the iteration duration when it is specified as 'auto'.
+  // In web-animations-1, auto is treated as "the value zero for the purpose of
+  // timing model calculations and for the result of the duration member
+  // returned from getComputedTiming()".
   virtual AnimationTimeDelta IntrinsicIterationDuration() const {
     return AnimationTimeDelta();
   }
diff --git a/third_party/blink/renderer/core/animation/animation_effect_test.cc b/third_party/blink/renderer/core/animation/animation_effect_test.cc
index 37203016..3d52df0 100644
--- a/third_party/blink/renderer/core/animation/animation_effect_test.cc
+++ b/third_party/blink/renderer/core/animation/animation_effect_test.cc
@@ -135,7 +135,7 @@
   EXPECT_TRUE(animation_node->IsCurrent());
   EXPECT_TRUE(animation_node->IsInEffect());
   EXPECT_EQ(0, animation_node->CurrentIteration());
-  EXPECT_EQ(2, animation_node->RepeatedDuration());
+  EXPECT_EQ(2, animation_node->ActiveDuration());
   EXPECT_EQ(0, animation_node->Progress());
 
   animation_node->UpdateInheritedTime(1);
@@ -145,7 +145,7 @@
   EXPECT_TRUE(animation_node->IsCurrent());
   EXPECT_TRUE(animation_node->IsInEffect());
   EXPECT_EQ(0, animation_node->CurrentIteration());
-  EXPECT_EQ(2, animation_node->RepeatedDuration());
+  EXPECT_EQ(2, animation_node->ActiveDuration());
   EXPECT_EQ(0.5, animation_node->Progress());
 
   animation_node->UpdateInheritedTime(2);
@@ -155,7 +155,7 @@
   EXPECT_FALSE(animation_node->IsCurrent());
   EXPECT_TRUE(animation_node->IsInEffect());
   EXPECT_EQ(0, animation_node->CurrentIteration());
-  EXPECT_EQ(2, animation_node->RepeatedDuration());
+  EXPECT_EQ(2, animation_node->ActiveDuration());
   EXPECT_EQ(1, animation_node->Progress());
 
   animation_node->UpdateInheritedTime(3);
@@ -165,7 +165,7 @@
   EXPECT_FALSE(animation_node->IsCurrent());
   EXPECT_TRUE(animation_node->IsInEffect());
   EXPECT_EQ(0, animation_node->CurrentIteration());
-  EXPECT_EQ(2, animation_node->RepeatedDuration());
+  EXPECT_EQ(2, animation_node->ActiveDuration());
   EXPECT_EQ(1, animation_node->Progress());
 }
 
@@ -245,12 +245,12 @@
   auto* animation_node = MakeGarbageCollected<TestAnimationEffect>(timing);
 
   animation_node->UpdateInheritedTime(-1);
-  EXPECT_EQ(0, animation_node->RepeatedDuration());
+  EXPECT_EQ(0, animation_node->ActiveDuration());
   EXPECT_TRUE(IsNull(animation_node->CurrentIteration()));
   EXPECT_FALSE(animation_node->Progress());
 
   animation_node->UpdateInheritedTime(0);
-  EXPECT_EQ(0, animation_node->RepeatedDuration());
+  EXPECT_EQ(0, animation_node->ActiveDuration());
   EXPECT_EQ(0, animation_node->CurrentIteration());
   EXPECT_EQ(0, animation_node->Progress());
 }
@@ -267,7 +267,7 @@
   EXPECT_FALSE(animation_node->Progress());
 
   EXPECT_EQ(std::numeric_limits<double>::infinity(),
-            animation_node->RepeatedDuration());
+            animation_node->ActiveDuration());
 
   animation_node->UpdateInheritedTime(0);
   EXPECT_EQ(0, animation_node->CurrentIteration());
@@ -373,7 +373,7 @@
   EXPECT_FALSE(animation_node->IsCurrent());
   EXPECT_TRUE(animation_node->IsInEffect());
   EXPECT_EQ(0, animation_node->CurrentIteration());
-  EXPECT_EQ(0, animation_node->RepeatedDuration());
+  EXPECT_EQ(0, animation_node->ActiveDuration());
   EXPECT_EQ(1, animation_node->Progress());
 
   animation_node->UpdateInheritedTime(1);
@@ -383,7 +383,7 @@
   EXPECT_FALSE(animation_node->IsCurrent());
   EXPECT_TRUE(animation_node->IsInEffect());
   EXPECT_EQ(0, animation_node->CurrentIteration());
-  EXPECT_EQ(0, animation_node->RepeatedDuration());
+  EXPECT_EQ(0, animation_node->ActiveDuration());
   EXPECT_EQ(1, animation_node->Progress());
 }
 
@@ -474,12 +474,12 @@
   auto* animation_node = MakeGarbageCollected<TestAnimationEffect>(timing);
 
   animation_node->UpdateInheritedTime(-1);
-  EXPECT_EQ(0, animation_node->RepeatedDuration());
+  EXPECT_EQ(0, animation_node->ActiveDuration());
   EXPECT_TRUE(IsNull(animation_node->CurrentIteration()));
   EXPECT_FALSE(animation_node->Progress());
 
   animation_node->UpdateInheritedTime(0);
-  EXPECT_EQ(0, animation_node->RepeatedDuration());
+  EXPECT_EQ(0, animation_node->ActiveDuration());
   EXPECT_EQ(std::numeric_limits<double>::infinity(),
             animation_node->CurrentIteration());
   EXPECT_EQ(1, animation_node->Progress());
@@ -573,7 +573,7 @@
   animation_node->UpdateInheritedTime(0);
 
   EXPECT_EQ(std::numeric_limits<double>::infinity(),
-            animation_node->RepeatedDuration());
+            animation_node->ActiveDuration());
   EXPECT_EQ(AnimationEffect::kPhaseActive, animation_node->GetPhase());
   EXPECT_TRUE(animation_node->IsInPlay());
   EXPECT_TRUE(animation_node->IsCurrent());
@@ -584,7 +584,7 @@
   animation_node->UpdateInheritedTime(1);
 
   EXPECT_EQ(std::numeric_limits<double>::infinity(),
-            animation_node->RepeatedDuration());
+            animation_node->ActiveDuration());
   EXPECT_EQ(AnimationEffect::kPhaseActive, animation_node->GetPhase());
   EXPECT_TRUE(animation_node->IsInPlay());
   EXPECT_TRUE(animation_node->IsCurrent());
@@ -602,7 +602,7 @@
 
   animation_node->UpdateInheritedTime(0);
 
-  EXPECT_EQ(0, animation_node->RepeatedDuration());
+  EXPECT_EQ(0, animation_node->ActiveDuration());
   EXPECT_EQ(AnimationEffect::kPhaseAfter, animation_node->GetPhase());
   EXPECT_FALSE(animation_node->IsInPlay());
   EXPECT_FALSE(animation_node->IsCurrent());
@@ -630,7 +630,7 @@
   animation_node->UpdateInheritedTime(0);
 
   EXPECT_EQ(std::numeric_limits<double>::infinity(),
-            animation_node->RepeatedDuration());
+            animation_node->ActiveDuration());
   EXPECT_EQ(AnimationEffect::kPhaseActive, animation_node->GetPhase());
   EXPECT_TRUE(animation_node->IsInPlay());
   EXPECT_TRUE(animation_node->IsCurrent());
@@ -641,7 +641,7 @@
   animation_node->UpdateInheritedTime(1);
 
   EXPECT_EQ(std::numeric_limits<double>::infinity(),
-            animation_node->RepeatedDuration());
+            animation_node->ActiveDuration());
   EXPECT_EQ(AnimationEffect::kPhaseActive, animation_node->GetPhase());
   EXPECT_TRUE(animation_node->IsInPlay());
   EXPECT_TRUE(animation_node->IsCurrent());
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index 4e456ba86..f391fd95 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -1175,7 +1175,7 @@
       previous_phase_ != AnimationEffect::kPhaseAfter) {
     MaybeDispatch(Document::kAnimationEndListener,
                   event_type_names::kAnimationend,
-                  animation_node.RepeatedDuration());
+                  animation_node.ActiveDuration());
   }
 
   previous_phase_ = current_phase;
@@ -1249,7 +1249,7 @@
       // "active time of the animation at the moment it was cancelled,
       // calculated using a fill mode of both".
       double cancel_active_time = CalculateActiveTime(
-          animation_node.RepeatedDuration(), Timing::FillMode::BOTH,
+          animation_node.ActiveDuration(), Timing::FillMode::BOTH,
           animation_node.LocalTime(), previous_phase_,
           animation_node.SpecifiedTiming());
       EnqueueEvent(event_type_names::kTransitioncancel, cancel_active_time);
diff --git a/third_party/blink/renderer/core/animation/interpolation_effect.cc b/third_party/blink/renderer/core/animation/interpolation_effect.cc
index 5f5af62..814730f1 100644
--- a/third_party/blink/renderer/core/animation/interpolation_effect.cc
+++ b/third_party/blink/renderer/core/animation/interpolation_effect.cc
@@ -4,13 +4,10 @@
 
 #include "third_party/blink/renderer/core/animation/interpolation_effect.h"
 
-#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
-
 namespace blink {
 
 void InterpolationEffect::GetActiveInterpolations(
     double fraction,
-    double iteration_duration,
     HeapVector<Member<Interpolation>>& result) const {
   wtf_size_t existing_size = result.size();
   wtf_size_t result_index = 0;
@@ -23,8 +20,7 @@
           record_length ? (fraction - record->start_) / record_length : 0.0;
       if (record->easing_) {
         local_fraction = record->easing_->Evaluate(
-            local_fraction, TimingFunction::LimitDirection::RIGHT,
-            AccuracyForDuration(iteration_duration));
+            local_fraction, TimingFunction::LimitDirection::RIGHT);
       }
       interpolation->Interpolate(0, local_fraction);
       if (result_index < existing_size)
diff --git a/third_party/blink/renderer/core/animation/interpolation_effect.h b/third_party/blink/renderer/core/animation/interpolation_effect.h
index 78cfee54..b8d5dfe 100644
--- a/third_party/blink/renderer/core/animation/interpolation_effect.h
+++ b/third_party/blink/renderer/core/animation/interpolation_effect.h
@@ -30,7 +30,6 @@
   }
 
   void GetActiveInterpolations(double fraction,
-                               double iteration_duration,
                                HeapVector<Member<Interpolation>>&) const;
 
   void AddInterpolation(Interpolation* interpolation,
diff --git a/third_party/blink/renderer/core/animation/interpolation_effect_test.cc b/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
index 4c238c6..8cab75b 100644
--- a/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
+++ b/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
@@ -13,8 +13,6 @@
 
 namespace {
 
-const double kInterpolationTestDuration = 1.0;
-
 double GetInterpolableNumber(Interpolation* value) {
   TransitionInterpolation* interpolation = ToTransitionInterpolation(value);
   std::unique_ptr<TypedInterpolationValue> interpolated_value =
@@ -45,31 +43,25 @@
       CreateInterpolation(0, 10), scoped_refptr<TimingFunction>(), 0, 1, -1, 2);
 
   HeapVector<Member<Interpolation>> active_interpolations;
-  interpolation_effect->GetActiveInterpolations(-2, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(-2, active_interpolations);
   EXPECT_EQ(0ul, active_interpolations.size());
 
-  interpolation_effect->GetActiveInterpolations(
-      -0.5, kInterpolationTestDuration, active_interpolations);
+  interpolation_effect->GetActiveInterpolations(-0.5, active_interpolations);
   EXPECT_EQ(1ul, active_interpolations.size());
   EXPECT_EQ(-5, GetInterpolableNumber(active_interpolations.at(0)));
 
-  interpolation_effect->GetActiveInterpolations(0.5, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(0.5, active_interpolations);
   EXPECT_EQ(1ul, active_interpolations.size());
   EXPECT_FLOAT_EQ(5, GetInterpolableNumber(active_interpolations.at(0)));
 
-  interpolation_effect->GetActiveInterpolations(1.5, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(1.5, active_interpolations);
   EXPECT_EQ(1ul, active_interpolations.size());
   EXPECT_FLOAT_EQ(15, GetInterpolableNumber(active_interpolations.at(0)));
 
-  interpolation_effect->GetActiveInterpolations(3, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(3, active_interpolations);
   EXPECT_EQ(0ul, active_interpolations.size());
 
-  interpolation_effect->GetActiveInterpolations(0, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(0, active_interpolations);
   EXPECT_EQ(1ul, active_interpolations.size());
 }
 
@@ -90,42 +82,29 @@
   // ease(0.5) = 0.8024033877399112
 
   HeapVector<Member<Interpolation>> active_interpolations;
-  interpolation_effect->GetActiveInterpolations(
-      -0.5, kInterpolationTestDuration, active_interpolations);
+  interpolation_effect->GetActiveInterpolations(-0.5, active_interpolations);
   EXPECT_EQ(0ul, active_interpolations.size());
 
-  interpolation_effect->GetActiveInterpolations(0, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(0, active_interpolations);
   EXPECT_EQ(1ul, active_interpolations.size());
   EXPECT_FLOAT_EQ(0, GetInterpolableNumber(active_interpolations.at(0)));
 
-  interpolation_effect->GetActiveInterpolations(0.5, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(0.5, active_interpolations);
   EXPECT_EQ(2ul, active_interpolations.size());
   EXPECT_FLOAT_EQ(0.5f, GetInterpolableNumber(active_interpolations.at(0)));
   EXPECT_FLOAT_EQ(1, GetInterpolableNumber(active_interpolations.at(1)));
 
-  interpolation_effect->GetActiveInterpolations(1, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(1, active_interpolations);
   EXPECT_EQ(2ul, active_interpolations.size());
   EXPECT_FLOAT_EQ(10, GetInterpolableNumber(active_interpolations.at(0)));
   EXPECT_FLOAT_EQ(5.0120169f,
                   GetInterpolableNumber(active_interpolations.at(1)));
 
-  interpolation_effect->GetActiveInterpolations(
-      1, kInterpolationTestDuration * 1000, active_interpolations);
-  EXPECT_EQ(2ul, active_interpolations.size());
-  EXPECT_FLOAT_EQ(10, GetInterpolableNumber(active_interpolations.at(0)));
-  EXPECT_FLOAT_EQ(5.0120169f,
-                  GetInterpolableNumber(active_interpolations.at(1)));
-
-  interpolation_effect->GetActiveInterpolations(1.5, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(1.5, active_interpolations);
   EXPECT_EQ(1ul, active_interpolations.size());
   EXPECT_FLOAT_EQ(12.5f, GetInterpolableNumber(active_interpolations.at(0)));
 
-  interpolation_effect->GetActiveInterpolations(2, kInterpolationTestDuration,
-                                                active_interpolations);
+  interpolation_effect->GetActiveInterpolations(2, active_interpolations);
   EXPECT_EQ(1ul, active_interpolations.size());
   EXPECT_FLOAT_EQ(15, GetInterpolableNumber(active_interpolations.at(0)));
 }
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc
index d403d96..3881c11 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -436,7 +436,7 @@
     double local_time,
     double time_to_next_iteration) const {
   const double start_time = SpecifiedTiming().start_delay;
-  const double end_time_minus_end_delay = start_time + RepeatedDuration();
+  const double end_time_minus_end_delay = start_time + ActiveDuration();
   const double end_time =
       end_time_minus_end_delay + SpecifiedTiming().end_delay;
   const double after_time = std::min(end_time_minus_end_delay, end_time);
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_model.cc b/third_party/blink/renderer/core/animation/keyframe_effect_model.cc
index ae77699..101f64db 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect_model.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect_model.cc
@@ -87,8 +87,7 @@
   last_iteration_ = iteration;
   last_fraction_ = fraction;
   last_iteration_duration_ = iteration_duration;
-  interpolation_effect_->GetActiveInterpolations(
-      fraction, iteration_duration.InSecondsF(), result);
+  interpolation_effect_->GetActiveInterpolations(fraction, result);
   return changed;
 }
 
diff --git a/third_party/blink/renderer/core/animation/length_interpolation_functions.cc b/third_party/blink/renderer/core/animation/length_interpolation_functions.cc
index c5ec0f7..6e7df16 100644
--- a/third_party/blink/renderer/core/animation/length_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/length_interpolation_functions.cc
@@ -233,8 +233,8 @@
     if (!root_node) {
       root_node = CSSCalcValue::CreateExpressionNode(first_value);
     }
-    root_node =
-        CSSCalcValue::CreateExpressionNode(root_node, current_node, kCalcAdd);
+    root_node = CSSCalcValue::CreateExpressionNode(root_node, current_node,
+                                                   CSSMathOperator::kAdd);
   }
 
   if (root_node) {
diff --git a/third_party/blink/renderer/core/animation/timing_calculations.h b/third_party/blink/renderer/core/animation/timing_calculations.h
index b2d2c07..a3570d2 100644
--- a/third_party/blink/renderer/core/animation/timing_calculations.h
+++ b/third_party/blink/renderer/core/animation/timing_calculations.h
@@ -33,7 +33,6 @@
 
 #include "third_party/blink/renderer/core/animation/animation_effect.h"
 #include "third_party/blink/renderer/core/animation/timing.h"
-#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 
 namespace blink {
@@ -48,6 +47,12 @@
 inline bool LessThanOrEqualToWithinEpsilon(double a, double b) {
   return a <= b || IsWithinEpsilon(a, b);
 }
+
+inline bool EndsOnIterationBoundary(double iteration_count,
+                                    double iteration_start) {
+  DCHECK(std::isfinite(iteration_count));
+  return !fmod(iteration_count + iteration_start, 1);
+}
 }  // namespace
 
 static inline double MultiplyZeroAlwaysGivesZero(double x, double y) {
@@ -90,6 +95,7 @@
   return AnimationEffect::kPhaseActive;
 }
 
+// https://drafts.csswg.org/web-animations/#calculating-the-active-time
 static inline double CalculateActiveTime(double active_duration,
                                          Timing::FillMode fill_mode,
                                          double local_time,
@@ -121,69 +127,6 @@
   }
 }
 
-static inline double CalculateOffsetActiveTime(double active_duration,
-                                               double active_time,
-                                               double start_offset) {
-  DCHECK_GE(active_duration, 0);
-  DCHECK_GE(start_offset, 0);
-
-  if (IsNull(active_time))
-    return NullValue();
-
-  DCHECK(active_time >= 0 &&
-         LessThanOrEqualToWithinEpsilon(active_time, active_duration));
-
-  if (!std::isfinite(active_time))
-    return std::numeric_limits<double>::infinity();
-
-  return active_time + start_offset;
-}
-
-static inline bool EndsOnIterationBoundary(double iteration_count,
-                                           double iteration_start) {
-  DCHECK(std::isfinite(iteration_count));
-  return !fmod(iteration_count + iteration_start, 1);
-}
-
-// TODO(crbug.com/630915): Align this function with current Web Animations spec
-// text.
-static inline double CalculateIterationTime(double iteration_duration,
-                                            double repeated_duration,
-                                            double offset_active_time,
-                                            double start_offset,
-                                            AnimationEffect::Phase phase,
-                                            const Timing& specified) {
-  DCHECK_GT(iteration_duration, 0);
-  DCHECK_EQ(repeated_duration,
-            MultiplyZeroAlwaysGivesZero(iteration_duration,
-                                        specified.iteration_count));
-
-  if (IsNull(offset_active_time))
-    return NullValue();
-
-  DCHECK_GE(offset_active_time, 0);
-  DCHECK(LessThanOrEqualToWithinEpsilon(offset_active_time,
-                                        repeated_duration + start_offset));
-
-  if (!std::isfinite(offset_active_time) ||
-      (offset_active_time - start_offset == repeated_duration &&
-       specified.iteration_count &&
-       EndsOnIterationBoundary(specified.iteration_count,
-                               specified.iteration_start)))
-    return iteration_duration;
-
-  DCHECK(std::isfinite(offset_active_time));
-  double iteration_time = fmod(offset_active_time, iteration_duration);
-
-  // This implements step 3 of
-  // https://drafts.csswg.org/web-animations/#calculating-the-simple-iteration-progress
-  if (iteration_time == 0 && phase == AnimationEffect::kPhaseAfter &&
-      repeated_duration != 0 && offset_active_time != 0)
-    return iteration_duration;
-
-  return iteration_time;
-}
-
 // Calculates the overall progress, which describes the number of iterations
 // that have completed (including partial iterations).
 // https://drafts.csswg.org/web-animations/#calculating-the-overall-progress
@@ -354,8 +297,72 @@
 
   // Return the result of evaluating the animation effect’s timing function
   // passing directed progress as the input progress value.
-  return timing_function->Evaluate(directed_progress, limit_direction,
-                                   AccuracyForDuration(iteration_duration));
+  return timing_function->Evaluate(directed_progress, limit_direction);
+}
+
+// Offsets the active time by how far into the animation we start (i.e. the
+// product of the iteration start and iteration duration). This is not part of
+// the Web Animations spec; it is used for calculating the time until the next
+// iteration to optimize scheduling.
+static inline double CalculateOffsetActiveTime(double active_duration,
+                                               double active_time,
+                                               double start_offset) {
+  DCHECK_GE(active_duration, 0);
+  DCHECK_GE(start_offset, 0);
+
+  if (IsNull(active_time))
+    return NullValue();
+
+  DCHECK(active_time >= 0 &&
+         LessThanOrEqualToWithinEpsilon(active_time, active_duration));
+
+  if (!std::isfinite(active_time))
+    return std::numeric_limits<double>::infinity();
+
+  return active_time + start_offset;
+}
+
+// Maps the offset active time into 'iteration time space'[0], aka the offset
+// into the current iteration. This is not part of the Web Animations spec (note
+// that the section linked below is non-normative); it is used for calculating
+// the time until the next iteration to optimize scheduling.
+//
+// [0] https://drafts.csswg.org/web-animations-1/#iteration-time-space
+static inline double CalculateIterationTime(double iteration_duration,
+                                            double active_duration,
+                                            double offset_active_time,
+                                            double start_offset,
+                                            AnimationEffect::Phase phase,
+                                            const Timing& specified) {
+  DCHECK_GT(iteration_duration, 0);
+  DCHECK_EQ(active_duration,
+            MultiplyZeroAlwaysGivesZero(iteration_duration,
+                                        specified.iteration_count));
+
+  if (IsNull(offset_active_time))
+    return NullValue();
+
+  DCHECK_GE(offset_active_time, 0);
+  DCHECK(LessThanOrEqualToWithinEpsilon(offset_active_time,
+                                        active_duration + start_offset));
+
+  if (!std::isfinite(offset_active_time) ||
+      (offset_active_time - start_offset == active_duration &&
+       specified.iteration_count &&
+       EndsOnIterationBoundary(specified.iteration_count,
+                               specified.iteration_start)))
+    return iteration_duration;
+
+  DCHECK(std::isfinite(offset_active_time));
+  double iteration_time = fmod(offset_active_time, iteration_duration);
+
+  // This implements step 3 of
+  // https://drafts.csswg.org/web-animations/#calculating-the-simple-iteration-progress
+  if (iteration_time == 0 && phase == AnimationEffect::kPhaseAfter &&
+      active_duration != 0 && offset_active_time != 0)
+    return iteration_duration;
+
+  return iteration_time;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/timing_calculations_test.cc b/third_party/blink/renderer/core/animation/timing_calculations_test.cc
index 9a7f297f4..21db24e1 100644
--- a/third_party/blink/renderer/core/animation/timing_calculations_test.cc
+++ b/third_party/blink/renderer/core/animation/timing_calculations_test.cc
@@ -104,7 +104,7 @@
   Timing timing;
 
   // calculateIterationTime(
-  //     iterationDuration, repeatedDuration, scaledActiveTime, startOffset,
+  //     iterationDuration, activeDuration, scaledActiveTime, startOffset,
   //     phase, timing)
 
   // if the scaled active time is null
@@ -125,14 +125,14 @@
   EXPECT_EQ(8, CalculateIterationTime(12, 120, 20, 7,
                                       AnimationEffect::kPhaseActive, timing));
 
-  // Edge case for offset_active_time being within epsilon of (repeated_duration
+  // Edge case for offset_active_time being within epsilon of (active_duration
   // + start_offset). https://crbug.com/962138
   timing.iteration_count = 1;
   const double offset_active_time = 1.3435713716800004;
   const double iteration_duration = 1.3435713716800002;
-  const double repeated_duration = 1.3435713716800002;
+  const double active_duration = 1.3435713716800002;
   EXPECT_NEAR(2.22045e-16,
-              CalculateIterationTime(iteration_duration, repeated_duration,
+              CalculateIterationTime(iteration_duration, active_duration,
                                      offset_active_time, 0,
                                      AnimationEffect::kPhaseActive, timing),
               std::numeric_limits<float>::epsilon());
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index dc510c5..a22c8098 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -63,6 +63,7 @@
                     "css/css_style_rule.idl",
                     "css/css_style_sheet.idl",
                     "css/css_supports_rule.idl",
+                    "css/css_property_rule.idl",
                     "css/css_viewport_rule.idl",
                     "css/font_face.idl",
                     "css/font_face_set.idl",
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 46275fd..07bac32 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -108,6 +108,8 @@
     "css_layout_function_value.h",
     "css_markup.cc",
     "css_markup.h",
+    "css_math_operator.cc",
+    "css_math_operator.h",
     "css_media_rule.cc",
     "css_media_rule.h",
     "css_namespace_rule.cc",
@@ -130,6 +132,8 @@
     "css_property_id_templates.h",
     "css_property_name.cc",
     "css_property_name.h",
+    "css_property_rule.cc",
+    "css_property_rule.h",
     "css_property_source_data.cc",
     "css_property_source_data.h",
     "css_property_value.cc",
diff --git a/third_party/blink/renderer/core/css/css_calculation_value.cc b/third_party/blink/renderer/core/css/css_calculation_value.cc
index 283423a..f12e35af 100644
--- a/third_party/blink/renderer/core/css/css_calculation_value.cc
+++ b/third_party/blink/renderer/core/css/css_calculation_value.cc
@@ -272,9 +272,9 @@
     return nullptr;
   }
 
-  CalcOperator OperatorType() const override {
+  CSSMathOperator OperatorType() const override {
     NOTREACHED();
-    return kCalcAdd;
+    return CSSMathOperator::kInvalid;
   }
 
   void Trace(blink::Visitor* visitor) override {
@@ -328,7 +328,7 @@
 static CalculationCategory DetermineCategory(
     const CSSCalcExpressionNode& left_side,
     const CSSCalcExpressionNode& right_side,
-    CalcOperator op) {
+    CSSMathOperator op) {
   CalculationCategory left_category = left_side.Category();
   CalculationCategory right_category = right_side.Category();
 
@@ -336,17 +336,19 @@
     return kCalcOther;
 
   switch (op) {
-    case kCalcAdd:
-    case kCalcSubtract:
+    case CSSMathOperator::kAdd:
+    case CSSMathOperator::kSubtract:
       return kAddSubtractResult[left_category][right_category];
-    case kCalcMultiply:
+    case CSSMathOperator::kMultiply:
       if (left_category != kCalcNumber && right_category != kCalcNumber)
         return kCalcOther;
       return left_category == kCalcNumber ? right_category : left_category;
-    case kCalcDivide:
+    case CSSMathOperator::kDivide:
       if (right_category != kCalcNumber || right_side.IsZero())
         return kCalcOther;
       return left_category;
+    default:
+      break;
   }
 
   NOTREACHED();
@@ -355,18 +357,19 @@
 
 static bool IsIntegerResult(const CSSCalcExpressionNode* left_side,
                             const CSSCalcExpressionNode* right_side,
-                            CalcOperator op) {
+                            CSSMathOperator op) {
   // Not testing for actual integer values.
   // Performs W3C spec's type checking for calc integers.
   // http://www.w3.org/TR/css3-values/#calc-type-checking
-  return op != kCalcDivide && left_side->IsInteger() && right_side->IsInteger();
+  return op != CSSMathOperator::kDivide && left_side->IsInteger() &&
+         right_side->IsInteger();
 }
 
 class CSSCalcBinaryOperation final : public CSSCalcExpressionNode {
  public:
   static CSSCalcExpressionNode* Create(CSSCalcExpressionNode* left_side,
                                        CSSCalcExpressionNode* right_side,
-                                       CalcOperator op) {
+                                       CSSMathOperator op) {
     DCHECK_NE(left_side->Category(), kCalcOther);
     DCHECK_NE(right_side->Category(), kCalcOther);
 
@@ -382,7 +385,7 @@
   static CSSCalcExpressionNode* CreateSimplified(
       CSSCalcExpressionNode* left_side,
       CSSCalcExpressionNode* right_side,
-      CalcOperator op) {
+      CSSMathOperator op) {
     CalculationCategory left_category = left_side->Category();
     CalculationCategory right_category = right_side->Category();
     DCHECK_NE(left_category, kCalcOther);
@@ -399,7 +402,7 @@
     }
 
     // Simplify addition and subtraction between same types.
-    if (op == kCalcAdd || op == kCalcSubtract) {
+    if (op == CSSMathOperator::kAdd || op == CSSMathOperator::kSubtract) {
       if (left_category == right_side->Category()) {
         CSSPrimitiveValue::UnitType left_type =
             left_side->TypeWithCalcResolved();
@@ -437,11 +440,12 @@
       }
     } else {
       // Simplify multiplying or dividing by a number for simplifiable types.
-      DCHECK(op == kCalcMultiply || op == kCalcDivide);
+      DCHECK(op == CSSMathOperator::kMultiply ||
+             op == CSSMathOperator::kDivide);
       CSSCalcExpressionNode* number_side = GetNumberSide(left_side, right_side);
       if (!number_side)
         return Create(left_side, right_side, op);
-      if (number_side == left_side && op == kCalcDivide)
+      if (number_side == left_side && op == CSSMathOperator::kDivide)
         return nullptr;
       CSSCalcExpressionNode* other_side =
           left_side == number_side ? right_side : left_side;
@@ -449,7 +453,7 @@
       double number = number_side->DoubleValue();
       if (std::isnan(number) || std::isinf(number))
         return nullptr;
-      if (op == kCalcDivide && !number)
+      if (op == CSSMathOperator::kDivide && !number)
         return nullptr;
 
       CSSPrimitiveValue::UnitType other_type =
@@ -465,7 +469,7 @@
 
   CSSCalcBinaryOperation(CSSCalcExpressionNode* left_side,
                          CSSCalcExpressionNode* right_side,
-                         CalcOperator op,
+                         CSSMathOperator op,
                          CalculationCategory category)
       : CSSCalcExpressionNode(category,
                               IsIntegerResult(left_side, right_side, op)),
@@ -480,19 +484,19 @@
       PixelsAndPercent& value,
       float multiplier) const override {
     switch (operator_) {
-      case kCalcAdd:
+      case CSSMathOperator::kAdd:
         left_side_->AccumulatePixelsAndPercent(conversion_data, value,
                                                multiplier);
         right_side_->AccumulatePixelsAndPercent(conversion_data, value,
                                                 multiplier);
         break;
-      case kCalcSubtract:
+      case CSSMathOperator::kSubtract:
         left_side_->AccumulatePixelsAndPercent(conversion_data, value,
                                                multiplier);
         right_side_->AccumulatePixelsAndPercent(conversion_data, value,
                                                 -multiplier);
         break;
-      case kCalcMultiply:
+      case CSSMathOperator::kMultiply:
         DCHECK_NE((left_side_->Category() == kCalcNumber),
                   (right_side_->Category() == kCalcNumber));
         if (left_side_->Category() == kCalcNumber)
@@ -502,7 +506,7 @@
           left_side_->AccumulatePixelsAndPercent(
               conversion_data, value, multiplier * right_side_->DoubleValue());
         break;
-      case kCalcDivide:
+      case CSSMathOperator::kDivide:
         DCHECK_EQ(right_side_->Category(), kCalcNumber);
         left_side_->AccumulatePixelsAndPercent(
             conversion_data, value, multiplier / right_side_->DoubleValue());
@@ -526,15 +530,15 @@
   void AccumulateLengthArray(CSSLengthArray& length_array,
                              double multiplier) const override {
     switch (operator_) {
-      case kCalcAdd:
+      case CSSMathOperator::kAdd:
         left_side_->AccumulateLengthArray(length_array, multiplier);
         right_side_->AccumulateLengthArray(length_array, multiplier);
         break;
-      case kCalcSubtract:
+      case CSSMathOperator::kSubtract:
         left_side_->AccumulateLengthArray(length_array, multiplier);
         right_side_->AccumulateLengthArray(length_array, -multiplier);
         break;
-      case kCalcMultiply:
+      case CSSMathOperator::kMultiply:
         DCHECK_NE((left_side_->Category() == kCalcNumber),
                   (right_side_->Category() == kCalcNumber));
         if (left_side_->Category() == kCalcNumber)
@@ -544,7 +548,7 @@
           left_side_->AccumulateLengthArray(
               length_array, multiplier * right_side_->DoubleValue());
         break;
-      case kCalcDivide:
+      case CSSMathOperator::kDivide:
         DCHECK_EQ(right_side_->Category(), kCalcNumber);
         left_side_->AccumulateLengthArray(
             length_array, multiplier / right_side_->DoubleValue());
@@ -556,12 +560,12 @@
 
   static String BuildCSSText(const String& left_expression,
                              const String& right_expression,
-                             CalcOperator op) {
+                             CSSMathOperator op) {
     StringBuilder result;
     result.Append('(');
     result.Append(left_expression);
     result.Append(' ');
-    result.Append(static_cast<char>(op));
+    result.Append(ToString(op));
     result.Append(' ');
     result.Append(right_expression);
     result.Append(')');
@@ -594,7 +598,7 @@
     return right_side_;
   }
 
-  CalcOperator OperatorType() const override { return operator_; }
+  CSSMathOperator OperatorType() const override { return operator_; }
 
   CSSPrimitiveValue::UnitType TypeWithCalcResolved() const override {
     switch (category_) {
@@ -654,25 +658,28 @@
 
   static double EvaluateOperator(double left_value,
                                  double right_value,
-                                 CalcOperator op) {
+                                 CSSMathOperator op) {
     switch (op) {
-      case kCalcAdd:
+      case CSSMathOperator::kAdd:
         return clampTo<double>(left_value + right_value);
-      case kCalcSubtract:
+      case CSSMathOperator::kSubtract:
         return clampTo<double>(left_value - right_value);
-      case kCalcMultiply:
+      case CSSMathOperator::kMultiply:
         return clampTo<double>(left_value * right_value);
-      case kCalcDivide:
+      case CSSMathOperator::kDivide:
         if (right_value)
           return clampTo<double>(left_value / right_value);
         return std::numeric_limits<double>::quiet_NaN();
+      default:
+        NOTREACHED();
+        break;
     }
     return 0;
   }
 
   const Member<CSSCalcExpressionNode> left_side_;
   const Member<CSSCalcExpressionNode> right_side_;
-  const CalcOperator operator_;
+  const CSSMathOperator operator_;
 };
 
 static ParseState CheckDepthAndIndex(int* depth, CSSParserTokenRange tokens) {
@@ -699,12 +706,6 @@
   }
 
  private:
-  char OperatorValue(const CSSParserToken& token) {
-    if (token.GetType() == kDelimiterToken)
-      return token.Delimiter();
-    return 0;
-  }
-
   CSSCalcExpressionNode* ParseValue(CSSParserTokenRange& tokens) {
     CSSParserToken token = tokens.ConsumeIncludingWhitespace();
     if (!(token.GetType() == kNumberToken ||
@@ -752,9 +753,9 @@
       return nullptr;
 
     while (!tokens.AtEnd()) {
-      char operator_character = OperatorValue(tokens.Peek());
-      if (operator_character != kCalcMultiply &&
-          operator_character != kCalcDivide)
+      CSSMathOperator math_operator = ParseCSSArithmeticOperator(tokens.Peek());
+      if (math_operator != CSSMathOperator::kMultiply &&
+          math_operator != CSSMathOperator::kDivide)
         break;
       tokens.ConsumeIncludingWhitespace();
 
@@ -762,8 +763,8 @@
       if (!rhs)
         return nullptr;
 
-      result = CSSCalcBinaryOperation::CreateSimplified(
-          result, rhs, static_cast<CalcOperator>(operator_character));
+      result =
+          CSSCalcBinaryOperation::CreateSimplified(result, rhs, math_operator);
 
       if (!result)
         return nullptr;
@@ -784,8 +785,9 @@
       return nullptr;
 
     while (!tokens.AtEnd()) {
-      char operator_character = OperatorValue(tokens.Peek());
-      if (operator_character != kCalcAdd && operator_character != kCalcSubtract)
+      CSSMathOperator math_operator = ParseCSSArithmeticOperator(tokens.Peek());
+      if (math_operator != CSSMathOperator::kAdd &&
+          math_operator != CSSMathOperator::kSubtract)
         break;
       if ((&tokens.Peek() - 1)->GetType() != kWhitespaceToken)
         return nullptr;  // calc(1px+ 2px) is invalid
@@ -799,8 +801,8 @@
       if (!rhs)
         return nullptr;
 
-      result = CSSCalcBinaryOperation::CreateSimplified(
-          result, rhs, static_cast<CalcOperator>(operator_character));
+      result =
+          CSSCalcBinaryOperation::CreateSimplified(result, rhs, math_operator);
 
       if (!result)
         return nullptr;
@@ -824,7 +826,7 @@
 CSSCalcExpressionNode* CSSCalcValue::CreateExpressionNode(
     CSSCalcExpressionNode* left_side,
     CSSCalcExpressionNode* right_side,
-    CalcOperator op) {
+    CSSMathOperator op) {
   return CSSCalcBinaryOperation::Create(left_side, right_side, op);
 }
 
@@ -838,7 +840,7 @@
       CreateExpressionNode(CSSPrimitiveValue::Create(
                                pixels, CSSPrimitiveValue::UnitType::kPixels),
                            pixels == trunc(pixels)),
-      kCalcAdd);
+      CSSMathOperator::kAdd);
 }
 
 CSSCalcValue* CSSCalcValue::Create(const CSSParserTokenRange& tokens,
diff --git a/third_party/blink/renderer/core/css/css_calculation_value.h b/third_party/blink/renderer/core/css/css_calculation_value.h
index 609924e..3d068e3 100644
--- a/third_party/blink/renderer/core/css/css_calculation_value.h
+++ b/third_party/blink/renderer/core/css/css_calculation_value.h
@@ -32,6 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_CALCULATION_VALUE_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/css_math_operator.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
@@ -42,13 +43,6 @@
 
 class CalculationValue;
 
-enum CalcOperator {
-  kCalcAdd = '+',
-  kCalcSubtract = '-',
-  kCalcMultiply = '*',
-  kCalcDivide = '/'
-};
-
 // The order of this enum should not change since its elements are used as
 // indices in the addSubtractResult matrix.
 enum CalculationCategory {
@@ -84,7 +78,7 @@
   virtual Type GetType() const = 0;
   virtual const CSSCalcExpressionNode* LeftExpressionNode() const = 0;
   virtual const CSSCalcExpressionNode* RightExpressionNode() const = 0;
-  virtual CalcOperator OperatorType() const = 0;
+  virtual CSSMathOperator OperatorType() const = 0;
 
   CalculationCategory Category() const { return category_; }
   virtual CSSPrimitiveValue::UnitType TypeWithCalcResolved() const = 0;
@@ -116,7 +110,7 @@
                                                      bool is_integer = false);
   static CSSCalcExpressionNode* CreateExpressionNode(CSSCalcExpressionNode*,
                                                      CSSCalcExpressionNode*,
-                                                     CalcOperator);
+                                                     CSSMathOperator);
   static CSSCalcExpressionNode* CreateExpressionNode(double pixels,
                                                      double percent);
 
diff --git a/third_party/blink/renderer/core/css/css_calculation_value_test.cc b/third_party/blink/renderer/core/css/css_calculation_value_test.cc
index 476ea358..21edc4b 100644
--- a/third_party/blink/renderer/core/css/css_calculation_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_calculation_value_test.cc
@@ -101,7 +101,7 @@
               CSSPrimitiveValue::Create(20,
                                         CSSPrimitiveValue::UnitType::kPixels),
               true),
-          kCalcAdd),
+          CSSMathOperator::kAdd),
       150, 0);
 
   TestAccumulatePixelsAndPercent(
@@ -115,7 +115,7 @@
               CSSPrimitiveValue::Create(2,
                                         CSSPrimitiveValue::UnitType::kNumber),
               true),
-          kCalcMultiply),
+          CSSMathOperator::kMultiply),
       960, 0);
 
   TestAccumulatePixelsAndPercent(
@@ -130,7 +130,7 @@
                   CSSPrimitiveValue::Create(
                       0.25, CSSPrimitiveValue::UnitType::kNumber),
                   false),
-              kCalcMultiply),
+              CSSMathOperator::kMultiply),
           CSSCalcValue::CreateExpressionNode(
               CSSCalcValue::CreateExpressionNode(
                   CSSPrimitiveValue::Create(
@@ -140,8 +140,8 @@
                   CSSPrimitiveValue::Create(
                       40, CSSPrimitiveValue::UnitType::kPercentage),
                   false),
-              kCalcSubtract),
-          kCalcSubtract),
+              CSSMathOperator::kSubtract),
+          CSSMathOperator::kSubtract),
       -37.5, 40);
 }
 
diff --git a/third_party/blink/renderer/core/css/css_computed_style_declaration.cc b/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
index b39e2e1..f1cbdfa 100644
--- a/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
+++ b/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
@@ -334,13 +334,13 @@
 }
 
 const CSSValue* CSSComputedStyleDeclaration::GetPropertyCSSValue(
-    AtomicString custom_property_name) const {
-  Node* styled_node = StyledNode();
-  if (!styled_node)
-    return nullptr;
+    CSSPropertyID property_id) const {
+  return GetPropertyCSSValue(CSSPropertyName(property_id));
+}
 
-  CSSPropertyRef ref(custom_property_name, styled_node->GetDocument());
-  return GetPropertyCSSValue(ref.GetProperty());
+const CSSValue* CSSComputedStyleDeclaration::GetPropertyCSSValue(
+    AtomicString custom_property_name) const {
+  return GetPropertyCSSValue(CSSPropertyName(custom_property_name));
 }
 
 HeapHashMap<AtomicString, Member<const CSSValue>>
@@ -354,7 +354,7 @@
 }
 
 const CSSValue* CSSComputedStyleDeclaration::GetPropertyCSSValue(
-    const CSSProperty& property_class) const {
+    const CSSPropertyName& property_name) const {
   Node* styled_node = StyledNode();
   if (!styled_node)
     return nullptr;
@@ -370,7 +370,10 @@
     // TODO(futhark@chromium.org): There is an open question what the computed
     // style should be in a display:none iframe. If the property we are querying
     // is not layout dependent, we will not update the iframe layout box here.
-    if (property_class.IsLayoutDependentProperty() ||
+    bool is_layout_dependent_property =
+        !property_name.IsCustomProperty() &&
+        CSSProperty::Get(property_name.Id()).IsLayoutDependentProperty();
+    if (is_layout_dependent_property ||
         document.GetStyleEngine().HasViewportDependentMediaQueries()) {
       owner->GetDocument().UpdateStyleAndLayout();
       // The style recalc could have caused the styled node to be discarded or
@@ -381,6 +384,11 @@
 
   document.UpdateStyleAndLayoutTreeForNode(styled_node);
 
+  CSSPropertyRef ref(property_name, document);
+  if (!ref.IsValid())
+    return nullptr;
+  const CSSProperty& property_class = ref.GetProperty();
+
   // The style recalc could have caused the styled node to be discarded or
   // replaced if it was a PseudoElement so we need to update it.
   styled_node = StyledNode();
@@ -415,7 +423,7 @@
         node_->GetDocument(),
         WebFeature::kGetComputedStyleForWebkitAppearanceExcludeDevTools);
   }
-  const CSSValue* value = GetPropertyCSSValue(CSSProperty::Get(property_id));
+  const CSSValue* value = GetPropertyCSSValue(property_id);
   if (value)
     return value->CssText();
   return "";
@@ -452,7 +460,7 @@
         return true;
     }
   }
-  const CSSValue* value = GetPropertyCSSValue(CSSProperty::Get(property_id));
+  const CSSValue* value = GetPropertyCSSValue(property_id);
   return DataEquivalent(value, &property_value);
 }
 
@@ -467,7 +475,7 @@
   list.ReserveInitialCapacity(properties.size());
   for (unsigned i = 0; i < properties.size(); ++i) {
     const CSSProperty& property = *properties[i];
-    const CSSValue* value = GetPropertyCSSValue(property);
+    const CSSValue* value = GetPropertyCSSValue(property.GetCSSPropertyName());
     if (value)
       list.push_back(CSSPropertyValue(property, *value, false));
   }
@@ -536,7 +544,7 @@
     UseCounter::Count(node_->GetDocument(),
                       WebFeature::kGetComputedStyleWebkitAppearance);
   }
-  return GetPropertyCSSValue(CSSProperty::Get(property_id));
+  return GetPropertyCSSValue(property_id);
 }
 
 const CSSValue* CSSComputedStyleDeclaration::GetPropertyCSSValueInternal(
diff --git a/third_party/blink/renderer/core/css/css_computed_style_declaration.h b/third_party/blink/renderer/core/css/css_computed_style_declaration.h
index b9ccd3f..0b699ed9 100644
--- a/third_party/blink/renderer/core/css/css_computed_style_declaration.h
+++ b/third_party/blink/renderer/core/css/css_computed_style_declaration.h
@@ -54,8 +54,9 @@
 
   MutableCSSPropertyValueSet* CopyProperties() const;
 
-  const CSSValue* GetPropertyCSSValue(const CSSProperty&) const;
+  const CSSValue* GetPropertyCSSValue(CSSPropertyID) const;
   const CSSValue* GetPropertyCSSValue(AtomicString custom_property_name) const;
+  const CSSValue* GetPropertyCSSValue(const CSSPropertyName&) const;
   HeapHashMap<AtomicString, Member<const CSSValue>> GetVariables() const;
 
   const CSSValue* GetFontSizeCSSValuePreferringKeyword() const;
diff --git a/third_party/blink/renderer/core/css/css_math_operator.cc b/third_party/blink/renderer/core/css/css_math_operator.cc
new file mode 100644
index 0000000..38abe530
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_math_operator.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/css_math_operator.h"
+
+#include "third_party/blink/renderer/core/css/parser/css_parser_token.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+CSSMathOperator ParseCSSArithmeticOperator(const CSSParserToken& token) {
+  if (token.GetType() != kDelimiterToken)
+    return CSSMathOperator::kInvalid;
+  switch (token.Delimiter()) {
+    case '+':
+      return CSSMathOperator::kAdd;
+    case '-':
+      return CSSMathOperator::kSubtract;
+    case '*':
+      return CSSMathOperator::kMultiply;
+    case '/':
+      return CSSMathOperator::kDivide;
+    default:
+      return CSSMathOperator::kInvalid;
+  }
+}
+
+String ToString(CSSMathOperator op) {
+  switch (op) {
+    case CSSMathOperator::kAdd:
+      return "+";
+    case CSSMathOperator::kSubtract:
+      return "-";
+    case CSSMathOperator::kMultiply:
+      return "*";
+    case CSSMathOperator::kDivide:
+      return "/";
+    default:
+      NOTREACHED();
+      return String();
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_math_operator.h b/third_party/blink/renderer/core/css/css_math_operator.h
new file mode 100644
index 0000000..99fb20db
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_math_operator.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_MATH_OPERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_MATH_OPERATOR_H_
+
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class CSSParserToken;
+
+enum class CSSMathOperator { kAdd, kSubtract, kMultiply, kDivide, kInvalid };
+
+CSSMathOperator ParseCSSArithmeticOperator(const CSSParserToken& token);
+String ToString(CSSMathOperator);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_MATH_OPERATOR_H_
diff --git a/third_party/blink/renderer/core/css/css_paint_value.cc b/third_party/blink/renderer/core/css/css_paint_value.cc
index eb8ff347..7b57a8e 100644
--- a/third_party/blink/renderer/core/css/css_paint_value.cc
+++ b/third_party/blink/renderer/core/css/css_paint_value.cc
@@ -20,7 +20,8 @@
 CSSPaintValue::CSSPaintValue(CSSCustomIdentValue* name)
     : CSSImageGeneratorValue(kPaintClass),
       name_(name),
-      paint_image_generator_observer_(MakeGarbageCollected<Observer>(this)) {}
+      paint_image_generator_observer_(MakeGarbageCollected<Observer>(this)),
+      paint_off_thread_(true) {}
 
 CSSPaintValue::CSSPaintValue(
     CSSCustomIdentValue* name,
@@ -68,25 +69,31 @@
   // For Off-Thread PaintWorklet, we just collect the necessary inputs together
   // and defer the actual JavaScript call until much later (during cc Raster).
   if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
-    // TODO(crbug.com/946515): Break dependency on LayoutObject.
-    const LayoutObject& layout_object =
-        static_cast<const LayoutObject&>(client);
-    Vector<CSSPropertyID> native_properties =
-        generator_->NativeInvalidationProperties();
-    Vector<AtomicString> custom_properties =
-        generator_->CustomInvalidationProperties();
-    float zoom = layout_object.StyleRef().EffectiveZoom();
-    PaintWorkletStylePropertyMap::CrossThreadData style_data =
-        PaintWorkletStylePropertyMap::BuildCrossThreadData(
-            document, style, layout_object.GetNode(), native_properties,
-            custom_properties);
-    Vector<std::unique_ptr<CrossThreadStyleValue>> cross_thread_input_arguments;
-    BuildInputArgumentValues(cross_thread_input_arguments);
-    scoped_refptr<PaintWorkletInput> input =
-        base::MakeRefCounted<PaintWorkletInput>(
-            GetName(), target_size, zoom, generator_->WorkletId(),
-            std::move(style_data), std::move(cross_thread_input_arguments));
-    return PaintWorkletDeferredImage::Create(std::move(input), target_size);
+    if (paint_off_thread_) {
+      // TODO(crbug.com/946515): Break dependency on LayoutObject.
+      const LayoutObject& layout_object =
+          static_cast<const LayoutObject&>(client);
+      Vector<CSSPropertyID> native_properties =
+          generator_->NativeInvalidationProperties();
+      Vector<AtomicString> custom_properties =
+          generator_->CustomInvalidationProperties();
+      float zoom = layout_object.StyleRef().EffectiveZoom();
+      auto style_data = PaintWorkletStylePropertyMap::BuildCrossThreadData(
+          document, style, layout_object.GetNode(), native_properties,
+          custom_properties);
+      paint_off_thread_ = style_data.has_value();
+      if (paint_off_thread_) {
+        Vector<std::unique_ptr<CrossThreadStyleValue>>
+            cross_thread_input_arguments;
+        BuildInputArgumentValues(cross_thread_input_arguments);
+        scoped_refptr<PaintWorkletInput> input =
+            base::MakeRefCounted<PaintWorkletInput>(
+                GetName(), target_size, zoom, generator_->WorkletId(),
+                std::move(style_data.value()),
+                std::move(cross_thread_input_arguments));
+        return PaintWorkletDeferredImage::Create(std::move(input), target_size);
+      }
+    }
   }
 
   return generator_->Paint(client, target_size, parsed_input_arguments_);
diff --git a/third_party/blink/renderer/core/css/css_paint_value.h b/third_party/blink/renderer/core/css/css_paint_value.h
index 3b67651..91433563 100644
--- a/third_party/blink/renderer/core/css/css_paint_value.h
+++ b/third_party/blink/renderer/core/css/css_paint_value.h
@@ -93,6 +93,7 @@
   Member<Observer> paint_image_generator_observer_;
   Member<CSSStyleValueVector> parsed_input_arguments_;
   Vector<scoped_refptr<CSSVariableData>> argument_variable_data_;
+  bool paint_off_thread_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/css/css_primitive_value_test.cc b/third_party/blink/renderer/core/css/css_primitive_value_test.cc
index b1c55ff..c375ad1 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_primitive_value_test.cc
@@ -25,7 +25,8 @@
   return CSSPrimitiveValue::Create(
       CSSCalcValue::Create(CSSCalcValue::CreateExpressionNode(
           CSSCalcValue::CreateExpressionNode(Create(a)),
-          CSSCalcValue::CreateExpressionNode(Create(b)), kCalcAdd)));
+          CSSCalcValue::CreateExpressionNode(Create(b)),
+          CSSMathOperator::kAdd)));
 }
 
 TEST(CSSPrimitiveValueTest, IsTime) {
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 276b0545..d1c31756 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -4553,6 +4553,24 @@
       is_property: false,
       runtime_flag: "DisplayCutoutAPI",
     },
+    {
+      name: "syntax",
+      is_descriptor: true,
+      is_property: false,
+      runtime_flag: "CSSVariables2AtProperty",
+    },
+    {
+      name: "initial-value",
+      is_descriptor: true,
+      is_property: false,
+      runtime_flag: "CSSVariables2AtProperty",
+    },
+    {
+      name: "inherits",
+      is_descriptor: true,
+      is_property: false,
+      runtime_flag: "CSSVariables2AtProperty",
+    },
 
     // Shorthands
     {
diff --git a/third_party/blink/renderer/core/css/css_property_rule.cc b/third_party/blink/renderer/core/css/css_property_rule.cc
new file mode 100644
index 0000000..643b1e8
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_property_rule.cc
@@ -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.
+
+#include "third_party/blink/renderer/core/css/css_property_rule.h"
+
+#include "third_party/blink/renderer/core/css/css_property_value_set.h"
+#include "third_party/blink/renderer/core/css/style_rule.h"
+#include "third_party/blink/renderer/core/css/style_rule_css_style_declaration.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+CSSPropertyRule::CSSPropertyRule(StyleRuleProperty* property_rule,
+                                 CSSStyleSheet* sheet)
+    : CSSRule(sheet), property_rule_(property_rule) {}
+
+CSSPropertyRule::~CSSPropertyRule() = default;
+
+CSSStyleDeclaration* CSSPropertyRule::style() const {
+  if (!properties_cssom_wrapper_) {
+    properties_cssom_wrapper_ =
+        MakeGarbageCollected<StyleRuleCSSStyleDeclaration>(
+            property_rule_->MutableProperties(),
+            const_cast<CSSPropertyRule*>(this));
+  }
+
+  return properties_cssom_wrapper_.Get();
+}
+
+String CSSPropertyRule::cssText() const {
+  // TODO(https://crbug.com/978783): Implement this.
+  return "";
+}
+
+void CSSPropertyRule::Reattach(StyleRuleBase* rule) {
+  DCHECK(rule);
+  property_rule_ = To<StyleRuleProperty>(rule);
+  if (properties_cssom_wrapper_)
+    properties_cssom_wrapper_->Reattach(property_rule_->MutableProperties());
+}
+
+void CSSPropertyRule::Trace(blink::Visitor* visitor) {
+  visitor->Trace(property_rule_);
+  visitor->Trace(properties_cssom_wrapper_);
+  CSSRule::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_property_rule.h b/third_party/blink/renderer/core/css/css_property_rule.h
new file mode 100644
index 0000000..e977bb3
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_property_rule.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 THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_PROPERTY_RULE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_PROPERTY_RULE_H_
+
+#include "third_party/blink/renderer/core/css/css_rule.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
+
+namespace blink {
+
+class CSSStyleDeclaration;
+class StyleRuleProperty;
+class StyleRuleCSSStyleDeclaration;
+
+class CSSPropertyRule final : public CSSRule {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  CSSPropertyRule(StyleRuleProperty*, CSSStyleSheet*);
+  ~CSSPropertyRule() override;
+
+  String cssText() const override;
+  void Reattach(StyleRuleBase*) override;
+
+  CSSStyleDeclaration* style() const;
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  CSSRule::Type type() const override { return kPropertyRule; }
+
+  Member<StyleRuleProperty> property_rule_;
+  mutable Member<StyleRuleCSSStyleDeclaration> properties_cssom_wrapper_;
+};
+
+template <>
+struct DowncastTraits<CSSPropertyRule> {
+  static bool AllowFrom(const CSSRule& rule) {
+    return rule.type() == CSSRule::kPropertyRule;
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_PROPERTY_RULE_H_
diff --git a/third_party/blink/renderer/core/css/css_property_rule.idl b/third_party/blink/renderer/core/css/css_property_rule.idl
new file mode 100644
index 0000000..6ab4ed7
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_property_rule.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.
+
+[
+    RuntimeEnabled=CSSVariables2AtProperty
+] interface CSSPropertyRule : CSSRule {
+    readonly attribute CSSStyleDeclaration style;
+};
diff --git a/third_party/blink/renderer/core/css/css_rule.h b/third_party/blink/renderer/core/css/css_rule.h
index d44ffbff..3036f6e 100644
--- a/third_party/blink/renderer/core/css/css_rule.h
+++ b/third_party/blink/renderer/core/css/css_rule.h
@@ -42,6 +42,8 @@
  public:
   ~CSSRule() override = default;
 
+  // The values must match the table in [1]. See also css_rule.idl.
+  // [1] https://wiki.csswg.org/spec/cssom-constants
   enum Type {
     kStyleRule = 1,
     kCharsetRule = 2,
@@ -55,6 +57,11 @@
     kSupportsRule = 12,
     kFontFeatureValuesRule = 14,
     kViewportRule = 15,
+    // Experimental features below. Such features must be greater than 1000:
+    // the 0-1000 range is reserved by the CSS Working Group.
+    //
+    // TODO(https://crbug.com/978781): Spec a proper number.
+    kPropertyRule = 1001,
   };
 
   virtual Type type() const = 0;
diff --git a/third_party/blink/renderer/core/css/css_rule.idl b/third_party/blink/renderer/core/css/css_rule.idl
index 49e771e..d63735b 100644
--- a/third_party/blink/renderer/core/css/css_rule.idl
+++ b/third_party/blink/renderer/core/css/css_rule.idl
@@ -53,4 +53,8 @@
     // CSS Device Adaptation
     // https://drafts.csswg.org/css-device-adapt/#css-rule-interface
     [RuntimeEnabled=CSSViewport] const unsigned short VIEWPORT_RULE = 15;
+
+    // CSS Properties and Values Level 1
+    // TODO(https://crbug.com/978781): Spec a proper number.
+    [RuntimeEnabled=CSSVariables2AtProperty] const unsigned short PROPERTY_RULE = 1001;
 };
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index f92c9f1e..ac46936 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -1107,8 +1107,13 @@
     "ray",
     "sides",
 
+    // math functions
+    // https://drafts.csswg.org/css-values-4/#math
     "calc",
     "-webkit-calc",
+    "min",
+    "max",
+    "clamp",
 
     // scroll-snap-type
     // none
@@ -1176,6 +1181,10 @@
     // pan-down
     // none
 
+    // @property
+    "true",
+    "false",
+
     // (prefers-*:) media features
     "no-preference",
 
diff --git a/third_party/blink/renderer/core/css/cssom/css_math_product.cc b/third_party/blink/renderer/core/css/cssom/css_math_product.cc
index 39f7bb9..7ab8056 100644
--- a/third_party/blink/renderer/core/css/cssom/css_math_product.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_math_product.cc
@@ -88,11 +88,12 @@
 
   CSSCalcExpressionNode* node = CSSCalcValue::CreateExpressionNode(
       NumericValues()[0]->ToCalcExpressionNode(),
-      NumericValues()[1]->ToCalcExpressionNode(), kCalcMultiply);
+      NumericValues()[1]->ToCalcExpressionNode(), CSSMathOperator::kMultiply);
 
   for (wtf_size_t i = 2; i < NumericValues().size(); i++) {
     node = CSSCalcValue::CreateExpressionNode(
-        node, NumericValues()[i]->ToCalcExpressionNode(), kCalcMultiply);
+        node, NumericValues()[i]->ToCalcExpressionNode(),
+        CSSMathOperator::kMultiply);
   }
 
   return node;
diff --git a/third_party/blink/renderer/core/css/cssom/css_math_sum.cc b/third_party/blink/renderer/core/css/cssom/css_math_sum.cc
index 06327d1..3697b908 100644
--- a/third_party/blink/renderer/core/css/cssom/css_math_sum.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_math_sum.cc
@@ -106,11 +106,12 @@
 
   CSSCalcExpressionNode* node = CSSCalcValue::CreateExpressionNode(
       NumericValues()[0]->ToCalcExpressionNode(),
-      NumericValues()[1]->ToCalcExpressionNode(), kCalcAdd);
+      NumericValues()[1]->ToCalcExpressionNode(), CSSMathOperator::kAdd);
 
   for (wtf_size_t i = 2; i < NumericValues().size(); i++) {
     node = CSSCalcValue::CreateExpressionNode(
-        node, NumericValues()[i]->ToCalcExpressionNode(), kCalcAdd);
+        node, NumericValues()[i]->ToCalcExpressionNode(),
+        CSSMathOperator::kAdd);
   }
 
   return node;
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
index a883fe0..c0ea6b3 100644
--- a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
@@ -79,10 +79,10 @@
   return CSSUnitValue::Create(final_value, unit_other_than_number);
 }
 
-CalcOperator CanonicalOperator(CalcOperator op) {
-  if (op == kCalcAdd || op == kCalcSubtract)
-    return kCalcAdd;
-  return kCalcMultiply;
+CSSMathOperator CanonicalOperator(CSSMathOperator op) {
+  if (op == CSSMathOperator::kAdd || op == CSSMathOperator::kSubtract)
+    return CSSMathOperator::kAdd;
+  return CSSMathOperator::kMultiply;
 }
 
 bool CanCombineNodes(const CSSCalcExpressionNode& root,
@@ -94,12 +94,12 @@
                                      CanonicalOperator(node.OperatorType());
 }
 
-CSSNumericValue* NegateOrInvertIfRequired(CalcOperator parent_op,
+CSSNumericValue* NegateOrInvertIfRequired(CSSMathOperator parent_op,
                                           CSSNumericValue* value) {
   DCHECK(value);
-  if (parent_op == kCalcSubtract)
+  if (parent_op == CSSMathOperator::kSubtract)
     return CSSMathNegate::Create(value);
-  if (parent_op == kCalcDivide)
+  if (parent_op == CSSMathOperator::kDivide)
     return CSSMathInvert::Create(value);
   return value;
 }
@@ -162,7 +162,8 @@
   // Our algorithm collects the children in reverse order, so we have to reverse
   // the values.
   std::reverse(values.begin(), values.end());
-  if (root.OperatorType() == kCalcAdd || root.OperatorType() == kCalcSubtract)
+  if (root.OperatorType() == CSSMathOperator::kAdd ||
+      root.OperatorType() == CSSMathOperator::kSubtract)
     return CSSMathSum::Create(std::move(values));
   return CSSMathProduct::Create(std::move(values));
 }
diff --git a/third_party/blink/renderer/core/css/cssom/css_url_image_value.cc b/third_party/blink/renderer/core/css/cssom/css_url_image_value.cc
index 97005aba..922cd34 100644
--- a/third_party/blink/renderer/core/css/cssom/css_url_image_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_url_image_value.cc
@@ -5,34 +5,11 @@
 #include "third_party/blink/renderer/core/css/cssom/css_url_image_value.h"
 
 #include "third_party/blink/renderer/core/css/css_image_value.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
 #include "third_party/blink/renderer/core/style/style_image.h"
-#include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
 
-CSSURLImageValue* CSSURLImageValue::Create(ScriptState* script_state,
-                                           const AtomicString& url,
-                                           ExceptionState& exception_state) {
-  const auto* execution_context = ExecutionContext::From(script_state);
-  DCHECK(execution_context);
-  KURL parsed_url = execution_context->CompleteURL(url);
-  if (!parsed_url.IsValid()) {
-    exception_state.ThrowTypeError("Failed to parse URL from " + url);
-    return nullptr;
-  }
-  // Use absolute URL for CSSImageValue but keep relative URL for
-  // getter and serialization.
-  return MakeGarbageCollected<CSSURLImageValue>(
-      *CSSImageValue::Create(url, parsed_url, Referrer()));
-}
-
-CSSURLImageValue* CSSURLImageValue::FromCSSValue(const CSSImageValue& value) {
-  return MakeGarbageCollected<CSSURLImageValue>(value);
-}
-
 const String& CSSURLImageValue::url() const {
   return value_->RelativeUrl();
 }
diff --git a/third_party/blink/renderer/core/css/cssom/css_url_image_value.h b/third_party/blink/renderer/core/css/cssom/css_url_image_value.h
index 9635cb3..f8ca0aa2 100644
--- a/third_party/blink/renderer/core/css/cssom/css_url_image_value.h
+++ b/third_party/blink/renderer/core/css/cssom/css_url_image_value.h
@@ -10,17 +10,10 @@
 
 namespace blink {
 
-class ScriptState;
 class CSSImageValue;
 
 class CORE_EXPORT CSSURLImageValue final : public CSSStyleImageValue {
  public:
-  static CSSURLImageValue* Create(ScriptState*,
-                                  const AtomicString& url,
-                                  ExceptionState&);
-
-  static CSSURLImageValue* FromCSSValue(const CSSImageValue&);
-
   explicit CSSURLImageValue(const CSSImageValue& value) : value_(value) {}
 
   const String& url() const;
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.cc
index 06c9242..a90eb46 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.cc
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.cc
@@ -54,7 +54,7 @@
   const HeapVector<PaintWorkletStylePropertyMap::StylePropertyMapEntry> values_;
 };
 
-void BuildNativeValues(const ComputedStyle& style,
+bool BuildNativeValues(const ComputedStyle& style,
                        Node* styled_node,
                        const Vector<CSSPropertyID>& native_properties,
                        PaintWorkletStylePropertyMap::CrossThreadData& data) {
@@ -70,14 +70,17 @@
             .CrossThreadStyleValueFromComputedStyle(
                 style, /* layout_object */ nullptr, styled_node,
                 /* allow_visited_style */ false);
+    if (value->GetType() == CrossThreadStyleValue::StyleValueType::kUnknownType)
+      return false;
     String key = CSSProperty::Get(property_id).GetPropertyNameString();
     if (!key.IsSafeToSendToAnotherThread())
       key = key.IsolatedCopy();
     data.Set(key, std::move(value));
   }
+  return true;
 }
 
-void BuildCustomValues(const Document& document,
+bool BuildCustomValues(const Document& document,
                        const ComputedStyle& style,
                        Node* styled_node,
                        const Vector<AtomicString>& custom_properties,
@@ -89,18 +92,21 @@
         ref.GetProperty().CrossThreadStyleValueFromComputedStyle(
             style, /* layout_object */ nullptr, styled_node,
             /* allow_visited_style */ false);
+    if (value->GetType() == CrossThreadStyleValue::StyleValueType::kUnknownType)
+      return false;
     // Ensure that the String can be safely passed cross threads.
     String key = property_name.GetString();
     if (!key.IsSafeToSendToAnotherThread())
       key = key.IsolatedCopy();
     data.Set(key, std::move(value));
   }
+  return true;
 }
 
 }  // namespace
 
 // static
-PaintWorkletStylePropertyMap::CrossThreadData
+base::Optional<PaintWorkletStylePropertyMap::CrossThreadData>
 PaintWorkletStylePropertyMap::BuildCrossThreadData(
     const Document& document,
     const ComputedStyle& style,
@@ -111,8 +117,10 @@
   PaintWorkletStylePropertyMap::CrossThreadData data;
   data.ReserveCapacityForSize(native_properties.size() +
                               custom_properties.size());
-  BuildNativeValues(style, styled_node, native_properties, data);
-  BuildCustomValues(document, style, styled_node, custom_properties, data);
+  if (!BuildNativeValues(style, styled_node, native_properties, data))
+    return base::nullopt;
+  if (!BuildCustomValues(document, style, styled_node, custom_properties, data))
+    return base::nullopt;
   return data;
 }
 
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h
index 7b59e24..f769583 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h
@@ -31,7 +31,7 @@
       HashMap<String, std::unique_ptr<CrossThreadStyleValue>>;
   // Build the data that will be passed to the worklet thread to construct a
   // style map. Should be called on the main thread only.
-  static CrossThreadData BuildCrossThreadData(
+  static base::Optional<CrossThreadData> BuildCrossThreadData(
       const Document&,
       const ComputedStyle&,
       Node* styled_node,
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
index 2e8244d..e52f5a40 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
@@ -97,53 +97,34 @@
     exception_state.ClearException();
   }
 
-  void CheckStyleMap(base::WaitableEvent* waitable_event,
-                     scoped_refptr<PaintWorkletInput> input) {
+  void CheckCrossThreadData(base::WaitableEvent* waitable_event,
+                            scoped_refptr<PaintWorkletInput> input) {
     DCHECK(!IsMainThread());
 
     PaintWorkletStylePropertyMap* map =
         MakeGarbageCollected<PaintWorkletStylePropertyMap>(
             input->StyleMapData());
     DCHECK(map);
-    DummyExceptionStateForTesting exception_state;
-    CheckNativeProperties(map, exception_state);
-    CheckCustomProperties(map, exception_state);
 
     const PaintWorkletStylePropertyMap::CrossThreadData& data =
         map->StyleMapDataForTest();
-    EXPECT_EQ(data.size(), 8u);
-    EXPECT_EQ(data.at("color")->ToCSSStyleValue()->CSSText(), "rgb(0, 255, 0)");
-    EXPECT_EQ(data.at("color")->ToCSSStyleValue()->GetType(),
-              CSSStyleValue::StyleValueType::kUnknownType);
-    EXPECT_EQ(data.at("align-items")->ToCSSStyleValue()->CSSText(), "normal");
-    EXPECT_EQ(data.at("align-items")->ToCSSStyleValue()->GetType(),
-              CSSStyleValue::StyleValueType::kUnknownType);
-    EXPECT_EQ(
-        static_cast<CSSKeywordValue*>(data.at("display")->ToCSSStyleValue())
-            ->value(),
-        "block");
+    EXPECT_EQ(data.size(), 4u);
+    EXPECT_EQ(data.at("--foo")->ToCSSStyleValue()->GetType(),
+              CSSStyleValue::StyleValueType::kUnitType);
+    EXPECT_EQ(To<CSSUnitValue>(data.at("--foo")->ToCSSStyleValue())->value(),
+              134);
+    EXPECT_EQ(To<CSSUnitValue>(data.at("--foo")->ToCSSStyleValue())->unit(),
+              "px");
+    EXPECT_EQ(data.at("--bar")->ToCSSStyleValue()->GetType(),
+              CSSStyleValue::StyleValueType::kUnitType);
+    EXPECT_EQ(To<CSSUnitValue>(data.at("--bar")->ToCSSStyleValue())->value(),
+              42);
+    EXPECT_EQ(data.at("--loo")->ToCSSStyleValue()->GetType(),
+              CSSStyleValue::StyleValueType::kKeywordType);
+    EXPECT_EQ(To<CSSKeywordValue>(data.at("--loo")->ToCSSStyleValue())->value(),
+              "test");
     EXPECT_EQ(data.at("display")->ToCSSStyleValue()->GetType(),
               CSSStyleValue::StyleValueType::kKeywordType);
-    EXPECT_EQ(data.at("--foo")->ToCSSStyleValue()->CSSText(), "PaintWorklet");
-    EXPECT_EQ(data.at("--foo")->ToCSSStyleValue()->GetType(),
-              CSSStyleValue::StyleValueType::kUnknownType);
-    EXPECT_EQ(data.at("--bar")->ToCSSStyleValue()->CSSText(), "");
-    EXPECT_EQ(data.at("--bar")->ToCSSStyleValue()->GetType(),
-              CSSStyleValue::StyleValueType::kUnknownType);
-    EXPECT_EQ(data.at("--keyword")->ToCSSStyleValue()->GetType(),
-              CSSStyleValue::StyleValueType::kKeywordType);
-    EXPECT_EQ(
-        To<CSSKeywordValue>(data.at("--keyword")->ToCSSStyleValue())->value(),
-        "test");
-    EXPECT_EQ(data.at("--x")->ToCSSStyleValue()->GetType(),
-              CSSStyleValue::StyleValueType::kUnitType);
-    EXPECT_EQ(To<CSSUnitValue>(data.at("--x")->ToCSSStyleValue())->value(), 10);
-    EXPECT_EQ(To<CSSUnitValue>(data.at("--x")->ToCSSStyleValue())->unit(),
-              "px");
-    EXPECT_EQ(data.at("--y")->ToCSSStyleValue()->GetType(),
-              CSSStyleValue::StyleValueType::kUnknownType);
-    EXPECT_EQ(data.at("--y")->ToCSSStyleValue()->CSSText(), "rgb(0, 255, 0)");
-
     waitable_event->Signal();
   }
 
@@ -151,44 +132,28 @@
   std::unique_ptr<blink::Thread> thread_;
 };
 
-// This test ensures that Blink::PaintWorkletInput can be safely passed cross
-// threads and no information is lost.
-TEST_F(PaintWorkletStylePropertyMapTest, PassValuesCrossThread) {
-  Vector<CSSPropertyID> native_properties({CSSPropertyID::kColor,
-                                           CSSPropertyID::kAlignItems,
-                                           CSSPropertyID::kDisplay});
-  GetDocument().documentElement()->style()->setProperty(
-      &GetDocument(), "color", "rgb(0, 255, 0)", "", ASSERT_NO_EXCEPTION);
-  GetDocument().documentElement()->style()->setProperty(
-      &GetDocument(), "display", "block", "", ASSERT_NO_EXCEPTION);
-  Vector<AtomicString> custom_properties(
-      {"--foo", "--bar", "--keyword", "--x", "--y"});
-  css_test_helpers::RegisterProperty(GetDocument(), "--keyword", "test", "test",
+TEST_F(PaintWorkletStylePropertyMapTest, CreateSupportedCrossThreadData) {
+  Vector<CSSPropertyID> native_properties({CSSPropertyID::kDisplay});
+  Vector<AtomicString> custom_properties({"--foo", "--bar", "--loo"});
+  css_test_helpers::RegisterProperty(GetDocument(), "--foo", "<length>",
+                                     "134px", false);
+  css_test_helpers::RegisterProperty(GetDocument(), "--bar", "<number>", "42",
                                      false);
-  css_test_helpers::RegisterProperty(GetDocument(), "--x", "<length>", "42px",
+  css_test_helpers::RegisterProperty(GetDocument(), "--loo", "test", "test",
                                      false);
-  css_test_helpers::RegisterProperty(GetDocument(), "--y", "<color>",
-                                     "rgb(0, 0, 0)", false);
-  GetDocument().documentElement()->style()->setProperty(
-      &GetDocument(), "--foo", "PaintWorklet", "", ASSERT_NO_EXCEPTION);
-  GetDocument().documentElement()->style()->setProperty(
-      &GetDocument(), "--keyword", "test", "", ASSERT_NO_EXCEPTION);
-  GetDocument().documentElement()->style()->setProperty(
-      &GetDocument(), "--x", "10px", "", ASSERT_NO_EXCEPTION);
-  GetDocument().documentElement()->style()->setProperty(
-      &GetDocument(), "--y", "rgb(0, 255, 0)", "", ASSERT_NO_EXCEPTION);
 
   UpdateAllLifecyclePhasesForTest();
   Node* node = PageNode();
 
   Vector<std::unique_ptr<CrossThreadStyleValue>> input_arguments;
-  PaintWorkletStylePropertyMap::CrossThreadData data =
-      PaintWorkletStylePropertyMap::BuildCrossThreadData(
-          GetDocument(), node->ComputedStyleRef(), node, native_properties,
-          custom_properties);
+  auto data = PaintWorkletStylePropertyMap::BuildCrossThreadData(
+      GetDocument(), node->ComputedStyleRef(), node, native_properties,
+      custom_properties);
+
+  EXPECT_TRUE(data.has_value());
   scoped_refptr<PaintWorkletInput> input =
       base::MakeRefCounted<PaintWorkletInput>("test", FloatSize(100, 100), 1.0f,
-                                              1, std::move(data),
+                                              1, std::move(data.value()),
                                               std::move(input_arguments));
   DCHECK(input);
 
@@ -197,13 +162,44 @@
   base::WaitableEvent waitable_event;
   PostCrossThreadTask(
       *thread_->GetTaskRunner(), FROM_HERE,
-      CrossThreadBindOnce(&PaintWorkletStylePropertyMapTest::CheckStyleMap,
-                          CrossThreadUnretained(this),
-                          CrossThreadUnretained(&waitable_event),
-                          std::move(input)));
+      CrossThreadBindOnce(
+          &PaintWorkletStylePropertyMapTest::CheckCrossThreadData,
+          CrossThreadUnretained(this), CrossThreadUnretained(&waitable_event),
+          std::move(input)));
   waitable_event.Wait();
 
   ShutDownThread();
 }
 
+TEST_F(PaintWorkletStylePropertyMapTest, UnsupportedCrossThreadData) {
+  Vector<CSSPropertyID> native_properties1;
+  Vector<AtomicString> custom_properties1({"--foo", "--bar", "--loo"});
+  css_test_helpers::RegisterProperty(GetDocument(), "--foo", "<color>",
+                                     "rgb(0, 0, 0)", false);
+  css_test_helpers::RegisterProperty(GetDocument(), "--bar", "<number>", "42",
+                                     false);
+  css_test_helpers::RegisterProperty(GetDocument(), "--loo", "test", "test",
+                                     false);
+
+  UpdateAllLifecyclePhasesForTest();
+  Node* node = PageNode();
+
+  Vector<std::unique_ptr<CrossThreadStyleValue>> input_arguments;
+  auto data1 = PaintWorkletStylePropertyMap::BuildCrossThreadData(
+      GetDocument(), node->ComputedStyleRef(), node, native_properties1,
+      custom_properties1);
+
+  EXPECT_FALSE(data1.has_value());
+
+  Vector<CSSPropertyID> native_properties2(
+      {CSSPropertyID::kDisplay, CSSPropertyID::kColor});
+  Vector<AtomicString> custom_properties2;
+
+  auto data2 = PaintWorkletStylePropertyMap::BuildCrossThreadData(
+      GetDocument(), node->ComputedStyleRef(), node, native_properties2,
+      custom_properties2);
+
+  EXPECT_FALSE(data2.has_value());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/style_value_factory.cc b/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
index 7638247..03372727 100644
--- a/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
+++ b/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
@@ -54,9 +54,8 @@
     return CSSKeywordValue::FromCSSValue(value);
   if (auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value))
     return CSSNumericValue::FromCSSValue(*primitive_value);
-  if (auto* image_value = DynamicTo<CSSImageValue>(value)) {
-    return CSSURLImageValue::FromCSSValue(*image_value->Clone());
-  }
+  if (auto* image_value = DynamicTo<CSSImageValue>(value))
+    return MakeGarbageCollected<CSSURLImageValue>(*image_value->Clone());
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/css/local_font_face_source.cc b/third_party/blink/renderer/core/css/local_font_face_source.cc
index df60a91..2cac90d 100644
--- a/third_party/blink/renderer/core/css/local_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/local_font_face_source.cc
@@ -35,7 +35,6 @@
       font_selector_(font_selector),
       font_name_(font_name),
       weak_factory_(this) {
-  was_resolved_ = IsLocalNonBlocking();
 }
 
 LocalFontFaceSource::~LocalFontFaceSource() {}
@@ -80,7 +79,6 @@
     return CreateLoadingFallbackFontData(font_description);
   }
 
-  DCHECK(was_resolved_);
   // FIXME(drott) crbug.com/627143: We still have the issue of matching
   // family name instead of postscript name for local fonts. However, we
   // should definitely not try to take into account the full requested
@@ -119,8 +117,6 @@
 }
 
 void LocalFontFaceSource::NotifyFontUniqueNameLookupReady() {
-  was_resolved_ = IsLocalFontAvailable(FontDescription());
-
   PruneTable();
 
   if (face_->FontLoaded(this)) {
@@ -141,7 +137,7 @@
 }
 
 bool LocalFontFaceSource::IsValid() const {
-  return IsLoading() || was_resolved_;
+  return IsLoading() || IsLocalFontAvailable(FontDescription());
 }
 
 void LocalFontFaceSource::LocalFontHistograms::Record(bool load_success) {
diff --git a/third_party/blink/renderer/core/css/local_font_face_source.h b/third_party/blink/renderer/core/css/local_font_face_source.h
index 655f5ba..2494e9fe 100644
--- a/third_party/blink/renderer/core/css/local_font_face_source.h
+++ b/third_party/blink/renderer/core/css/local_font_face_source.h
@@ -73,7 +73,6 @@
   Member<CSSFontFace> face_;
   Member<FontSelector> font_selector_;
 
-  bool was_resolved_ = false;
   AtomicString font_name_;
   LocalFontHistograms histograms_;
   base::WeakPtrFactory<LocalFontFaceSource> weak_factory_;
diff --git a/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc b/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc
index b0d61a7..5971e24 100644
--- a/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc
@@ -4,13 +4,16 @@
 
 #include "third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.h"
 
+#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
 #include "third_party/blink/renderer/core/css/css_font_face_src_value.h"
+#include "third_party/blink/renderer/core/css/css_string_value.h"
 #include "third_party/blink/renderer/core/css/css_unicode_range_value.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_mode.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
+#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
 #include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
 #include "third_party/blink/renderer/core/css/properties/css_property.h"
 
@@ -209,14 +212,53 @@
   return ParseFontFaceDescriptor(id, range, context);
 }
 
+CSSValue* AtRuleDescriptorParser::ParseAtPropertyDescriptor(
+    AtRuleDescriptorID id,
+    CSSParserTokenRange& range,
+    const CSSParserContext& context) {
+  CSSValue* parsed_value = nullptr;
+  switch (id) {
+    case AtRuleDescriptorID::Syntax:
+      range.ConsumeWhitespace();
+      parsed_value = css_property_parser_helpers::ConsumeString(range);
+      break;
+    case AtRuleDescriptorID::InitialValue: {
+      // Note that we must retain leading whitespace here.
+      return CSSVariableParser::ParseDeclarationValue(
+          g_null_atom, range, false /* is_animation_tainted */, context);
+    }
+    case AtRuleDescriptorID::Inherits:
+      range.ConsumeWhitespace();
+      parsed_value =
+          css_property_parser_helpers::ConsumeIdent<CSSValueID::kTrue,
+                                                    CSSValueID::kFalse>(range);
+      break;
+    default:
+      break;
+  }
+
+  if (!parsed_value || !range.AtEnd())
+    return nullptr;
+
+  return parsed_value;
+}
+
 bool AtRuleDescriptorParser::ParseAtRule(
     AtRuleDescriptorID id,
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     HeapVector<CSSPropertyValue, 256>& parsed_descriptors) {
+  const CSSParserTokenRange original_range = range;
+
   // TODO(meade): Handle other descriptor types here.
   CSSValue* result =
       AtRuleDescriptorParser::ParseFontFaceDescriptor(id, range, context);
+
+  if (!result) {
+    range = original_range;
+    result =
+        AtRuleDescriptorParser::ParseAtPropertyDescriptor(id, range, context);
+  }
   if (!result)
     return false;
   // Convert to CSSPropertyID for legacy compatibility,
diff --git a/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.h b/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.h
index 51a30a7..9b275e7 100644
--- a/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.h
+++ b/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.h
@@ -31,6 +31,9 @@
                                            const CSSParserContext&);
   static CSSValue* ParseFontFaceDeclaration(CSSParserTokenRange&,
                                             const CSSParserContext&);
+  static CSSValue* ParseAtPropertyDescriptor(AtRuleDescriptorID,
+                                             CSSParserTokenRange&,
+                                             const CSSParserContext&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/at_rule_names.json5 b/third_party/blink/renderer/core/css/parser/at_rule_names.json5
index ccd71f4..c5c1f00 100644
--- a/third_party/blink/renderer/core/css/parser/at_rule_names.json5
+++ b/third_party/blink/renderer/core/css/parser/at_rule_names.json5
@@ -40,6 +40,12 @@
       name: "height",
     },
     {
+      name: "inherits"
+    },
+    {
+      name: "initial-value"
+    },
+    {
       name: "max-height",
     },
     {
@@ -64,6 +70,9 @@
       name: "src",
     },
     {
+      name: "syntax"
+    },
+    {
       name: "unicode-range",
     },
     {
diff --git a/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc b/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc
index 9b1926aa..c6414cf0 100644
--- a/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc
+++ b/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc
@@ -26,6 +26,8 @@
     return kCSSAtRuleNamespace;
   if (EqualIgnoringASCIICase(name, "page"))
     return kCSSAtRulePage;
+  if (EqualIgnoringASCIICase(name, "property"))
+    return kCSSAtRuleProperty;
   if (EqualIgnoringASCIICase(name, "supports"))
     return kCSSAtRuleSupports;
   if (EqualIgnoringASCIICase(name, "viewport"))
@@ -63,6 +65,9 @@
     case kCSSAtRulePage:
       feature = WebFeature::kCSSAtRulePage;
       break;
+    case kCSSAtRuleProperty:
+      feature = WebFeature::kCSSAtRuleProperty;
+      return;
     case kCSSAtRuleSupports:
       feature = WebFeature::kCSSAtRuleSupports;
       break;
diff --git a/third_party/blink/renderer/core/css/parser/css_at_rule_id.h b/third_party/blink/renderer/core/css/parser/css_at_rule_id.h
index 8940436..73fa0fd 100644
--- a/third_party/blink/renderer/core/css/parser/css_at_rule_id.h
+++ b/third_party/blink/renderer/core/css/parser/css_at_rule_id.h
@@ -21,6 +21,7 @@
   kCSSAtRuleMedia,
   kCSSAtRuleNamespace,
   kCSSAtRulePage,
+  kCSSAtRuleProperty,
   kCSSAtRuleSupports,
   kCSSAtRuleViewport,
   kCSSAtRuleWebkitKeyframes,
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index 80b44b40..c9b448d 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -546,6 +546,8 @@
       return ConsumeKeyframesRule(false, prelude, prelude_offset, stream);
     case kCSSAtRulePage:
       return ConsumePageRule(prelude, prelude_offset, stream);
+    case kCSSAtRuleProperty:
+      return ConsumePropertyRule(prelude, prelude_offset, stream);
     default:
       return nullptr;  // Parse error, unrecognised at-rule with block
   }
@@ -863,6 +865,30 @@
       CreateCSSPropertyValueSet(parsed_properties_, context_->Mode()));
 }
 
+StyleRuleProperty* CSSParserImpl::ConsumePropertyRule(
+    CSSParserTokenRange prelude,
+    const RangeOffset& prelude_offset,
+    CSSParserTokenStream& block) {
+  if (!RuntimeEnabledFeatures::CSSVariables2AtPropertyEnabled())
+    return nullptr;
+
+  const CSSParserToken& name_token = prelude.ConsumeIncludingWhitespace();
+  if (!prelude.AtEnd())
+    return nullptr;
+  if (!CSSVariableParser::IsValidVariableName(name_token))
+    return nullptr;
+  String name = name_token.Value().ToString();
+
+  if (observer_) {
+    observer_->StartRuleHeader(StyleRule::kProperty, prelude_offset.start);
+    observer_->EndRuleHeader(prelude_offset.end);
+  }
+
+  ConsumeDeclarationList(block, StyleRule::kProperty);
+  return StyleRuleProperty::Create(
+      name, CreateCSSPropertyValueSet(parsed_properties_, context_->Mode()));
+}
+
 StyleRuleKeyframe* CSSParserImpl::ConsumeKeyframeStyleRule(
     const CSSParserTokenRange prelude,
     const RangeOffset& prelude_offset,
@@ -1019,7 +1045,7 @@
 
   CSSPropertyID unresolved_property = CSSPropertyID::kInvalid;
   AtRuleDescriptorID atrule_id = AtRuleDescriptorID::Invalid;
-  if (rule_type == StyleRule::kFontFace) {
+  if (rule_type == StyleRule::kFontFace || rule_type == StyleRule::kProperty) {
     if (important)  // Invalid
       return;
     atrule_id = lhs.ParseAsAtRuleDescriptorID();
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.h b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
index 58c53ab..84d90f2 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
@@ -34,6 +34,7 @@
 class StyleRuleMedia;
 class StyleRuleNamespace;
 class StyleRulePage;
+class StyleRuleProperty;
 class StyleRuleSupports;
 class StyleRuleViewport;
 class StyleSheetContents;
@@ -174,6 +175,9 @@
   StyleRulePage* ConsumePageRule(CSSParserTokenRange prelude,
                                  const RangeOffset& prelude_offset,
                                  CSSParserTokenStream& block);
+  StyleRuleProperty* ConsumePropertyRule(CSSParserTokenRange prelude,
+                                         const RangeOffset& prelude_offset,
+                                         CSSParserTokenStream& block);
 
   StyleRuleKeyframe* ConsumeKeyframeStyleRule(CSSParserTokenRange prelude,
                                               const RangeOffset& prelude_offset,
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser.cc b/third_party/blink/renderer/core/css/parser/css_property_parser.cc
index 57c513db..db6b889 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser.cc
@@ -388,7 +388,8 @@
   // TODO(meade): This function should eventually take an AtRuleDescriptorID.
   const AtRuleDescriptorID id =
       CSSPropertyIDAsAtRuleDescriptor(resolved_property);
-  DCHECK_NE(id, AtRuleDescriptorID::Invalid);
+  if (id == AtRuleDescriptorID::Invalid)
+    return false;
   CSSValue* parsed_value =
       AtRuleDescriptorParser::ParseFontFaceDescriptor(id, range_, *context_);
   if (!parsed_value)
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index f4a9edf..1923178 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -1380,13 +1380,14 @@
       }
     } else {
       CSSPrimitiveValue* center =
-          ConsumeLengthOrPercent(args, context.Mode(), kValueRangeAll);
+          ConsumeLengthOrPercent(args, context.Mode(), kValueRangeNonNegative);
       if (!center)
         break;
       if (horizontal_size)
         return nullptr;
       horizontal_size = center;
-      center = ConsumeLengthOrPercent(args, context.Mode(), kValueRangeAll);
+      center =
+          ConsumeLengthOrPercent(args, context.Mode(), kValueRangeNonNegative);
       if (center) {
         vertical_size = center;
         ++i;
diff --git a/third_party/blink/renderer/core/css/parser/sizes_calc_parser.cc b/third_party/blink/renderer/core/css/parser/sizes_calc_parser.cc
index 95b22c9..cfcc796 100644
--- a/third_party/blink/renderer/core/css/parser/sizes_calc_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/sizes_calc_parser.cc
@@ -20,10 +20,10 @@
   return result_;
 }
 
-static bool OperatorPriority(UChar cc, bool& high_priority) {
-  if (cc == '+' || cc == '-')
+static bool OperatorPriority(CSSMathOperator cc, bool& high_priority) {
+  if (cc == CSSMathOperator::kAdd || cc == CSSMathOperator::kSubtract)
     high_priority = false;
-  else if (cc == '*' || cc == '/')
+  else if (cc == CSSMathOperator::kMultiply || cc == CSSMathOperator::kDivide)
     high_priority = true;
   else
     return false;
@@ -35,7 +35,8 @@
   // If the token is not an operator, then return. Else determine the
   // precedence of the new operator (op1).
   bool incoming_operator_priority;
-  if (!OperatorPriority(token.Delimiter(), incoming_operator_priority))
+  if (!OperatorPriority(ParseCSSArithmeticOperator(token),
+                        incoming_operator_priority))
     return false;
 
   while (!stack.IsEmpty()) {
@@ -45,7 +46,8 @@
     if (top_of_stack.GetType() != kDelimiterToken)
       break;
     bool stack_operator_priority;
-    if (!OperatorPriority(top_of_stack.Delimiter(), stack_operator_priority))
+    if (!OperatorPriority(ParseCSSArithmeticOperator(top_of_stack),
+                          stack_operator_priority))
       return false;
     // ...if op1 is left-associative (all currently supported
     // operators are) and its precedence is equal to that of op2, or
@@ -81,7 +83,7 @@
 
 void SizesCalcParser::AppendOperator(const CSSParserToken& token) {
   SizesCalcValue value;
-  value.operation = token.Delimiter();
+  value.operation = ParseCSSArithmeticOperator(token);
   value_list_.push_back(value);
 }
 
@@ -107,7 +109,7 @@
           return false;
         break;
       case kFunctionToken:
-        if (!EqualIgnoringASCIICase(token.Value(), "calc"))
+        if (token.FunctionId() != CSSValueID::kCalc)
           return false;
         // "calc(" is the same as "("
         FALLTHROUGH;
@@ -182,7 +184,8 @@
   return true;
 }
 
-static bool OperateOnStack(Vector<SizesCalcValue>& stack, UChar operation) {
+static bool OperateOnStack(Vector<SizesCalcValue>& stack,
+                           CSSMathOperator operation) {
   if (stack.size() < 2)
     return false;
   SizesCalcValue right_operand = stack.back();
@@ -191,28 +194,28 @@
   stack.pop_back();
   bool is_length;
   switch (operation) {
-    case '+':
+    case CSSMathOperator::kAdd:
       if (right_operand.is_length != left_operand.is_length)
         return false;
       is_length = (right_operand.is_length && left_operand.is_length);
       stack.push_back(
           SizesCalcValue(left_operand.value + right_operand.value, is_length));
       break;
-    case '-':
+    case CSSMathOperator::kSubtract:
       if (right_operand.is_length != left_operand.is_length)
         return false;
       is_length = (right_operand.is_length && left_operand.is_length);
       stack.push_back(
           SizesCalcValue(left_operand.value - right_operand.value, is_length));
       break;
-    case '*':
+    case CSSMathOperator::kMultiply:
       if (right_operand.is_length && left_operand.is_length)
         return false;
       is_length = (right_operand.is_length || left_operand.is_length);
       stack.push_back(
           SizesCalcValue(left_operand.value * right_operand.value, is_length));
       break;
-    case '/':
+    case CSSMathOperator::kDivide:
       if (right_operand.is_length || right_operand.value == 0)
         return false;
       stack.push_back(SizesCalcValue(left_operand.value / right_operand.value,
@@ -227,7 +230,7 @@
 bool SizesCalcParser::Calculate() {
   Vector<SizesCalcValue> stack;
   for (const auto& value : value_list_) {
-    if (value.operation == 0) {
+    if (value.operation == CSSMathOperator::kInvalid) {
       stack.push_back(value);
     } else {
       if (!OperateOnStack(stack, value.operation))
diff --git a/third_party/blink/renderer/core/css/parser/sizes_calc_parser.h b/third_party/blink/renderer/core/css/parser/sizes_calc_parser.h
index b5605dc7..fa3b96f 100644
--- a/third_party/blink/renderer/core/css/parser/sizes_calc_parser.h
+++ b/third_party/blink/renderer/core/css/parser/sizes_calc_parser.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PARSER_SIZES_CALC_PARSER_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/css_math_operator.h"
 #include "third_party/blink/renderer/core/css/media_values.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
@@ -15,14 +16,14 @@
 
 struct SizesCalcValue {
   DISALLOW_NEW();
-  double value;
-  bool is_length;
-  UChar operation;
+  double value = 0;
+  bool is_length = false;
+  CSSMathOperator operation = CSSMathOperator::kInvalid;
 
-  SizesCalcValue() : value(0), is_length(false), operation(0) {}
+  SizesCalcValue() = default;
 
   SizesCalcValue(double numeric_value, bool length)
-      : value(numeric_value), is_length(length), operation(0) {}
+      : value(numeric_value), is_length(length) {}
 };
 
 class CORE_EXPORT SizesCalcParser {
diff --git a/third_party/blink/renderer/core/css/property_registration.cc b/third_party/blink/renderer/core/css/property_registration.cc
index 040300d..4c45d91e 100644
--- a/third_party/blink/renderer/core/css/property_registration.cc
+++ b/third_party/blink/renderer/core/css/property_registration.cc
@@ -5,6 +5,9 @@
 #include "third_party/blink/renderer/core/css/property_registration.h"
 
 #include "third_party/blink/renderer/core/animation/css_interpolation_types_map.h"
+#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
+#include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_string_value.h"
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
 #include "third_party/blink/renderer/core/css/css_syntax_descriptor.h"
 #include "third_party/blink/renderer/core/css/css_syntax_string_parser.h"
@@ -18,6 +21,7 @@
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
+#include "third_party/blink/renderer/core/css/style_rule.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 
@@ -90,6 +94,75 @@
   return true;
 }
 
+static base::Optional<CSSSyntaxDescriptor> ConvertSyntax(
+    const CSSValue& value) {
+  return CSSSyntaxStringParser(To<CSSStringValue>(value).Value()).Parse();
+}
+
+static bool ConvertInherts(const CSSValue& value) {
+  CSSValueID inherits_id = To<CSSIdentifierValue>(value).GetValueID();
+  DCHECK(inherits_id == CSSValueID::kTrue || inherits_id == CSSValueID::kFalse);
+  return inherits_id == CSSValueID::kTrue;
+}
+
+static scoped_refptr<CSSVariableData> ConvertInitialVariableData(
+    const CSSValue* value) {
+  if (!value)
+    return nullptr;
+  return To<CSSCustomPropertyDeclaration>(*value).Value();
+}
+
+PropertyRegistration* PropertyRegistration::MaybeCreate(
+    Document& document,
+    const AtomicString& name,
+    StyleRuleProperty& rule) {
+  const auto& properties = rule.Properties();
+
+  // syntax
+  const CSSValue* syntax_value =
+      properties.GetPropertyCSSValue(CSSPropertyID::kSyntax);
+  if (!syntax_value)
+    return nullptr;
+  base::Optional<CSSSyntaxDescriptor> syntax = ConvertSyntax(*syntax_value);
+  if (!syntax)
+    return nullptr;
+
+  // inherits
+  const CSSValue* inherits_value =
+      properties.GetPropertyCSSValue(CSSPropertyID::kInherits);
+  if (!inherits_value)
+    return nullptr;
+  bool inherits = ConvertInherts(*inherits_value);
+
+  // initial-value (optional)
+  const CSSValue* initial_value =
+      properties.GetPropertyCSSValue(CSSPropertyID::kInitialValue);
+  scoped_refptr<CSSVariableData> initial_variable_data =
+      ConvertInitialVariableData(initial_value);
+
+  // Parse initial value, if we have it.
+  const CSSValue* initial = nullptr;
+  if (initial_variable_data) {
+    const CSSParserContext* parser_context =
+        document.ElementSheet().Contents()->ParserContext();
+    const bool is_animation_tainted = false;
+    initial = syntax->Parse(initial_variable_data->TokenRange(), parser_context,
+                            is_animation_tainted);
+    if (!initial)
+      return nullptr;
+    if (!ComputationallyIndependent(*initial))
+      return nullptr;
+    initial = &StyleBuilderConverter::ConvertRegisteredPropertyInitialValue(
+        document, *initial);
+    initial_variable_data =
+        StyleBuilderConverter::ConvertRegisteredPropertyVariableData(
+            *initial, is_animation_tainted);
+  }
+
+  return MakeGarbageCollected<PropertyRegistration>(
+      name, *syntax, inherits, initial, initial_variable_data);
+}
+
 void PropertyRegistration::registerProperty(
     ExecutionContext* execution_context,
     const PropertyDescriptor* descriptor,
diff --git a/third_party/blink/renderer/core/css/property_registration.h b/third_party/blink/renderer/core/css/property_registration.h
index 146e9015..4f0ed65 100644
--- a/third_party/blink/renderer/core/css/property_registration.h
+++ b/third_party/blink/renderer/core/css/property_registration.h
@@ -19,12 +19,17 @@
 class ExceptionState;
 class ExecutionContext;
 class PropertyDescriptor;
+class StyleRuleProperty;
 
 using CSSInterpolationTypes = Vector<std::unique_ptr<CSSInterpolationType>>;
 
 class CORE_EXPORT PropertyRegistration
     : public GarbageCollectedFinalized<PropertyRegistration> {
  public:
+  static PropertyRegistration* MaybeCreate(Document&,
+                                           const AtomicString& name,
+                                           StyleRuleProperty&);
+
   static void registerProperty(ExecutionContext*,
                                const PropertyDescriptor*,
                                ExceptionState&);
diff --git a/third_party/blink/renderer/core/css/rule_set.cc b/third_party/blink/renderer/core/css/rule_set.cc
index 9668205..8227f2d 100644
--- a/third_party/blink/renderer/core/css/rule_set.cc
+++ b/third_party/blink/renderer/core/css/rule_set.cc
@@ -287,6 +287,11 @@
   keyframes_rules_.push_back(rule);
 }
 
+void RuleSet::AddPropertyRule(StyleRuleProperty* rule) {
+  EnsurePendingRules();  // So that property_rules_.ShrinkToFit() gets called.
+  property_rules_.push_back(rule);
+}
+
 void RuleSet::AddChildRules(const HeapVector<Member<StyleRuleBase>>& rules,
                             const MediaQueryEvaluator& medium,
                             AddRuleFlags add_rule_flags) {
@@ -326,6 +331,8 @@
       AddFontFeatureValuesRule(font_feature_values_rule);
     } else if (auto* keyframes_rule = DynamicTo<StyleRuleKeyframes>(rule)) {
       AddKeyframesRule(keyframes_rule);
+    } else if (auto* property_rule = DynamicTo<StyleRuleProperty>(rule)) {
+      AddPropertyRule(property_rule);
     } else if (auto* supports_rule = DynamicTo<StyleRuleSupports>(rule)) {
       if (supports_rule->ConditionIsSupported())
         AddChildRules(supports_rule->ChildRules(), medium, add_rule_flags);
@@ -402,6 +409,7 @@
   font_face_rules_.ShrinkToFit();
   font_feature_values_rules_.ShrinkToFit();
   keyframes_rules_.ShrinkToFit();
+  property_rules_.ShrinkToFit();
   deep_combinator_or_shadow_pseudo_rules_.ShrinkToFit();
   part_pseudo_rules_.ShrinkToFit();
   content_pseudo_element_rules_.ShrinkToFit();
@@ -438,6 +446,7 @@
   visitor->Trace(font_face_rules_);
   visitor->Trace(font_feature_values_rules_);
   visitor->Trace(keyframes_rules_);
+  visitor->Trace(property_rules_);
   visitor->Trace(deep_combinator_or_shadow_pseudo_rules_);
   visitor->Trace(part_pseudo_rules_);
   visitor->Trace(content_pseudo_element_rules_);
diff --git a/third_party/blink/renderer/core/css/rule_set.h b/third_party/blink/renderer/core/css/rule_set.h
index bf13377..6776c98ac 100644
--- a/third_party/blink/renderer/core/css/rule_set.h
+++ b/third_party/blink/renderer/core/css/rule_set.h
@@ -250,6 +250,9 @@
   const HeapVector<Member<StyleRuleKeyframes>>& KeyframesRules() const {
     return keyframes_rules_;
   }
+  const HeapVector<Member<StyleRuleProperty>>& PropertyRules() const {
+    return property_rules_;
+  }
   const HeapVector<MinimalRuleData>& DeepCombinatorOrShadowPseudoRules() const {
     return deep_combinator_or_shadow_pseudo_rules_;
   }
@@ -301,6 +304,7 @@
   void AddFontFaceRule(StyleRuleFontFace*);
   void AddFontFeatureValuesRule(StyleRuleFontFeatureValues*);
   void AddKeyframesRule(StyleRuleKeyframes*);
+  void AddPropertyRule(StyleRuleProperty*);
 
   void AddChildRules(const HeapVector<Member<StyleRuleBase>>&,
                      const MediaQueryEvaluator& medium,
@@ -344,6 +348,7 @@
   HeapVector<Member<StyleRuleFontFace>> font_face_rules_;
   HeapVector<Member<StyleRuleFontFeatureValues>> font_feature_values_rules_;
   HeapVector<Member<StyleRuleKeyframes>> keyframes_rules_;
+  HeapVector<Member<StyleRuleProperty>> property_rules_;
   HeapVector<MinimalRuleData> deep_combinator_or_shadow_pseudo_rules_;
   HeapVector<MinimalRuleData> content_pseudo_element_rules_;
   HeapVector<MinimalRuleData> slotted_pseudo_element_rules_;
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 92af53a..12011a64 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/core/css/document_style_sheet_collector.h"
 #include "third_party/blink/renderer/core/css/font_face_cache.h"
 #include "third_party/blink/renderer/core/css/invalidation/invalidation_set.h"
+#include "third_party/blink/renderer/core/css/property_registration.h"
 #include "third_party/blink/renderer/core/css/property_registry.h"
 #include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.h"
@@ -1343,6 +1344,7 @@
   kFullRecalcRules = 1 << 2,
   kFontFeatureValuesRules = 1 << 3,
   kFontRules = kFontFaceRules | kFontFeatureValuesRules,
+  kPropertyRules = 1 << 4
 };
 
 unsigned GetRuleSetFlags(const HeapHashSet<Member<RuleSet>> rule_sets) {
@@ -1357,6 +1359,8 @@
       flags |= kFullRecalcRules;
     if (!rule_set->FontFeatureValuesRules().IsEmpty())
       flags |= kFontFeatureValuesRules;
+    if (!rule_set->PropertyRules().IsEmpty())
+      flags |= kPropertyRules;
   }
   return flags;
 }
@@ -1483,6 +1487,20 @@
   if (changed_rule_flags & kKeyframesRules)
     ScopedStyleResolver::KeyframesRulesAdded(tree_scope);
 
+  if (changed_rule_flags & kPropertyRules) {
+    // TODO(https://crbug.com/978786): Don't ignore TreeScope.
+
+    // TODO(https://crbug.com/978781): Support unregistration.
+    // At this point we could have unregistered properties for
+    // change==kActiveSheetsChanged, but we don't yet support that.
+
+    for (auto* it = new_style_sheets.begin(); it != new_style_sheets.end();
+         it++) {
+      DCHECK(it->second);
+      AddPropertyRules(*it->second);
+    }
+  }
+
   if (rebuild_font_cache)
     ClearFontCacheAndAddUserFonts();
 
@@ -1694,6 +1712,33 @@
   }
 }
 
+void StyleEngine::AddPropertyRules(const RuleSet& rule_set) {
+  PropertyRegistry* registry = GetDocument().GetPropertyRegistry();
+  if (!registry)
+    return;
+  const HeapVector<Member<StyleRuleProperty>> property_rules =
+      rule_set.PropertyRules();
+  for (unsigned i = 0; i < property_rules.size(); ++i) {
+    StyleRuleProperty* rule = property_rules[i];
+
+    AtomicString name(rule->GetName());
+
+    // For now, ignore silently if registration already exists.
+    // TODO(https://crbug.com/978781): Support unregistration.
+    if (registry->Registration(name))
+      continue;
+
+    PropertyRegistration* registration =
+        PropertyRegistration::MaybeCreate(GetDocument(), name, *rule);
+
+    if (!registration)
+      continue;
+
+    registry->RegisterProperty(name, *registration);
+    CustomPropertyRegistered();
+  }
+}
+
 StyleRuleKeyframes* StyleEngine::KeyframeStylesForAnimation(
     const AtomicString& animation_name) {
   if (keyframes_rule_map_.IsEmpty())
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 1ddc4800..17ee2cf 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -436,10 +436,12 @@
 
   void ClearFontCacheAndAddUserFonts();
   void ClearKeyframeRules() { keyframes_rule_map_.clear(); }
+  void ClearPropertyRules();
 
   void AddUserFontFaceRules(const RuleSet&);
   void AddUserKeyframeRules(const RuleSet&);
   void AddUserKeyframeStyle(StyleRuleKeyframes*);
+  void AddPropertyRules(const RuleSet&);
 
   void UpdateColorScheme();
   bool SupportsDarkColorScheme();
diff --git a/third_party/blink/renderer/core/css/style_rule.cc b/third_party/blink/renderer/core/css/style_rule.cc
index 466a6f3..fc4f780 100644
--- a/third_party/blink/renderer/core/css/style_rule.cc
+++ b/third_party/blink/renderer/core/css/style_rule.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/css/css_media_rule.h"
 #include "third_party/blink/renderer/core/css/css_namespace_rule.h"
 #include "third_party/blink/renderer/core/css/css_page_rule.h"
+#include "third_party/blink/renderer/core/css/css_property_rule.h"
 #include "third_party/blink/renderer/core/css/css_style_rule.h"
 #include "third_party/blink/renderer/core/css/css_supports_rule.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
@@ -66,6 +67,9 @@
     case kPage:
       To<StyleRulePage>(this)->TraceAfterDispatch(visitor);
       return;
+    case kProperty:
+      To<StyleRuleProperty>(this)->TraceAfterDispatch(visitor);
+      return;
     case kFontFace:
       To<StyleRuleFontFace>(this)->TraceAfterDispatch(visitor);
       return;
@@ -108,6 +112,9 @@
     case kPage:
       To<StyleRulePage>(this)->~StyleRulePage();
       return;
+    case kProperty:
+      To<StyleRuleProperty>(this)->~StyleRuleProperty();
+      return;
     case kFontFace:
       To<StyleRuleFontFace>(this)->~StyleRuleFontFace();
       return;
@@ -145,6 +152,8 @@
       return To<StyleRule>(this)->Copy();
     case kPage:
       return To<StyleRulePage>(this)->Copy();
+    case kProperty:
+      return To<StyleRuleProperty>(this)->Copy();
     case kFontFace:
       return To<StyleRuleFontFace>(this)->Copy();
     case kMedia:
@@ -185,6 +194,10 @@
       rule = MakeGarbageCollected<CSSPageRule>(To<StyleRulePage>(self),
                                                parent_sheet);
       break;
+    case kProperty:
+      rule = MakeGarbageCollected<CSSPropertyRule>(To<StyleRuleProperty>(self),
+                                                   parent_sheet);
+      break;
     case kFontFace:
       rule = MakeGarbageCollected<CSSFontFaceRule>(To<StyleRuleFontFace>(self),
                                                    parent_sheet);
@@ -317,6 +330,27 @@
   StyleRuleBase::TraceAfterDispatch(visitor);
 }
 
+StyleRuleProperty::StyleRuleProperty(const String& name,
+                                     CSSPropertyValueSet* properties)
+    : StyleRuleBase(kProperty), name_(name), properties_(properties) {}
+
+StyleRuleProperty::StyleRuleProperty(const StyleRuleProperty& property_rule)
+    : StyleRuleBase(property_rule),
+      properties_(property_rule.properties_->MutableCopy()) {}
+
+StyleRuleProperty::~StyleRuleProperty() = default;
+
+MutableCSSPropertyValueSet& StyleRuleProperty::MutableProperties() {
+  if (!properties_->IsMutable())
+    properties_ = properties_->MutableCopy();
+  return *To<MutableCSSPropertyValueSet>(properties_.Get());
+}
+
+void StyleRuleProperty::TraceAfterDispatch(blink::Visitor* visitor) {
+  visitor->Trace(properties_);
+  StyleRuleBase::TraceAfterDispatch(visitor);
+}
+
 StyleRuleFontFace::StyleRuleFontFace(CSSPropertyValueSet* properties)
     : StyleRuleBase(kFontFace), properties_(properties) {}
 
diff --git a/third_party/blink/renderer/core/css/style_rule.h b/third_party/blink/renderer/core/css/style_rule.h
index 774bb0b..d465b10 100644
--- a/third_party/blink/renderer/core/css/style_rule.h
+++ b/third_party/blink/renderer/core/css/style_rule.h
@@ -47,6 +47,7 @@
     kMedia,
     kFontFace,
     kPage,
+    kProperty,
     kKeyframes,
     kKeyframe,
     kNamespace,
@@ -64,6 +65,7 @@
   bool IsNamespaceRule() const { return GetType() == kNamespace; }
   bool IsMediaRule() const { return GetType() == kMedia; }
   bool IsPageRule() const { return GetType() == kPage; }
+  bool IsPropertyRule() const { return GetType() == kProperty; }
   bool IsStyleRule() const { return GetType() == kStyle; }
   bool IsSupportsRule() const { return GetType() == kSupports; }
   bool IsViewportRule() const { return GetType() == kViewport; }
@@ -192,6 +194,32 @@
   CSSSelectorList selector_list_;
 };
 
+class StyleRuleProperty : public StyleRuleBase {
+ public:
+  static StyleRuleProperty* Create(const String& name,
+                                   CSSPropertyValueSet* properties) {
+    return MakeGarbageCollected<StyleRuleProperty>(name, properties);
+  }
+
+  StyleRuleProperty(const String& name, CSSPropertyValueSet*);
+  StyleRuleProperty(const StyleRuleProperty&);
+  ~StyleRuleProperty();
+
+  const CSSPropertyValueSet& Properties() const { return *properties_; }
+  MutableCSSPropertyValueSet& MutableProperties();
+  const String& GetName() const { return name_; }
+
+  StyleRuleProperty* Copy() const {
+    return MakeGarbageCollected<StyleRuleProperty>(*this);
+  }
+
+  void TraceAfterDispatch(blink::Visitor*);
+
+ private:
+  String name_;
+  Member<CSSPropertyValueSet> properties_;
+};
+
 class CORE_EXPORT StyleRuleGroup : public StyleRuleBase {
  public:
   const HeapVector<Member<StyleRuleBase>>& ChildRules() const {
@@ -340,6 +368,13 @@
 };
 
 template <>
+struct DowncastTraits<StyleRuleProperty> {
+  static bool AllowFrom(const StyleRuleBase& rule) {
+    return rule.IsPropertyRule();
+  }
+};
+
+template <>
 struct DowncastTraits<StyleRuleMedia> {
   static bool AllowFrom(const StyleRuleBase& rule) {
     return rule.IsMediaRule();
diff --git a/third_party/blink/renderer/core/css/style_sheet_contents.cc b/third_party/blink/renderer/core/css/style_sheet_contents.cc
index 5cbf757..31140d7 100644
--- a/third_party/blink/renderer/core/css/style_sheet_contents.cc
+++ b/third_party/blink/renderer/core/css/style_sheet_contents.cc
@@ -510,6 +510,7 @@
         NOTREACHED();
         break;
       case StyleRuleBase::kPage:
+      case StyleRuleBase::kProperty:
       case StyleRuleBase::kKeyframes:
       case StyleRuleBase::kKeyframe:
       case StyleRuleBase::kSupports:
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_budget_test.cc b/third_party/blink/renderer/core/display_lock/display_lock_budget_test.cc
index 435c210..81fb66e 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_budget_test.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_budget_test.cc
@@ -27,16 +27,15 @@
     test_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
   }
 
-  double GetBudgetMs(const YieldingDisplayLockBudget& budget,
-                     const LifecycleData& lifecycle_data) const {
-    return budget.GetCurrentBudgetMs(lifecycle_data);
+  base::TimeDelta GetBudget(const YieldingDisplayLockBudget& budget,
+                            const LifecycleData& lifecycle_data) const {
+    return budget.GetCurrentBudget(lifecycle_data);
   }
 
   void ResetDeadlineForTesting(YieldingDisplayLockBudget& budget,
                                const LifecycleData& lifecycle_data) {
     budget.deadline_ =
-        CurrentTimeTicks() + base::TimeDelta::FromMillisecondsD(
-                                 budget.GetCurrentBudgetMs(lifecycle_data));
+        CurrentTimeTicks() + budget.GetCurrentBudget(lifecycle_data);
   }
 
   void ResetBudget(std::unique_ptr<DisplayLockBudget> budget,
@@ -365,8 +364,7 @@
   EXPECT_TRUE(budget.NeedsLifecycleUpdates());
 
   // Advancing the clock a bit will make us still want to the phases.
-  test_task_runner_->FastForwardBy(base::TimeDelta::FromMillisecondsD(
-      GetBudgetMs(budget, lifecycle_data) / 2));
+  test_task_runner_->FastForwardBy(GetBudget(budget, lifecycle_data) / 2);
   EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle,
                                         lifecycle_data));
   EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout,
@@ -375,8 +373,7 @@
                                         lifecycle_data));
 
   // However, once we're out of budget, we will only do the next phase.
-  test_task_runner_->FastForwardBy(
-      base::TimeDelta::FromMillisecondsD(GetBudgetMs(budget, lifecycle_data)));
+  test_task_runner_->FastForwardBy(GetBudget(budget, lifecycle_data));
 
   EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle,
                                         lifecycle_data));
@@ -409,8 +406,7 @@
 
   // Now that we're out of budget, phases performed previously should remain
   // true.
-  test_task_runner_->FastForwardBy(base::TimeDelta::FromMillisecondsD(
-      GetBudgetMs(budget, lifecycle_data) * 2));
+  test_task_runner_->FastForwardBy(GetBudget(budget, lifecycle_data) * 2);
   EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle,
                                         lifecycle_data));
   EXPECT_FALSE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout,
@@ -433,8 +429,7 @@
                                         lifecycle_data));
   EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kPrePaint,
                                         lifecycle_data));
-  test_task_runner_->FastForwardBy(base::TimeDelta::FromMillisecondsD(
-      GetBudgetMs(budget, lifecycle_data) * 2));
+  test_task_runner_->FastForwardBy(GetBudget(budget, lifecycle_data) * 2);
   EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle,
                                         lifecycle_data));
   EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kLayout,
@@ -446,7 +441,8 @@
   lifecycle_data.count += 60;
   budget.OnLifecycleChange(lifecycle_data);
 
-  EXPECT_GT(GetBudgetMs(budget, lifecycle_data), 1e6);
+  EXPECT_GT(GetBudget(budget, lifecycle_data),
+            base::TimeDelta::FromMilliseconds(1e6));
   for (int i = 0; i < 60; ++i) {
     EXPECT_TRUE(budget.ShouldPerformPhase(DisplayLockBudget::Phase::kStyle,
                                           lifecycle_data));
@@ -484,7 +480,6 @@
 
   auto budget_owned = base::WrapUnique(
       new YieldingDisplayLockBudget(element->GetDisplayLockContext()));
-  ;
   auto* budget = budget_owned.get();
   budget->SetTickClockForTesting(test_task_runner_->GetMockTickClock());
   {
@@ -525,8 +520,8 @@
   EXPECT_TRUE(parent->NeedsStyleRecalc() || parent->ChildNeedsStyleRecalc());
   EXPECT_TRUE(element->NeedsStyleRecalc() || element->ChildNeedsStyleRecalc());
 
-  test_task_runner_->FastForwardBy(base::TimeDelta::FromMillisecondsD(
-      GetBudgetMs(*budget, GetDocument().View()->CurrentLifecycleData()) * 2));
+  test_task_runner_->FastForwardBy(
+      GetBudget(*budget, GetDocument().View()->CurrentLifecycleData()) * 2);
   EXPECT_TRUE(
       budget->ShouldPerformPhase(DisplayLockBudget::Phase::kStyle,
                                  GetDocument().View()->CurrentLifecycleData()));
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 f255c8b..cd9d145f 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
@@ -44,6 +44,7 @@
 const char* kElementIsDisconnected = "Element is disconnected.";
 const char* kLockCommitted = "Lock commit was requested.";
 const char* kInvalidOptions = "Invalid options.";
+const char* kElementIsNested = "Element is nested under a locked element.";
 }  // namespace rejection_names
 
 // Helper function to convert a display locking state to a string. Used in
@@ -253,6 +254,10 @@
     return update_resolver_->Promise();
   }
 
+  if (DisplayLockUtilities::NearestLockedExclusiveAncestor(*element_)) {
+    return GetRejectedPromise(script_state, rejection_names::kElementIsNested);
+  }
+
   MakeResolver(script_state, &update_resolver_);
   StartUpdateIfNeeded();
   return update_resolver_->Promise();
@@ -326,10 +331,12 @@
     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
-  // whatever commit() would do.
-  if (state_ == kCommitting || !ConnectedToView())
+  // haven't acquired the lock, or we're already sync committing, or we're under
+  // a nested lock), then do whatever commit() would do.
+  if (state_ == kCommitting || !ConnectedToView() ||
+      DisplayLockUtilities::NearestLockedExclusiveAncestor(*element_)) {
     return commit(script_state);
+  }
 
   // If we have a commit resolver already, return it.
   if (commit_resolver_) {
@@ -840,7 +847,14 @@
 
 void DisplayLockContext::ScheduleAnimation() {
   DCHECK(element_);
-  DCHECK(ConnectedToView());
+  // We could have posted a task to run ScheduleAnimation if we're updating.
+  // However, before that task runs, we could have disconnected the element
+  // already. If that's the case and we don't need to finalize update, then we
+  // can skip scheduling animation. If we do need to finalize update (ie reset
+  // update_budget_), then we should still schedule an animation just in case
+  // one was not scheduled.
+  if ((!ConnectedToView() && !update_budget_) || !document_->GetPage())
+    return;
 
   // Schedule an animation to perform the lifecycle phases.
   document_->GetPage()->Animator().ScheduleVisualUpdate(document_->GetFrame());
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc b/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
index 42a9312..36e04a0 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
@@ -3,9 +3,11 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
+
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_options.h"
+#include "third_party/blink/renderer/core/display_lock/strict_yielding_display_lock_budget.h"
 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
@@ -161,6 +163,12 @@
     test::RunPendingTasks();
   }
 
+  void ResetBudget(std::unique_ptr<DisplayLockBudget> budget,
+                   DisplayLockContext* context) {
+    ASSERT_TRUE(context->update_budget_);
+    context->update_budget_ = std::move(budget);
+  }
+
   const int FAKE_FIND_ID = 1;
 
  private:
@@ -1553,4 +1561,60 @@
   EXPECT_FALSE(locked_object->DescendantNeedsPaintPropertyUpdate());
   EXPECT_FALSE(handler_object->DescendantNeedsPaintPropertyUpdate());
 }
+
+TEST_F(DisplayLockContextTest, DisconnectedWhileUpdating) {
+  SetHtmlInnerHTML(R"HTML(
+    <style>
+    #container {
+      contain: style layout;
+    }
+    </style>
+    <div id="container"></div>
+  )HTML");
+
+  auto* container = GetDocument().getElementById("container");
+  LockElement(*container, false);
+
+  EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
+  EXPECT_FALSE(container->GetDisplayLockContext()->ShouldStyle(
+      DisplayLockContext::kChildren));
+  EXPECT_FALSE(container->GetDisplayLockContext()->ShouldLayout(
+      DisplayLockContext::kChildren));
+  EXPECT_FALSE(container->GetDisplayLockContext()->ShouldPrePaint(
+      DisplayLockContext::kChildren));
+
+  auto* script_state = ToScriptStateForMainWorld(GetDocument().GetFrame());
+  {
+    ScriptState::Scope scope(script_state);
+    container->GetDisplayLockContext()->update(script_state);
+  }
+  auto budget = base::WrapUnique(
+      new StrictYieldingDisplayLockBudget(container->GetDisplayLockContext()));
+  ResetBudget(std::move(budget), container->GetDisplayLockContext());
+
+  // This should style and allow layout, but not actually do layout (thus
+  // pre-paint would be blocked). Furthermore, this should schedule a task to
+  // run DisplayLockContext::ScheduleAnimation (since we can't directly schedule
+  // it from within a lifecycle).
+  UpdateAllLifecyclePhasesForTest();
+
+  ASSERT_FALSE(GetDocument().View()->InLifecycleUpdate());
+  GetDocument().View()->SetInLifecycleUpdateForTest(true);
+  EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
+  EXPECT_TRUE(container->GetDisplayLockContext()->ShouldStyle(
+      DisplayLockContext::kChildren));
+  EXPECT_TRUE(container->GetDisplayLockContext()->ShouldLayout(
+      DisplayLockContext::kChildren));
+  EXPECT_FALSE(container->GetDisplayLockContext()->ShouldPrePaint(
+      DisplayLockContext::kChildren));
+  GetDocument().View()->SetInLifecycleUpdateForTest(false);
+
+  // Now disconnect the element.
+  container->remove();
+
+  // Flushing the pending tasks would call ScheduleAnimation, but since we're no
+  // longer connected and can't schedule from within the element, we should
+  // gracefully exit (and not crash).
+  test::RunPendingTasks();
+}
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.cc b/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.cc
index ea45591..ec51cde 100644
--- a/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.cc
+++ b/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.cc
@@ -44,9 +44,7 @@
     const LifecycleData& lifecycle_data) {
   if (first_lifecycle_count_ == 0)
     first_lifecycle_count_ = lifecycle_data.count;
-  deadline_ =
-      lifecycle_data.start_time +
-      base::TimeDelta::FromMillisecondsD(GetCurrentBudgetMs(lifecycle_data));
+  deadline_ = lifecycle_data.start_time + GetCurrentBudget(lifecycle_data);
 
   // Figure out the next phase we would run. If we had completed a phase before,
   // then we should try to complete the next one, otherwise we'll start with the
@@ -77,24 +75,24 @@
   return false;
 }
 
-double YieldingDisplayLockBudget::GetCurrentBudgetMs(
+base::TimeDelta YieldingDisplayLockBudget::GetCurrentBudget(
     const LifecycleData& lifecycle_data) const {
   int lifecycle_count = lifecycle_data.count - first_lifecycle_count_ + 1;
   if (base::TimeTicks::IsHighResolution()) {
     if (lifecycle_count < 3)
-      return 4.;
+      return base::TimeDelta::FromMilliseconds(4);
     if (lifecycle_count < 10)
-      return 8.;
+      return base::TimeDelta::FromMilliseconds(8);
     if (lifecycle_count < 60)
-      return 16.;
+      return base::TimeDelta::FromMilliseconds(16);
   } else {
     // Without a high resolution clock, the resolution can be as bad as 15ms, so
     // increase the budgets accordingly to ensure we don't abort before doing
     // any phases.
     if (lifecycle_count < 60)
-      return 16.;
+      return base::TimeDelta::FromMilliseconds(16);
   }
-  return 1e9;
+  return base::TimeDelta::FromMilliseconds(1e9);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h b/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h
index 06e0cfa..4edf28e 100644
--- a/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h
+++ b/third_party/blink/renderer/core/display_lock/yielding_display_lock_budget.h
@@ -35,7 +35,7 @@
  protected:
   friend class DisplayLockBudgetTest;
 
-  double GetCurrentBudgetMs(const LifecycleData& lifecycle_data) const;
+  base::TimeDelta GetCurrentBudget(const LifecycleData& lifecycle_data) const;
 
  private:
   unsigned first_lifecycle_count_ = 0;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 7510ef47..7f2c424b5 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -40,7 +40,7 @@
 #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
-#include "services/metrics/public/mojom/ukm_interface.mojom-shared.h"
+#include "services/metrics/public/mojom/ukm_interface.mojom-blink.h"
 #include "services/resource_coordinator/public/mojom/coordination_unit.mojom-blink.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -6510,10 +6510,13 @@
     // load local resources. The latter lets about:blank iframes in
     // file:// URL documents load images and other resources from
     // the file system.
+    //
+    // Note: Sandboxed about:srcdoc iframe without "allow-same-origin" aren't
+    // allowed to load user's file, even if its parent can.
     if (initializer.OwnerDocument()) {
       if (document_origin->IsPotentiallyTrustworthy())
         sandboxed_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
-      if (document_origin->CanLoadLocalResources())
+      if (document_origin->CanLoadLocalResources() && !IsSrcdocDocument())
         sandboxed_origin->GrantLoadLocalResources();
       if (url_.IsEmpty()) {
         last_origin_document_csp_ =
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index adee584..430ca56 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -39,7 +39,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/navigation_initiator.mojom-blink.h"
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/web_focus_type.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/renderer/core/accessibility/axid.h"
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index 520c41c..9c022cef 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -34,7 +34,7 @@
 
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/isolated_world_csp.h"
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 25caed6..6ae7689 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -1256,7 +1256,7 @@
 
   DCHECK(!ChildNeedsDistributionRecalc());
 
-  if (this != GetDocument()) {
+  if (this != GetDocument() && this != GetDocument().documentElement()) {
     const Element* modal_element = GetDocument().ActiveModalDialog();
     if (!modal_element)
       modal_element = Fullscreen::FullscreenElementFrom(GetDocument());
diff --git a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
index c6b2e7d..aff5d4a0 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
@@ -2044,7 +2044,7 @@
     return 0;
 
   const auto* value = To<CSSPrimitiveValue>(
-      style->GetPropertyCSSValue(GetCSSPropertyFontSize()));
+      style->GetPropertyCSSValue(CSSPropertyID::kFontSize));
   if (!value)
     return 0;
 
diff --git a/third_party/blink/renderer/core/editing/commands/style_commands.cc b/third_party/blink/renderer/core/editing/commands/style_commands.cc
index 318e135..00eef3d 100644
--- a/third_party/blink/renderer/core/editing/commands/style_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/style_commands.cc
@@ -444,7 +444,7 @@
       const CSSComputedStyleDeclaration& style =
           *MakeGarbageCollected<CSSComputedStyleDeclaration>(&node);
       const CSSValue* unicode_bidi =
-          style.GetPropertyCSSValue(GetCSSPropertyUnicodeBidi());
+          style.GetPropertyCSSValue(CSSPropertyID::kUnicodeBidi);
       auto* unicode_bidi_identifier_value =
           DynamicTo<CSSIdentifierValue>(unicode_bidi);
       if (!unicode_bidi_identifier_value)
@@ -482,7 +482,7 @@
     const CSSComputedStyleDeclaration& style =
         *MakeGarbageCollected<CSSComputedStyleDeclaration>(element);
     const CSSValue* unicode_bidi =
-        style.GetPropertyCSSValue(GetCSSPropertyUnicodeBidi());
+        style.GetPropertyCSSValue(CSSPropertyID::kUnicodeBidi);
     auto* unicode_bidi_identifier_value =
         DynamicTo<CSSIdentifierValue>(unicode_bidi);
     if (!unicode_bidi_identifier_value)
@@ -499,7 +499,7 @@
     DCHECK(EditingStyleUtilities::IsEmbedOrIsolate(unicode_bidi_value))
         << static_cast<int>(unicode_bidi_value);
     const CSSValue* direction =
-        style.GetPropertyCSSValue(GetCSSPropertyDirection());
+        style.GetPropertyCSSValue(CSSPropertyID::kDirection);
     auto* direction_identifier_value = DynamicTo<CSSIdentifierValue>(direction);
     if (!direction_identifier_value)
       continue;
diff --git a/third_party/blink/renderer/core/editing/editing_style.cc b/third_party/blink/renderer/core/editing/editing_style.cc
index 0ba2281d..755b7bb 100644
--- a/third_party/blink/renderer/core/editing/editing_style.cc
+++ b/third_party/blink/renderer/core/editing/editing_style.cc
@@ -515,7 +515,7 @@
           /* important */ false, node->GetDocument().GetSecureContextMode());
     }
     if (const CSSValue* value = computed_style_at_position->GetPropertyCSSValue(
-            GetCSSPropertyWebkitTextDecorationsInEffect())) {
+            CSSPropertyID::kWebkitTextDecorationsInEffect)) {
       mutable_style_->SetProperty(
           CSSPropertyID::kTextDecoration, value->CssText(),
           /* important */ false, node->GetDocument().GetSecureContextMode());
@@ -1451,7 +1451,8 @@
         continue;
       if (primitive_value->IsPercentage()) {
         if (const CSSValue* computed_property_value =
-                computed_style_for_element->GetPropertyCSSValue(css_property)) {
+                computed_style_for_element->GetPropertyCSSValue(
+                    property.Name())) {
           from_computed_style->AddRespectingCascade(
               CSSPropertyValue(css_property, *computed_property_value));
         }
diff --git a/third_party/blink/renderer/core/editing/editing_style_utilities.cc b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
index d184475..a2276f0 100644
--- a/third_party/blink/renderer/core/editing/editing_style_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
@@ -232,7 +232,7 @@
         MakeGarbageCollected<CSSComputedStyleDeclaration>(ancestor);
     if (!HasTransparentBackgroundColor(ancestor_style)) {
       return ancestor_style->GetPropertyCSSValue(
-          GetCSSPropertyBackgroundColor());
+          CSSPropertyID::kBackgroundColor);
     }
   }
   return nullptr;
diff --git a/third_party/blink/renderer/core/editing/ime/ime_text_span.h b/third_party/blink/renderer/core/editing/ime/ime_text_span.h
index 878be9e..b99f960 100644
--- a/third_party/blink/renderer/core/editing/ime/ime_text_span.h
+++ b/third_party/blink/renderer/core/editing/ime/ime_text_span.h
@@ -31,7 +31,7 @@
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
-#include "ui/base/ime/mojo/ime_types.mojom-shared.h"
+#include "ui/base/ime/mojo/ime_types.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/editing/markers/styleable_marker.h b/third_party/blink/renderer/core/editing/markers/styleable_marker.h
index 86294a6..6d78e8d6 100644
--- a/third_party/blink/renderer/core/editing/markers/styleable_marker.h
+++ b/third_party/blink/renderer/core/editing/markers/styleable_marker.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/renderer/core/editing/markers/document_marker.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
-#include "ui/base/ime/mojo/ime_types.mojom-shared.h"
+#include "ui/base/ime/mojo/ime_types.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index 4ef5096..8a9ee855 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -1023,7 +1023,8 @@
   if (!Selection().IsAvailable())
     return;
   if (selection_state_ != SelectionState::kExtendedSelection) {
-    HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
+    HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive |
+                           HitTestRequest::kRetargetForInert);
     HitTestLocation location(mouse_down_pos);
     HitTestResult result(request, location);
     frame_->GetDocument()->GetLayoutView()->HitTest(location, result);
@@ -1046,7 +1047,8 @@
     return;
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive |
-                         HitTestRequest::kMove);
+                         HitTestRequest::kMove |
+                         HitTestRequest::kRetargetForInert);
   HitTestLocation location(view->ViewportToFrame(last_known_mouse_position));
   HitTestResult result(request, location);
   layout_view->HitTest(location, result);
diff --git a/third_party/blink/renderer/core/editing/visible_units.cc b/third_party/blink/renderer/core/editing/visible_units.cc
index ecff1b1c..80585db 100644
--- a/third_party/blink/renderer/core/editing/visible_units.cc
+++ b/third_party/blink/renderer/core/editing/visible_units.cc
@@ -474,7 +474,8 @@
     LocalFrame* frame) {
   HitTestRequest request = HitTestRequest::kMove | HitTestRequest::kReadOnly |
                            HitTestRequest::kActive |
-                           HitTestRequest::kIgnoreClipping;
+                           HitTestRequest::kIgnoreClipping |
+                           HitTestRequest::kRetargetForInert;
   HitTestLocation location(contents_point);
   HitTestResult result(request, location);
   frame->GetDocument()->GetLayoutView()->HitTest(location, result);
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
index 9f45470..ce29235 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
@@ -34,7 +34,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
 #include "third_party/blink/public/platform/web_document_subresource_filter.h"
 #include "third_party/blink/public/platform/web_url.h"
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 3142050..7b75657 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -44,8 +44,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/page/launching_process_state.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
-#include "third_party/blink/public/mojom/frame/find_in_page.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_cache.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
@@ -3131,46 +3131,6 @@
   EXPECT_EQ(2.0f, web_view_helper.GetWebView()->MaximumPageScaleFactor());
 }
 
-// Test that setting the "ignore viewport tag scale limits" override remembers
-// the current defaults and restores them when the override is removed.
-TEST_F(WebFrameTest, RestoreOriginalDefaultScaleLimits) {
-  RegisterMockedHttpURLLoad("simple_div.html");
-
-  FixedLayoutTestWebViewClient client;
-  client.screen_info_.device_scale_factor = 1;
-  int viewport_width = 640;
-  int viewport_height = 480;
-
-  frame_test_helpers::WebViewHelper web_view_helper;
-  web_view_helper.InitializeAndLoad(base_url_ + "simple_div.html", nullptr,
-                                    &client, nullptr, ConfigureAndroid);
-  web_view_helper.Resize(WebSize(viewport_width, viewport_height));
-  web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5);
-
-  const float minimum_scale_factor =
-      web_view_helper.GetWebView()->MinimumPageScaleFactor();
-
-  web_view_helper.GetWebView()->SetInitialPageScaleOverride(2.0f);
-
-  // Removing the override when none is set shouldn't change the initial scale.
-  web_view_helper.GetWebView()->SetIgnoreViewportTagScaleLimits(false);
-  UpdateAllLifecyclePhases(web_view_helper.GetWebView());
-  EXPECT_EQ(2.0f, web_view_helper.GetWebView()->PageScaleFactor());
-
-  // Setting the override when will change the initial scale.
-  web_view_helper.GetWebView()->SetIgnoreViewportTagScaleLimits(true);
-  web_view_helper.GetWebView()->ResetScaleStateImmediately();
-  UpdateAllLifecyclePhases(web_view_helper.GetWebView());
-  EXPECT_EQ(minimum_scale_factor,
-            web_view_helper.GetWebView()->PageScaleFactor());
-
-  // Disable the override, we should now use the minimum scale factor
-  web_view_helper.GetWebView()->SetIgnoreViewportTagScaleLimits(false);
-  web_view_helper.GetWebView()->ResetScaleStateImmediately();
-  UpdateAllLifecyclePhases(web_view_helper.GetWebView());
-  EXPECT_EQ(2.0f, web_view_helper.GetWebView()->PageScaleFactor());
-}
-
 // Android doesn't have scrollbars on the main LocalFrameView
 #if defined(OS_ANDROID)
 TEST_F(WebFrameTest, DISABLED_updateOverlayScrollbarLayers)
@@ -11183,40 +11143,6 @@
   }
 };
 
-// This test ensures that setting the "Force Ignore Zoom" accessibility setting
-// also causes us to override the initial scale set by the page so that we load
-// fully zoomed out. Since we're overriding the minimum-scale, we're making the
-// layout viewport larger. Since |position: fixed| elements are sized based on
-// the layout viewport size, they may be cut off when the page first loads.
-TEST_F(WebFrameSimTest, ForceIgnoreZoomShouldOverrideInitialScale) {
-  UseAndroidSettings();
-  WebView().MainFrameWidget()->Resize(WebSize(500, 300));
-  WebView().SetIgnoreViewportTagScaleLimits(true);
-
-  SimRequest r("https://example.com/test.html", "text/html");
-  LoadURL("https://example.com/test.html");
-  r.Complete(R"HTML(
-      <!DOCTYPE html>
-      <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1">
-      <style>
-        body, html {
-          width: 100%;
-          height: 100%;
-          margin: 0;
-        }
-        #wide {
-          width: 1000px;
-          height: 10px;
-        }
-      </style>
-      <div id="wide"></div>
-  )HTML");
-
-  Compositor().BeginFrame();
-
-  EXPECT_EQ(0.5f, WebView().PageScaleFactor());
-}
-
 TEST_F(WebFrameSimTest, HitTestWithIgnoreClippingAtNegativeOffset) {
   WebView().MainFrameWidget()->Resize(WebSize(500, 300));
   WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
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 c8d9a7f..05d1ed7 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
@@ -40,7 +40,7 @@
 #include "services/service_manager/public/mojom/interface_provider.mojom-blink.h"
 #include "third_party/blink/public/common/privacy_preferences.h"
 #include "third_party/blink/public/mojom/csp/content_security_policy.mojom-blink.h"
-#include "third_party/blink/public/mojom/net/ip_address_space.mojom-shared.h"
+#include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
 #include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink.h"
 #include "third_party/blink/public/web/web_shared_worker_client.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 1ecd9e6..698b0665 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -2501,26 +2501,16 @@
 }
 
 void WebViewImpl::SetIgnoreViewportTagScaleLimits(bool ignore) {
-  // This method should be idempotent.
-  if (ignore == pre_override_default_constraints_.has_value())
-    return;
-
   PageScaleConstraints constraints =
       GetPageScaleConstraintsSet().UserAgentConstraints();
   if (ignore) {
-    DCHECK(!pre_override_default_constraints_);
-    pre_override_default_constraints_.emplace(constraints);
-
     constraints.minimum_scale =
         GetPageScaleConstraintsSet().DefaultConstraints().minimum_scale;
     constraints.maximum_scale =
         GetPageScaleConstraintsSet().DefaultConstraints().maximum_scale;
-    constraints.initial_scale =
-        GetPageScaleConstraintsSet().DefaultConstraints().minimum_scale;
   } else {
-    DCHECK(pre_override_default_constraints_);
-    constraints = pre_override_default_constraints_.value();
-    pre_override_default_constraints_.reset();
+    constraints.minimum_scale = -1;
+    constraints.maximum_scale = -1;
   }
   GetPage()->SetUserAgentPageScaleConstraints(constraints);
 }
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 9e20f34..496a60d94 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -691,10 +691,6 @@
 
   FloatSize elastic_overscroll_;
 
-  // When overriding the default page scale constraints, store the original so
-  // we can revert to them when the override is removed.
-  base::Optional<PageScaleConstraints> pre_override_default_constraints_;
-
   Persistent<EventListener> popup_mouse_wheel_event_listener_;
 
   // The local root whose document has |popup_mouse_wheel_event_listener_|
diff --git a/third_party/blink/renderer/core/exported/worker_shadow_page.cc b/third_party/blink/renderer/core/exported/worker_shadow_page.cc
index 2f8854a3..6b8a4b07 100644
--- a/third_party/blink/renderer/core/exported/worker_shadow_page.cc
+++ b/third_party/blink/renderer/core/exported/worker_shadow_page.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/exported/worker_shadow_page.h"
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
diff --git a/third_party/blink/renderer/core/fetch/fetch_request_data.h b/third_party/blink/renderer/core/fetch/fetch_request_data.h
index 63fc46b..86b6f85 100644
--- a/third_party/blink/renderer/core/fetch/fetch_request_data.h
+++ b/third_party/blink/renderer/core/fetch/fetch_request_data.h
@@ -9,7 +9,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/unguessable_token.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
diff --git a/third_party/blink/renderer/core/fetch/request.h b/third_party/blink/renderer/core/fetch/request.h
index 4f070be3..0f1cf26 100644
--- a/third_party/blink/renderer/core/fetch/request.h
+++ b/third_party/blink/renderer/core/fetch/request.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_REQUEST_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_REQUEST_H_
 
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
diff --git a/third_party/blink/renderer/core/fileapi/file_reader.cc b/third_party/blink/renderer/core/fileapi/file_reader.cc
index 5aeef60..2623ec2 100644
--- a/third_party/blink/renderer/core/fileapi/file_reader.cc
+++ b/third_party/blink/renderer/core/fileapi/file_reader.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/fileapi/file_reader.h"
 
 #include "base/auto_reset.h"
+#include "base/timer/elapsed_timer.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_or_array_buffer.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -65,7 +66,8 @@
 // excessive IPC congestion. We limit this to 100 per thread to throttle the
 // requests (the value is arbitrarily chosen).
 static const size_t kMaxOutstandingRequestsPerThread = 100;
-static const double kProgressNotificationIntervalMS = 50;
+static const base::TimeDelta kProgressNotificationInterval =
+    base::TimeDelta::FromMilliseconds(50);
 
 class FileReader::ThrottlingController final
     : public GarbageCollected<FileReader::ThrottlingController>,
@@ -200,8 +202,7 @@
       state_(kEmpty),
       loading_state_(kLoadingStateNone),
       still_firing_events_(false),
-      read_type_(FileReaderLoader::kReadAsBinaryString),
-      last_progress_notification_time_ms_(0) {}
+      read_type_(FileReaderLoader::kReadAsBinaryString) {}
 
 FileReader::~FileReader() {
   Terminate();
@@ -389,14 +390,13 @@
 
 void FileReader::DidReceiveData() {
   // Fire the progress event at least every 50ms.
-  double now = CurrentTimeMS();
-  if (!last_progress_notification_time_ms_) {
-    last_progress_notification_time_ms_ = now;
-  } else if (now - last_progress_notification_time_ms_ >
-             kProgressNotificationIntervalMS) {
+  if (!last_progress_notification_time_) {
+    last_progress_notification_time_ = base::ElapsedTimer();
+  } else if (last_progress_notification_time_->Elapsed() >
+             kProgressNotificationInterval) {
     base::AutoReset<bool> firing_events(&still_firing_events_, true);
     FireEvent(event_type_names::kProgress);
-    last_progress_notification_time_ms_ = now;
+    last_progress_notification_time_ = base::ElapsedTimer();
   }
 }
 
diff --git a/third_party/blink/renderer/core/fileapi/file_reader.h b/third_party/blink/renderer/core/fileapi/file_reader.h
index 17ed1a4..a066397 100644
--- a/third_party/blink/renderer/core/fileapi/file_reader.h
+++ b/third_party/blink/renderer/core/fileapi/file_reader.h
@@ -41,6 +41,10 @@
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
+namespace base {
+class ElapsedTimer;
+}
+
 namespace blink {
 
 class Blob;
@@ -131,7 +135,7 @@
 
   std::unique_ptr<FileReaderLoader> loader_;
   Member<DOMException> error_;
-  double last_progress_notification_time_ms_;
+  base::Optional<base::ElapsedTimer> last_progress_notification_time_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.h b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
index 7a2d6bd..616556f 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.h
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
@@ -29,7 +29,7 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/platform/web_content_security_policy_struct.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
diff --git a/third_party/blink/renderer/core/frame/frame_console.h b/third_party/blink/renderer/core/frame/frame_console.h
index bf284df..720cd843 100644
--- a/third_party/blink/renderer/core/frame/frame_console.h
+++ b/third_party/blink/renderer/core/frame/frame_console.h
@@ -29,7 +29,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_CONSOLE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_CONSOLE_H_
 
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index 2a02f5bc..540097a 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -503,6 +503,7 @@
     // Rules in which no external resources can be referenced
     case CSSRule::kCharsetRule:
     case CSSRule::kPageRule:
+    case CSSRule::kPropertyRule:
     case CSSRule::kKeyframesRule:
     case CSSRule::kKeyframeRule:
     case CSSRule::kNamespaceRule:
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.h b/third_party/blink/renderer/core/frame/frame_test_helpers.h
index 8001ae6..7bae591 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.h
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.h
@@ -43,7 +43,7 @@
 #include "content/test/stub_layer_tree_view_delegate.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/scheduler/test/web_fake_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_mouse_event.h"
diff --git a/third_party/blink/renderer/core/frame/fullscreen_controller.cc b/third_party/blink/renderer/core/frame/fullscreen_controller.cc
index f1e66ec..bddf14c 100644
--- a/third_party/blink/renderer/core/frame/fullscreen_controller.cc
+++ b/third_party/blink/renderer/core/frame/fullscreen_controller.cc
@@ -44,6 +44,8 @@
 #include "third_party/blink/renderer/core/fullscreen/fullscreen_options.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
 #include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/core/page/spatial_navigation.h"
+#include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
 
 namespace blink {
 
@@ -219,8 +221,14 @@
 
   // Tell the browser the fullscreen state has changed.
   if (Element* owner = new_element ? new_element : old_element) {
-    if (LocalFrame* frame = owner->GetDocument().GetFrame())
+    Document& doc = owner->GetDocument();
+    if (LocalFrame* frame = doc.GetFrame()) {
       GetWebFrameClient(*frame).FullscreenStateChanged(!!new_element);
+      if (IsSpatialNavigationEnabled(frame)) {
+        doc.GetPage()->GetSpatialNavigationController().FullscreenStateChanged(
+            new_element);
+      }
+    }
   }
 }
 
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 4df86006..69f4db5 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1962,8 +1962,10 @@
 
   if (Settings* settings = frame_->GetSettings()) {
     DCHECK(frame_->GetPage());
-    if (settings->GetSpatialNavigationEnabled())
-      frame_->GetPage()->GetSpatialNavigationController().DidDetachFrameView();
+    if (settings->GetSpatialNavigationEnabled()) {
+      frame_->GetPage()->GetSpatialNavigationController().DidDetachFrameView(
+          *this);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/frame/use_counter_helper.cc b/third_party/blink/renderer/core/frame/use_counter_helper.cc
index c92d3b8..4235a98 100644
--- a/third_party/blink/renderer/core/frame/use_counter_helper.cc
+++ b/third_party/blink/renderer/core/frame/use_counter_helper.cc
@@ -1243,6 +1243,12 @@
       return 639;
     case CSSPropertyID::kForcedColorAdjust:
       return 640;
+    case CSSPropertyID::kInherits:
+      return 641;
+    case CSSPropertyID::kInitialValue:
+      return 642;
+    case CSSPropertyID::kSyntax:
+      return 643;
     // 1. Add new features above this line (don't change the assigned numbers of
     // the existing items).
     // 2. Update kMaximumCSSSampleId (defined in
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 7ecc2ce..4a48109 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -9,7 +9,7 @@
 #include "cc/layers/picture_layer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_input_event.h"
diff --git a/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc b/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc
index 142944f..0221f293 100644
--- a/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc
+++ b/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc
@@ -33,7 +33,7 @@
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
-#include "ui/base/ime/mojo/ime_types.mojom-shared.h"
+#include "ui/base/ime/mojo/ime_types.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index b95ebe78..ace7634 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -20,7 +20,7 @@
 
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
@@ -359,7 +359,6 @@
 
   embedded_content_view_ = embedded_content_view;
   FrameOwnerPropertiesChanged();
-  SetNeedsCompositingUpdate();
 
   GetDocument().GetRootScrollerController().DidUpdateIFrameFrameView(*this);
 
@@ -368,6 +367,8 @@
   if (!layout_embedded_content)
     return;
 
+  layout_embedded_content->UpdateOnEmbeddedContentViewChange();
+
   if (embedded_content_view_) {
     // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout
     // crashes.  Perhaps view is getting reattached while document is shutting
@@ -375,7 +376,6 @@
     if (doc) {
       CHECK_NE(doc->Lifecycle().GetState(), DocumentLifecycle::kStopping);
     }
-    layout_embedded_content->UpdateOnEmbeddedContentViewChange();
 
     DCHECK_EQ(GetDocument().View(), layout_embedded_content->GetFrameView());
     DCHECK(layout_embedded_content->GetFrameView());
diff --git a/third_party/blink/renderer/core/html/imports/link_import.cc b/third_party/blink/renderer/core/html/imports/link_import.cc
index c5fc72d..73df690 100644
--- a/third_party/blink/renderer/core/html/imports/link_import.cc
+++ b/third_party/blink/renderer/core/html/imports/link_import.cc
@@ -30,7 +30,7 @@
 
 #include "third_party/blink/renderer/core/html/imports/link_import.h"
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/html/html_link_element.h"
 #include "third_party/blink/renderer/core/html/imports/html_import_child.h"
diff --git a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
index 934ea14..4412e3c 100644
--- a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
+++ b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
@@ -57,6 +58,24 @@
   return nullptr;
 }
 
+// Returns if the element or its ancestors are invisible, due to their style or
+// attribute or due to themselves not connected to the main document tree.
+bool IsElementInInvisibleSubTree(const Element& element) {
+  if (!element.isConnected())
+    return true;
+  for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(element)) {
+    auto* ancestor_element = DynamicTo<Element>(ancestor);
+    if (!ancestor_element)
+      continue;
+    const ComputedStyle* style = ancestor_element->EnsureComputedStyle();
+    if (style && (style->Visibility() != EVisibility::kVisible ||
+                  style->Display() == EDisplay::kNone)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace
 
 void LazyLoadImageObserver::StartMonitoring(Element* element,
@@ -139,10 +158,26 @@
   DCHECK(!entries.IsEmpty());
 
   for (auto entry : entries) {
+    Element* element = entry->target();
+    HTMLImageElement* image_element = ToHTMLImageElementOrNull(element);
+    if (!entry->isIntersecting() && image_element) {
+      // Fully load the invisible image elements. The elements can be invisible
+      // by style such as display:none, visibility: hidden, or hidden via
+      // attribute, etc. Style might also not be calculated if the ancestors
+      // were invisible.
+      const ComputedStyle* style = entry->target()->GetComputedStyle();
+      if (!style || style->Visibility() != EVisibility::kVisible ||
+          style->Display() == EDisplay::kNone) {
+        // Check that style was null because it was not computed since the
+        // element was in an invisible subtree.
+        DCHECK(style || IsElementInInvisibleSubTree(*element));
+        image_element->LoadDeferredImage();
+        lazy_load_intersection_observer_->unobserve(element);
+      }
+    }
     if (!entry->isIntersecting())
       continue;
-    Element* element = entry->target();
-    if (auto* image_element = ToHTMLImageElementOrNull(element))
+    if (image_element)
       image_element->LoadDeferredImage();
 
     // Load the background image if the element has one deferred.
diff --git a/third_party/blink/renderer/core/html/link_style.cc b/third_party/blink/renderer/core/html/link_style.cc
index e9be1ac..b72aef33 100644
--- a/third_party/blink/renderer/core/html/link_style.cc
+++ b/third_party/blink/renderer/core/html/link_style.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/html/link_style.h"
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
@@ -69,9 +69,10 @@
   CSSStyleSheetResource* cached_style_sheet = ToCSSStyleSheetResource(resource);
   // See the comment in pending_script.cc about why this check is necessary
   // here, instead of in the resource fetcher. https://crbug.com/500701.
-  if (!cached_style_sheet->ErrorOccurred() &&
-      !owner_->FastGetAttribute(kIntegrityAttr).IsEmpty() &&
-      !cached_style_sheet->IntegrityMetadata().IsEmpty()) {
+  if ((!cached_style_sheet->ErrorOccurred() &&
+       !owner_->FastGetAttribute(kIntegrityAttr).IsEmpty() &&
+       !cached_style_sheet->IntegrityMetadata().IsEmpty()) ||
+      resource->IsLinkPreload()) {
     ResourceIntegrityDisposition disposition =
         cached_style_sheet->IntegrityDisposition();
 
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index e2e34959..184ee3e8 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -732,6 +732,7 @@
   friend class Internals;
   friend class TrackDisplayUpdateScope;
   friend class MediaControlsImplTest;
+  friend class MediaControlsTouchlessImplTest;
   friend class HTMLMediaElementTest;
   friend class HTMLMediaElementEventListenersTest;
   friend class HTMLVideoElement;
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index dbb186e..3245d1116e 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -387,7 +387,7 @@
 }
 
 void HTMLVideoElement::UpdateDisplayState() {
-  if (PosterImageURL().IsEmpty())
+  if (PosterImageURL().IsEmpty() || HasAvailableVideoFrame())
     SetDisplayMode(kVideo);
   else if (GetDisplayMode() < kPoster)
     SetDisplayMode(kPoster);
@@ -516,13 +516,9 @@
 }
 
 bool HTMLVideoElement::HasAvailableVideoFrame() const {
-  if (!GetWebMediaPlayer())
-    return false;
-
-  // The ready state maximum is used here instead of the current ready state
-  // since a frame is still available during a seek.
-  return GetWebMediaPlayer()->HasVideo() &&
-         ready_state_maximum_ >= kHaveCurrentData;
+  if (auto* wmp = GetWebMediaPlayer())
+    return wmp->HasAvailableVideoFrame();
+  return false;
 }
 
 void HTMLVideoElement::webkitEnterFullscreen() {
diff --git a/third_party/blink/renderer/core/html/media/html_video_element_test.cc b/third_party/blink/renderer/core/html/media/html_video_element_test.cc
index d5a1f3d1..a0dc8fe 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element_test.cc
@@ -20,12 +20,15 @@
 #include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
+using testing::_;
+
 namespace blink {
 
 class HTMLVideoElementMockMediaPlayer : public EmptyWebMediaPlayer {
  public:
   MOCK_METHOD1(SetIsEffectivelyFullscreen, void(WebFullscreenVideoStatus));
   MOCK_METHOD1(OnDisplayTypeChanged, void(WebMediaPlayer::DisplayType));
+  MOCK_CONST_METHOD0(HasAvailableVideoFrame, bool());
 };
 
 class HTMLVideoElementTest : public PageTestBase {
@@ -71,6 +74,7 @@
   video()->UpdateTextTrackDisplay();
 
   // Simulate entering Picture-in-Picture.
+  EXPECT_CALL(*MockWebMediaPlayer(), OnDisplayTypeChanged(_));
   video()->OnEnteredPictureInPicture();
 
   // Simulate that text track are displayed again.
@@ -90,6 +94,10 @@
   video()->SetSrc("http://example.com/foo.mp4");
   test::RunPendingTasks();
 
+  EXPECT_CALL(*MockWebMediaPlayer(), OnDisplayTypeChanged(_));
+  EXPECT_CALL(*MockWebMediaPlayer(), HasAvailableVideoFrame())
+      .WillRepeatedly(testing::Return(true));
+
   // Simulate entering Picture-in-Picture.
   video()->OnEnteredPictureInPicture();
 
@@ -100,6 +108,10 @@
 }
 
 TEST_F(HTMLVideoElementTest, EffectivelyFullscreen_DisplayType) {
+  video()->SetSrc("http://example.com/foo.mp4");
+  test::RunPendingTasks();
+  UpdateAllLifecyclePhasesForTest();
+
   EXPECT_EQ(WebMediaPlayer::DisplayType::kInline, video()->DisplayType());
 
   // Vector of data to use for tests. First value is to be set when calling
@@ -125,6 +137,7 @@
     video()->SetIsEffectivelyFullscreen(test.first);
 
     EXPECT_EQ(test.second, video()->DisplayType());
+    testing::Mock::VerifyAndClearExpectations(MockWebMediaPlayer());
   }
 }
 
@@ -156,4 +169,16 @@
   EXPECT_FALSE(paint_layer->NeedsCompositingInputsUpdate());
 }
 
+TEST_F(HTMLVideoElementTest, HasAvailableVideoFrameChecksWMP) {
+  video()->SetSrc("http://example.com/foo.mp4");
+  test::RunPendingTasks();
+  UpdateAllLifecyclePhasesForTest();
+
+  EXPECT_CALL(*MockWebMediaPlayer(), HasAvailableVideoFrame())
+      .WillOnce(testing::Return(false))
+      .WillOnce(testing::Return(true));
+  EXPECT_FALSE(video()->HasAvailableVideoFrame());
+  EXPECT_TRUE(video()->HasAvailableVideoFrame());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
index da197da..624f5e4 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -29,7 +29,7 @@
 
 #include <memory>
 #include "base/optional.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_property_name.h"
@@ -338,10 +338,7 @@
     }
     request->SetIsLazyLoadImageEnabled(is_lazy_load_image_enabled);
 
-    // The only link tags that should keep the integrity metadata are
-    // stylesheets until crbug.com/677022 is resolved.
-    if (link_is_style_sheet_ || !Match(tag_impl_, kLinkTag))
-      request->SetIntegrityMetadata(integrity_metadata_);
+    request->SetIntegrityMetadata(integrity_metadata_);
 
     if (scanner_type_ == ScannerType::kInsertion)
       request->SetFromInsertionScanner(true);
diff --git a/third_party/blink/renderer/core/html/parser/preload_request.h b/third_party/blink/renderer/core/html/parser/preload_request.h
index d5e88d4c..81fa048 100644
--- a/third_party/blink/renderer/core/html/parser/preload_request.h
+++ b/third_party/blink/renderer/core/html/parser/preload_request.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/script/script.h"
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 702a1f5..c3189ff 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -668,7 +668,8 @@
   if (!frame_->View())
     return WebInputEventResult::kNotHandled;
 
-  HitTestRequest request(HitTestRequest::kActive);
+  HitTestRequest request(HitTestRequest::kActive |
+                         HitTestRequest::kRetargetForInert);
   // Save the document point we generate in case the window coordinate is
   // invalidated by what happens when we dispatch the event.
   PhysicalOffset document_point = frame_->View()->ConvertFromRootFrame(
@@ -768,8 +769,10 @@
   // dragging if we keep the selection in case of mousedown. FireFox also has
   // the same behavior and it's more compatible with other browsers.
   GetSelectionController().InitializeSelectionState();
+
   HitTestResult hit_test_result = event_handling_util::HitTestResultInFrame(
-      frame_, HitTestLocation(document_point), HitTestRequest::kReadOnly);
+      frame_, HitTestLocation(document_point),
+      HitTestRequest::kReadOnly | HitTestRequest::kRetargetForInert);
   InputDeviceCapabilities* source_capabilities =
       frame_->GetDocument()
           ->domWindow()
@@ -801,7 +804,8 @@
   if (event_result == WebInputEventResult::kNotHandled) {
     if (ShouldRefetchEventTarget(mev)) {
       HitTestRequest read_only_request(HitTestRequest::kReadOnly |
-                                       HitTestRequest::kActive);
+                                       HitTestRequest::kActive |
+                                       HitTestRequest::kRetargetForInert);
       mev = frame_->GetDocument()->PerformMouseEventHitTest(
           read_only_request, document_point, mouse_event);
     }
@@ -918,7 +922,8 @@
     return WebInputEventResult::kHandledSystem;
   }
 
-  HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kMove;
+  HitTestRequest::HitTestRequestType hit_type =
+      HitTestRequest::kMove | HitTestRequest::kRetargetForInert;
   if (mouse_event_manager_->MousePressed()) {
     hit_type |= HitTestRequest::kActive;
   } else if (only_update_scrollbars) {
@@ -1081,7 +1086,8 @@
 
   // Mouse events simulated from touch should not hit-test again.
   DCHECK(!mouse_event.FromTouch());
-  HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kRelease;
+  HitTestRequest::HitTestRequestType hit_type =
+      HitTestRequest::kRelease | HitTestRequest::kRetargetForInert;
   HitTestRequest request(hit_type);
   MouseEventWithHitTestResults mev = GetMouseEventTarget(request, mouse_event);
   LocalFrame* subframe = event_handling_util::GetTargetSubframe(
@@ -1139,7 +1145,8 @@
   if (!frame_->View())
     return event_result;
 
-  HitTestRequest request(HitTestRequest::kReadOnly);
+  HitTestRequest request(HitTestRequest::kReadOnly |
+                         HitTestRequest::kRetargetForInert);
   MouseEventWithHitTestResults mev =
       event_handling_util::PerformMouseEventHitTest(frame_, request, event);
 
@@ -1824,6 +1831,7 @@
   // disabled). Note that we don't yet apply hover/active state here because
   // we need to resolve touch adjustment first so that we apply hover/active
   // it to the final adjusted node.
+  hit_type |= HitTestRequest::kRetargetForInert;
   hit_type |= HitTestRequest::kReadOnly;
   WebGestureEvent adjusted_event = gesture_event;
   LayoutSize hit_rect_size;
@@ -1938,7 +1946,8 @@
 
   PhysicalOffset position_in_contents(
       v->ConvertFromRootFrame(FlooredIntPoint(event.PositionInRootFrame())));
-  HitTestRequest request(HitTestRequest::kActive);
+  HitTestRequest request(HitTestRequest::kActive |
+                         HitTestRequest::kRetargetForInert);
   MouseEventWithHitTestResults mev =
       frame_->GetDocument()->PerformMouseEventHitTest(
           request, position_in_contents, event);
@@ -2025,7 +2034,8 @@
     target_node = doc;
 
   // Use the focused node as the target for hover and active.
-  HitTestRequest request(HitTestRequest::kActive);
+  HitTestRequest request(HitTestRequest::kActive |
+                         HitTestRequest::kRetargetForInert);
   HitTestLocation location(location_in_root_frame);
   HitTestResult result(request, location);
   result.SetInnerNode(target_node);
@@ -2111,7 +2121,8 @@
 
   if (auto* layout_object = frame_->ContentLayoutObject()) {
     if (LocalFrameView* view = frame_->View()) {
-      HitTestRequest request(HitTestRequest::kMove);
+      HitTestRequest request(HitTestRequest::kMove |
+                             HitTestRequest::kRetargetForInert);
       HitTestLocation location(view->ViewportToFrame(
           mouse_event_manager_->LastKnownMousePositionInViewport()));
       HitTestResult result(request, location);
@@ -2129,7 +2140,8 @@
     // FIXME: Enable condition when http://crbug.com/226842 lands
     // m_lastDeferredTapElement.get() == m_frame->document()->activeElement()
     HitTestRequest request(HitTestRequest::kTouchEvent |
-                           HitTestRequest::kRelease);
+                           HitTestRequest::kRelease |
+                           HitTestRequest::kRetargetForInert);
     frame_->GetDocument()->UpdateHoverActiveState(
         request.Active(), !request.Move(), last_deferred_tap_element_.Get());
   }
@@ -2169,7 +2181,8 @@
                                      DragOperation operation) {
   // Asides from routing the event to the correct frame, the hit test is also an
   // opportunity for Layer to update the :hover and :active pseudoclasses.
-  HitTestRequest request(HitTestRequest::kRelease);
+  HitTestRequest request(HitTestRequest::kRelease |
+                         HitTestRequest::kRetargetForInert);
   MouseEventWithHitTestResults mev =
       event_handling_util::PerformMouseEventHitTest(frame_, request, event);
 
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h
index 8f4e489..7666b16 100644
--- a/third_party/blink/renderer/core/input/event_handler.h
+++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -104,8 +104,9 @@
 
   HitTestResult HitTestResultAtLocation(
       const HitTestLocation&,
-      HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kReadOnly |
-                                                    HitTestRequest::kActive,
+      HitTestRequest::HitTestRequestType hit_type =
+          HitTestRequest::kReadOnly | HitTestRequest::kActive |
+          HitTestRequest::kRetargetForInert,
       const LayoutObject* stop_node = nullptr,
       bool no_lifecycle_update = false);
 
diff --git a/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc b/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc
index 64ab5bd..86629b9 100644
--- a/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc
+++ b/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc
@@ -55,7 +55,8 @@
 
 HitTestResult HitTest(LayoutView* layout_view, const IntPoint& point_in_root) {
   HitTestRequest request(HitTestRequest::kReadOnly |
-                         HitTestRequest::kAllowChildFrameContent);
+                         HitTestRequest::kAllowChildFrameContent |
+                         HitTestRequest::kRetargetForInert);
   HitTestLocation location(point_in_root);
   HitTestResult result(request, location);
   layout_view->HitTest(location, result);
@@ -91,8 +92,26 @@
   IntPoint location_in_frame =
       view->ConvertFromRootFrame(location_in_root_frame);
 
-  if (&scrollable == view->GetScrollableArea())
+  if (&scrollable == view->GetScrollableArea()) {
+    LocalFrame& frame = view->GetFrame();
+    if (frame.IsMainFrame() && frame.GetPage()) {
+      // For the main frame, the scroller whose location we want to be relative
+      // to is the visual viewport so that the cursor works under pinch zoom
+      // scenarios.
+      VisualViewport& viewport = frame.GetPage()->GetVisualViewport();
+      IntPoint point_in_viewport =
+          viewport.RootFrameToViewport(location_in_frame);
+
+      // Scale since we're in visual viewport coordinates, and want the offset
+      // relative to the visual viewport, but all the comparisons related to
+      // scroller size are done in the root frame.
+      float scale = 1.f / viewport.Scale();
+      point_in_viewport.Scale(scale, scale);
+      return point_in_viewport;
+    }
+
     return location_in_frame;
+  }
 
   DCHECK(scrollable.IsPaintLayerScrollableArea());
 
diff --git a/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc b/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc
index 8b8cda46..5e70d4c 100644
--- a/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc
+++ b/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/public/platform/web_mouse_event.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
@@ -407,6 +408,47 @@
   EXPECT_FALSE(GetDocument().FocusedElement());
 }
 
+// Ensure the cursor causes correct locking and scrolling when the web page is
+// zoomed in and the visual viewport is offset.
+TEST_F(FallbackCursorEventManagerTest, ZoomedIn) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+    html, body {
+      margin: 0px;
+    }
+    .big {
+      height: 10000px;
+      width: 10000px;
+    }
+    </style>
+    <div class='big'></div>
+  )HTML");
+  TurnOnFallbackCursorMode();
+  VisualViewport& visual_viewport =
+      GetDocument().GetPage()->GetVisualViewport();
+  visual_viewport.SetScaleAndLocation(4, /*is_pinch_gesture_active=*/false,
+                                      FloatPoint(400, 300));
+
+  ASSERT_EQ(IntSize(800, 600), GetDocument().View()->Size());
+  ASSERT_EQ(FloatSize(200, 150), visual_viewport.VisibleRect().Size());
+
+  // Note: MouseMove position is specified in root frame coordinates. To
+  // determine the coordinates in the visual viewport, subtract the visual
+  // viewport offset (400, 300) from the MouseMove position.
+
+  // Move to the center of the viewport.
+  MouseMove(500, 375);
+  ExpectLock(false, false, false, false);
+
+  // Move below the scroll down line.
+  MouseMove(500, 430);
+  ExpectLock(false, false, false, true);
+
+  // Move to the left of scroll left line.
+  MouseMove(450, 375);
+  ExpectLock(true, false, false, false);
+}
+
 TEST_F(FallbackCursorEventManagerTest, NotInCursorMode) {
   GetPage().SetIsCursorVisible(false);
   EXPECT_FALSE(KeyBack());
diff --git a/third_party/blink/renderer/core/input/gesture_manager.cc b/third_party/blink/renderer/core/input/gesture_manager.cc
index 77ef8b8..9536c9f8 100644
--- a/third_party/blink/renderer/core/input/gesture_manager.cc
+++ b/third_party/blink/renderer/core/input/gesture_manager.cc
@@ -65,7 +65,8 @@
 
 HitTestRequest::HitTestRequestType GestureManager::GetHitTypeForGestureType(
     WebInputEvent::Type type) {
-  HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent;
+  HitTestRequest::HitTestRequestType hit_type =
+      HitTestRequest::kTouchEvent | HitTestRequest::kRetargetForInert;
   switch (type) {
     case WebInputEvent::kGestureShowPress:
     case WebInputEvent::kGestureTapUnconfirmed:
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc
index c8ba932f..4bde658 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -920,7 +920,8 @@
     return false;
 
   if (mouse_down_may_start_drag_) {
-    HitTestRequest request(HitTestRequest::kReadOnly);
+    HitTestRequest request(HitTestRequest::kReadOnly |
+                           HitTestRequest::kRetargetForInert);
     HitTestLocation location(mouse_down_pos_);
     HitTestResult result(request, location);
     frame_->ContentLayoutObject()->HitTest(location, result);
diff --git a/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc b/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
index 66cb45e..9a3074a 100644
--- a/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
@@ -119,7 +119,8 @@
   PhysicalOffset v_point(
       view->ConvertFromRootFrame(FlooredIntPoint(event.PositionInRootFrame())));
 
-  HitTestRequest request(HitTestRequest::kReadOnly);
+  HitTestRequest request(HitTestRequest::kReadOnly |
+                         HitTestRequest::kRetargetForInert);
   HitTestLocation location(v_point);
   HitTestResult result(request, location);
   doc->GetLayoutView()->HitTest(location, result);
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 ba9752f29..ed303b4 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -364,7 +364,8 @@
 
   HitTestRequest::HitTestRequestType hit_type =
       HitTestRequest::kTouchEvent | HitTestRequest::kReadOnly |
-      HitTestRequest::kActive | HitTestRequest::kListBased;
+      HitTestRequest::kActive | HitTestRequest::kListBased |
+      HitTestRequest::kRetargetForInert;
   LocalFrame& root_frame = frame_->LocalFrameRoot();
   // TODO(szager): Shouldn't this be PositionInScreen() ?
   PhysicalOffset hit_test_point =
@@ -420,9 +421,9 @@
   // before firing the event.
   if (web_pointer_event.GetType() == WebInputEvent::kPointerDown ||
       !pending_pointer_capture_target_.Contains(pointer_id)) {
-    HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent |
-                                                  HitTestRequest::kReadOnly |
-                                                  HitTestRequest::kActive;
+    HitTestRequest::HitTestRequestType hit_type =
+        HitTestRequest::kTouchEvent | HitTestRequest::kReadOnly |
+        HitTestRequest::kActive | HitTestRequest::kRetargetForInert;
     HitTestLocation location(frame_->View()->ConvertFromRootFrame(
         PhysicalOffset::FromFloatPointRound(
             web_pointer_event.PositionInWidget())));
@@ -808,7 +809,7 @@
         if (pointer_capture_target_.find(pointer_event->pointerId()) !=
             pointer_capture_target_.end()) {
           HitTestRequest::HitTestRequestType hit_type =
-              HitTestRequest::kRelease;
+              HitTestRequest::kRelease | HitTestRequest::kRetargetForInert;
           HitTestRequest request(hit_type);
           MouseEventWithHitTestResults mev =
               event_handling_util::PerformMouseEventHitTest(frame_, request,
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 70de36ad..29dbf51e2 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -324,7 +324,7 @@
             if (area)
               area->MarkHoverStateDirty();
           },
-          scrollable_area));
+          WrapWeakPersistent(scrollable_area)));
     }
     ScrollResult result = scrollable_area->UserScroll(
         granularity, ToScrollDelta(physical_direction, 1), std::move(callback));
@@ -889,7 +889,8 @@
     LocalFrameView* view = frame_->View();
     PhysicalOffset view_point(view->ConvertFromRootFrame(
         FlooredIntPoint(gesture_event.PositionInRootFrame())));
-    HitTestRequest request(HitTestRequest::kReadOnly);
+    HitTestRequest request(HitTestRequest::kReadOnly |
+                           HitTestRequest::kRetargetForInert);
     HitTestLocation location(view_point);
     HitTestResult result(request, location);
     document->GetLayoutView()->HitTest(location, result);
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 2675c6b..649f926c 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -5591,10 +5591,35 @@
   # Pauses page execution. Can be resumed using generic Runtime.runIfWaitingForDebugger.
   experimental command waitForDebugger
 
+  # Intercept file chooser requests and transfer control to protocol clients.
+  # When file chooser interception is enabled, native file chooser dialog is not shown.
+  # Instead, a protocol event `Page.fileChooserOpened` is emitted.
+  # File chooser can be handled with `page.handleFileChooser` command.
+  experimental command setInterceptFileChooserDialog
+    parameters
+      boolean enabled
+
+  # Accepts or cancels an intercepted file chooser dialog.
+  experimental command handleFileChooser
+    parameters
+      enum action
+        accept
+        cancel
+        fallback
+      # Array of absolute file paths to set, only respected with `accept` action.
+      optional array of string files
+
   event domContentEventFired
     parameters
       Network.MonotonicTime timestamp
 
+  # Emitted only when `page.interceptFileChooser` is enabled.
+  event fileChooserOpened
+    parameters
+      enum mode
+        selectSingle
+        selectMultiple
+
   # Fired when frame has been attached to its parent.
   event frameAttached
     parameters
diff --git a/third_party/blink/renderer/core/inspector/console_message.h b/third_party/blink/renderer/core/inspector/console_message.h
index 2d9eaf6..ecaf42b 100644
--- a/third_party/blink/renderer/core/inspector/console_message.h
+++ b/third_party/blink/renderer/core/inspector/console_message.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_CONSOLE_MESSAGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_CONSOLE_MESSAGE_H_
 
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index d45e364..9f1c9772 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -2371,11 +2371,11 @@
   auto* computed_style_info =
       MakeGarbageCollected<CSSComputedStyleDeclaration>(element, true);
   const CSSValue* font_size =
-      computed_style_info->GetPropertyCSSValue(GetCSSPropertyFontSize());
+      computed_style_info->GetPropertyCSSValue(CSSPropertyID::kFontSize);
   if (font_size)
     *computed_font_size = font_size->CssText();
   const CSSValue* font_weight =
-      computed_style_info->GetPropertyCSSValue(GetCSSPropertyFontWeight());
+      computed_style_info->GetPropertyCSSValue(CSSPropertyID::kFontWeight);
   if (font_weight)
     *computed_font_weight = font_weight->CssText();
 }
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 ca139ca..9ed25195 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -32,7 +32,7 @@
     WebLocalFrameImpl* web_local_frame_impl)
     : web_local_frame_(web_local_frame_impl),
       default_background_color_override_rgba_(&agent_state_,
-                                              /*default_value=*/WTF::String()),
+                                              /*default_value=*/{}),
       script_execution_disabled_(&agent_state_, /*default_value=*/false),
       scrollbars_hidden_(&agent_state_, /*default_value=*/false),
       document_cookie_disabled_(&agent_state_, /*default_value=*/false),
@@ -60,6 +60,20 @@
   return web_local_frame_ ? web_local_frame_->ViewImpl() : nullptr;
 }
 
+namespace {
+std::unique_ptr<protocol::DOM::RGBA> ParseRGBA(
+    const std::vector<uint8_t>& cbor) {
+  auto parsed = protocol::Value::parseBinary(cbor.data(), cbor.size());
+  if (!parsed)
+    return nullptr;
+  blink::protocol::ErrorSupport errors;
+  auto rgba = protocol::DOM::RGBA::fromValue(parsed.get(), &errors);
+  if (errors.hasErrors())
+    return nullptr;
+  return rgba;
+}
+}  // namespace
+
 void InspectorEmulationAgent::Restore() {
   setUserAgentOverride(user_agent_override_.Get(),
                        accept_language_override_.Get(),
@@ -77,18 +91,9 @@
   setTouchEmulationEnabled(touch_event_emulation_enabled_.Get(),
                            max_touch_points_.Get());
   setEmulatedMedia(emulated_media_.Get());
-  if (!default_background_color_override_rgba_.Get().IsNull()) {
-    std::unique_ptr<protocol::Value> parsed = protocol::StringUtil::parseJSON(
-        default_background_color_override_rgba_.Get());
-    if (parsed) {
-      blink::protocol::ErrorSupport errors;
-      auto rgba = protocol::DOM::RGBA::fromValue(parsed.get(), &errors);
-      if (!errors.hasErrors()) {
-        setDefaultBackgroundColorOverride(
-            Maybe<protocol::DOM::RGBA>(std::move(rgba)));
-      }
-    }
-  }
+  auto rgba = ParseRGBA(default_background_color_override_rgba_.Get());
+  if (rgba)
+    setDefaultBackgroundColorOverride(std::move(rgba));
   setFocusEmulationEnabled(emulate_focus_.Get());
 
   if (!timezone_id_override_.Get().IsNull())
@@ -404,7 +409,7 @@
   }
 
   blink::protocol::DOM::RGBA* rgba = color.fromJust();
-  default_background_color_override_rgba_.Set(rgba->toJSON());
+  default_background_color_override_rgba_.Set(rgba->serializeToBinary());
   // Clamping of values is done by Color() constructor.
   int alpha = static_cast<int>(lroundf(255.0f * rgba->getA(1.0f)));
   GetWebViewImpl()->SetBaseBackgroundColorOverride(
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 4150a23a..fc13e54 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
@@ -116,7 +116,7 @@
   base::Optional<PendingVirtualTimePolicy> pending_virtual_time_policy_;
   bool enabled_ = false;
 
-  InspectorAgentState::String default_background_color_override_rgba_;
+  InspectorAgentState::Bytes default_background_color_override_rgba_;
   InspectorAgentState::Boolean script_execution_disabled_;
   InspectorAgentState::Boolean scrollbars_hidden_;
   InspectorAgentState::Boolean document_cookie_disabled_;
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 31c899e..0efc5a8 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -37,9 +37,9 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "services/network/public/mojom/websocket.mojom-blink.h"
-#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/public/platform/web_mixed_content_context_type.h"
@@ -539,16 +539,16 @@
 
   String security_state = protocol::Security::SecurityStateEnum::Unknown;
   switch (response.GetSecurityStyle()) {
-    case ResourceResponse::kSecurityStyleUnknown:
+    case kWebSecurityStyleUnknown:
       security_state = protocol::Security::SecurityStateEnum::Unknown;
       break;
-    case ResourceResponse::kSecurityStyleUnauthenticated:
+    case kWebSecurityStyleNeutral:
       security_state = protocol::Security::SecurityStateEnum::Neutral;
       break;
-    case ResourceResponse::kSecurityStyleAuthenticationBroken:
+    case kWebSecurityStyleInsecure:
       security_state = protocol::Security::SecurityStateEnum::Insecure;
       break;
-    case ResourceResponse::kSecurityStyleAuthenticated:
+    case kWebSecurityStyleSecure:
       security_state = protocol::Security::SecurityStateEnum::Secure;
       break;
   }
diff --git a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
index f203f95..2fb6fdf8 100644
--- a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
+++ b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
@@ -74,7 +74,7 @@
             {
                 "domain": "Page",
                 "exclude": ["getNavigationHistory", "navigateToHistoryEntry", "resetNavigationHistory", "captureScreenshot", "screencastFrameAck", "handleJavaScriptDialog", "setColorPickerEnabled",
-                            "getAppManifest", "setControlNavigations", "processNavigation", "printToPDF", "bringToFront", "setDownloadBehavior", "navigate", "crash", "close", "setWebLifecycleState", "captureSnapshot", "getInstallabilityErrors"],
+                            "getAppManifest", "setControlNavigations", "processNavigation", "printToPDF", "bringToFront", "setDownloadBehavior", "navigate", "crash", "close", "setWebLifecycleState", "captureSnapshot", "getInstallabilityErrors", "setInterceptFileChooserDialog", "handleFileChooser"],
                 "async": ["getResourceContent", "searchInResource"],
                 "exclude_events": ["screencastFrame", "screencastVisibilityChanged", "colorPicked", "interstitialShown", "interstitialHidden", "javascriptDialogOpening", "javascriptDialogClosed", "navigationRequested"]
             },
diff --git a/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc b/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
index 057e35ea..ba9eb28 100644
--- a/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/inspector/inspector_resource_content_loader.h"
 
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
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 daf6c80..c11673d 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -9,7 +9,7 @@
 #include <memory>
 
 #include "cc/layers/picture_layer.h"
-#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/web_layer_tree_view.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
diff --git a/third_party/blink/renderer/core/inspector/thread_debugger.h b/third_party/blink/renderer/core/inspector/thread_debugger.h
index 38e3407..7adb02f 100644
--- a/third_party/blink/renderer/core/inspector/thread_debugger.h
+++ b/third_party/blink/renderer/core/inspector/thread_debugger.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 #include "base/macros.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
diff --git a/third_party/blink/renderer/core/layout/BUILD.gn b/third_party/blink/renderer/core/layout/BUILD.gn
index 523cd42..0cfa624c 100644
--- a/third_party/blink/renderer/core/layout/BUILD.gn
+++ b/third_party/blink/renderer/core/layout/BUILD.gn
@@ -454,7 +454,7 @@
     "ng/ng_link.h",
     "ng/ng_out_of_flow_layout_part.cc",
     "ng/ng_out_of_flow_layout_part.h",
-    "ng/ng_out_of_flow_positioned_descendant.h",
+    "ng/ng_out_of_flow_positioned_node.h",
     "ng/ng_outline_type.h",
     "ng/ng_outline_utils.cc",
     "ng/ng_outline_utils.h",
diff --git a/third_party/blink/renderer/core/layout/geometry/logical_offset.h b/third_party/blink/renderer/core/layout/geometry/logical_offset.h
index 22060600..ae0a241f 100644
--- a/third_party/blink/renderer/core/layout/geometry/logical_offset.h
+++ b/third_party/blink/renderer/core/layout/geometry/logical_offset.h
@@ -25,7 +25,7 @@
   constexpr LogicalOffset(LayoutUnit inline_offset, LayoutUnit block_offset)
       : inline_offset(inline_offset), block_offset(block_offset) {}
 
-  // For testing only. It's defined in core/testing/core_unit_test_helpers.h.
+  // For testing only. It's defined in core/testing/core_unit_test_helper.h.
   inline LogicalOffset(int inline_offset, int block_offset);
 
   LayoutUnit inline_offset;
diff --git a/third_party/blink/renderer/core/layout/hit_test_request.h b/third_party/blink/renderer/core/layout/hit_test_request.h
index 2892c03..e96db5cc 100644
--- a/third_party/blink/renderer/core/layout/hit_test_request.h
+++ b/third_party/blink/renderer/core/layout/hit_test_request.h
@@ -45,15 +45,16 @@
     kAllowChildFrameContent = 1 << 8,
     kChildFrameHitTest = 1 << 9,
     kIgnorePointerEventsNone = 1 << 10,
+    kRetargetForInert = 1 << 11,
     // Collect a list of nodes instead of just one.
     // (This is for elementsFromPoint and rect-based tests).
-    kListBased = 1 << 11,
+    kListBased = 1 << 12,
     // When using list-based testing, this flag causes us to continue hit
     // testing after a hit has been found.
-    kPenetratingList = 1 << 12,
-    kAvoidCache = 1 << 13,
-    kIgnoreZeroOpacityObjects = 1 << 14,
-    kHitTestVisualOverflow = 1 << 15,
+    kPenetratingList = 1 << 13,
+    kAvoidCache = 1 << 14,
+    kIgnoreZeroOpacityObjects = 1 << 15,
+    kHitTestVisualOverflow = 1 << 16,
   };
 
   typedef unsigned HitTestRequestType;
@@ -81,6 +82,7 @@
   bool IgnorePointerEventsNone() const {
     return request_type_ & kIgnorePointerEventsNone;
   }
+  bool RetargetForInert() const { return request_type_ & kRetargetForInert; }
   bool ListBased() const { return request_type_ & kListBased; }
   bool PenetratingList() const { return request_type_ & kPenetratingList; }
   bool AvoidCache() const { return request_type_ & kAvoidCache; }
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.cc b/third_party/blink/renderer/core/layout/hit_test_result.cc
index b8bcc9c3a..4eb246b7 100644
--- a/third_party/blink/renderer/core/layout/hit_test_result.cc
+++ b/third_party/blink/renderer/core/layout/hit_test_result.cc
@@ -64,6 +64,7 @@
     : hit_test_request_(other.hit_test_request_),
       cacheable_(other.cacheable_),
       inner_node_(other.InnerNode()),
+      inert_node_(other.InertNode()),
       inner_element_(other.InnerElement()),
       inner_possibly_pseudo_node_(other.inner_possibly_pseudo_node_),
       point_in_inner_node_frame_(other.point_in_inner_node_frame_),
@@ -90,7 +91,7 @@
 
 bool HitTestResult::EqualForCacheability(const HitTestResult& other) const {
   return hit_test_request_.EqualForCacheability(other.hit_test_request_) &&
-         inner_node_ == other.InnerNode() &&
+         inner_node_ == other.InnerNode() && inert_node_ == other.InertNode() &&
          inner_element_ == other.InnerElement() &&
          inner_possibly_pseudo_node_ == other.InnerPossiblyPseudoNode() &&
          point_in_inner_node_frame_ == other.point_in_inner_node_frame_ &&
@@ -107,6 +108,7 @@
 
 void HitTestResult::PopulateFromCachedResult(const HitTestResult& other) {
   inner_node_ = other.InnerNode();
+  inert_node_ = other.InertNode();
   inner_element_ = other.InnerElement();
   inner_possibly_pseudo_node_ = other.InnerPossiblyPseudoNode();
   point_in_inner_node_frame_ = other.point_in_inner_node_frame_;
@@ -126,6 +128,7 @@
 
 void HitTestResult::Trace(blink::Visitor* visitor) {
   visitor->Trace(inner_node_);
+  visitor->Trace(inert_node_);
   visitor->Trace(inner_element_);
   visitor->Trace(inner_possibly_pseudo_node_);
   visitor->Trace(inner_url_element_);
@@ -201,6 +204,23 @@
     inner_element_ = nullptr;
     return;
   }
+
+  if (RuntimeEnabledFeatures::InertAttributeEnabled()) {
+    if (GetHitTestRequest().RetargetForInert()) {
+      if (n->IsInert()) {
+        if (!inert_node_)
+          inert_node_ = n;
+
+        return;
+      }
+
+      if (inert_node_ && n != inert_node_ &&
+          !n->IsShadowIncludingInclusiveAncestorOf(*inert_node_)) {
+        return;
+      }
+    }
+  }
+
   inner_possibly_pseudo_node_ = n;
   if (auto* pseudo_element = DynamicTo<PseudoElement>(n))
     n = pseudo_element->InnerNodeForHitTesting();
@@ -215,6 +235,14 @@
     inner_element_ = FlatTreeTraversal::ParentElement(*inner_node_);
 }
 
+void HitTestResult::SetInertNode(Node* n) {
+  // Don't overwrite an existing value for inert_node_
+  if (inert_node_)
+    DCHECK(n == inert_node_);
+
+  inert_node_ = n;
+}
+
 void HitTestResult::SetURLElement(Element* n) {
   inner_url_element_ = n;
 }
@@ -393,6 +421,10 @@
     Node* node,
     const HitTestLocation& location,
     const PhysicalRect& rect) {
+  // If we are in the process of retargeting for `inert`, continue.
+  if (GetHitTestRequest().RetargetForInert() && InertNode() && !InnerNode())
+    return kContinueHitTesting;
+
   // If not a list-based test, stop testing because the hit has been found.
   if (!GetHitTestRequest().ListBased())
     return kStopHitTesting;
@@ -413,6 +445,10 @@
     Node* node,
     const HitTestLocation& location,
     const Region& region) {
+  // If we are in the process of retargeting for `inert`, continue.
+  if (GetHitTestRequest().RetargetForInert() && InertNode() && !InnerNode())
+    return kContinueHitTesting;
+
   // If not a list-based test, stop testing because the hit has been found.
   if (!GetHitTestRequest().ListBased())
     return kStopHitTesting;
@@ -447,6 +483,9 @@
     canvas_region_id_ = other.CanvasRegionId();
   }
 
+  if (!inert_node_ && other.InertNode())
+    SetInertNode(other.InertNode());
+
   if (other.list_based_test_result_) {
     NodeSet& set = MutableListBasedTestResult();
     for (NodeSet::const_iterator it = other.list_based_test_result_->begin(),
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.h b/third_party/blink/renderer/core/layout/hit_test_result.h
index 1ad00ef..c6ea07e 100644
--- a/third_party/blink/renderer/core/layout/hit_test_result.h
+++ b/third_party/blink/renderer/core/layout/hit_test_result.h
@@ -82,6 +82,7 @@
   // FIXME: Make these less error-prone for rect-based hit tests (center point
   // or fail).
   Node* InnerNode() const { return inner_node_.Get(); }
+  Node* InertNode() const { return inert_node_.Get(); }
   Node* InnerPossiblyPseudoNode() const {
     return inner_possibly_pseudo_node_.Get();
   }
@@ -125,6 +126,7 @@
   const HitTestRequest& GetHitTestRequest() const { return hit_test_request_; }
 
   void SetInnerNode(Node*);
+  void SetInertNode(Node*);
   HTMLAreaElement* ImageAreaForImage() const;
   void SetURLElement(Element*);
   void SetScrollbar(Scrollbar*);
@@ -188,6 +190,7 @@
   bool cacheable_;
 
   Member<Node> inner_node_;
+  Member<Node> inert_node_;
   // This gets calculated in the first call to InnerElement function.
   Member<Element> inner_element_;
   Member<Node> inner_possibly_pseudo_node_;
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index b2dbdd5..d3e2b755 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -381,9 +381,9 @@
   // text control are known, so they don't get layout until their parent has had
   // layout - this is unique in the layout tree and means when we call
   // isSelfCollapsingBlock on them we find that they still need layout.
-  DCHECK(!NeedsLayout() || (GetNode() && GetNode()->IsElementNode() &&
-                            ToElement(GetNode())->ShadowPseudoId() ==
-                                "-webkit-input-placeholder"));
+  auto* element = DynamicTo<Element>(GetNode());
+  DCHECK(!NeedsLayout() ||
+         (element && element->ShadowPseudoId() == "-webkit-input-placeholder"));
 
   if (LogicalHeight() > LayoutUnit() ||
       StyleRef().LogicalMinHeight().IsPositive() ||
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index bccb36e..04d1a46 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -5362,10 +5362,9 @@
   // their own layoutObject in which to override avoidFloats().
   if (IsAtomicInlineLevel())
     return true;
-  Node* node = GetNode();
-  return node && node->IsElementNode() &&
-         (ToElement(node)->IsFormControlElement() ||
-          IsHTMLImageElement(ToElement(node)));
+  auto* element = DynamicTo<Element>(GetNode());
+  return element &&
+         (element->IsFormControlElement() || IsHTMLImageElement(element));
 }
 
 bool LayoutBox::HasNonCompositedScrollbars() const {
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc b/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc
index 9aa0490b..f9b5261 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc
@@ -1139,7 +1139,7 @@
   // Now make the scroller into an actual scroller. This will reparent the
   // sticky element to be a child of the scroller, and will set its previous
   // overflow layer to nullptr.
-  ToElement(scroller->GetNode())
+  To<Element>(scroller->GetNode())
       ->SetInlineStyleProperty(CSSPropertyID::kOverflow, "scroll");
   UpdateAllLifecyclePhasesForTest();
 
@@ -1149,9 +1149,9 @@
   // Making the scroller have visible overflow but still have a PaintLayer
   // (in this case by making it position: relative) will cause us to need to
   // recompute the sticky element's ancestor overflow layer.
-  ToElement(scroller->GetNode())
+  To<Element>(scroller->GetNode())
       ->SetInlineStyleProperty(CSSPropertyID::kPosition, "relative");
-  ToElement(scroller->GetNode())
+  To<Element>(scroller->GetNode())
       ->SetInlineStyleProperty(CSSPropertyID::kOverflow, "visible");
 
   // Now try to scroll to the sticky element, this used to crash.
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 ada8f7b1..04fc9c3 100644
--- a/third_party/blink/renderer/core/layout/layout_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_test.cc
@@ -1434,7 +1434,7 @@
   auto* div = GetLayoutBoxByElementId("div");
   EXPECT_FALSE(div->HasNonCollapsedBorderDecoration());
 
-  ToElement(div->GetNode())
+  To<Element>(div->GetNode())
       ->setAttribute(html_names::kStyleAttr, "border: 1px solid black");
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
   EXPECT_TRUE(div->HasNonCollapsedBorderDecoration());
diff --git a/third_party/blink/renderer/core/layout/layout_counter.cc b/third_party/blink/renderer/core/layout/layout_counter.cc
index 54e14d1..0d7cb55 100644
--- a/third_party/blink/renderer/core/layout/layout_counter.cc
+++ b/third_party/blink/renderer/core/layout/layout_counter.cc
@@ -122,7 +122,7 @@
 static LayoutObject* NextInPreOrder(const LayoutObject& object,
                                     const Element* stay_within,
                                     bool skip_descendants = false) {
-  Element* self = ToElement(object.GetNode());
+  auto* self = To<Element>(object.GetNode());
   DCHECK(self);
   Element* next =
       skip_descendants
@@ -225,7 +225,7 @@
   // We cannot stop searching for counters with the same identifier before we
   // also check this layout object, because it may affect the positioning in the
   // tree of our counter.
-  Element* counter_owner_element = ToElement(counter_owner.GetNode());
+  auto* counter_owner_element = To<Element>(counter_owner.GetNode());
   Element* search_end_element =
       PreviousSiblingOrParentRespectingContainment(*counter_owner_element);
   Element* current_element =
@@ -364,7 +364,7 @@
 }
 
 static inline Element* ParentElement(LayoutObject& object) {
-  return ToElement(object.GetNode())->parentElement();
+  return To<Element>(object.GetNode())->parentElement();
 }
 
 static CounterNode* MakeCounterNodeIfNeeded(LayoutObject& object,
diff --git a/third_party/blink/renderer/core/layout/layout_details_marker.cc b/third_party/blink/renderer/core/layout/layout_details_marker.cc
index 857827e..190448dd 100644
--- a/third_party/blink/renderer/core/layout/layout_details_marker.cc
+++ b/third_party/blink/renderer/core/layout/layout_details_marker.cc
@@ -64,7 +64,7 @@
     if (!layout_object->GetNode())
       continue;
     if (IsHTMLDetailsElement(*layout_object->GetNode()))
-      return !ToElement(layout_object->GetNode())
+      return !To<Element>(layout_object->GetNode())
                   ->getAttribute(kOpenAttr)
                   .IsNull();
     if (IsHTMLInputElement(*layout_object->GetNode()))
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
index 80016a9d..927ff59 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/paint/embedded_content_painter.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 
 namespace blink {
 
@@ -201,6 +202,7 @@
           result.GetHitTestRequest().GetStopNode());
       HitTestResult child_frame_result(new_hit_test_request,
                                        new_hit_test_location);
+      child_frame_result.SetInertNode(result.InertNode());
 
       // The frame's layout and style must be up to date if we reach here.
       bool is_inside_child_frame = child_layout_view->HitTestNoLifecycleUpdate(
@@ -316,24 +318,31 @@
 }
 
 void LayoutEmbeddedContent::UpdateOnEmbeddedContentViewChange() {
-  EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
-  if (!embedded_content_view)
-    return;
-
   if (!Style())
     return;
 
-  if (!NeedsLayout())
-    UpdateGeometry(*embedded_content_view);
+  if (EmbeddedContentView* embedded_content_view = GetEmbeddedContentView()) {
+    if (!NeedsLayout())
+      UpdateGeometry(*embedded_content_view);
 
-  if (StyleRef().Visibility() != EVisibility::kVisible) {
-    embedded_content_view->Hide();
-  } else {
-    embedded_content_view->Show();
-    // FIXME: Why do we issue a full paint invalidation in this case, but not
-    // the other?
-    SetShouldDoFullPaintInvalidation();
+    if (StyleRef().Visibility() != EVisibility::kVisible)
+      embedded_content_view->Hide();
+    else
+      embedded_content_view->Show();
   }
+
+  // One of the reasons of the following is that the layout tree in the new
+  // embedded content view may have already had some paint property and paint
+  // invalidation flags set, and we need to propagate the flags into the host
+  // view. Adding, changing and removing are also significant changes to the
+  // tree so setting the flags ensures the required updates.
+  SetNeedsPaintPropertyUpdate();
+  SetShouldDoFullPaintInvalidation();
+  // Showing/hiding the embedded content view and changing the view between null
+  // and non-null affect compositing (see: PaintLayerCompositor::CanBeComposited
+  // and RootShouldAlwaysComposite).
+  if (HasLayer())
+    Layer()->SetNeedsCompositingInputsUpdate();
 }
 
 void LayoutEmbeddedContent::UpdateGeometry(
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_object.cc b/third_party/blink/renderer/core/layout/layout_embedded_object.cc
index d44c66f..cc9106b 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_object.cc
@@ -49,7 +49,7 @@
     Node* node,
     LayoutEmbeddedObject::PluginAvailability availability) {
   Locale& locale =
-      node ? ToElement(node)->GetLocale() : Locale::DefaultLocale();
+      node ? To<Element>(node)->GetLocale() : Locale::DefaultLocale();
   switch (availability) {
     case LayoutEmbeddedObject::kPluginAvailable:
       break;
diff --git a/third_party/blink/renderer/core/layout/layout_image_resource.cc b/third_party/blink/renderer/core/layout/layout_image_resource.cc
index dc3b5638..4ab57c5e 100644
--- a/third_party/blink/renderer/core/layout/layout_image_resource.cc
+++ b/third_party/blink/renderer/core/layout/layout_image_resource.cc
@@ -169,10 +169,9 @@
     return image;
 
   KURL url;
-  Node* node = layout_object_->GetNode();
-  if (node && node->IsElementNode()) {
-    const AtomicString& url_string = ToElement(node)->ImageSourceURL();
-    url = node->GetDocument().CompleteURL(url_string);
+  if (auto* element = DynamicTo<Element>(layout_object_->GetNode())) {
+    const AtomicString& url_string = element->ImageSourceURL();
+    url = element->GetDocument().CompleteURL(url_string);
   }
   return SVGImageForContainer::Create(
       ToSVGImage(image), container_size,
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index dbb4096..f46d59c 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -327,7 +327,7 @@
     } else {
       // When position was not static, containingBlockForAbsolutePosition
       // for our children is our existing containingBlock.
-      abs_containing_block = ContainingBlock();
+      abs_containing_block = FindNonAnonymousContainingBlock(this);
     }
     if (abs_containing_block)
       abs_containing_block->RemovePositionedObjects(this, kNewContainingBlock);
diff --git a/third_party/blink/renderer/core/layout/layout_inline.h b/third_party/blink/renderer/core/layout/layout_inline.h
index 565b0f42..de6e98d 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.h
+++ b/third_party/blink/renderer/core/layout/layout_inline.h
@@ -136,7 +136,7 @@
                 LayoutObject* before_child = nullptr) override;
 
   Element* GetNode() const {
-    return ToElement(LayoutBoxModelObject::GetNode());
+    return To<Element>(LayoutBoxModelObject::GetNode());
   }
 
   LayoutUnit MarginLeft() const final;
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index cb1bd70..f2a9123 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -144,29 +144,6 @@
   return nullptr;
 }
 
-LayoutBlock* FindContainingBlock(LayoutObject* container,
-                                 LayoutObject::AncestorSkipInfo* skip_info) {
-  // For inlines, we return the nearest non-anonymous enclosing
-  // block. We don't try to return the inline itself. This allows us to avoid
-  // having a positioned objects list in all LayoutInlines and lets us return a
-  // strongly-typed LayoutBlock* result from this method. The
-  // LayoutObject::Container() method can actually be used to obtain the inline
-  // directly.
-  if (container && container->IsInline() && !container->IsAtomicInlineLevel()) {
-    DCHECK(container->StyleRef().HasInFlowPosition() ||
-           container->StyleRef().HasFilter());
-    container = container->ContainingBlock(skip_info);
-  }
-
-  if (container && !container->IsLayoutBlock())
-    container = container->ContainingBlock(skip_info);
-
-  while (container && container->IsAnonymousBlock())
-    container = container->ContainingBlock(skip_info);
-
-  return DynamicTo<LayoutBlock>(container);
-}
-
 }  // namespace
 
 #if DCHECK_IS_ON()
@@ -1205,16 +1182,38 @@
   });
 }
 
+LayoutBlock* LayoutObject::FindNonAnonymousContainingBlock(
+    LayoutObject* container,
+    LayoutObject::AncestorSkipInfo* skip_info) const {
+  // For inlines, we return the nearest non-anonymous enclosing
+  // block. We don't try to return the inline itself. This allows us to avoid
+  // having a positioned objects list in all LayoutInlines and lets us return a
+  // strongly-typed LayoutBlock* result from this method. The
+  // LayoutObject::Container() method can actually be used to obtain the inline
+  // directly.
+  if (container && container->IsInline() && !container->IsAtomicInlineLevel()) {
+    container = container->ContainingBlock(skip_info);
+  }
+
+  if (container && !container->IsLayoutBlock())
+    container = container->ContainingBlock(skip_info);
+
+  while (container && container->IsAnonymousBlock())
+    container = container->ContainingBlock(skip_info);
+
+  return DynamicTo<LayoutBlock>(container);
+}
+
 LayoutBlock* LayoutObject::ContainingBlockForAbsolutePosition(
     AncestorSkipInfo* skip_info) const {
   auto* container = ContainerForAbsolutePosition(skip_info);
-  return FindContainingBlock(container, skip_info);
+  return FindNonAnonymousContainingBlock(container, skip_info);
 }
 
 LayoutBlock* LayoutObject::ContainingBlockForFixedPosition(
     AncestorSkipInfo* skip_info) const {
   auto* container = ContainerForFixedPosition(skip_info);
-  return FindContainingBlock(container, skip_info);
+  return FindNonAnonymousContainingBlock(container, skip_info);
 }
 
 const LayoutBlock* LayoutObject::InclusiveContainingBlock() const {
@@ -2276,12 +2275,11 @@
 }
 
 void LayoutObject::ClearBaseComputedStyle() {
-  if (!GetNode())
+  auto* element = DynamicTo<Element>(GetNode());
+  if (!element)
     return;
-  if (!GetNode()->IsElementNode())
-    return;
-  if (ElementAnimations* animations =
-          ToElement(GetNode())->GetElementAnimations())
+
+  if (ElementAnimations* animations = element->GetElementAnimations())
     animations->ClearBaseComputedStyle();
 }
 
@@ -3603,7 +3601,7 @@
       break;
   }
 
-  return node && node->IsElementNode() ? ToElement(node) : nullptr;
+  return DynamicTo<Element>(node);
 }
 
 void LayoutObject::NotifyImageFullyRemoved(ImageResourceContent* image) {
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 43e1b619..7a7a448 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -222,6 +222,21 @@
                                  public DisplayItemClient {
   friend class LayoutObjectChildList;
   FRIEND_TEST_ALL_PREFIXES(LayoutObjectTest, MutableForPaintingClearPaintFlags);
+  FRIEND_TEST_ALL_PREFIXES(
+      LayoutObjectTest,
+      ContainingBlockAbsoluteLayoutObjectShouldBeNonStaticallyPositionedBlockAncestor);
+  FRIEND_TEST_ALL_PREFIXES(LayoutObjectTest,
+                           ContainingBlockFixedLayoutObjectInTransformedDiv);
+  FRIEND_TEST_ALL_PREFIXES(LayoutObjectTest,
+                           ContainingBlockFixedLayoutObjectInTransformedDiv);
+  FRIEND_TEST_ALL_PREFIXES(LayoutObjectTest,
+                           ContainingBlockFixedLayoutObjectInBody);
+  FRIEND_TEST_ALL_PREFIXES(LayoutObjectTest,
+                           ContainingBlockAbsoluteLayoutObjectInBody);
+  FRIEND_TEST_ALL_PREFIXES(
+      LayoutObjectTest,
+      ContainingBlockAbsoluteLayoutObjectShouldNotBeNonStaticallyPositionedInlineAncestor);
+
   friend class VisualRectMappingTest;
 
  public:
@@ -1254,15 +1269,6 @@
   // Finds the container as if this object is absolute-position.
   LayoutObject* ContainerForFixedPosition(AncestorSkipInfo* = nullptr) const;
 
-  // Returns ContainerForAbsolutePosition() if it's a LayoutBlock, or the
-  // containing LayoutBlock of it.
-  LayoutBlock* ContainingBlockForAbsolutePosition(
-      AncestorSkipInfo* = nullptr) const;
-  // Returns ContainerForFixedPosition() if it's a LayoutBlock, or the
-  // containing LayoutBlock of it.
-  LayoutBlock* ContainingBlockForFixedPosition(
-      AncestorSkipInfo* = nullptr) const;
-
   bool CanContainOutOfFlowPositionedElement(EPosition position) const {
     DCHECK(position == EPosition::kAbsolute || position == EPosition::kFixed);
     return (position == EPosition::kAbsolute &&
@@ -1941,14 +1947,10 @@
   bool VisibleToHitTestRequest(const HitTestRequest& request) const {
     return StyleRef().Visibility() == EVisibility::kVisible &&
            (request.IgnorePointerEventsNone() ||
-            StyleRef().PointerEvents() != EPointerEvents::kNone) &&
-           !IsInert();
+            StyleRef().PointerEvents() != EPointerEvents::kNone);
   }
 
-  // Warning: inertness can change without causing relayout.
-  bool VisibleToHitTesting() const {
-    return StyleRef().VisibleToHitTesting() && !IsInert();
-  }
+  bool VisibleToHitTesting() const { return StyleRef().VisibleToHitTesting(); }
 
   // Map points and quads through elements, potentially via 3d transforms. You
   // should never need to call these directly; use localToAbsolute/
@@ -2434,9 +2436,10 @@
   DisplayLockContext* GetDisplayLockContext() const {
     if (!RuntimeEnabledFeatures::DisplayLockingEnabled())
       return nullptr;
-    if (!GetNode() || !GetNode()->IsElementNode())
+    auto* element = DynamicTo<Element>(GetNode());
+    if (!element)
       return nullptr;
-    return ToElement(GetNode())->GetDisplayLockContext();
+    return element->GetDisplayLockContext();
   }
 
  protected:
@@ -2621,6 +2624,19 @@
     bitfields_.SetBackgroundIsKnownToBeObscured(b);
   }
 
+  // Returns |container|'s containing block.
+  LayoutBlock* FindNonAnonymousContainingBlock(
+      LayoutObject* container,
+      LayoutObject::AncestorSkipInfo* = nullptr) const;
+  // Returns ContainerForAbsolutePosition() if it's a LayoutBlock, or the
+  // containing LayoutBlock of it.
+  LayoutBlock* ContainingBlockForAbsolutePosition(
+      AncestorSkipInfo* = nullptr) const;
+  // Returns ContainerForFixedPosition() if it's a LayoutBlock, or the
+  // containing LayoutBlock of it.
+  LayoutBlock* ContainingBlockForFixedPosition(
+      AncestorSkipInfo* = nullptr) const;
+
  private:
   // Used only by applyFirstLineChanges to get a first line style based off of a
   // given new style, without accessing the cache.
diff --git a/third_party/blink/renderer/core/layout/layout_object_factory.cc b/third_party/blink/renderer/core/layout/layout_object_factory.cc
index 22b83178e..9b6024f 100644
--- a/third_party/blink/renderer/core/layout/layout_object_factory.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_factory.cc
@@ -30,8 +30,8 @@
 namespace {
 
 inline Element* GetElementForLayoutObject(Node& node) {
-  if (node.IsElementNode())
-    return &ToElement(node);
+  if (auto* element = DynamicTo<Element>(node))
+    return element;
   // If |node| is a Document, the layout object is going to be anonymous.
   DCHECK(node.IsDocumentNode());
   return nullptr;
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
index 7eb9a9b..f4a595efb 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
@@ -168,6 +168,11 @@
   GeometryMapper::SourceToDestinationRect(property_tree_state.Transform(),
                                           root_state.Transform(), new_rect);
 
+  if (EqualWithinMovementThreshold(old_rect.Location(), new_rect.Location(),
+                                   source)) {
+    return;
+  }
+
   FloatRect clipped_old_rect(old_rect), clipped_new_rect(new_rect);
   if (!clip_rect.IsInfinite()) {
     clipped_old_rect.Intersect(clip_rect.Rect());
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
index 7d2f781..4e42d5c 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
@@ -394,6 +394,27 @@
   EXPECT_FLOAT_EQ(0.0, GetLayoutShiftTracker().Score());
 }
 
+TEST_F(LayoutShiftTrackerTest, LocalShiftWithoutViewportShift) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #c { position: relative; width: 300px; height: 100px; transform: scale(0.1); }
+      #j { position: relative; width: 100px; height: 10px; }
+    </style>
+    <div id='c'>
+      <div id='j'></div>
+    </div>
+    </div>
+  )HTML");
+
+  GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
+                                                  AtomicString("top: 4px"));
+
+  UpdateAllLifecyclePhases();
+  // Make sure no shift score is reported, since the element didn't move in the
+  // viewport.
+  EXPECT_FLOAT_EQ(0.0, GetLayoutShiftTracker().Score());
+}
+
 class LayoutShiftTrackerSimTest : public SimTest {};
 
 TEST_F(LayoutShiftTrackerSimTest, SubframeWeighting) {
diff --git a/third_party/blink/renderer/core/layout/layout_slider.cc b/third_party/blink/renderer/core/layout/layout_slider.cc
index db984bd95..f41bbd8 100644
--- a/third_party/blink/renderer/core/layout/layout_slider.cc
+++ b/third_party/blink/renderer/core/layout/layout_slider.cc
@@ -59,7 +59,7 @@
 
 inline SliderThumbElement* LayoutSlider::GetSliderThumbElement() const {
   return To<SliderThumbElement>(
-      ToElement(GetNode())->UserAgentShadowRoot()->getElementById(
+      To<Element>(GetNode())->UserAgentShadowRoot()->getElementById(
           shadow_element_names::SliderThumb()));
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_table_cell.cc b/third_party/blink/renderer/core/layout/layout_table_cell.cc
index e18a62e..7b44e7aa 100644
--- a/third_party/blink/renderer/core/layout/layout_table_cell.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_cell.cc
@@ -202,7 +202,7 @@
     // See if nowrap was set.
     Length w = StyleOrColLogicalWidth();
     const AtomicString& nowrap =
-        ToElement(GetNode())->getAttribute(kNowrapAttr);
+        To<Element>(GetNode())->getAttribute(kNowrapAttr);
     if (!nowrap.IsNull() && w.IsFixed()) {
       // Nowrap is set, but we didn't actually use it because of the fixed width
       // set on the cell. Even so, it is a WinIE/Moz trait to make the minwidth
diff --git a/third_party/blink/renderer/core/layout/layout_table_cell_test.cc b/third_party/blink/renderer/core/layout/layout_table_cell_test.cc
index 263d0f7..d073f4f 100644
--- a/third_party/blink/renderer/core/layout/layout_table_cell_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_cell_test.cc
@@ -291,7 +291,7 @@
   EXPECT_EQ(1u, cell2->CollapsedOuterBorderBefore());
   EXPECT_EQ(2u, cell2->CollapsedOuterBorderAfter());
 
-  ToElement(cell1->Table()->GetNode())
+  To<Element>(cell1->Table()->GetNode())
       ->setAttribute(html_names::kStyleAttr,
                      "writing-mode: vertical-rl; direction: rtl");
   UpdateAllLifecyclePhasesForTest();
@@ -333,22 +333,23 @@
   auto* cell = GetCellByElementId("cell");
   EXPECT_FALSE(cell->HasNonCollapsedBorderDecoration());
 
-  ToElement(cell->GetNode())
+  To<Element>(cell->GetNode())
       ->setAttribute(html_names::kStyleAttr, "border: 1px solid black");
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
   EXPECT_TRUE(cell->HasNonCollapsedBorderDecoration());
 
-  ToElement(cell->Table()->GetNode())
+  To<Element>(cell->Table()->GetNode())
       ->setAttribute(html_names::kStyleAttr, "border-collapse: collapse");
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
   EXPECT_FALSE(cell->HasNonCollapsedBorderDecoration());
 
-  ToElement(cell->GetNode())
+  To<Element>(cell->GetNode())
       ->setAttribute(html_names::kStyleAttr, "border: 2px solid black");
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
   EXPECT_FALSE(cell->HasNonCollapsedBorderDecoration());
 
-  ToElement(cell->Table()->GetNode())->setAttribute(html_names::kStyleAttr, "");
+  To<Element>(cell->Table()->GetNode())
+      ->setAttribute(html_names::kStyleAttr, "");
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
   EXPECT_TRUE(cell->HasNonCollapsedBorderDecoration());
 }
diff --git a/third_party/blink/renderer/core/layout/layout_table_section_test.cc b/third_party/blink/renderer/core/layout/layout_table_section_test.cc
index 40a88039..80963e97 100644
--- a/third_party/blink/renderer/core/layout/layout_table_section_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_section_test.cc
@@ -314,7 +314,7 @@
 
 static void SetCellsOverflowInRow(LayoutTableRow* row) {
   for (auto* cell = row->FirstCell(); cell; cell = cell->NextCell()) {
-    ToElement(cell->GetNode())
+    To<Element>(cell->GetNode())
         ->setAttribute(html_names::kClassAttr, "overflow");
   }
 }
diff --git a/third_party/blink/renderer/core/layout/layout_table_test.cc b/third_party/blink/renderer/core/layout/layout_table_test.cc
index edb14ad..bbe66c63 100644
--- a/third_party/blink/renderer/core/layout/layout_table_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_test.cc
@@ -29,11 +29,11 @@
   )HTML");
   auto* target = GetTableByElementId("target");
   EXPECT_EQ(LayoutRect(0, 0, 100, 200), target->SelfVisualOverflowRect());
-  ToElement(target->GetNode())
+  To<Element>(target->GetNode())
       ->setAttribute(html_names::kStyleAttr, "outline: 2px solid black");
 
   auto* child = GetTableByElementId("child");
-  ToElement(child->GetNode())
+  To<Element>(child->GetNode())
       ->setAttribute(html_names::kStyleAttr, "outline: 2px solid black");
 
   target->GetFrameView()->UpdateAllLifecyclePhases(
@@ -331,7 +331,7 @@
   )HTML");
   auto* table = GetTableByElementId("table");
   EXPECT_EQ(LayoutRect(-3, -3, 66, 66), table->SelfVisualOverflowRect());
-  ToElement(table->GetNode())
+  To<Element>(table->GetNode())
       ->setAttribute(html_names::kStyleAttr, "box-shadow: initial");
   GetDocument().View()->UpdateAllLifecyclePhases(
       DocumentLifecycle::LifecycleUpdateReason::kTest);
@@ -343,12 +343,12 @@
   auto* table = GetTableByElementId("table");
   EXPECT_FALSE(table->HasNonCollapsedBorderDecoration());
 
-  ToElement(table->GetNode())
+  To<Element>(table->GetNode())
       ->setAttribute(html_names::kStyleAttr, "border: 1px solid black");
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
   EXPECT_TRUE(table->HasNonCollapsedBorderDecoration());
 
-  ToElement(table->GetNode())
+  To<Element>(table->GetNode())
       ->setAttribute(html_names::kStyleAttr,
                      "border: 1px solid black; border-collapse: collapse");
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_single_line_test.cc b/third_party/blink/renderer/core/layout/layout_text_control_single_line_test.cc
index 5624741..a0533fd75 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control_single_line_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control_single_line_test.cc
@@ -29,7 +29,7 @@
 #else
   EXPECT_EQ(LayoutRect(-3, -3, 70, 72), input->SelfVisualOverflowRect());
 #endif
-  ToElement(input->GetNode())
+  To<Element>(input->GetNode())
       ->setAttribute(html_names::kStyleAttr, "box-shadow: initial");
   GetDocument().View()->UpdateAllLifecyclePhases(
       DocumentLifecycle::LifecycleUpdateReason::kTest);
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc
index f217d34..f6ecefd 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -351,9 +351,10 @@
 }
 
 bool LayoutTheme::IsEnabled(const Node* node) {
-  if (!node || !node->IsElementNode())
+  auto* element = DynamicTo<Element>(node);
+  if (!element)
     return true;
-  return !ToElement(node)->IsDisabledFormControl();
+  return !element->IsDisabledFormControl();
 }
 
 bool LayoutTheme::IsFocused(const Node* node) {
@@ -375,16 +376,15 @@
 }
 
 bool LayoutTheme::IsSpinUpButtonPartPressed(const Node* node) {
-  if (!node || !node->IsActive() || !node->IsElementNode() ||
-      !ToElement(node)->IsSpinButtonElement())
+  const auto* element = DynamicTo<SpinButtonElement>(node);
+  if (!element || !element->IsActive())
     return false;
-  const auto* element = To<SpinButtonElement>(node);
   return element->GetUpDownState() == SpinButtonElement::kUp;
 }
 
 bool LayoutTheme::IsReadOnlyControl(const Node* node) {
-  if (!node || !node->IsElementNode() ||
-      !ToElement(node)->IsFormControlElement())
+  auto* toElement = DynamicTo<Element>(node);
+  if (!toElement || !toElement->IsFormControlElement())
     return false;
   const HTMLFormControlElement* element = ToHTMLFormControlElement(node);
   return element->IsReadOnly();
@@ -393,18 +393,17 @@
 bool LayoutTheme::IsHovered(const Node* node) {
   if (!node)
     return false;
-  if (!node->IsElementNode() || !ToElement(node)->IsSpinButtonElement())
+  const auto* element = DynamicTo<SpinButtonElement>(node);
+  if (!element)
     return node->IsHovered();
-  const auto* element = To<SpinButtonElement>(node);
   return element->IsHovered() &&
          element->GetUpDownState() != SpinButtonElement::kIndeterminate;
 }
 
 bool LayoutTheme::IsSpinUpButtonPartHovered(const Node* node) {
-  if (!node || !node->IsElementNode() ||
-      !ToElement(node)->IsSpinButtonElement())
+  const auto* element = DynamicTo<SpinButtonElement>(node);
+  if (!element)
     return false;
-  const auto* element = To<SpinButtonElement>(node);
   return element->GetUpDownState() == SpinButtonElement::kUp;
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc b/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc
index b90c8e3..1c64cea5 100644
--- a/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc
@@ -6,66 +6,41 @@
 
 namespace blink {
 
-NGStaticPosition NGStaticPosition::Create(WritingMode writing_mode,
-                                          TextDirection direction,
-                                          PhysicalOffset offset) {
-  NGStaticPosition position;
-  position.offset = offset;
-  switch (writing_mode) {
-    case WritingMode::kHorizontalTb:
-      position.type = (direction == TextDirection::kLtr) ? kTopLeft : kTopRight;
-      break;
-    case WritingMode::kVerticalRl:
-    case WritingMode::kSidewaysRl:
-      position.type =
-          (direction == TextDirection::kLtr) ? kTopRight : kBottomRight;
-      break;
-    case WritingMode::kVerticalLr:
-      position.type =
-          (direction == TextDirection::kLtr) ? kTopLeft : kBottomLeft;
-      break;
-    case WritingMode::kSidewaysLr:
-      position.type =
-          (direction == TextDirection::kLtr) ? kBottomLeft : kTopLeft;
-      break;
-  }
-  return position;
-}
-
-LayoutUnit NGStaticPosition::LeftInset(LayoutUnit container_size,
-                                       LayoutUnit width,
-                                       LayoutUnit margin_left,
-                                       LayoutUnit margin_right) const {
+LayoutUnit NGPhysicalStaticPosition::LeftInset(LayoutUnit container_size,
+                                               LayoutUnit width,
+                                               LayoutUnit margin_left,
+                                               LayoutUnit margin_right) const {
   if (HasLeft())
     return offset.left;
   else
     return offset.left - width - margin_left - margin_right;
 }
 
-LayoutUnit NGStaticPosition::RightInset(LayoutUnit container_size,
-                                        LayoutUnit width,
-                                        LayoutUnit margin_left,
-                                        LayoutUnit margin_right) const {
+LayoutUnit NGPhysicalStaticPosition::RightInset(LayoutUnit container_size,
+                                                LayoutUnit width,
+                                                LayoutUnit margin_left,
+                                                LayoutUnit margin_right) const {
   if (HasLeft())
     return container_size - offset.left - width - margin_left - margin_right;
   else
     return container_size - offset.left;
 }
 
-LayoutUnit NGStaticPosition::TopInset(LayoutUnit container_size,
-                                      LayoutUnit height,
-                                      LayoutUnit margin_top,
-                                      LayoutUnit margin_bottom) const {
+LayoutUnit NGPhysicalStaticPosition::TopInset(LayoutUnit container_size,
+                                              LayoutUnit height,
+                                              LayoutUnit margin_top,
+                                              LayoutUnit margin_bottom) const {
   if (HasTop())
     return offset.top;
   else
     return offset.top - height - margin_bottom - margin_top;
 }
 
-LayoutUnit NGStaticPosition::BottomInset(LayoutUnit container_size,
-                                         LayoutUnit height,
-                                         LayoutUnit margin_top,
-                                         LayoutUnit margin_bottom) const {
+LayoutUnit NGPhysicalStaticPosition::BottomInset(
+    LayoutUnit container_size,
+    LayoutUnit height,
+    LayoutUnit margin_top,
+    LayoutUnit margin_bottom) const {
   if (HasTop())
     return container_size - offset.top - height - margin_top - margin_bottom;
   else
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h b/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h
index 68f72b4c..012af871 100644
--- a/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h
@@ -6,23 +6,41 @@
 #define NGStaticPosition_h
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_offset.h"
 #include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/text/text_direction.h"
 #include "third_party/blink/renderer/platform/text/writing_mode.h"
 
 namespace blink {
 
-// Represents static position of an out of flow descendant.
-struct CORE_EXPORT NGStaticPosition {
-  enum Type { kTopLeft, kTopRight, kBottomLeft, kBottomRight };
+struct NGPhysicalStaticPosition;
 
-  Type type;  // Logical corner that corresponds to physical top left.
+// Represents the static-position of an OOF-positioned descendant, in the
+// logical coordinate space.
+//
+// |offset| is the position of the descandant's |inline_edge|, and |block_edge|.
+struct CORE_EXPORT NGLogicalStaticPosition {
+  enum InlineEdge { kInlineStart, kInlineEnd };
+  enum BlockEdge { kBlockStart, kBlockEnd };
+
+  inline NGPhysicalStaticPosition
+  ConvertToPhysical(WritingMode, TextDirection, const PhysicalSize& size) const;
+
+  LogicalOffset offset;
+  InlineEdge inline_edge;
+  BlockEdge block_edge;
+};
+
+// Similar to |NGLogicalStaticPosition| but in the physical coordinate space.
+struct CORE_EXPORT NGPhysicalStaticPosition {
+  enum HorizontalEdge { kLeft, kRight };
+  enum VerticalEdge { kTop, kBottom };
+
   PhysicalOffset offset;
-
-  // Creates a position with proper type wrt writing mode and direction.
-  // It expects physical offset of inline_start/block_start vertex.
-  static NGStaticPosition Create(WritingMode, TextDirection, PhysicalOffset);
+  HorizontalEdge horizontal_edge;
+  VerticalEdge vertical_edge;
 
   // Left/Right/TopPosition functions map static position to inset of
   // left/right/top edge wrt container space.
@@ -65,10 +83,109 @@
     return offset.top;
   }
 
-  bool HasTop() const { return type == kTopLeft || type == kTopRight; }
-  bool HasLeft() const { return type == kTopLeft || type == kBottomLeft; }
+  bool HasLeft() const { return horizontal_edge == kLeft; }
+  bool HasTop() const { return vertical_edge == kTop; }
+
+  NGLogicalStaticPosition ConvertToLogical(WritingMode writing_mode,
+                                           TextDirection direction,
+                                           const PhysicalSize& size) const {
+    LogicalOffset logical_offset =
+        offset.ConvertToLogical(writing_mode, direction, /* outer_size */ size,
+                                /* inner_size */ PhysicalSize());
+
+    NGLogicalStaticPosition::InlineEdge inline_edge;
+    NGLogicalStaticPosition::BlockEdge block_edge;
+
+    switch (writing_mode) {
+      case WritingMode::kHorizontalTb:
+        inline_edge = ((horizontal_edge == kLeft) == IsLtr(direction))
+                          ? NGLogicalStaticPosition::InlineEdge::kInlineStart
+                          : NGLogicalStaticPosition::InlineEdge::kInlineEnd;
+        block_edge = (vertical_edge == kTop)
+                         ? NGLogicalStaticPosition::BlockEdge::kBlockStart
+                         : NGLogicalStaticPosition::BlockEdge::kBlockEnd;
+        break;
+      case WritingMode::kVerticalRl:
+      case WritingMode::kSidewaysRl:
+        inline_edge = ((vertical_edge == kTop) == IsLtr(direction))
+                          ? NGLogicalStaticPosition::InlineEdge::kInlineStart
+                          : NGLogicalStaticPosition::InlineEdge::kInlineEnd;
+        block_edge = (horizontal_edge == kRight)
+                         ? NGLogicalStaticPosition::BlockEdge::kBlockStart
+                         : NGLogicalStaticPosition::BlockEdge::kBlockEnd;
+        break;
+      case WritingMode::kVerticalLr:
+        inline_edge = ((vertical_edge == kTop) == IsLtr(direction))
+                          ? NGLogicalStaticPosition::InlineEdge::kInlineStart
+                          : NGLogicalStaticPosition::InlineEdge::kInlineEnd;
+        block_edge = (horizontal_edge == kLeft)
+                         ? NGLogicalStaticPosition::BlockEdge::kBlockStart
+                         : NGLogicalStaticPosition::BlockEdge::kBlockEnd;
+        break;
+      case WritingMode::kSidewaysLr:
+        inline_edge = ((vertical_edge == kBottom) == IsLtr(direction))
+                          ? NGLogicalStaticPosition::InlineEdge::kInlineStart
+                          : NGLogicalStaticPosition::InlineEdge::kInlineEnd;
+        block_edge = (horizontal_edge == kLeft)
+                         ? NGLogicalStaticPosition::BlockEdge::kBlockStart
+                         : NGLogicalStaticPosition::BlockEdge::kBlockEnd;
+        break;
+    }
+
+    return {logical_offset, inline_edge, block_edge};
+  }
 };
 
+inline NGPhysicalStaticPosition NGLogicalStaticPosition::ConvertToPhysical(
+    WritingMode writing_mode,
+    TextDirection direction,
+    const PhysicalSize& size) const {
+  PhysicalOffset physical_offset =
+      offset.ConvertToPhysical(writing_mode, direction, /* outer_size */ size,
+                               /* inner_size */ PhysicalSize());
+
+  NGPhysicalStaticPosition::HorizontalEdge horizontal_edge;
+  NGPhysicalStaticPosition::VerticalEdge vertical_edge;
+
+  switch (writing_mode) {
+    case WritingMode::kHorizontalTb:
+      horizontal_edge = ((inline_edge == kInlineStart) == IsLtr(direction))
+                            ? NGPhysicalStaticPosition::HorizontalEdge::kLeft
+                            : NGPhysicalStaticPosition::HorizontalEdge::kRight;
+      vertical_edge = (block_edge == kBlockStart)
+                          ? NGPhysicalStaticPosition::VerticalEdge::kTop
+                          : NGPhysicalStaticPosition::VerticalEdge::kBottom;
+      break;
+    case WritingMode::kVerticalRl:
+    case WritingMode::kSidewaysRl:
+      horizontal_edge = (block_edge == kBlockEnd)
+                            ? NGPhysicalStaticPosition::HorizontalEdge::kLeft
+                            : NGPhysicalStaticPosition::HorizontalEdge::kRight;
+      vertical_edge = ((inline_edge == kInlineStart) == IsLtr(direction))
+                          ? NGPhysicalStaticPosition::VerticalEdge::kTop
+                          : NGPhysicalStaticPosition::VerticalEdge::kBottom;
+      break;
+    case WritingMode::kVerticalLr:
+      horizontal_edge = (block_edge == kBlockStart)
+                            ? NGPhysicalStaticPosition::HorizontalEdge::kLeft
+                            : NGPhysicalStaticPosition::HorizontalEdge::kRight;
+      vertical_edge = ((inline_edge == kInlineStart) == IsLtr(direction))
+                          ? NGPhysicalStaticPosition::VerticalEdge::kTop
+                          : NGPhysicalStaticPosition::VerticalEdge::kBottom;
+      break;
+    case WritingMode::kSidewaysLr:
+      horizontal_edge = (block_edge == kBlockStart)
+                            ? NGPhysicalStaticPosition::HorizontalEdge::kLeft
+                            : NGPhysicalStaticPosition::HorizontalEdge::kRight;
+      vertical_edge = ((inline_edge == kInlineEnd) == IsLtr(direction))
+                          ? NGPhysicalStaticPosition::VerticalEdge::kTop
+                          : NGPhysicalStaticPosition::VerticalEdge::kBottom;
+      break;
+  }
+
+  return {physical_offset, horizontal_edge, vertical_edge};
+}
+
 }  // namespace blink
 
 #endif  // NGStaticPosition_h
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position_test.cc b/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position_test.cc
new file mode 100644
index 0000000..09b282a0
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position_test.cc
@@ -0,0 +1,194 @@
+// Copyright 2019 The Chromium 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/layout/ng/geometry/ng_static_position.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+
+namespace blink {
+namespace {
+
+using InlineEdge = NGLogicalStaticPosition::InlineEdge;
+using BlockEdge = NGLogicalStaticPosition::BlockEdge;
+using HorizontalEdge = NGPhysicalStaticPosition::HorizontalEdge;
+using VerticalEdge = NGPhysicalStaticPosition::VerticalEdge;
+
+struct NGStaticPositionTestData {
+  NGLogicalStaticPosition logical;
+  NGPhysicalStaticPosition physical;
+  WritingMode writing_mode;
+  TextDirection direction;
+
+} ng_static_position_test_data[] = {
+    // |WritingMode::kHorizontalTb|, |TextDirection::kLtr|
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockStart},
+     {PhysicalOffset(20, 30), HorizontalEdge::kLeft, VerticalEdge::kTop},
+     WritingMode::kHorizontalTb,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockStart},
+     {PhysicalOffset(20, 30), HorizontalEdge::kRight, VerticalEdge::kTop},
+     WritingMode::kHorizontalTb,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockEnd},
+     {PhysicalOffset(20, 30), HorizontalEdge::kLeft, VerticalEdge::kBottom},
+     WritingMode::kHorizontalTb,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockEnd},
+     {PhysicalOffset(20, 30), HorizontalEdge::kRight, VerticalEdge::kBottom},
+     WritingMode::kHorizontalTb,
+     TextDirection::kLtr},
+    // |WritingMode::kHorizontalTb|, |TextDirection::kRtl|
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockStart},
+     {PhysicalOffset(80, 30), HorizontalEdge::kRight, VerticalEdge::kTop},
+     WritingMode::kHorizontalTb,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockStart},
+     {PhysicalOffset(80, 30), HorizontalEdge::kLeft, VerticalEdge::kTop},
+     WritingMode::kHorizontalTb,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockEnd},
+     {PhysicalOffset(80, 30), HorizontalEdge::kRight, VerticalEdge::kBottom},
+     WritingMode::kHorizontalTb,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockEnd},
+     {PhysicalOffset(80, 30), HorizontalEdge::kLeft, VerticalEdge::kBottom},
+     WritingMode::kHorizontalTb,
+     TextDirection::kRtl},
+    // |WritingMode::kVerticalRl|, |TextDirection::kLtr|
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockStart},
+     {PhysicalOffset(70, 20), HorizontalEdge::kRight, VerticalEdge::kTop},
+     WritingMode::kVerticalRl,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockStart},
+     {PhysicalOffset(70, 20), HorizontalEdge::kRight, VerticalEdge::kBottom},
+     WritingMode::kVerticalRl,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockEnd},
+     {PhysicalOffset(70, 20), HorizontalEdge::kLeft, VerticalEdge::kTop},
+     WritingMode::kVerticalRl,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockEnd},
+     {PhysicalOffset(70, 20), HorizontalEdge::kLeft, VerticalEdge::kBottom},
+     WritingMode::kVerticalRl,
+     TextDirection::kLtr},
+    // |WritingMode::kVerticalRl|, |TextDirection::kRtl|
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockStart},
+     {PhysicalOffset(70, 80), HorizontalEdge::kRight, VerticalEdge::kBottom},
+     WritingMode::kVerticalRl,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockStart},
+     {PhysicalOffset(70, 80), HorizontalEdge::kRight, VerticalEdge::kTop},
+     WritingMode::kVerticalRl,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockEnd},
+     {PhysicalOffset(70, 80), HorizontalEdge::kLeft, VerticalEdge::kBottom},
+     WritingMode::kVerticalRl,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockEnd},
+     {PhysicalOffset(70, 80), HorizontalEdge::kLeft, VerticalEdge::kTop},
+     WritingMode::kVerticalRl,
+     TextDirection::kRtl},
+    // |WritingMode::kVerticalLr|, |TextDirection::kLtr|
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockStart},
+     {PhysicalOffset(30, 20), HorizontalEdge::kLeft, VerticalEdge::kTop},
+     WritingMode::kVerticalLr,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockStart},
+     {PhysicalOffset(30, 20), HorizontalEdge::kLeft, VerticalEdge::kBottom},
+     WritingMode::kVerticalLr,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockEnd},
+     {PhysicalOffset(30, 20), HorizontalEdge::kRight, VerticalEdge::kTop},
+     WritingMode::kVerticalLr,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockEnd},
+     {PhysicalOffset(30, 20), HorizontalEdge::kRight, VerticalEdge::kBottom},
+     WritingMode::kVerticalLr,
+     TextDirection::kLtr},
+    // |WritingMode::kVerticalLr|, |TextDirection::kRtl|
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockStart},
+     {PhysicalOffset(30, 80), HorizontalEdge::kLeft, VerticalEdge::kBottom},
+     WritingMode::kVerticalLr,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockStart},
+     {PhysicalOffset(30, 80), HorizontalEdge::kLeft, VerticalEdge::kTop},
+     WritingMode::kVerticalLr,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockEnd},
+     {PhysicalOffset(30, 80), HorizontalEdge::kRight, VerticalEdge::kBottom},
+     WritingMode::kVerticalLr,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockEnd},
+     {PhysicalOffset(30, 80), HorizontalEdge::kRight, VerticalEdge::kTop},
+     WritingMode::kVerticalLr,
+     TextDirection::kRtl},
+    // |WritingMode::kSidewaysLr|, |TextDirection::kLtr|
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockStart},
+     {PhysicalOffset(30, 80), HorizontalEdge::kLeft, VerticalEdge::kBottom},
+     WritingMode::kSidewaysLr,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockStart},
+     {PhysicalOffset(30, 80), HorizontalEdge::kLeft, VerticalEdge::kTop},
+     WritingMode::kSidewaysLr,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockEnd},
+     {PhysicalOffset(30, 80), HorizontalEdge::kRight, VerticalEdge::kBottom},
+     WritingMode::kSidewaysLr,
+     TextDirection::kLtr},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockEnd},
+     {PhysicalOffset(30, 80), HorizontalEdge::kRight, VerticalEdge::kTop},
+     WritingMode::kSidewaysLr,
+     TextDirection::kLtr},
+    // |WritingMode::kSidewaysLr|, |TextDirection::kRtl|
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockStart},
+     {PhysicalOffset(30, 20), HorizontalEdge::kLeft, VerticalEdge::kTop},
+     WritingMode::kSidewaysLr,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockStart},
+     {PhysicalOffset(30, 20), HorizontalEdge::kLeft, VerticalEdge::kBottom},
+     WritingMode::kSidewaysLr,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineStart, BlockEdge::kBlockEnd},
+     {PhysicalOffset(30, 20), HorizontalEdge::kRight, VerticalEdge::kTop},
+     WritingMode::kSidewaysLr,
+     TextDirection::kRtl},
+    {{LogicalOffset(20, 30), InlineEdge::kInlineEnd, BlockEdge::kBlockEnd},
+     {PhysicalOffset(30, 20), HorizontalEdge::kRight, VerticalEdge::kBottom},
+     WritingMode::kSidewaysLr,
+     TextDirection::kRtl},
+};
+
+class NGStaticPositionTest
+    : public testing::Test,
+      public testing::WithParamInterface<NGStaticPositionTestData> {};
+
+TEST_P(NGStaticPositionTest, Convert) {
+  const auto& data = GetParam();
+
+  // These tests take the logical static-position, and convert it to a physical
+  // static-position with a 100x100 rect.
+  //
+  // It asserts that it is the same as the expected physical static-position,
+  // then performs the same operation in reverse.
+
+  NGPhysicalStaticPosition physical_result = data.logical.ConvertToPhysical(
+      data.writing_mode, data.direction, PhysicalSize(100, 100));
+  EXPECT_EQ(physical_result.offset, data.physical.offset);
+  EXPECT_EQ(physical_result.horizontal_edge, data.physical.horizontal_edge);
+  EXPECT_EQ(physical_result.vertical_edge, data.physical.vertical_edge);
+
+  NGLogicalStaticPosition logical_result = data.physical.ConvertToLogical(
+      data.writing_mode, data.direction, PhysicalSize(100, 100));
+  EXPECT_EQ(logical_result.offset, data.logical.offset);
+  EXPECT_EQ(logical_result.inline_edge, data.logical.inline_edge);
+  EXPECT_EQ(logical_result.block_edge, data.logical.block_edge);
+}
+
+INSTANTIATE_TEST_SUITE_P(NGStaticPositionTest,
+                         NGStaticPositionTest,
+                         testing::ValuesIn(ng_static_position_test_data));
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/empty_offset_mapping_builder.h b/third_party/blink/renderer/core/layout/ng/inline/empty_offset_mapping_builder.h
index 89f01c7..0de484a0 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/empty_offset_mapping_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/empty_offset_mapping_builder.h
@@ -9,6 +9,8 @@
 
 namespace blink {
 
+class LayoutText;
+
 // A mock class providing all APIs of an offset mapping builder, but not doing
 // anything. For templates functions/classes that can optionally create an
 // offset mapping, this mock class is passed to create an instantiation that
@@ -29,6 +31,7 @@
   void CollapseTrailingSpace(unsigned) {}
   void Composite(const EmptyOffsetMappingBuilder&) {}
   void Concatenate(const EmptyOffsetMappingBuilder&) {}
+  void RestoreTrailingCollapsibleSpace(const LayoutText&, unsigned) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(EmptyOffsetMappingBuilder);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
index b735a108..78f3c1f7 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
@@ -975,6 +975,9 @@
   DCHECK(item);
   DCHECK(item->EndCollapseType() == NGInlineItem::kCollapsed);
 
+  mapping_builder_.RestoreTrailingCollapsibleSpace(
+      ToLayoutText(*item->GetLayoutObject()), item->EndOffset());
+
   // TODO(kojii): Implement StringBuilder::insert().
   if (text_.length() == item->EndOffset()) {
     text_.Append(' ');
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 a7a5e66a..2e844d2 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
@@ -872,7 +872,7 @@
   EXPECT_FALSE(layout_block_flow_->NeedsCollectInlines());
   unsigned item_count_before = Items().size();
 
-  Element* parent = ToElement(layout_block_flow_->GetNode());
+  auto* parent = To<Element>(layout_block_flow_->GetNode());
   Element* span = GetDocument().CreateRawElement(html_names::kSpanTag);
   parent->appendChild(span);
 
@@ -943,7 +943,7 @@
   EXPECT_FALSE(layout_block_flow_->NeedsCollectInlines());
   unsigned item_count_before = Items().size();
 
-  Element* parent = ToElement(layout_block_flow_->GetNode());
+  auto* parent = To<Element>(layout_block_flow_->GetNode());
   Element* span = GetDocument().CreateRawElement(html_names::kSpanTag);
   parent->appendChild(span);
 
@@ -1008,7 +1008,7 @@
   EXPECT_FALSE(layout_block_flow_->NeedsCollectInlines());
   unsigned item_count_before = Items().size();
 
-  Element* parent = ToElement(layout_block_flow_->GetNode());
+  auto* parent = To<Element>(layout_block_flow_->GetNode());
   Element* span = GetDocument().CreateRawElement(html_names::kSpanTag);
   parent->appendChild(span);
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
index 9524bfb..aa39b7c8 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h"
 
 #include <utility>
+#include "base/containers/adapters.h"
 #include "third_party/blink/renderer/core/layout/layout_text.h"
 #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
@@ -174,6 +175,42 @@
   }
 }
 
+void NGOffsetMappingBuilder::RestoreTrailingCollapsibleSpace(
+    const LayoutText& layout_text,
+    unsigned offset) {
+  ++destination_length_;
+  for (auto& unit : base::Reversed(mapping_units_)) {
+    if (unit.text_content_end_ < offset) {
+      // There are no collapsed unit.
+      NOTREACHED();
+      return;
+    }
+    if (unit.text_content_start_ != offset ||
+        unit.text_content_end_ != offset ||
+        unit.layout_object_ != layout_text) {
+      ++unit.text_content_start_;
+      ++unit.text_content_end_;
+      continue;
+    }
+    DCHECK_EQ(unit.type_, NGOffsetMappingUnitType::kCollapsed);
+    const unsigned original_dom_end = unit.dom_end_;
+    unit.type_ = NGOffsetMappingUnitType::kIdentity;
+    unit.dom_end_ = unit.dom_start_ + 1;
+    unit.text_content_end_ = unit.text_content_start_ + 1;
+    if (original_dom_end - unit.dom_start_ == 1)
+      return;
+    // When we collapsed multiple spaces, e.g. <b>   </b>.
+    mapping_units_.insert(
+        std::distance(mapping_units_.begin(), &unit) + 1,
+        NGOffsetMappingUnit(NGOffsetMappingUnitType::kCollapsed, layout_text,
+                            unit.dom_end_, original_dom_end,
+                            unit.text_content_end_, unit.text_content_end_));
+    return;
+  }
+  NOTREACHED();
+  return;
+}
+
 void NGOffsetMappingBuilder::SetDestinationString(String string) {
   DCHECK_EQ(destination_length_, string.length());
   destination_string_ = string;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
index d642a9f3..652d603 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
@@ -16,6 +16,7 @@
 namespace blink {
 
 class LayoutObject;
+class LayoutText;
 
 // This is the helper class for constructing the DOM-to-TextContent offset
 // mapping. It holds an offset mapping, and provides APIs to modify the mapping
@@ -106,6 +107,11 @@
   // TODO(xiaochengh): Implement when adding support for 'text-transform'
   // void Composite(const NGOffsetMappingBuilder&);
 
+  // Restore a trailing collapsible space at |offset| of text content. The space
+  // is associated with |layout_text|.
+  void RestoreTrailingCollapsibleSpace(const LayoutText& layout_text,
+                                       unsigned offset);
+
   // Set the destination string of the offset mapping.
   void SetDestinationString(String);
 
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 29b7ec1..533fb64 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
@@ -1253,6 +1253,154 @@
             mapping.GetUnits());
 }
 
+TEST_F(NGOffsetMappingTest, RestoreTrailingCollapsibleSpaceReplace) {
+  // A space inside <b> is collapsed by during handling "\n" then it is restored
+  // by handling a newline. Restored space is removed at end of block.
+  // When RestoreTrailingCollapsibleSpace(), units are:
+  //  0: kIdentity text in <a>, dom=0,1 content=0,1
+  //  1: kCollapsed text in <b>, dom=0,1, content=2,2
+  //  2: kCollapsed "\n", dom=0,1, content=2,2
+  // layout_text is a child of <b> and offset is 2
+  SetupHtml("t",
+            "<div id=t>"
+            "<a style='white-space: pre-wrap;'> </a><b> </b>\n<i> </i>"
+            "</div>");
+  const NGOffsetMapping& result = GetOffsetMapping();
+  const LayoutObject& layout_object_a = *layout_object_;
+  const LayoutObject& layout_object_b = *layout_object_a.NextSibling();
+  const LayoutObject& newline = *layout_object_b.NextSibling();
+  const LayoutObject& layout_object_i = *newline.NextSibling();
+  EXPECT_EQ(
+      (Vector<NGOffsetMappingUnit>{
+          NGOffsetMappingUnit(kIdentity, *layout_object_a.SlowFirstChild(), 0u,
+                              1u, 0u, 1u),
+          NGOffsetMappingUnit(kCollapsed, *layout_object_b.SlowFirstChild(), 0u,
+                              1u, 2u, 2u),
+          NGOffsetMappingUnit(kCollapsed, newline, 0u, 1u, 2u, 2u),
+          NGOffsetMappingUnit(kCollapsed, *layout_object_i.SlowFirstChild(), 0u,
+                              1u, 2u, 2u),
+      }),
+      result.GetUnits());
+}
+
+TEST_F(NGOffsetMappingTest, RestoreTrailingCollapsibleSpaceReplaceKeep) {
+  // A space inside <b> is collapsed by during handling "\n" then it is restored
+  // by handling a newline.
+  // When RestoreTrailingCollapsibleSpace(), units are:
+  //  0: kIdentity text in <a>, dom=0,1 content=0,1
+  //  1: kCollapsed text in <b>, dom=0,1, content=2,2
+  //  2: kCollapsed "\n", dom=0,1, content=2,2
+  // layout_text is a child of <b> and offset is 2
+  SetupHtml("t",
+            "<div id=t>"
+            "<a style='white-space: pre-wrap;'> </a><b> </b>\n<i>x</i>"
+            "</div>");
+  const NGOffsetMapping& result = GetOffsetMapping();
+  const LayoutObject& layout_object_a = *layout_object_;
+  const LayoutObject& layout_object_b = *layout_object_a.NextSibling();
+  const LayoutObject& newline = *layout_object_b.NextSibling();
+  const LayoutObject& layout_object_i = *newline.NextSibling();
+  EXPECT_EQ(
+      (Vector<NGOffsetMappingUnit>{
+          NGOffsetMappingUnit(kIdentity, *layout_object_a.SlowFirstChild(), 0u,
+                              1u, 0u, 1u),
+          NGOffsetMappingUnit(kIdentity, *layout_object_b.SlowFirstChild(), 0u,
+                              1u, 2u, 3u),
+          NGOffsetMappingUnit(kCollapsed, newline, 0u, 1u, 3u, 3u),
+          NGOffsetMappingUnit(kIdentity, *layout_object_i.SlowFirstChild(), 0u,
+                              1u, 3u, 4u),
+      }),
+      result.GetUnits());
+}
+
+TEST_F(NGOffsetMappingTest, RestoreTrailingCollapsibleSpaceNone) {
+  SetupHtml("t",
+            "<div id=t>"
+            "<a>x</a><b>   </b>\n<i>y</i>"
+            "</div>");
+  const NGOffsetMapping& result = GetOffsetMapping();
+  const LayoutObject& layout_object_a = *layout_object_;
+  const LayoutObject& layout_object_b = *layout_object_a.NextSibling();
+  const LayoutObject& newline = *layout_object_b.NextSibling();
+  const LayoutObject& layout_object_i = *newline.NextSibling();
+  EXPECT_EQ(
+      (Vector<NGOffsetMappingUnit>{
+          NGOffsetMappingUnit(kIdentity, *layout_object_a.SlowFirstChild(), 0u,
+                              1u, 0u, 1u),
+          // We take the first space character.
+          NGOffsetMappingUnit(kIdentity, *layout_object_b.SlowFirstChild(), 0u,
+                              1u, 1u, 2u),
+          NGOffsetMappingUnit(kCollapsed, *layout_object_b.SlowFirstChild(), 1u,
+                              3u, 2u, 2u),
+          NGOffsetMappingUnit(kCollapsed, newline, 0u, 1u, 2u, 2u),
+          NGOffsetMappingUnit(kIdentity, *layout_object_i.SlowFirstChild(), 0u,
+                              1u, 2u, 3u),
+      }),
+      result.GetUnits());
+}
+
+TEST_F(NGOffsetMappingTest, RestoreTrailingCollapsibleSpaceSplit) {
+  // Spaces inside <b> is collapsed by during handling "\n" then it is restored
+  // by handling a newline. Restored space is removed at end of block.
+  // When RestoreTrailingCollapsibleSpace(), units are:
+  //  0: kIdentity text in <a>, dom=0,1 content=0,1
+  //  1: kCollapsed text in <b>, dom=0,3, content=2,2
+  //  2: kCollapsed "\n", dom=0,1 content=3,3
+  // layout_text is a child of <b> and offset is 2
+  SetupHtml("t",
+            "<div id=t>"
+            "<a style='white-space: pre-wrap;'> </a><b>   </b>\n<i> </i>"
+            "</div>");
+  const NGOffsetMapping& result = GetOffsetMapping();
+  const LayoutObject& layout_object_a = *layout_object_;
+  const LayoutObject& layout_object_b = *layout_object_a.NextSibling();
+  const LayoutObject& newline = *layout_object_b.NextSibling();
+  const LayoutObject& layout_object_i = *newline.NextSibling();
+  EXPECT_EQ(
+      (Vector<NGOffsetMappingUnit>{
+          NGOffsetMappingUnit(kIdentity, *layout_object_a.SlowFirstChild(), 0u,
+                              1u, 0u, 1u),
+          NGOffsetMappingUnit(kCollapsed, *layout_object_b.SlowFirstChild(), 0u,
+                              3u, 2u, 2u),
+          NGOffsetMappingUnit(kCollapsed, newline, 0u, 1u, 2u, 2u),
+          NGOffsetMappingUnit(kCollapsed, *layout_object_i.SlowFirstChild(), 0u,
+                              1u, 2u, 2u),
+      }),
+      result.GetUnits());
+}
+
+TEST_F(NGOffsetMappingTest, RestoreTrailingCollapsibleSpaceSplitKeep) {
+  // Spaces inside <b> is collapsed by during handling "\n" then it is restored
+  // by handling a space in <i>.
+  // When RestoreTrailingCollapsibleSpace(), units are:
+  //  0: kIdentity text in <a>, dom=0,1 content=0,1
+  //  1: kCollapsed text in <b>, dom=0,3, content=2,2
+  //  2: kCollapsed "\n", dom=0,1 content=3,3
+  // layout_text is a child of <b> and offset is 2
+  SetupHtml("t",
+            "<div id=t>"
+            "<a style='white-space: pre-wrap;'> </a><b>   </b>\n<i>x</i>"
+            "</div>");
+  const NGOffsetMapping& result = GetOffsetMapping();
+  const LayoutObject& layout_object_a = *layout_object_;
+  const LayoutObject& layout_object_b = *layout_object_a.NextSibling();
+  const LayoutObject& newline = *layout_object_b.NextSibling();
+  const LayoutObject& layout_object_i = *newline.NextSibling();
+  EXPECT_EQ(
+      (Vector<NGOffsetMappingUnit>{
+          NGOffsetMappingUnit(kIdentity, *layout_object_a.SlowFirstChild(), 0u,
+                              1u, 0u, 1u),
+          NGOffsetMappingUnit(kIdentity, *layout_object_b.SlowFirstChild(), 0u,
+                              1u, 2u, 3u),
+          NGOffsetMappingUnit(kCollapsed, *layout_object_b.SlowFirstChild(), 1u,
+                              3u, 3u, 3u),
+          NGOffsetMappingUnit(kCollapsed, newline, 0u, 1u, 3u, 3u),
+          NGOffsetMappingUnit(kIdentity, *layout_object_i.SlowFirstChild(), 0u,
+                              1u, 3u, 4u),
+      }),
+      result.GetUnits());
+}
+
 TEST_F(NGOffsetMappingTest, TextOverflowEllipsis) {
   LoadAhem();
   SetupHtml("t",
diff --git a/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc b/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
index e1c609da..402f5bbe 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
@@ -66,117 +66,94 @@
   return box.ContainingBlockLogicalWidthForContent();
 }
 
-NGStaticPosition LayoutBoxUtils::ComputeStaticPositionFromLegacy(
+NGLogicalStaticPosition LayoutBoxUtils::ComputeStaticPositionFromLegacy(
     const LayoutBox& box,
+    const NGBoxStrut& container_border_scrollbar,
     const NGBoxFragmentBuilder* container_builder) {
-  LayoutBoxModelObject* css_container = ToLayoutBoxModelObject(box.Container());
-  LayoutBox* container = css_container->IsBox() ? ToLayoutBox(css_container)
-                                                : box.ContainingBlock();
-  const ComputedStyle* container_style = container->Style();
-  const ComputedStyle* parent_style = box.Parent()->Style();
-  const auto writing_mode = container_style->GetWritingMode();
+  const LayoutBoxModelObject* css_container =
+      ToLayoutBoxModelObject(box.Container());
+  const TextDirection parent_direction = box.Parent()->StyleRef().Direction();
 
-  // Calculate the actual size of the containing block for this out-of-flow
-  // descendant. This is what's used to size and position us.
+  // These two values represent the available-size for the OOF-positioned
+  // descandant, in the *descendant's* writing mode.
   LayoutUnit containing_block_logical_width =
       box.ContainingBlockLogicalWidthForPositioned(css_container);
   LayoutUnit containing_block_logical_height =
       box.ContainingBlockLogicalHeightForPositioned(css_container);
 
-  // Determine static position.
-
-  // static_inline and static_block are inline/block direction offsets from
-  // physical origin. This is an unexpected blend of logical and physical in a
-  // single variable.
-  LayoutUnit static_inline;
-  LayoutUnit static_block;
-
   Length logical_left;
   Length logical_right;
   Length logical_top;
   Length logical_bottom;
-
   box.ComputeInlineStaticDistance(logical_left, logical_right, &box,
                                   css_container, containing_block_logical_width,
                                   container_builder);
   box.ComputeBlockStaticDistance(logical_top, logical_bottom, &box,
                                  css_container, container_builder);
 
-  if (parent_style->IsLeftToRightDirection()) {
+  // Determine the static-position.
+  LayoutUnit static_line;
+  LayoutUnit static_block;
+  if (IsLtr(parent_direction)) {
     if (!logical_left.IsAuto()) {
-      static_inline =
+      static_line =
           MinimumValueForLength(logical_left, containing_block_logical_width);
     }
   } else {
     if (!logical_right.IsAuto()) {
-      static_inline =
+      static_line =
           MinimumValueForLength(logical_right, containing_block_logical_width);
     }
+
+    // |logical_right| is an adjustment from the right edge, to keep this
+    // relative to the line-left edge account for the
+    // |containing_block_logical_width|.
+    static_line = containing_block_logical_width - static_line;
   }
   if (!logical_top.IsAuto()) {
     static_block =
         MinimumValueForLength(logical_top, containing_block_logical_height);
   }
 
-  // Legacy static position is relative to padding box. Convert to border box.
-  // Also flip offsets as necessary to make them relative to to the left/top
-  // edges.
+  NGLogicalStaticPosition logical_static_position{
+      {static_line, static_block},
+      IsLtr(parent_direction)
+          ? NGLogicalStaticPosition::InlineEdge::kInlineStart
+          : NGLogicalStaticPosition::InlineEdge::kInlineEnd,
+      NGLogicalStaticPosition::BlockEdge::kBlockStart};
 
-  // First convert the static inline offset to a line-left offset, i.e.
-  // physical left or top.
-  LayoutUnit inline_left_or_top =
-      parent_style->IsLeftToRightDirection()
-          ? static_inline
-          : containing_block_logical_width - static_inline;
+  // Determine the physical available-size, remember that the available-size is
+  // currently in the *descendant's* writing-mode.
+  PhysicalSize container_size =
+      ToPhysicalSize(LogicalSize(containing_block_logical_width,
+                                 containing_block_logical_height),
+                     box.StyleRef().GetWritingMode());
 
-  // inline_left_or_top is now relative to the padding box.
-  // Make it relative to border box by adding border + scrollbar.
-  NGBlockNode container_node(container);
-  NGConstraintSpace non_anonymous_space =
-      NGConstraintSpaceBuilder(writing_mode, writing_mode,
-                               /* is_new_fc */ false)
-          .ToConstraintSpace();
-  NGBoxStrut border_scrollbar =
-      ComputeBorders(non_anonymous_space, container_node) +
-      ComputeScrollbars(non_anonymous_space, container_node);
+  const LayoutBox* container = css_container->IsBox()
+                                   ? ToLayoutBox(css_container)
+                                   : box.ContainingBlock();
+  const WritingMode container_writing_mode =
+      container->StyleRef().GetWritingMode();
+  const TextDirection container_direction = container->StyleRef().Direction();
 
-  // Now make it relative to the left or top border edge of the containing
-  // block.
-  inline_left_or_top += border_scrollbar.LineLeft(container_style->Direction());
+  // We perform a logical-physical-logical conversion to convert the
+  // static-position into the correct writing-mode, and direction combination.
+  //
+  // At the moment the static-position is in line-relative coordinates which is
+  // why we use |TextDirection::kLtr| for the first conversion.
+  logical_static_position =
+      logical_static_position
+          .ConvertToPhysical(container_writing_mode, TextDirection::kLtr,
+                             container_size)
+          .ConvertToLogical(container_writing_mode, container_direction,
+                            container_size);
 
-  // Make the static block offset relative to the start border edge of the
-  // containing block.
-  static_block += border_scrollbar.block_start;
-
-  // Calculate the border-box size of the object that's the containing block of
-  // this out-of-flow positioned descendant. This is needed to correct
-  // flipped-blocks coordinates.
-  LayoutUnit container_border_box_logical_height;
-  if (box.HasOverrideContainingBlockContentLogicalHeight()) {
-    container_border_box_logical_height =
-        box.OverrideContainingBlockContentLogicalHeight() +
-        border_scrollbar.BlockSum();
-  } else {
-    container_border_box_logical_height = container_builder
-                                              ? container_builder->BlockSize()
-                                              : container->LogicalHeight();
-  }
-
-  // Then convert it to a physical top or left offset. Since we're already
-  // border-box relative, flip it around the size of the border box, rather
-  // than the size of the containing block (padding box).
-  LayoutUnit block_top_or_left =
-      container_style->IsFlippedBlocksWritingMode()
-          ? container_border_box_logical_height - static_block
-          : static_block;
-
-  PhysicalOffset static_location =
-      container_style->IsHorizontalWritingMode()
-          ? PhysicalOffset(inline_left_or_top, block_top_or_left)
-          : PhysicalOffset(block_top_or_left, inline_left_or_top);
-
-  return NGStaticPosition::Create(writing_mode, parent_style->Direction(),
-                                  static_location);
+  // Finally we shift the static-position from being relative to the
+  // padding-box, to the border-box.
+  logical_static_position.offset +=
+      LogicalOffset{container_border_scrollbar.inline_start,
+                    container_border_scrollbar.block_start};
+  return logical_static_position;
 }
 
 bool LayoutBoxUtils::SkipContainingBlockForPercentHeightCalculation(
diff --git a/third_party/blink/renderer/core/layout/ng/layout_box_utils.h b/third_party/blink/renderer/core/layout/ng/layout_box_utils.h
index afe98d0d..c8ef21f 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_box_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_box_utils.h
@@ -12,7 +12,8 @@
 class LayoutBox;
 class LayoutBlock;
 class NGBoxFragmentBuilder;
-struct NGStaticPosition;
+struct NGBoxStrut;
+struct NGLogicalStaticPosition;
 
 // This static class should be used for querying information from a |LayoutBox|.
 class LayoutBoxUtils {
@@ -27,11 +28,12 @@
   static LayoutUnit AvailableLogicalHeight(const LayoutBox& box,
                                            const LayoutBlock* cb);
 
-  // Produces an |NGStaticPosition| for |box| from the layout-tree.
-  // |container_builder| is needed as not all information from current NG
+  // Produces a |NGLogicalStaticPosition| for |box| from the layout-tree.
+  // |container_builder| is needed as not all the information from current NG
   // layout is copied to the layout-tree yet.
-  static NGStaticPosition ComputeStaticPositionFromLegacy(
+  static NGLogicalStaticPosition ComputeStaticPositionFromLegacy(
       const LayoutBox& box,
+      const NGBoxStrut& container_border_scrollbar,
       const NGBoxFragmentBuilder* container_builder = nullptr);
 
   static bool SkipContainingBlockForPercentHeightCalculation(
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
index ac94ec7..ad11d85 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
@@ -45,7 +45,7 @@
   scoped_refptr<const NGLayoutResult> result =
       NGBlockNode(this).Layout(constraint_space);
 
-  for (const NGOutOfFlowPositionedDescendant& descendant :
+  for (const auto& descendant :
        result->PhysicalFragment().OutOfFlowPositionedDescendants())
     descendant.node.UseLegacyOutOfFlowPositioning();
 }
@@ -72,13 +72,15 @@
   container_builder.SetIsNewFormattingContext(
       container_node.CreatesNewFormattingContext());
 
-  // Compute ContainingBlock logical size.
-  // OverrideContainingBlockContentLogicalWidth/Height are used by e.g. grid
-  // layout. Override sizes are padding box size, not border box, so we must add
-  // borders and scrollbars to compensate.
-  NGBoxStrut border_scrollbar =
-      ComputeBorders(constraint_space, container_node) +
+  NGFragmentGeometry fragment_geometry;
+  fragment_geometry.border = ComputeBorders(constraint_space, container_node);
+  fragment_geometry.scrollbar =
       ComputeScrollbars(constraint_space, container_node);
+  fragment_geometry.padding =
+      ComputePadding(constraint_space, *container_style);
+
+  NGBoxStrut border_scrollbar =
+      fragment_geometry.border + fragment_geometry.scrollbar;
 
   // Calculate the border-box size of the object that's the containing block of
   // this out-of-flow positioned descendant. Note that this is not to be used as
@@ -104,16 +106,12 @@
     container_border_box_logical_height = container->LogicalHeight();
   }
 
-  NGFragmentGeometry fragment_geometry;
   fragment_geometry.border_box_size = {container_border_box_logical_width,
                                        container_border_box_logical_height};
-  fragment_geometry.border = ComputeBorders(constraint_space, container_node);
-  fragment_geometry.padding =
-      ComputePadding(constraint_space, *container_style);
   container_builder.SetInitialFragmentGeometry(fragment_geometry);
 
-  NGStaticPosition static_position =
-      LayoutBoxUtils::ComputeStaticPositionFromLegacy(*this);
+  NGLogicalStaticPosition static_position =
+      LayoutBoxUtils::ComputeStaticPositionFromLegacy(*this, border_scrollbar);
   // Set correct container for inline containing blocks.
   container_builder.AddOutOfFlowLegacyCandidate(
       NGBlockNode(this), static_position, ToLayoutInlineOrNull(css_container));
@@ -140,7 +138,7 @@
   scoped_refptr<const NGLayoutResult> result =
       container_builder.ToBoxFragment();
   // These are the unpositioned OOF descendants of the current OOF block.
-  for (const NGOutOfFlowPositionedDescendant& descendant :
+  for (const auto& descendant :
        result->PhysicalFragment().OutOfFlowPositionedDescendants())
     descendant.node.UseLegacyOutOfFlowPositioning();
 
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc
index 5b1bfac..6ca0936 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc
@@ -8,7 +8,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
 namespace blink {
@@ -28,7 +28,7 @@
   scoped_refptr<const NGLayoutResult> result =
       NGBlockNode(this).Layout(constraint_space);
 
-  for (NGOutOfFlowPositionedDescendant descendant :
+  for (const auto& descendant :
        result->PhysicalFragment().OutOfFlowPositionedDescendants())
     descendant.node.UseLegacyOutOfFlowPositioning();
 }
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc
index d6756cc0..7976107 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc
@@ -10,7 +10,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
 namespace blink {
@@ -69,7 +69,7 @@
   // point to LayoutNG. If our parent were LayoutNG, it wouldn't have called
   // UpdateBlockLayout, it would have packaged this LayoutObject into
   // NGBlockNode and called Layout on that.
-  for (NGOutOfFlowPositionedDescendant descendant :
+  for (const auto& descendant :
        result->PhysicalFragment().OutOfFlowPositionedDescendants())
     descendant.node.UseLegacyOutOfFlowPositioning();
 
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc
index d38d1a2..04411a9a 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc
@@ -9,7 +9,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
 namespace blink {
@@ -28,7 +28,7 @@
   scoped_refptr<const NGLayoutResult> result =
       NGBlockNode(this).Layout(constraint_space);
 
-  for (NGOutOfFlowPositionedDescendant descendant :
+  for (const auto& descendant :
        result->PhysicalFragment().OutOfFlowPositionedDescendants())
     descendant.node.UseLegacyOutOfFlowPositioning();
 }
diff --git a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
index 0231815b..8a4ff04 100644
--- a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
+++ b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
@@ -51,19 +51,18 @@
   return marker_layout_result;
 }
 
-bool NGUnpositionedListMarker::AddToBox(
+bool NGUnpositionedListMarker::CanAddToBox(
     const NGConstraintSpace& space,
     FontBaseline baseline_type,
     const NGPhysicalFragment& content,
-    LogicalOffset* content_offset,
-    NGBoxFragmentBuilder* container_builder,
-    const NGBoxStrut& border_scrollbar_padding) const {
+    NGLineHeightMetrics* content_metrics) const {
+  DCHECK(content_metrics);
+
   // Baselines from two different writing-mode cannot be aligned.
   if (UNLIKELY(space.GetWritingMode() != content.Style().GetWritingMode()))
     return false;
 
   // Compute the baseline of the child content.
-  NGLineHeightMetrics content_metrics;
   if (content.IsLineBox()) {
     const auto& line_box = To<NGPhysicalLineBoxFragment>(content);
 
@@ -73,26 +72,35 @@
     if (line_box.IsEmptyLineBox() && !line_box.BreakToken()->IsFinished())
       return false;
 
-    content_metrics = line_box.Metrics();
+    *content_metrics = line_box.Metrics();
   } else {
     NGBoxFragment content_fragment(space.GetWritingMode(), space.Direction(),
                                    To<NGPhysicalBoxFragment>(content));
-    content_metrics = content_fragment.BaselineMetricsWithoutSynthesize(
+    *content_metrics = content_fragment.BaselineMetricsWithoutSynthesize(
         {NGBaselineAlgorithmType::kFirstLine, baseline_type});
 
     // If this child content does not have any line boxes, the list marker
     // should be aligned to the first line box of next child.
     // https://github.com/w3c/csswg-drafts/issues/2417
-    if (content_metrics.IsEmpty())
+    if (content_metrics->IsEmpty())
       return false;
   }
+  return true;
+}
 
-  // Layout the list marker.
-  scoped_refptr<const NGLayoutResult> marker_layout_result =
-      Layout(space, container_builder->Style(), baseline_type);
-  DCHECK(marker_layout_result);
+void NGUnpositionedListMarker::AddToBox(
+    const NGConstraintSpace& space,
+    FontBaseline baseline_type,
+    const NGPhysicalFragment& content,
+    const NGBoxStrut& border_scrollbar_padding,
+    const NGLineHeightMetrics& content_metrics,
+    const NGLayoutResult& marker_layout_result,
+    LogicalOffset* content_offset,
+    NGBoxFragmentBuilder* container_builder) const {
+  DCHECK(!content_metrics.IsEmpty());
+
   const NGPhysicalBoxFragment& marker_physical_fragment =
-      To<NGPhysicalBoxFragment>(marker_layout_result->PhysicalFragment());
+      To<NGPhysicalBoxFragment>(marker_layout_result.PhysicalFragment());
 
   // Compute the inline offset of the marker.
   NGBoxFragment marker_fragment(space.GetWritingMode(), space.Direction(),
@@ -117,20 +125,15 @@
 
   DCHECK(container_builder);
   container_builder->AddChild(marker_physical_fragment, marker_offset);
-
-  return true;
 }
 
 LayoutUnit NGUnpositionedListMarker::AddToBoxWithoutLineBoxes(
     const NGConstraintSpace& space,
     FontBaseline baseline_type,
+    const NGLayoutResult& marker_layout_result,
     NGBoxFragmentBuilder* container_builder) const {
-  // Layout the list marker.
-  scoped_refptr<const NGLayoutResult> marker_layout_result =
-      Layout(space, container_builder->Style(), baseline_type);
-  DCHECK(marker_layout_result);
   const NGPhysicalBoxFragment& marker_physical_fragment =
-      To<NGPhysicalBoxFragment>(marker_layout_result->PhysicalFragment());
+      To<NGPhysicalBoxFragment>(marker_layout_result.PhysicalFragment());
 
   // When there are no line boxes, marker is top-aligned to the list item.
   // https://github.com/w3c/csswg-drafts/issues/2417
@@ -152,6 +155,10 @@
     const NGBoxStrut& border_scrollbar_padding,
     LayoutUnit marker_block_offset) const {
   DCHECK(container_builder);
+  // If the BFC block-offset isn't resolved, the intruded offset isn't
+  // available either.
+  if (!container_builder->BfcBlockOffset())
+    return LayoutUnit();
   // Because opportunity.rect is in the content area of LI, so origin_offset
   // should plus border_scrollbar_padding.inline_start, and available_size
   // should minus border_scrollbar_padding.
@@ -177,7 +184,19 @@
     return origin_offset.line_offset + available_size -
            opportunity.rect.LineEndOffset();
   }
-  return LayoutUnit(0);
+  return LayoutUnit();
 }
 
+#if DCHECK_IS_ON()
+// TODO: Currently we haven't supported ::marker, so the margin-top of marker
+// should always be zero. And this make us could resolve LI's BFC block-offset
+// in NGBlockLayoutAlgorithm::PositionOrPropagateListMarker and
+// NGBlockLayoutAlgorithm::PositionListMarkerWithoutLineBoxes without consider
+// marker's margin-top.
+void NGUnpositionedListMarker::CheckMargin() const {
+  DCHECK(marker_layout_object_);
+  DCHECK(marker_layout_object_->StyleRef().MarginBefore().IsZero());
+}
+#endif
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h
index 39f28fb..8305cac 100644
--- a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h
+++ b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h
@@ -23,6 +23,7 @@
 class NGPhysicalFragment;
 
 struct LogicalOffset;
+struct NGLineHeightMetrics;
 
 // Represents an unpositioned list marker.
 //
@@ -33,6 +34,19 @@
 // To handle these two cases consistently, when list markers appear in these
 // algorithm, they are set as "unpositioned", and are propagated to ancestors
 // through NGLayoutResult until they meet the corresponding list items.
+//
+// In order to adjust with the other content of LI, marker will be handled
+// after other children.
+// First, try to find the adjusted content_metrics for the marker. See
+// |CanAddToBox()| for details.
+// If found, layout marker, compute the content adjusted offset and float
+// intuded offset. See |AddToBox()| for details.
+// If not, layout marker and deal with it in |AddToBoxWithoutLineBoxes()|.
+//
+// In addition, marker makes LI non self-collapsing. If the BFC block-offset of
+// LI isn't resolved after layout marker, we'll resolve it. See
+// |NGBlockLayoutAlgorithm::PositionOrPropagateListMarker()| and
+// |NGBlockLayoutAlgorithm::PositionListMarkerWithoutLineBoxes()| for details.
 class CORE_EXPORT NGUnpositionedListMarker final {
   DISALLOW_NEW();
 
@@ -43,36 +57,49 @@
 
   explicit operator bool() const { return marker_layout_object_; }
 
-  // Add a fragment for an outside list marker.
-  // Returns true if the list marker was successfully added. False indicates
+  // Returns true if the list marker can be added to box. False indicates
   // that the child content does not have a baseline to align to, and that
   // caller should try next child, or "WithoutLineBoxes" version.
-  bool AddToBox(const NGConstraintSpace&,
+  bool CanAddToBox(const NGConstraintSpace&,
+                   FontBaseline,
+                   const NGPhysicalFragment& content,
+                   NGLineHeightMetrics* content_metrics) const;
+  // Add a fragment for an outside list marker.
+  void AddToBox(const NGConstraintSpace&,
                 FontBaseline,
                 const NGPhysicalFragment& content,
+                const NGBoxStrut&,
+                const NGLineHeightMetrics& content_metrics,
+                const NGLayoutResult& marker_layout_result,
                 LogicalOffset* content_offset,
-                NGBoxFragmentBuilder*,
-                const NGBoxStrut&) const;
+                NGBoxFragmentBuilder*) const;
 
   // Add a fragment for an outside list marker when the list item has no line
   // boxes.
   // Returns the block size of the list marker.
-  LayoutUnit AddToBoxWithoutLineBoxes(const NGConstraintSpace&,
-                                      FontBaseline,
-                                      NGBoxFragmentBuilder*) const;
+  LayoutUnit AddToBoxWithoutLineBoxes(
+      const NGConstraintSpace&,
+      FontBaseline,
+      const NGLayoutResult& marker_layout_result,
+      NGBoxFragmentBuilder*) const;
   LayoutUnit InlineOffset(const LayoutUnit marker_inline_size) const;
 
   bool operator==(const NGUnpositionedListMarker& other) const {
     return marker_layout_object_ == other.marker_layout_object_;
   }
 
- private:
-  bool IsImage() const;
-
   scoped_refptr<const NGLayoutResult> Layout(
       const NGConstraintSpace& parent_space,
       const ComputedStyle& parent_style,
       FontBaseline) const;
+
+#if DCHECK_IS_ON()
+  void CheckMargin() const;
+#endif
+
+ private:
+  bool IsImage() const;
+
   LayoutUnit ComputeIntrudedFloatOffset(const NGConstraintSpace&,
                                         const NGBoxFragmentBuilder*,
                                         const NGBoxStrut&,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
index bc79a72..561f565 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
@@ -153,12 +153,13 @@
 // Available size can is maximum length Element can have without overflowing
 // container bounds. The position of Element's edges will determine
 // how much space there is available.
-LayoutUnit ComputeAvailableWidth(LayoutUnit container_width,
-                                 const base::Optional<LayoutUnit>& left,
-                                 const base::Optional<LayoutUnit>& right,
-                                 const base::Optional<LayoutUnit>& margin_left,
-                                 const base::Optional<LayoutUnit>& margin_right,
-                                 const NGStaticPosition& static_position) {
+LayoutUnit ComputeAvailableWidth(
+    LayoutUnit container_width,
+    const base::Optional<LayoutUnit>& left,
+    const base::Optional<LayoutUnit>& right,
+    const base::Optional<LayoutUnit>& margin_left,
+    const base::Optional<LayoutUnit>& margin_right,
+    const NGPhysicalStaticPosition& static_position) {
   LayoutUnit available_width = container_width;
   DCHECK(!left || !right);
   if (!left && !right) {
@@ -182,7 +183,7 @@
     const base::Optional<LayoutUnit>& bottom,
     const base::Optional<LayoutUnit>& margin_top,
     const base::Optional<LayoutUnit>& margin_bottom,
-    const NGStaticPosition& static_position) {
+    const NGPhysicalStaticPosition& static_position) {
   LayoutUnit available_height = container_height;
   DCHECK(!top || !bottom);
   if (!top && !bottom) {
@@ -206,7 +207,7 @@
                                const ComputedStyle& style,
                                const NGBoxStrut& border_padding,
                                const base::Optional<LayoutUnit>& incoming_width,
-                               const NGStaticPosition& static_position,
+                               const NGPhysicalStaticPosition& static_position,
                                const base::Optional<MinMaxSize>& child_minmax,
                                const WritingMode container_writing_mode,
                                const TextDirection container_direction,
@@ -380,7 +381,7 @@
                              const ComputedStyle& style,
                              const NGBoxStrut& border_padding,
                              const base::Optional<LayoutUnit>& incoming_height,
-                             const NGStaticPosition& static_position,
+                             const NGPhysicalStaticPosition& static_position,
                              const base::Optional<MinMaxSize>& child_minmax,
                              const WritingMode container_writing_mode,
                              const TextDirection container_direction,
@@ -610,7 +611,7 @@
     const NGConstraintSpace& space,
     const ComputedStyle& style,
     const NGBoxStrut& border_padding,
-    const NGStaticPosition& static_position,
+    const NGPhysicalStaticPosition& static_position,
     const base::Optional<MinMaxSize>& child_minmax,
     const base::Optional<LogicalSize>& replaced_size,
     const WritingMode container_writing_mode,
@@ -646,7 +647,7 @@
     const NGConstraintSpace& space,
     const ComputedStyle& style,
     const NGBoxStrut& border_padding,
-    const NGStaticPosition& static_position,
+    const NGPhysicalStaticPosition& static_position,
     const base::Optional<LayoutUnit>& child_block_size,
     const base::Optional<LogicalSize>& replaced_size,
     const WritingMode container_writing_mode,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
index 67029cd..ce04e50d0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
@@ -17,7 +17,7 @@
 class ComputedStyle;
 class LayoutObject;
 class NGConstraintSpace;
-struct NGStaticPosition;
+struct NGPhysicalStaticPosition;
 
 struct CORE_EXPORT NGAbsolutePhysicalPosition {
   NGPhysicalBoxStrut inset;
@@ -63,7 +63,7 @@
     const NGConstraintSpace&,
     const ComputedStyle&,
     const NGBoxStrut& border_padding,
-    const NGStaticPosition&,
+    const NGPhysicalStaticPosition&,
     const base::Optional<MinMaxSize>& child_minmax,
     const base::Optional<LogicalSize>& replaced_size,
     const WritingMode container_writing_mode,
@@ -74,7 +74,7 @@
     const NGConstraintSpace&,
     const ComputedStyle&,
     const NGBoxStrut& border_padding,
-    const NGStaticPosition&,
+    const NGPhysicalStaticPosition&,
     const base::Optional<LayoutUnit>& child_block_size,
     const base::Optional<LogicalSize>& replaced_size,
     const WritingMode container_writing_mode,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
index 20c8a4a..82b135a2 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
@@ -141,11 +141,14 @@
   NGBoxStrut vrl_border_padding =
       ComputeBordersForTest(*style_) + ComputePadding(vrl_space_, *style_);
 
-  NGStaticPosition static_position{NGStaticPosition::kTopLeft,
-                                   {LayoutUnit(), LayoutUnit()}};
+  NGPhysicalStaticPosition static_position = {{LayoutUnit(), LayoutUnit()},
+                                              NGPhysicalStaticPosition::kLeft,
+                                              NGPhysicalStaticPosition::kTop};
   // Same as regular static position, but with right edge.
-  NGStaticPosition static_right_position{NGStaticPosition::kTopRight,
-                                         {LayoutUnit(), LayoutUnit()}};
+  NGPhysicalStaticPosition static_position_right = {
+      {LayoutUnit(), LayoutUnit()},
+      NGPhysicalStaticPosition::kRight,
+      NGPhysicalStaticPosition::kTop};
   //
   // Tests.
   //
@@ -168,7 +171,7 @@
   EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), true);
   estimated_inline = minmax_60;
   p = ComputePartialAbsoluteWithChildInlineSize(
-      ltr_space_, *style_, ltr_border_padding, static_right_position,
+      ltr_space_, *style_, ltr_border_padding, static_position_right,
       estimated_inline, base::nullopt, WritingMode::kHorizontalTb,
       TextDirection::kLtr);
   EXPECT_EQ(minmax_60.min_size, p.size.width);
@@ -368,10 +371,13 @@
   NGBoxStrut vrl_border_padding =
       ComputeBordersForTest(*style_) + ComputePadding(vrl_space_, *style_);
 
-  NGStaticPosition static_position{NGStaticPosition::kTopLeft,
-                                   {LayoutUnit(), LayoutUnit()}};
-  NGStaticPosition static_position_bottom{NGStaticPosition::kBottomLeft,
-                                          {LayoutUnit(), LayoutUnit()}};
+  NGPhysicalStaticPosition static_position = {{LayoutUnit(), LayoutUnit()},
+                                              NGPhysicalStaticPosition::kLeft,
+                                              NGPhysicalStaticPosition::kTop};
+  NGPhysicalStaticPosition static_position_bottom = {
+      {LayoutUnit(), LayoutUnit()},
+      NGPhysicalStaticPosition::kLeft,
+      NGPhysicalStaticPosition::kBottom};
 
   //
   // Tests
@@ -515,8 +521,9 @@
   NGBoxStrut ltr_border_padding =
       ComputeBordersForTest(*style_) + ComputePadding(ltr_space_, *style_);
 
-  NGStaticPosition static_position{NGStaticPosition::kTopLeft,
-                                   {LayoutUnit(), LayoutUnit()}};
+  NGPhysicalStaticPosition static_position = {{LayoutUnit(), LayoutUnit()},
+                                              NGPhysicalStaticPosition::kLeft,
+                                              NGPhysicalStaticPosition::kTop};
   MinMaxSize estimated_inline{LayoutUnit(20), LayoutUnit(20)};
   NGAbsolutePhysicalPosition p;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 399b7f5..ea8897d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -344,7 +344,7 @@
 
 inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
     NGInlineChildLayoutContext* inline_child_layout_context) {
-  LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
+  const LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
   child_available_size_ =
       ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
 
@@ -528,16 +528,12 @@
   // To save space of the stack when we recurse into children, the rest of this
   // function is continued within |FinishLayout|. However it should be read as
   // one function.
-  return FinishLayout(&previous_inflow_position, border_box_size,
-                      container_builder_.Borders(),
-                      container_builder_.Scrollbar());
+  return FinishLayout(&previous_inflow_position);
 }
 
 scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
-    NGPreviousInflowPosition* previous_inflow_position,
-    LogicalSize border_box_size,
-    const NGBoxStrut& borders,
-    const NGBoxStrut& scrollbars) {
+    NGPreviousInflowPosition* previous_inflow_position) {
+  LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
   NGMarginStrut end_margin_strut = previous_inflow_position->margin_strut;
 
   // If the current layout is a new formatting context, we need to encapsulate
@@ -640,8 +636,10 @@
   // List markers should have been positioned if we had line boxes, or boxes
   // that have line boxes. If there were no line boxes, position without line
   // boxes.
-  if (container_builder_.UnpositionedListMarker() && node_.IsListItem())
-    PositionListMarkerWithoutLineBoxes();
+  if (container_builder_.UnpositionedListMarker() && node_.IsListItem()) {
+    if (!PositionListMarkerWithoutLineBoxes(previous_inflow_position))
+      return container_builder_.Abort(NGLayoutResult::kBfcBlockOffsetResolved);
+  }
 
   container_builder_.SetEndMarginStrut(end_margin_strut);
   container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
@@ -676,7 +674,9 @@
   // layout.
   if (!container_builder_.AdjoiningFloatTypes() ||
       ConstraintSpace().ForcedBfcBlockOffset()) {
-    NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders + scrollbars,
+    NGBoxStrut borders_and_scrollbars =
+        container_builder_.Borders() + container_builder_.Scrollbar();
+    NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders_and_scrollbars,
                           &container_builder_)
         .Run();
   }
@@ -1073,7 +1073,9 @@
     container_builder_.SetPreviousBreakAfter(break_after);
   }
 
-  PositionOrPropagateListMarker(*layout_result, &logical_offset);
+  if (!PositionOrPropagateListMarker(*layout_result, &logical_offset,
+                                     previous_inflow_position))
+    return false;
 
   container_builder_.AddChild(layout_result->PhysicalFragment(),
                               logical_offset);
@@ -1505,7 +1507,9 @@
     container_builder_.SetPreviousBreakAfter(break_after);
   }
 
-  PositionOrPropagateListMarker(*layout_result, &logical_offset);
+  if (!PositionOrPropagateListMarker(*layout_result, &logical_offset,
+                                     previous_inflow_position))
+    return false;
 
   container_builder_.AddChild(physical_fragment, logical_offset);
   if (child.IsBlock())
@@ -2379,9 +2383,10 @@
       .ClampNegativeToZero();
 }
 
-void NGBlockLayoutAlgorithm::PositionOrPropagateListMarker(
+bool NGBlockLayoutAlgorithm::PositionOrPropagateListMarker(
     const NGLayoutResult& layout_result,
-    LogicalOffset* content_offset) {
+    LogicalOffset* content_offset,
+    NGPreviousInflowPosition* previous_inflow_position) {
   // If this is not a list-item, propagate unpositioned list markers to
   // ancestors.
   if (!node_.IsListItem()) {
@@ -2390,7 +2395,7 @@
       container_builder_.SetUnpositionedListMarker(
           layout_result.UnpositionedListMarker());
     }
-    return;
+    return true;
   }
 
   // If this is a list item, add the unpositioned list marker as a child.
@@ -2398,44 +2403,91 @@
   if (!list_marker) {
     list_marker = container_builder_.UnpositionedListMarker();
     if (!list_marker)
-      return;
+      return true;
     container_builder_.SetUnpositionedListMarker(NGUnpositionedListMarker());
   }
-  if (list_marker.AddToBox(ConstraintSpace(), Style().GetFontBaseline(),
-                           layout_result.PhysicalFragment(), content_offset,
-                           &container_builder_, border_scrollbar_padding_))
-    return;
+
+  NGLineHeightMetrics content_metrics;
+  const NGConstraintSpace& space = ConstraintSpace();
+  const NGPhysicalFragment& content = layout_result.PhysicalFragment();
+  FontBaseline baseline_type = Style().GetFontBaseline();
+  if (list_marker.CanAddToBox(space, baseline_type, content,
+                              &content_metrics)) {
+    // TODO: We are reusing the ConstraintSpace for LI here. It works well for
+    // now because authors cannot style list-markers currently. If we want to
+    // support `::marker` pseudo, we need to create ConstraintSpace for marker
+    // separately.
+    scoped_refptr<const NGLayoutResult> marker_layout_result =
+        list_marker.Layout(space, container_builder_.Style(), baseline_type);
+    DCHECK(marker_layout_result);
+    // If the BFC block-offset of li is still not resolved, resolved it now.
+    if (!container_builder_.BfcBlockOffset() &&
+        marker_layout_result->BfcBlockOffset()) {
+      // TODO: Currently the margin-top of marker is always zero. To support
+      // `::marker` pseudo, we should count marker's margin-top in.
+#if DCHECK_IS_ON()
+      list_marker.CheckMargin();
+#endif
+      if (!ResolveBfcBlockOffset(previous_inflow_position))
+        return false;
+    }
+
+    list_marker.AddToBox(space, baseline_type, content,
+                         border_scrollbar_padding_, content_metrics,
+                         *marker_layout_result, content_offset,
+                         &container_builder_);
+    return true;
+  }
 
   // If the list marker could not be positioned against this child because it
   // does not have the baseline to align to, keep it as unpositioned and try
   // the next child.
   container_builder_.SetUnpositionedListMarker(list_marker);
+  return true;
 }
 
-void NGBlockLayoutAlgorithm::PositionListMarkerWithoutLineBoxes() {
+bool NGBlockLayoutAlgorithm::PositionListMarkerWithoutLineBoxes(
+    NGPreviousInflowPosition* previous_inflow_position) {
   DCHECK(node_.IsListItem());
   DCHECK(container_builder_.UnpositionedListMarker());
 
+  NGUnpositionedListMarker list_marker =
+      container_builder_.UnpositionedListMarker();
+  const NGConstraintSpace& space = ConstraintSpace();
+  FontBaseline baseline_type = Style().GetFontBaseline();
+  // Layout the list marker.
+  scoped_refptr<const NGLayoutResult> marker_layout_result =
+      list_marker.Layout(space, container_builder_.Style(), baseline_type);
+  DCHECK(marker_layout_result);
+  // If the BFC block-offset of li is still not resolved, resolve it now.
+  if (!container_builder_.BfcBlockOffset() &&
+      marker_layout_result->BfcBlockOffset()) {
+    // TODO: Currently the margin-top of marker is always zero. To support
+    // `::marker` pseudo, we should count marker's margin-top in.
+#if DCHECK_IS_ON()
+    list_marker.CheckMargin();
+#endif
+    if (!ResolveBfcBlockOffset(previous_inflow_position))
+      return false;
+  }
   // Position the list marker without aligning to line boxes.
-  LayoutUnit marker_block_size =
-      container_builder_.UnpositionedListMarker().AddToBoxWithoutLineBoxes(
-          ConstraintSpace(), Style().GetFontBaseline(), &container_builder_);
+  LayoutUnit marker_block_size = list_marker.AddToBoxWithoutLineBoxes(
+      space, baseline_type, *marker_layout_result, &container_builder_);
   container_builder_.SetUnpositionedListMarker(NGUnpositionedListMarker());
 
   // Whether the list marker should affect the block size or not is not
   // well-defined, but 3 out of 4 impls do.
   // https://github.com/w3c/csswg-drafts/issues/2418
   //
-  // TODO(kojii): Since this makes this block non self-collapsing, it's
-  // probably better to resolve BFC block-offset if not done yet, but that
-  // involves additional complexity without knowing how much this is needed.
-  // For now, include the marker into the block-size only if BFC was resolved.
+  // The BFC block-offset has been resolved after layout marker. We'll always
+  // include the marker into the block-size.
   if (container_builder_.BfcBlockOffset()) {
     intrinsic_block_size_ = std::max(marker_block_size, intrinsic_block_size_);
     container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
     container_builder_.SetBlockSize(
         std::max(marker_block_size, container_builder_.Size().block_size));
   }
+  return true;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
index 70db5f9..f5fdabef 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
@@ -66,11 +66,7 @@
   inline scoped_refptr<const NGLayoutResult> Layout(
       NGInlineChildLayoutContext* inline_child_layout_context);
 
-  scoped_refptr<const NGLayoutResult> FinishLayout(
-      NGPreviousInflowPosition*,
-      LogicalSize border_box_size,
-      const NGBoxStrut& borders,
-      const NGBoxStrut& scrollbars);
+  scoped_refptr<const NGLayoutResult> FinishLayout(NGPreviousInflowPosition*);
 
   // Return the BFC block offset of this block.
   LayoutUnit BfcBlockOffset() const {
@@ -292,10 +288,14 @@
   void PositionPendingFloats(LayoutUnit origin_block_offset);
 
   // Positions a list marker for the specified block content.
-  void PositionOrPropagateListMarker(const NGLayoutResult&, LogicalOffset*);
+  // Return false if it aborts when resolving BFC block offset for LI.
+  bool PositionOrPropagateListMarker(const NGLayoutResult&,
+                                     LogicalOffset*,
+                                     NGPreviousInflowPosition*);
 
   // Positions a list marker when the block does not have any line boxes.
-  void PositionListMarkerWithoutLineBoxes();
+  // Return false if it aborts when resolving BFC block offset for LI.
+  bool PositionListMarkerWithoutLineBoxes(NGPreviousInflowPosition*);
 
   // Calculates logical offset for the current fragment using either {@code
   // intrinsic_block_size_} when the fragment doesn't know it's offset or
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 0c87e9b..48a0d63 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
@@ -322,7 +322,8 @@
   if (!box_->NeedsLayout())
     return previous_result;
 
-  DCHECK(box_->NeedsSimplifiedLayoutOnly());
+  DCHECK(box_->NeedsSimplifiedLayoutOnly() ||
+         box_->LayoutBlockedByDisplayLock(DisplayLockContext::kChildren));
 
   // Perform layout on ourselves using the previous constraint space.
   const NGConstraintSpace space(
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 7668edb..9ba80d6f 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
@@ -164,42 +164,12 @@
 
 void NGBoxFragmentBuilder::AddOutOfFlowLegacyCandidate(
     NGBlockNode node,
-    const NGStaticPosition& static_position,
+    const NGLogicalStaticPosition& static_position,
     const LayoutInline* inline_container) {
-  DCHECK_GE(InlineSize(), LayoutUnit());
-  DCHECK_GE(BlockSize(), LayoutUnit());
-
-  NGOutOfFlowPositionedDescendant descendant(
+  oof_positioned_candidates_.emplace_back(
       node, static_position,
       inline_container ? ToLayoutInline(inline_container->ContinuationRoot())
                        : nullptr);
-  // Need 0,0 physical coordinates as child offset. Because offset
-  // is stored as logical, must convert physical 0,0 to logical.
-  LogicalOffset zero_offset;
-  switch (GetWritingMode()) {
-    case WritingMode::kHorizontalTb:
-      if (IsLtr(Direction()))
-        zero_offset = LogicalOffset();
-      else
-        zero_offset = LogicalOffset(InlineSize(), LayoutUnit());
-      break;
-    case WritingMode::kVerticalRl:
-    case WritingMode::kSidewaysRl:
-      if (IsLtr(Direction()))
-        zero_offset = LogicalOffset(LayoutUnit(), BlockSize());
-      else
-        zero_offset = LogicalOffset(InlineSize(), BlockSize());
-      break;
-    case WritingMode::kVerticalLr:
-    case WritingMode::kSidewaysLr:
-      if (IsLtr(Direction()))
-        zero_offset = LogicalOffset();
-      else
-        zero_offset = LogicalOffset(InlineSize(), LayoutUnit());
-      break;
-  }
-  oof_positioned_candidates_.push_back(
-      NGOutOfFlowPositionedCandidate{descendant, zero_offset});
 }
 
 NGPhysicalFragment::NGBoxType NGBoxFragmentBuilder::BoxType() const {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index a72b991..a222725 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -100,7 +100,7 @@
   NGBoxFragmentBuilder& PropagateBreak(const NGPhysicalContainerFragment&);
 
   void AddOutOfFlowLegacyCandidate(NGBlockNode,
-                                   const NGStaticPosition&,
+                                   const NGLogicalStaticPosition&,
                                    const LayoutInline* inline_container);
 
   // Set how much of the block size we've used so far for this box.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index c8fc8c3d..d738e141 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -14,16 +14,14 @@
 namespace blink {
 
 namespace {
-// This routine returns true if inline_container should replace descendant's
-// inline container.
-bool IsInlineContainerForDescendant(
-    const NGOutOfFlowPositionedDescendant& descendant,
-    const LayoutObject* inline_container) {
-  return !descendant.inline_container && inline_container &&
-         inline_container->IsLayoutInline() &&
+
+bool IsInlineContainerForNode(const NGBlockNode& node,
+                              const LayoutObject* inline_container) {
+  return inline_container && inline_container->IsLayoutInline() &&
          inline_container->CanContainOutOfFlowPositionedElement(
-             descendant.node.Style().GetPosition());
+             node.Style().GetPosition());
 }
+
 }  // namespace
 
 NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddChild(
@@ -36,34 +34,7 @@
   if (child.HasOutOfFlowPositionedDescendants()) {
     const auto& out_of_flow_descendants =
         child.OutOfFlowPositionedDescendants();
-    LogicalOffset top_left_offset;
     PhysicalSize child_size = child.Size();
-    switch (GetWritingMode()) {
-      case WritingMode::kHorizontalTb:
-        top_left_offset =
-            (IsRtl(Direction()))
-                ? LogicalOffset{child_offset.inline_offset + child_size.width,
-                                child_offset.block_offset}
-                : child_offset;
-        break;
-      case WritingMode::kVerticalRl:
-      case WritingMode::kSidewaysRl:
-        top_left_offset =
-            (IsRtl(Direction()))
-                ? LogicalOffset{child_offset.inline_offset + child_size.height,
-                                child_offset.block_offset + child_size.width}
-                : LogicalOffset{child_offset.inline_offset,
-                                child_offset.block_offset + child_size.width};
-        break;
-      case WritingMode::kVerticalLr:
-      case WritingMode::kSidewaysLr:
-        top_left_offset =
-            (IsRtl(Direction()))
-                ? LogicalOffset{child_offset.inline_offset + child_size.height,
-                                child_offset.block_offset}
-                : child_offset;
-        break;
-    }
 
     // We can end up in a case where we need to account for the relative
     // position of an element to correctly determine the static position of a
@@ -75,20 +46,27 @@
     // </div>
     // TODO(layout-dev): This code should eventually be removed once we handle
     // relative positioned objects directly in the fragment tree.
+    LogicalOffset offset = child_offset;
     if (const LayoutBox* child_box =
             ToLayoutBoxOrNull(child.GetLayoutObject())) {
-      top_left_offset += PhysicalOffset(child_box->OffsetForInFlowPosition())
-                             .ConvertToLogical(GetWritingMode(), Direction(),
-                                               PhysicalSize(), PhysicalSize());
+      offset += PhysicalOffset(child_box->OffsetForInFlowPosition())
+                    .ConvertToLogical(GetWritingMode(), Direction(),
+                                      PhysicalSize(), PhysicalSize());
     }
 
-    for (NGOutOfFlowPositionedDescendant& descendant :
-         out_of_flow_descendants) {
-      if (IsInlineContainerForDescendant(descendant, inline_container)) {
-        descendant.inline_container = inline_container;
-      }
-      oof_positioned_candidates_.push_back(
-          NGOutOfFlowPositionedCandidate(descendant, top_left_offset));
+    for (const auto& descendant : out_of_flow_descendants) {
+      NGLogicalStaticPosition static_position =
+          descendant.static_position.ConvertToLogical(GetWritingMode(),
+                                                      Direction(), child_size);
+      static_position.offset += offset;
+
+      const LayoutInline* new_inline_container = descendant.inline_container;
+      if (!descendant.inline_container &&
+          IsInlineContainerForNode(descendant.node, inline_container))
+        new_inline_container = inline_container;
+
+      oof_positioned_candidates_.emplace_back(descendant.node, static_position,
+                                              new_inline_container);
     }
   }
 
@@ -207,78 +185,59 @@
   // As all inline-level fragments are built in the line-logical coordinate
   // system (Direction() is kLtr), we need to know the direction of the
   // parent element to correctly determine an OOF childs static position.
-  TextDirection direction = container_direction.value_or(Direction());
+  TextDirection direction = container_direction.value_or(TextDirection::kLtr);
 
-  oof_positioned_candidates_.push_back(NGOutOfFlowPositionedCandidate(
-      NGOutOfFlowPositionedDescendant(
-          child, NGStaticPosition::Create(GetWritingMode(), direction,
-                                          PhysicalOffset())),
-      child_offset));
+  oof_positioned_candidates_.emplace_back(
+      child,
+      NGLogicalStaticPosition{
+          child_offset,
+          IsLtr(direction) ? NGLogicalStaticPosition::InlineEdge::kInlineStart
+                           : NGLogicalStaticPosition::InlineEdge::kInlineEnd,
+          NGLogicalStaticPosition::BlockEdge::kBlockStart});
 
   return *this;
 }
 
 NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddOutOfFlowDescendant(
-    const NGOutOfFlowPositionedDescendant& descendant) {
+    const NGLogicalOutOfFlowPositionedNode& descendant) {
   oof_positioned_descendants_.push_back(descendant);
   return *this;
 }
 
-void NGContainerFragmentBuilder::GetAndClearOutOfFlowDescendantCandidates(
-    Vector<NGOutOfFlowPositionedDescendant>* descendant_candidates,
+void NGContainerFragmentBuilder::SwapOutOfFlowPositionedCandidates(
+    Vector<NGLogicalOutOfFlowPositionedNode>* candidates,
     const LayoutObject* current_container) {
-  DCHECK(descendant_candidates->IsEmpty());
+  DCHECK(candidates->IsEmpty());
 
-  if (oof_positioned_candidates_.size() == 0)
-    return;
+  // The |oof_positioned_candidates_| list may get additional candidates, in the
+  // following case:
+  //  - If an absolute-positioned element gets added to the builder, and it has
+  //    a fixed-positioned descendant.
+  std::swap(oof_positioned_candidates_, *candidates);
 
-  descendant_candidates->ReserveCapacity(oof_positioned_candidates_.size());
-
-  DCHECK_GE(InlineSize(), LayoutUnit());
-  DCHECK_GE(BlockSize(), LayoutUnit());
-  PhysicalSize builder_physical_size = ToPhysicalSize(Size(), GetWritingMode());
-
-  for (NGOutOfFlowPositionedCandidate& candidate : oof_positioned_candidates_) {
-    PhysicalOffset child_offset = candidate.child_offset.ConvertToPhysical(
-        GetWritingMode(), Direction(), builder_physical_size, PhysicalSize());
-
-    NGStaticPosition builder_relative_position;
-    builder_relative_position.type = candidate.descendant.static_position.type;
-    builder_relative_position.offset =
-        child_offset + candidate.descendant.static_position.offset;
-
+  for (auto& candidate : *candidates) {
     // If we are inside the inline algorithm, (and creating a fragment for a
     // <span> or similar), we may add a child (e.g. an atomic-inline) which has
     // OOF descandants.
     //
     // This checks if the object creating this box will be the container for
     // the given descendant.
-    const LayoutInline* inline_container =
-        candidate.descendant.inline_container;
-    if (IsInlineContainerForDescendant(candidate.descendant, layout_object_)) {
-      inline_container = ToLayoutInline(layout_object_);
-    }
-    descendant_candidates->push_back(NGOutOfFlowPositionedDescendant(
-        candidate.descendant.node, builder_relative_position,
-        inline_container ? ToLayoutInline(inline_container->ContinuationRoot())
-                         : nullptr));
+    if (!candidate.inline_container &&
+        IsInlineContainerForNode(candidate.node, layout_object_))
+      candidate.inline_container = ToLayoutInline(layout_object_);
 
-    LogicalOffset container_offset =
-        builder_relative_position.offset.ConvertToLogical(
-            GetWritingMode(), Direction(), builder_physical_size,
-            PhysicalSize());
-    candidate.descendant.node.SaveStaticOffsetForLegacy(container_offset,
-                                                        current_container);
+    // Ensure that the inline_container is a continuation root.
+    if (candidate.inline_container) {
+      candidate.inline_container =
+          ToLayoutInline(candidate.inline_container->ContinuationRoot());
+    }
+
+    // Copy-back the static-position for legacy.
+    candidate.node.SaveStaticOffsetForLegacy(candidate.static_position.offset,
+                                             current_container);
   }
 
-  // Clear our current canidate list. This may get modified again if the
-  // current fragment is a containing block, and AddChild is called with a
-  // descendant from this list.
-  //
-  // The descendant may be a "position: absolute" which contains a "position:
-  // fixed" for example. (This fragment isn't the containing block for the
-  // fixed descendant).
-  oof_positioned_candidates_.Shrink(0);
+  DCHECK(oof_positioned_candidates_.IsEmpty());
 }
 
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index fd4bacd..a1ad393 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -16,7 +16,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_link.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/text/text_direction.h"
 #include "third_party/blink/renderer/platform/text/writing_mode.h"
@@ -104,11 +104,8 @@
   // Warning: Do not call unless necessary.
   LogicalOffset GetChildOffset(const LayoutObject* child) const;
 
-  // Builder has non-trivial out-of-flow descendant methods.
-  // These methods are building blocks for implementation of
-  // out-of-flow descendants by layout algorithms.
-  //
-  // They are intended to be used by layout algorithm like this:
+  // Builder has non-trivial OOF-positioned methods.
+  // They are intended to be used by a layout algorithm like this:
   //
   // Part 1: layout algorithm positions in-flow children.
   //   out-of-flow children, and out-of-flow descendants of fragments
@@ -124,7 +121,7 @@
   //
   // builder->SetSize
   //
-  // Part 2: Out-of-flow layout part positions out-of-flow descendants.
+  // Part 2: Out-of-flow layout part positions OOF-positioned nodes.
   //
   // NGOutOfFlowLayoutPart(container_style, builder).Run();
   //
@@ -138,26 +135,25 @@
       base::Optional<TextDirection> container_direction = base::nullopt);
 
   NGContainerFragmentBuilder& AddOutOfFlowDescendant(
-      const NGOutOfFlowPositionedDescendant& descendant);
+      const NGLogicalOutOfFlowPositionedNode& descendant);
 
-  void GetAndClearOutOfFlowDescendantCandidates(
-      Vector<NGOutOfFlowPositionedDescendant>* descendant_candidates,
-      const LayoutObject* container);
+  void SwapOutOfFlowPositionedCandidates(
+      Vector<NGLogicalOutOfFlowPositionedNode>* candidates,
+      const LayoutObject* current_container);
 
-  bool HasOutOfFlowDescendantCandidates() const {
+  bool HasOutOfFlowPositionedCandidates() const {
     return !oof_positioned_candidates_.IsEmpty();
   }
 
   // This method should only be used within the inline layout algorithm. It is
-  // used to convert all OOF descendant candidates to descendants.
+  // used to convert all OOF-positioned candidates to descendants.
   //
   // During the inline layout algorithm, we don't have enough information to
   // position OOF candidates yet, (as a containing box may be split over
   // multiple lines), instead we bubble all the descendants up to the parent
   // block layout algorithm, to perform the final OOF layout and positioning.
   void MoveOutOfFlowDescendantCandidatesToDescendants() {
-    GetAndClearOutOfFlowDescendantCandidates(&oof_positioned_descendants_,
-                                             nullptr);
+    SwapOutOfFlowPositionedCandidates(&oof_positioned_descendants_, nullptr);
   }
 
   NGContainerFragmentBuilder& SetIsSelfCollapsing() {
@@ -200,31 +196,6 @@
   friend class NGPhysicalContainerFragment;
   friend class NGLayoutResult;
 
-  // An out-of-flow positioned-candidate is a temporary data structure used
-  // within the NGBoxFragmentBuilder.
-  //
-  // A positioned-candidate can be:
-  // 1. A direct out-of-flow positioned child. The child_offset is (0,0).
-  // 2. A fragment containing an out-of-flow positioned-descendant. The
-  //    child_offset in this case is the containing fragment's offset.
-  //
-  // The child_offset is stored as a LogicalOffset as the physical offset
-  // cannot be computed until we know the current fragment's size.
-  //
-  // When returning the positioned-candidates (from
-  // GetAndClearOutOfFlowDescendantCandidates), the NGBoxFragmentBuilder will
-  // convert the positioned-candidate to a positioned-descendant using the
-  // physical size the fragment builder.
-  struct NGOutOfFlowPositionedCandidate {
-    NGOutOfFlowPositionedDescendant descendant;
-    LogicalOffset child_offset;  // Logical offset of child's top left vertex.
-
-    NGOutOfFlowPositionedCandidate(
-        const NGOutOfFlowPositionedDescendant& descendant,
-        LogicalOffset child_offset)
-        : descendant(descendant), child_offset(child_offset) {}
-  };
-
   NGContainerFragmentBuilder(NGLayoutInputNode node,
                              scoped_refptr<const ComputedStyle> style,
                              const NGConstraintSpace* space,
@@ -247,8 +218,8 @@
   NGMarginStrut end_margin_strut_;
   NGExclusionSpace exclusion_space_;
 
-  Vector<NGOutOfFlowPositionedCandidate> oof_positioned_candidates_;
-  Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants_;
+  Vector<NGLogicalOutOfFlowPositionedNode> oof_positioned_candidates_;
+  Vector<NGLogicalOutOfFlowPositionedNode> oof_positioned_descendants_;
 
   NGUnpositionedListMarker unpositioned_list_marker_;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index f6990c1..7df0c59 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -18,7 +18,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 
@@ -32,7 +32,7 @@
 }
 
 // When the containing block is a split inline, Legacy and NG use different
-// containers to place the OOF descendant:
+// containers to place the OOF-positioned nodes:
 //  - Legacy uses the anonymous block generated by inline.
 //  - NG uses the anonymous' parent block, that contains all the anonymous
 //    continuations.
@@ -78,8 +78,8 @@
                             container_builder) {}
 
 NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
-    bool contains_absolute,
-    bool contains_fixed,
+    bool is_absolute_container,
+    bool is_fixed_container,
     const ComputedStyle& container_style,
     const NGConstraintSpace& container_space,
     const NGBoxStrut& border_scrollbar,
@@ -87,14 +87,15 @@
     base::Optional<LogicalSize> initial_containing_block_fixed_size)
     : container_space_(container_space),
       container_builder_(container_builder),
-      contains_absolute_(contains_absolute),
-      contains_fixed_(contains_fixed) {
-  if (!container_builder->HasOutOfFlowDescendantCandidates() &&
+      writing_mode_(container_style.GetWritingMode()),
+      is_absolute_container_(is_absolute_container),
+      is_fixed_container_(is_fixed_container) {
+  if (!container_builder->HasOutOfFlowPositionedCandidates() &&
       !To<LayoutBlock>(container_builder_->GetLayoutObject())
            ->HasPositionedObjects())
     return;
 
-  default_containing_block_.style = &container_style;
+  default_containing_block_.direction = container_style.Direction();
   default_containing_block_.content_size_for_absolute =
       ShrinkAvailableSize(container_builder_->Size(), border_scrollbar);
   default_containing_block_.content_size_for_fixed =
@@ -104,42 +105,36 @@
 
   default_containing_block_.container_offset = LogicalOffset(
       border_scrollbar.inline_start, border_scrollbar.block_start);
-
-  NGPhysicalBoxStrut physical_border_scrollbar =
-      border_scrollbar.ConvertToPhysical(container_style.GetWritingMode(),
-                                         container_style.Direction());
-  default_containing_block_.physical_container_offset = PhysicalOffset(
-      physical_border_scrollbar.left, physical_border_scrollbar.top);
 }
 
 void NGOutOfFlowLayoutPart::Run(const LayoutBox* only_layout) {
-  Vector<NGOutOfFlowPositionedDescendant> descendant_candidates;
+  Vector<NGLogicalOutOfFlowPositionedNode> candidates;
   const LayoutObject* current_container = container_builder_->GetLayoutObject();
 
-  container_builder_->GetAndClearOutOfFlowDescendantCandidates(
-      &descendant_candidates, current_container);
+  container_builder_->SwapOutOfFlowPositionedCandidates(&candidates,
+                                                        current_container);
 
-  if (descendant_candidates.IsEmpty() &&
+  if (candidates.IsEmpty() &&
       !To<LayoutBlock>(current_container)->HasPositionedObjects())
     return;
 
   // Special case: containing block is a split inline.
   // If current container was generated by a split inline, do not position
-  // descendants inside this container. Let its non-anonymous parent handle it.
-  // Only parent has geometry information needed to compute containing block
-  // geometry.
+  // OOF-positioned nodes inside this container. Let its non-anonymous parent
+  // handle it. Only the parent has geometry information needed to compute
+  // containing block geometry.
   // See "Special case: oof css container" comment for detailed description.
-  if (descendant_candidates.size() > 0 && current_container && !only_layout &&
+  if (candidates.size() > 0 && current_container && !only_layout &&
       IsAnonymousContainer(current_container)) {
     const LayoutInline* absolute_containing_block =
-        contains_absolute_ ? GetOOFContainingBlockFromAnonymous(
-                                 current_container, EPosition::kAbsolute)
-                           : nullptr;
+        is_absolute_container_ ? GetOOFContainingBlockFromAnonymous(
+                                     current_container, EPosition::kAbsolute)
+                               : nullptr;
     const LayoutInline* fixed_containing_block =
-        contains_fixed_ ? GetOOFContainingBlockFromAnonymous(current_container,
-                                                             EPosition::kFixed)
-                        : nullptr;
-    for (auto& candidate : descendant_candidates) {
+        is_fixed_container_ ? GetOOFContainingBlockFromAnonymous(
+                                  current_container, EPosition::kFixed)
+                            : nullptr;
+    for (auto& candidate : candidates) {
       if (absolute_containing_block &&
           absolute_containing_block->CanContainOutOfFlowPositionedElement(
               candidate.node.Style().GetPosition())) {
@@ -155,30 +150,42 @@
   }
 
   HashSet<const LayoutObject*> placed_objects;
-  LayoutDescendantCandidates(&descendant_candidates, only_layout,
-                             &placed_objects);
+  LayoutCandidates(&candidates, only_layout, &placed_objects);
 
   if (only_layout)
     return;
 
   // If we're in a block fragmentation context, we've already ruled out the
   // possibility of having legacy objects in here. The code below would pick up
-  // every OOF descendant not in placed_objects, and treat them as a legacy
+  // every OOF candidate not in placed_objects, and treat them as a legacy
   // object (even if they aren't one), while in fact it could be an NG object
   // that we have finished laying out in an earlier fragmentainer. Just bail.
   if (container_space_.HasBlockFragmentation())
     return;
 
-  while (SweepLegacyDescendants(&placed_objects)) {
-    container_builder_->GetAndClearOutOfFlowDescendantCandidates(
-        &descendant_candidates, current_container);
+  wtf_size_t prev_placed_objects_size = placed_objects.size();
+  while (SweepLegacyCandidates(&placed_objects)) {
+    container_builder_->SwapOutOfFlowPositionedCandidates(&candidates,
+                                                          current_container);
 
     // We must have at least one new candidate, otherwise we shouldn't have
     // entered this branch.
-    DCHECK_GT(descendant_candidates.size(), 0u);
+    DCHECK_GT(candidates.size(), 0u);
 
-    LayoutDescendantCandidates(&descendant_candidates, only_layout,
-                               &placed_objects);
+    LayoutCandidates(&candidates, only_layout, &placed_objects);
+
+    // Legacy currently has a bug where an OOF-positioned node is present
+    // within the current node's |LayoutBlock::PositionedObjects|, however it
+    // is not the containing-block for this node.
+    //
+    // This results in |LayoutDescendantCandidates| never performing layout on
+    // any additional objects.
+    wtf_size_t placed_objects_size = placed_objects.size();
+    if (prev_placed_objects_size == placed_objects_size) {
+      NOTREACHED();
+      break;
+    }
+    prev_placed_objects_size = placed_objects_size;
   }
 }
 
@@ -191,7 +198,7 @@
 //   </div>
 // </div>
 // Returns false if no new candidates were found.
-bool NGOutOfFlowLayoutPart::SweepLegacyDescendants(
+bool NGOutOfFlowLayoutPart::SweepLegacyCandidates(
     HashSet<const LayoutObject*>* placed_objects) {
   const auto* container_block =
       DynamicTo<LayoutBlock>(container_builder_->GetLayoutObject());
@@ -217,22 +224,25 @@
     if (layout_box->Parent()->IsFlexibleBox()) {
       LayoutFlexibleBox* parent = ToLayoutFlexibleBox(layout_box->Parent());
       if (parent->SetStaticPositionForPositionedLayout(*layout_box)) {
-        NGOutOfFlowPositionedDescendant descendant((NGBlockNode(layout_box)),
-                                                   NGStaticPosition());
-        LayoutDescendant(descendant, /* only_layout */ nullptr);
+        NGLogicalOutOfFlowPositionedNode candidate((NGBlockNode(layout_box)),
+                                                   NGLogicalStaticPosition());
+        LayoutCandidate(candidate, /* only_layout */ nullptr);
         parent->SetStaticPositionForPositionedLayout(*layout_box);
       }
     }
 
-    NGStaticPosition static_position =
-        LayoutBoxUtils::ComputeStaticPositionFromLegacy(*layout_box,
-                                                        container_builder_);
+    NGLogicalStaticPosition static_position =
+        LayoutBoxUtils::ComputeStaticPositionFromLegacy(
+            *layout_box,
+            container_builder_->Borders() + container_builder_->Scrollbar(),
+            container_builder_);
 
     const LayoutObject* css_container = layout_box->Container();
     if (IsAnonymousContainer(css_container)) {
       css_container = GetOOFContainingBlockFromAnonymous(
           css_container, layout_box->Style()->GetPosition());
     }
+
     container_builder_->AddOutOfFlowLegacyCandidate(
         NGBlockNode(layout_box), static_position,
         ToLayoutInlineOrNull(css_container));
@@ -242,9 +252,9 @@
 
 const NGOutOfFlowLayoutPart::ContainingBlockInfo&
 NGOutOfFlowLayoutPart::GetContainingBlockInfo(
-    const NGOutOfFlowPositionedDescendant& descendant) const {
-  if (descendant.inline_container) {
-    const auto it = containing_blocks_map_.find(descendant.inline_container);
+    const NGLogicalOutOfFlowPositionedNode& candidate) const {
+  if (candidate.inline_container) {
+    const auto it = containing_blocks_map_.find(candidate.inline_container);
     DCHECK(it != containing_blocks_map_.end());
     return it->value;
   }
@@ -252,14 +262,14 @@
 }
 
 void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks(
-    const Vector<NGOutOfFlowPositionedDescendant>& descendants) {
+    const Vector<NGLogicalOutOfFlowPositionedNode>& candidates) {
   NGBoxFragmentBuilder::InlineContainingBlockMap inline_container_fragments;
 
-  for (auto& descendant : descendants) {
-    if (descendant.inline_container &&
-        !inline_container_fragments.Contains(descendant.inline_container)) {
+  for (auto& candidate : candidates) {
+    if (candidate.inline_container &&
+        !inline_container_fragments.Contains(candidate.inline_container)) {
       NGBoxFragmentBuilder::InlineContainingBlockGeometry inline_geometry = {};
-      inline_container_fragments.insert(descendant.inline_container,
+      inline_container_fragments.insert(candidate.inline_container,
                                         inline_geometry);
     }
   }
@@ -268,15 +278,13 @@
       &inline_container_fragments);
   LogicalSize container_builder_size = container_builder_->Size();
   PhysicalSize container_builder_physical_size =
-      ToPhysicalSize(container_builder_size,
-                     default_containing_block_.style->GetWritingMode());
+      ToPhysicalSize(container_builder_size, writing_mode_);
   // Translate start/end fragments into ContainingBlockInfo.
   for (auto& block_info : inline_container_fragments) {
     // Variables needed to describe ContainingBlockInfo
     const ComputedStyle* inline_cb_style = block_info.key->Style();
     LogicalSize inline_cb_size;
     LogicalOffset container_offset;
-    PhysicalOffset physical_container_offset;
 
     DCHECK(block_info.value.has_value());
     DCHECK(inline_cb_style);
@@ -334,10 +342,7 @@
     //
     // Note in cases [2a, 2b] we don't allow a "negative" containing block size,
     // we clamp negative sizes to zero.
-    WritingMode container_writing_mode =
-        default_containing_block_.style->GetWritingMode();
-    TextDirection container_direction =
-        default_containing_block_.style->Direction();
+    TextDirection container_direction = default_containing_block_.direction;
 
     bool is_same_direction =
         container_direction == inline_cb_style->Direction();
@@ -346,8 +351,8 @@
     const PhysicalRect& start_rect =
         block_info.value->start_fragment_union_rect;
     LogicalOffset start_offset = start_rect.offset.ConvertToLogical(
-        container_writing_mode, container_direction,
-        container_builder_physical_size, start_rect.size);
+        writing_mode_, container_direction, container_builder_physical_size,
+        start_rect.size);
 
     // Make sure we add the inline borders, we don't need to do this in the
     // inline direction if the blocks are in opposite directions.
@@ -358,11 +363,11 @@
     // Step 2 - determine the end_offset.
     const PhysicalRect& end_rect = block_info.value->end_fragment_union_rect;
     LogicalOffset end_offset = end_rect.offset.ConvertToLogical(
-        container_writing_mode, container_direction,
-        container_builder_physical_size, end_rect.size);
+        writing_mode_, container_direction, container_builder_physical_size,
+        end_rect.size);
 
     // Add in the size of the fragment to get the logical end of the fragment.
-    end_offset += end_rect.size.ConvertToLogical(container_writing_mode);
+    end_offset += end_rect.size.ConvertToLogical(writing_mode_);
 
     // Make sure we subtract the inline borders, we don't need to do this in the
     // inline direction if the blocks are in opposite directions.
@@ -383,30 +388,27 @@
     DCHECK_GE(inline_cb_size.inline_size, LayoutUnit());
     DCHECK_GE(inline_cb_size.block_size, LayoutUnit());
 
-    // Determine the container offsets.
+    // Set the container padding-box offset.
     container_offset = start_offset;
-    physical_container_offset = container_offset.ConvertToPhysical(
-        container_writing_mode, container_direction,
-        container_builder_physical_size,
-        ToPhysicalSize(inline_cb_size, container_writing_mode));
+
     containing_blocks_map_.insert(
         block_info.key,
-        ContainingBlockInfo{inline_cb_style, inline_cb_size, inline_cb_size,
-                            container_offset, physical_container_offset});
+        ContainingBlockInfo{inline_cb_style->Direction(), inline_cb_size,
+                            inline_cb_size, container_offset});
   }
 }
 
-void NGOutOfFlowLayoutPart::LayoutDescendantCandidates(
-    Vector<NGOutOfFlowPositionedDescendant>* descendant_candidates,
+void NGOutOfFlowLayoutPart::LayoutCandidates(
+    Vector<NGLogicalOutOfFlowPositionedNode>* candidates,
     const LayoutBox* only_layout,
     HashSet<const LayoutObject*>* placed_objects) {
-  while (descendant_candidates->size() > 0) {
-    ComputeInlineContainingBlocks(*descendant_candidates);
-    for (auto& candidate : *descendant_candidates) {
-      if (IsContainingBlockForDescendant(candidate) &&
+  while (candidates->size() > 0) {
+    ComputeInlineContainingBlocks(*candidates);
+    for (auto& candidate : *candidates) {
+      if (IsContainingBlockForCandidate(candidate) &&
           (!only_layout || candidate.node.GetLayoutBox() == only_layout)) {
         scoped_refptr<const NGLayoutResult> result =
-            LayoutDescendant(candidate, only_layout);
+            LayoutCandidate(candidate, only_layout);
         container_builder_->AddChild(result->PhysicalFragment(),
                                      result->OutOfFlowPositionedOffset(),
                                      candidate.inline_container);
@@ -417,18 +419,18 @@
         container_builder_->AddOutOfFlowDescendant(candidate);
       }
     }
-    // Sweep any descendants that might have been added.
+    // Sweep any candidates that might have been added.
     // This happens when an absolute container has a fixed child.
-    descendant_candidates->Shrink(0);
-    container_builder_->GetAndClearOutOfFlowDescendantCandidates(
-        descendant_candidates, container_builder_->GetLayoutObject());
+    candidates->Shrink(0);
+    container_builder_->SwapOutOfFlowPositionedCandidates(
+        candidates, container_builder_->GetLayoutObject());
   }
 }
 
-scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
-    const NGOutOfFlowPositionedDescendant& descendant,
+scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutCandidate(
+    const NGLogicalOutOfFlowPositionedNode& candidate,
     const LayoutBox* only_layout) {
-  NGBlockNode node = descendant.node;
+  NGBlockNode node = candidate.node;
 
   // "NGOutOfFlowLayoutPart container is ContainingBlock" invariant cannot
   // be enforced for tables. Tables are special, in that the ContainingBlock is
@@ -438,47 +440,52 @@
           node.GetLayoutBox()->ContainingBlock()) ||
          node.GetLayoutBox()->ContainingBlock()->IsTable());
 
-  const ContainingBlockInfo& container_info =
-      GetContainingBlockInfo(descendant);
-  const ComputedStyle& container_style = *container_info.style;
-  const ComputedStyle& descendant_style = node.Style();
+  const ContainingBlockInfo& container_info = GetContainingBlockInfo(candidate);
 
-  WritingMode container_writing_mode = container_style.GetWritingMode();
-  WritingMode descendant_writing_mode = descendant_style.GetWritingMode();
+  const TextDirection container_direction = container_info.direction;
+  const TextDirection default_direction = default_containing_block_.direction;
+
+  const ComputedStyle& candidate_style = node.Style();
+  const WritingMode candidate_writing_mode = candidate_style.GetWritingMode();
 
   LogicalSize container_content_size =
-      container_info.ContentSize(descendant_style.GetPosition());
-  LogicalSize container_content_size_in_child_writing_mode =
-      ToPhysicalSize(container_content_size, container_writing_mode)
-          .ConvertToLogical(descendant_writing_mode);
+      container_info.ContentSize(candidate_style.GetPosition());
+  PhysicalSize container_physical_content_size =
+      ToPhysicalSize(container_content_size, writing_mode_);
+  LogicalSize container_content_size_in_candidate_writing_mode =
+      container_physical_content_size.ConvertToLogical(candidate_writing_mode);
 
   // Determine if we need to actually run the full OOF-positioned sizing, and
   // positioning algorithm.
   if (scoped_refptr<const NGLayoutResult> cached_result =
           node.CachedLayoutResultForOutOfFlowPositioned(
-              container_content_size_in_child_writing_mode))
+              container_content_size_in_candidate_writing_mode))
     return cached_result;
 
   // Adjust the |static_position| (which is currently relative to the default
   // container's border-box). ng_absolute_utils expects the static position to
   // be relative to the container's padding-box.
-  NGStaticPosition static_position(descendant.static_position);
-  static_position.offset -= container_info.physical_container_offset;
+  NGLogicalStaticPosition static_position = candidate.static_position;
+  static_position.offset -= container_info.container_offset;
+
+  NGPhysicalStaticPosition physical_static_position =
+      static_position.ConvertToPhysical(writing_mode_, default_direction,
+                                        container_physical_content_size);
 
   // Need a constraint space to resolve offsets.
-  NGConstraintSpace descendant_constraint_space =
-      NGConstraintSpaceBuilder(container_writing_mode, descendant_writing_mode,
+  NGConstraintSpace candidate_constraint_space =
+      NGConstraintSpaceBuilder(writing_mode_, candidate_writing_mode,
                                /* is_new_fc */ true)
-          .SetTextDirection(descendant_style.Direction())
+          .SetTextDirection(candidate_style.Direction())
           .SetAvailableSize(container_content_size)
           .SetPercentageResolutionSize(container_content_size)
           .ToConstraintSpace();
 
   NGBoxStrut border_padding =
-      ComputeBorders(descendant_constraint_space, node) +
-      ComputePadding(descendant_constraint_space, descendant_style);
+      ComputeBorders(candidate_constraint_space, node) +
+      ComputePadding(candidate_constraint_space, candidate_style);
 
-  // The |block_estimate| is wrt. the descendant's writing mode.
+  // The |block_estimate| is wrt. the candidate's writing mode.
   base::Optional<LayoutUnit> block_estimate;
   base::Optional<MinMaxSize> min_max_size;
   scoped_refptr<const NGLayoutResult> layout_result = nullptr;
@@ -494,42 +501,42 @@
   bool is_replaced = node.IsReplaced();
   bool should_be_considered_as_replaced = node.ShouldBeConsideredAsReplaced();
 
-  if (AbsoluteNeedsChildInlineSize(descendant_style) ||
-      NeedMinMaxSize(descendant_style) || should_be_considered_as_replaced) {
+  if (AbsoluteNeedsChildInlineSize(candidate_style) ||
+      NeedMinMaxSize(candidate_style) || should_be_considered_as_replaced) {
     // This is a new formatting context, so whatever happened on the outside
     // doesn't concern us.
     MinMaxSizeInput input(container_content_size.block_size);
     min_max_size = ComputeMinAndMaxContentSizeForOutOfFlow(
-        descendant_constraint_space, node, border_padding, input);
+        candidate_constraint_space, node, border_padding, input);
   }
 
   base::Optional<LogicalSize> replaced_size;
   if (is_replaced) {
     replaced_size =
-        ComputeReplacedSize(node, descendant_constraint_space, min_max_size);
+        ComputeReplacedSize(node, candidate_constraint_space, min_max_size);
   } else if (should_be_considered_as_replaced) {
-    replaced_size = LogicalSize{
-        min_max_size->ShrinkToFit(
-            descendant_constraint_space.AvailableSize().inline_size),
-        kIndefiniteSize};
+    replaced_size =
+        LogicalSize{min_max_size->ShrinkToFit(
+                        candidate_constraint_space.AvailableSize().inline_size),
+                    kIndefiniteSize};
   }
   NGAbsolutePhysicalPosition node_position =
       ComputePartialAbsoluteWithChildInlineSize(
-          descendant_constraint_space, descendant_style, border_padding,
-          static_position, min_max_size, replaced_size, container_writing_mode,
-          container_style.Direction());
+          candidate_constraint_space, candidate_style, border_padding,
+          physical_static_position, min_max_size, replaced_size, writing_mode_,
+          container_direction);
 
   // |should_be_considered_as_replaced| sets the inline-size.
   // It does not set the block-size. This is a compatibility quirk.
   if (!is_replaced && should_be_considered_as_replaced)
     replaced_size.reset();
 
-  if (AbsoluteNeedsChildBlockSize(descendant_style)) {
+  if (AbsoluteNeedsChildBlockSize(candidate_style)) {
     layout_result =
-        GenerateFragment(node, container_content_size_in_child_writing_mode,
+        GenerateFragment(node, container_content_size_in_candidate_writing_mode,
                          block_estimate, node_position);
 
-    NGFragment fragment(descendant_writing_mode,
+    NGFragment fragment(candidate_writing_mode,
                         layout_result->PhysicalFragment());
 
     block_estimate = fragment.BlockSize();
@@ -538,12 +545,12 @@
   // Calculate the offsets.
 
   ComputeFullAbsoluteWithChildBlockSize(
-      descendant_constraint_space, descendant_style, border_padding,
-      static_position, block_estimate, replaced_size, container_writing_mode,
-      container_style.Direction(), &node_position);
+      candidate_constraint_space, candidate_style, border_padding,
+      physical_static_position, block_estimate, replaced_size, writing_mode_,
+      container_direction, &node_position);
 
-  NGBoxStrut inset = node_position.inset.ConvertToLogical(
-      container_writing_mode, default_containing_block_.style->Direction());
+  NGBoxStrut inset =
+      node_position.inset.ConvertToLogical(writing_mode_, default_direction);
 
   // |inset| is relative to the container's padding-box. Convert this to being
   // relative to the default container's border-box.
@@ -609,9 +616,9 @@
   // Skip this step if we produced a fragment when estimating the block-size.
   if (!layout_result) {
     block_estimate =
-        node_position.size.ConvertToLogical(descendant_writing_mode).block_size;
+        node_position.size.ConvertToLogical(candidate_writing_mode).block_size;
     layout_result =
-        GenerateFragment(node, container_content_size_in_child_writing_mode,
+        GenerateFragment(node, container_content_size_in_candidate_writing_mode,
                          block_estimate, node_position);
   }
 
@@ -632,7 +639,7 @@
       *node.GetLayoutBox(), layout_result->PhysicalFragment().Size().height);
   if (y.has_value()) {
     DCHECK(!container_space_.HasBlockFragmentation());
-    if (IsHorizontalWritingMode(container_writing_mode))
+    if (IsHorizontalWritingMode(writing_mode_))
       offset.block_offset = *y;
     else
       offset.inline_offset = *y;
@@ -642,44 +649,45 @@
   return layout_result;
 }
 
-bool NGOutOfFlowLayoutPart::IsContainingBlockForDescendant(
-    const NGOutOfFlowPositionedDescendant& descendant) {
-  EPosition position = descendant.node.Style().GetPosition();
+bool NGOutOfFlowLayoutPart::IsContainingBlockForCandidate(
+    const NGLogicalOutOfFlowPositionedNode& candidate) {
+  EPosition position = candidate.node.Style().GetPosition();
 
-  // Descendants whose containing block is inline are always positioned
+  // Candidates whose containing block is inline are always positioned
   // inside closest parent block flow.
-  if (descendant.inline_container) {
+  if (candidate.inline_container) {
     DCHECK(
-        descendant.node.Style().GetPosition() == EPosition::kAbsolute &&
-            descendant.inline_container->CanContainAbsolutePositionObjects() ||
-        (descendant.node.Style().GetPosition() == EPosition::kFixed &&
-         descendant.inline_container->CanContainFixedPositionObjects()));
+        candidate.node.Style().GetPosition() == EPosition::kAbsolute &&
+            candidate.inline_container->CanContainAbsolutePositionObjects() ||
+        (candidate.node.Style().GetPosition() == EPosition::kFixed &&
+         candidate.inline_container->CanContainFixedPositionObjects()));
     return container_builder_->GetLayoutObject() ==
-           descendant.node.GetLayoutBox()->ContainingBlock();
+           candidate.node.GetLayoutBox()->ContainingBlock();
   }
-  return (contains_absolute_ && position == EPosition::kAbsolute) ||
-         (contains_fixed_ && position == EPosition::kFixed);
+  return (is_absolute_container_ && position == EPosition::kAbsolute) ||
+         (is_fixed_container_ && position == EPosition::kFixed);
 }
 
 // The fragment is generated in one of these two scenarios:
-// 1. To estimate descendant's block size, in this case block_size is
+// 1. To estimate candidate's block size, in this case block_size is
 //    container's available size.
 // 2. To compute final fragment, when block size is known from the absolute
 //    position calculation.
 scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment(
-    NGBlockNode descendant,
-    const LogicalSize& container_content_size_in_child_writing_mode,
+    NGBlockNode node,
+    const LogicalSize& container_content_size_in_candidate_writing_mode,
     const base::Optional<LayoutUnit>& block_estimate,
     const NGAbsolutePhysicalPosition& node_position) {
-  // As the |block_estimate| is always in the descendant's writing mode, we
-  // build the constraint space in the descendant's writing mode.
-  WritingMode writing_mode = descendant.Style().GetWritingMode();
+  // As the |block_estimate| is always in the node's writing mode, we
+  // build the constraint space in the node's writing mode.
+  WritingMode writing_mode = node.Style().GetWritingMode();
 
   LayoutUnit inline_size =
       node_position.size.ConvertToLogical(writing_mode).inline_size;
   LayoutUnit block_size =
-      block_estimate ? *block_estimate
-                     : container_content_size_in_child_writing_mode.block_size;
+      block_estimate
+          ? *block_estimate
+          : container_content_size_in_candidate_writing_mode.block_size;
 
   LogicalSize available_size(inline_size, block_size);
 
@@ -687,14 +695,15 @@
   NGConstraintSpaceBuilder builder(writing_mode, writing_mode,
                                    /* is_new_fc */ true);
   builder.SetAvailableSize(available_size)
-      .SetTextDirection(descendant.Style().Direction())
-      .SetPercentageResolutionSize(container_content_size_in_child_writing_mode)
+      .SetTextDirection(node.Style().Direction())
+      .SetPercentageResolutionSize(
+          container_content_size_in_candidate_writing_mode)
       .SetIsFixedSizeInline(true);
   if (block_estimate)
     builder.SetIsFixedSizeBlock(true);
   NGConstraintSpace space = builder.ToConstraintSpace();
 
-  return descendant.Layout(space);
+  return node.Layout(space);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 87f0df7..9f5f90e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -23,7 +23,7 @@
 class NGBoxFragmentBuilder;
 class NGConstraintSpace;
 class NGLayoutResult;
-struct NGOutOfFlowPositionedDescendant;
+struct NGLogicalOutOfFlowPositionedNode;
 
 // Helper class for positioning of out-of-flow blocks.
 // It should be used together with NGBoxFragmentBuilder.
@@ -45,8 +45,8 @@
   // (e.g. a relatively positioned inline instead), the containing block here is
   // the containing block of said non-block.
   NGOutOfFlowLayoutPart(
-      bool contains_absolute,
-      bool contains_fixed,
+      bool is_absolute_container,
+      bool is_fixed_container,
       const ComputedStyle& container_style,
       const NGConstraintSpace& container_space,
       const NGBoxStrut& border_scrollbar,
@@ -77,17 +77,15 @@
     STACK_ALLOCATED();
 
    public:
-    // Containing block style.
-    const ComputedStyle* style;
+    // The direction of the container.
+    TextDirection direction;
     // Logical in containing block coordinates.
     LogicalSize content_size_for_absolute;
     // Content size for fixed is different for the ICB.
     LogicalSize content_size_for_fixed;
 
-    // Offsets (both logical and physical) of the container's padding-box, wrt.
-    // the default container's border-box.
+    // Offset of the container's padding-box.
     LogicalOffset container_offset;
-    PhysicalOffset physical_container_offset;
 
     LogicalSize ContentSize(EPosition position) const {
       return position == EPosition::kAbsolute ? content_size_for_absolute
@@ -95,25 +93,23 @@
     }
   };
 
-  bool SweepLegacyDescendants(HashSet<const LayoutObject*>* placed_objects);
+  bool SweepLegacyCandidates(HashSet<const LayoutObject*>* placed_objects);
 
   const ContainingBlockInfo& GetContainingBlockInfo(
-      const NGOutOfFlowPositionedDescendant&) const;
+      const NGLogicalOutOfFlowPositionedNode&) const;
 
   void ComputeInlineContainingBlocks(
-      const Vector<NGOutOfFlowPositionedDescendant>&);
+      const Vector<NGLogicalOutOfFlowPositionedNode>&);
 
-  void LayoutDescendantCandidates(
-      Vector<NGOutOfFlowPositionedDescendant>* descendant_candidates,
-      const LayoutBox* only_layout,
-      HashSet<const LayoutObject*>* placed_objects);
+  void LayoutCandidates(Vector<NGLogicalOutOfFlowPositionedNode>* candidates,
+                        const LayoutBox* only_layout,
+                        HashSet<const LayoutObject*>* placed_objects);
 
-  scoped_refptr<const NGLayoutResult> LayoutDescendant(
-      const NGOutOfFlowPositionedDescendant&,
+  scoped_refptr<const NGLayoutResult> LayoutCandidate(
+      const NGLogicalOutOfFlowPositionedNode&,
       const LayoutBox* only_layout);
 
-  bool IsContainingBlockForDescendant(
-      const NGOutOfFlowPositionedDescendant& descendant);
+  bool IsContainingBlockForCandidate(const NGLogicalOutOfFlowPositionedNode&);
 
   scoped_refptr<const NGLayoutResult> GenerateFragment(
       NGBlockNode node,
@@ -125,8 +121,9 @@
   NGBoxFragmentBuilder* container_builder_;
   ContainingBlockInfo default_containing_block_;
   HashMap<const LayoutObject*, ContainingBlockInfo> containing_blocks_map_;
-  bool contains_absolute_;
-  bool contains_fixed_;
+  const WritingMode writing_mode_;
+  bool is_absolute_container_;
+  bool is_fixed_container_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h
deleted file mode 100644
index cab4bdc..0000000
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h
+++ /dev/null
@@ -1,46 +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 NGOutOfFlowPositionedDescendant_h
-#define NGOutOfFlowPositionedDescendant_h
-
-#include "third_party/blink/renderer/core/core_export.h"
-
-#include "third_party/blink/renderer/core/layout/layout_inline.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
-
-namespace blink {
-
-// An out-of-flow positioned-descendant is an element with the style "postion:
-// absolute" or "position: fixed" which hasn't been bubbled up to its
-// containing block yet, e.g. an element with "position: relative". As soon as
-// a descendant reaches its containing block, it gets placed, and doesn't bubble
-// up the tree.
-//
-// This needs its static position [1] to be placed correcting in its containing
-// block.
-//
-// [1] https://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-width
-struct CORE_EXPORT NGOutOfFlowPositionedDescendant {
-  NGBlockNode node;
-  NGStaticPosition static_position;
-  // Continuation root of the optional inline container.
-  const LayoutInline* inline_container;
-
-  NGOutOfFlowPositionedDescendant(
-      NGBlockNode node,
-      NGStaticPosition static_position,
-      const LayoutInline* inline_container = nullptr)
-      : node(node),
-        static_position(static_position),
-        inline_container(inline_container) {
-    DCHECK(!inline_container ||
-           inline_container == inline_container->ContinuationRoot());
-  }
-};
-
-}  // namespace blink
-
-#endif  // NGOutOfFlowPositionedDescendant_h
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
new file mode 100644
index 0000000..1037e23
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
@@ -0,0 +1,72 @@
+// Copyright 2019 The Chromium 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_LAYOUT_NG_NG_OUT_OF_FLOW_POSITIONED_NODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_OUT_OF_FLOW_POSITIONED_NODE_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+
+#include "third_party/blink/renderer/core/layout/layout_inline.h"
+#include "third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
+
+namespace blink {
+
+// A physical out-of-flow positioned-node is an element with the style
+// "postion: absolute" or "position: fixed" which hasn't been bubbled up to its
+// containing block yet, (e.g. an element with "position: relative"). As soon
+// as a positioned-node reaches its containing block, it gets placed, and
+// doesn't bubble further up the tree.
+//
+// This needs its static position [1] to be placed correctly in its containing
+// block.
+//
+// This is struct is allowed to be stored/persisted.
+//
+// [1] https://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-width
+struct CORE_EXPORT NGPhysicalOutOfFlowPositionedNode {
+  NGBlockNode node;
+  NGPhysicalStaticPosition static_position;
+  // Continuation root of the optional inline container.
+  const LayoutInline* inline_container;
+
+  NGPhysicalOutOfFlowPositionedNode(
+      NGBlockNode node,
+      NGPhysicalStaticPosition static_position,
+      const LayoutInline* inline_container = nullptr)
+      : node(node),
+        static_position(static_position),
+        inline_container(inline_container) {
+    DCHECK(!inline_container ||
+           inline_container == inline_container->ContinuationRoot());
+  }
+};
+
+// The logical version of above. It is used within a an algorithm pass (within
+// an |NGContainerFragmentBuilder|), and its logical coordinate system is wrt.
+// the container builder's writing-mode.
+//
+// It is *only* used within an algorithm pass, (it is temporary, and should not
+// be stored/persisted).
+struct NGLogicalOutOfFlowPositionedNode {
+  NGBlockNode node;
+  NGLogicalStaticPosition static_position;
+  // Continuation root of the optional inline container.
+  const LayoutInline* inline_container;
+
+  NGLogicalOutOfFlowPositionedNode(
+      NGBlockNode node,
+      NGLogicalStaticPosition static_position,
+      const LayoutInline* inline_container = nullptr)
+      : node(node),
+        static_position(static_position),
+        inline_container(inline_container) {
+    DCHECK(!inline_container ||
+           inline_container == inline_container->ContinuationRoot());
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_OUT_OF_FLOW_POSITIONED_NODE_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
index 910c39d..7527c24 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
@@ -18,7 +18,7 @@
 
 struct SameSizeAsNGPhysicalContainerFragment : NGPhysicalFragment {
   void* break_token;
-  std::unique_ptr<Vector<NGOutOfFlowPositionedDescendant>>
+  std::unique_ptr<Vector<NGPhysicalOutOfFlowPositionedNode>>
       oof_positioned_descendants_;
   void* pointer;
   wtf_size_t size;
@@ -41,8 +41,7 @@
       oof_positioned_descendants_(
           builder->oof_positioned_descendants_.IsEmpty()
               ? nullptr
-              : new Vector<NGOutOfFlowPositionedDescendant>(
-                    std::move(builder->oof_positioned_descendants_))),
+              : new Vector<NGPhysicalOutOfFlowPositionedNode>()),
       buffer_(buffer),
       num_children_(builder->children_.size()) {
   has_floating_descendants_ = builder->has_floating_descendants_;
@@ -51,6 +50,19 @@
       builder->may_have_descendant_above_block_start_;
   depends_on_percentage_block_size_ = DependsOnPercentageBlockSize(*builder);
 
+  PhysicalSize size = Size();
+  if (oof_positioned_descendants_) {
+    oof_positioned_descendants_->ReserveCapacity(
+        builder->oof_positioned_descendants_.size());
+    for (const auto& descendant : builder->oof_positioned_descendants_) {
+      oof_positioned_descendants_->emplace_back(
+          descendant.node,
+          descendant.static_position.ConvertToPhysical(
+              builder->Style().GetWritingMode(), builder->Direction(), size),
+          descendant.inline_container);
+    }
+  }
+
   // Because flexible arrays need to be the last member in a class, we need to
   // have the buffer passed as a constructor argument and have the actual
   // storage be part of the subclass.
@@ -59,7 +71,7 @@
     buffer[i].fragment = child.fragment.get();
     buffer[i].fragment->AddRef();
     buffer[i].offset = child.offset.ConvertToPhysical(
-        block_or_line_writing_mode, builder->Direction(), Size(),
+        block_or_line_writing_mode, builder->Direction(), size,
         child.fragment->Size());
     ++i;
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
index de824de..9feddca9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
@@ -17,7 +17,7 @@
 namespace blink {
 
 class NGContainerFragmentBuilder;
-struct NGOutOfFlowPositionedDescendant;
+struct NGPhysicalOutOfFlowPositionedNode;
 enum class NGOutlineType;
 
 class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment {
@@ -123,10 +123,10 @@
     return oof_positioned_descendants_.get();
   }
 
-  base::span<NGOutOfFlowPositionedDescendant> OutOfFlowPositionedDescendants()
+  base::span<NGPhysicalOutOfFlowPositionedNode> OutOfFlowPositionedDescendants()
       const {
     if (!HasOutOfFlowPositionedDescendants())
-      return base::span<NGOutOfFlowPositionedDescendant>();
+      return base::span<NGPhysicalOutOfFlowPositionedNode>();
     return {oof_positioned_descendants_->data(),
             oof_positioned_descendants_->size()};
   }
@@ -154,7 +154,7 @@
   static bool DependsOnPercentageBlockSize(const NGContainerFragmentBuilder&);
 
   scoped_refptr<NGBreakToken> break_token_;
-  const std::unique_ptr<Vector<NGOutOfFlowPositionedDescendant>>
+  const std::unique_ptr<Vector<NGPhysicalOutOfFlowPositionedNode>>
       oof_positioned_descendants_;
 
   // Because flexible arrays need to be the last member in a class, the actual
diff --git a/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
index cb6babb..ce4af47 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
@@ -84,6 +84,11 @@
 }
 
 scoped_refptr<const NGLayoutResult> NGSimplifiedLayoutAlgorithm::Layout() {
+  // Since simplified layout's |Layout()| function deals with laying out
+  // children, we can early out if we are display-locked.
+  if (Node().LayoutBlockedByDisplayLock(DisplayLockContext::kChildren))
+    return container_builder_.ToBoxFragment();
+
   const auto previous_child_fragments =
       To<NGPhysicalBoxFragment>(previous_result_.PhysicalFragment()).Children();
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
index 1acb46c..f494825 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
@@ -140,6 +140,7 @@
   // element, but PaintLayer::HitTestLayer assumes it has not been.
   HitTestLocation local_without_offset(*local_location, -PhysicalLocation());
   HitTestResult layer_result(result.GetHitTestRequest(), local_without_offset);
+  layer_result.SetInertNode(result.InertNode());
   bool retval = Layer()->HitTest(local_without_offset, layer_result,
                                  PhysicalRect(PhysicalRect::InfiniteIntRect()));
 
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
index 19c8fcfb..bd1373fe 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
@@ -545,7 +545,7 @@
                                int indent) {
   WriteStandardPrefix(ts, object, indent);
 
-  Element* element = ToElement(object.GetNode());
+  auto* element = To<Element>(object.GetNode());
   const AtomicString& id = element->GetIdAttribute();
   WriteNameAndQuotedValue(ts, "id", id);
 
diff --git a/third_party/blink/renderer/core/loader/BUILD.gn b/third_party/blink/renderer/core/loader/BUILD.gn
index 95aa8ade..c9f941d 100644
--- a/third_party/blink/renderer/core/loader/BUILD.gn
+++ b/third_party/blink/renderer/core/loader/BUILD.gn
@@ -146,6 +146,8 @@
     "threadable_loader.cc",
     "threadable_loader.h",
     "threadable_loader_client.h",
+    "threaded_icon_loader.cc",
+    "threaded_icon_loader.h",
     "worker_fetch_context.cc",
     "worker_fetch_context.h",
     "worker_resource_fetcher_properties.cc",
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.h b/third_party/blink/renderer/core/loader/base_fetch_context.h
index c8117e9..1f3fc31 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/base_fetch_context.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_BASE_FETCH_CONTEXT_H_
 
 #include "base/optional.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 68600318..6d4d478 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -37,7 +37,7 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "third_party/blink/public/common/origin_policy/origin_policy.h"
-#include "third_party/blink/public/mojom/commit_result/commit_result.mojom-shared.h"
+#include "third_party/blink/public/mojom/commit_result/commit_result.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
@@ -1460,7 +1460,7 @@
   if (!origin_policy)
     return;
 
-  for (const std::string& policy : origin_policy->GetFeaturePolicies()) {
+  for (const auto& policy : origin_policy->GetFeaturePolicies()) {
     if (!feature_policy.IsEmpty()) {
       feature_policy.Append(',');
     }
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index 57d37d53..be60065c 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -34,7 +34,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "base/unguessable_token.h"
-#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink.h"
 #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h"
 #include "third_party/blink/public/platform/web_loading_behavior_flag.h"
 #include "third_party/blink/public/platform/web_navigation_body_loader.h"
diff --git a/third_party/blink/renderer/core/loader/document_loader_test.cc b/third_party/blink/renderer/core/loader/document_loader_test.cc
index 86fa47f9..7304331 100644
--- a/third_party/blink/renderer/core/loader/document_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/document_loader_test.cc
@@ -4,8 +4,6 @@
 
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 
-#include <queue>
-#include <string>
 #include <utility>
 #include "base/auto_reset.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,6 +18,7 @@
 #include "third_party/blink/renderer/platform/loader/static_data_navigation_body_loader.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
 
 namespace blink {
 
@@ -102,9 +101,9 @@
       params->response.SetMimeType("application/pdf");
       params->response.SetHttpStatusCode(200);
 
-      std::string data("<html><body>foo</body></html>");
-      for (size_t i = 0; i < data.size(); i++)
-        data_.push(data[i]);
+      String data("<html><body>foo</body></html>");
+      for (wtf_size_t i = 0; i < data.length(); i++)
+        data_.push_back(data[i]);
 
       auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>();
       body_loader_ = body_loader.get();
@@ -122,8 +121,8 @@
       }
 
       // Serve the remaining bytes to complete the load.
-      EXPECT_FALSE(data_.empty());
-      while (!data_.empty())
+      EXPECT_FALSE(data_.IsEmpty());
+      while (!data_.IsEmpty())
         DispatchOneByte();
 
       body_loader_->Finish();
@@ -147,15 +146,14 @@
     }
 
     void DispatchOneByte() {
-      char c = data_.front();
-      data_.pop();
+      char c = data_.TakeFirst();
       body_loader_->Write(&c, 1);
     }
 
     bool ServedReentrantly() const { return served_reentrantly_; }
 
    private:
-    std::queue<char> data_;
+    Deque<char> data_;
     bool dispatching_did_receive_data_ = false;
     bool served_reentrantly_ = false;
     StaticDataNavigationBodyLoader* body_loader_ = nullptr;
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 d7f912d..18c9721 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -38,7 +38,7 @@
 #include "build/build_config.h"
 #include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/device_memory/approximated_device_memory.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
 #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h"
@@ -544,8 +544,7 @@
   if (RuntimeEnabledFeatures::UserAgentClientHintEnabled()) {
     StringBuilder result;
     result.Append(ua.brand.data());
-    const std::string& version =
-        use_full_ua ? ua.full_version : ua.major_version;
+    const auto& version = use_full_ua ? ua.full_version : ua.major_version;
     if (!version.empty()) {
       result.Append(' ');
       result.Append(version.data());
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 cd22cb7..a7666e4 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
@@ -38,7 +38,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/device_memory/approximated_device_memory.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
 #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h"
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.h b/third_party/blink/renderer/core/loader/frame_load_request.h
index 9d98e85..c56168f8 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.h
+++ b/third_party/blink/renderer/core/loader/frame_load_request.h
@@ -27,7 +27,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FRAME_LOAD_REQUEST_H_
 
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink.h"
-#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "third_party/blink/public/web/web_window_features.h"
 #include "third_party/blink/renderer/core/dom/document.h"
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index e6b9e11..3e36506 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -42,7 +42,7 @@
 #include "base/auto_reset.h"
 #include "base/unguessable_token.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/navigation_initiator.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index ebf0c0166..0a53fae3 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -35,7 +35,7 @@
 
 #include "base/macros.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
-#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h"
 #include "third_party/blink/public/web/web_document_loader.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index a67a609..b173bbe 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -25,7 +25,7 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_client_hints_type.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
diff --git a/third_party/blink/renderer/core/loader/importance_attribute.h b/third_party/blink/renderer/core/loader/importance_attribute.h
index 859102e..92f8aa2 100644
--- a/third_party/blink/renderer/core/loader/importance_attribute.h
+++ b/third_party/blink/renderer/core/loader/importance_attribute.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_IMPORTANCE_ATTRIBUTE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_IMPORTANCE_ATTRIBUTE_H_
 
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/loader/interactive_detector.cc b/third_party/blink/renderer/core/loader/interactive_detector.cc
index 6f353fd5..3fcb18dd 100644
--- a/third_party/blink/renderer/core/loader/interactive_detector.cc
+++ b/third_party/blink/renderer/core/loader/interactive_detector.cc
@@ -368,7 +368,8 @@
 }
 
 void InteractiveDetector::OnPageHiddenChanged(bool is_hidden) {
-  visibility_change_events_.push_back({clock_->NowTicks(), is_hidden});
+  visibility_change_events_.push_back(
+      VisibilityChangeEvent{clock_->NowTicks(), is_hidden});
 }
 
 void InteractiveDetector::TimeToInteractiveTimerFired(TimerBase*) {
@@ -402,13 +403,13 @@
 }
 
 void InteractiveDetector::RemoveCurrentlyActiveQuietIntervals() {
-  if (!network_quiet_windows_.empty() &&
+  if (!network_quiet_windows_.IsEmpty() &&
       network_quiet_windows_.back().Low() ==
           active_network_quiet_window_start_) {
     network_quiet_windows_.pop_back();
   }
 
-  if (!main_thread_quiet_windows_.empty() &&
+  if (!main_thread_quiet_windows_.IsEmpty() &&
       main_thread_quiet_windows_.back().Low() ==
           active_main_thread_quiet_window_start_) {
     main_thread_quiet_windows_.pop_back();
@@ -418,9 +419,9 @@
 base::TimeTicks InteractiveDetector::FindInteractiveCandidate(
     base::TimeTicks lower_bound) {
   // Main thread iterator.
-  auto it_mt = main_thread_quiet_windows_.begin();
+  auto* it_mt = main_thread_quiet_windows_.begin();
   // Network iterator.
-  auto it_net = network_quiet_windows_.begin();
+  auto* it_net = network_quiet_windows_.begin();
 
   while (it_mt < main_thread_quiet_windows_.end() &&
          it_net < network_quiet_windows_.end()) {
diff --git a/third_party/blink/renderer/core/loader/interactive_detector.h b/third_party/blink/renderer/core/loader/interactive_detector.h
index ce47be3..4b47fdf 100644
--- a/third_party/blink/renderer/core/loader/interactive_detector.h
+++ b/third_party/blink/renderer/core/loader/interactive_detector.h
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/platform/supplementable.h"
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/wtf/pod_interval.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace base {
 class TickClock;
@@ -153,8 +154,8 @@
   };
 
   // Stores sufficiently long quiet windows on main thread and network.
-  std::vector<WTF::PODInterval<base::TimeTicks>> main_thread_quiet_windows_;
-  std::vector<WTF::PODInterval<base::TimeTicks>> network_quiet_windows_;
+  Vector<WTF::PODInterval<base::TimeTicks>> main_thread_quiet_windows_;
+  Vector<WTF::PODInterval<base::TimeTicks>> network_quiet_windows_;
 
   // Start times of currently active main thread and network quiet windows.
   // Null base::TimeTicks values indicate main thread or network is not quiet at
@@ -185,7 +186,7 @@
   void CheckTimeToInteractiveReached();
   void OnTimeToInteractiveDetected();
 
-  std::vector<VisibilityChangeEvent> visibility_change_events_;
+  Vector<VisibilityChangeEvent> visibility_change_events_;
   bool initially_hidden_;
   // Returns true if page was ever backgrounded in the range
   // [event_time, CurrentTimeTicks()].
diff --git a/third_party/blink/renderer/core/loader/link_load_parameters.h b/third_party/blink/renderer/core/loader/link_load_parameters.h
index d64c125..ce2c3a00d 100644
--- a/third_party/blink/renderer/core/loader/link_load_parameters.h
+++ b/third_party/blink/renderer/core/loader/link_load_parameters.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_LINK_LOAD_PARAMETERS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_LINK_LOAD_PARAMETERS_H_
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/html/cross_origin_attribute.h"
 #include "third_party/blink/renderer/core/html/link_rel_attribute.h"
diff --git a/third_party/blink/renderer/core/loader/link_loader.cc b/third_party/blink/renderer/core/loader/link_loader.cc
index e4a1f2c..e8c9a52 100644
--- a/third_party/blink/renderer/core/loader/link_loader.cc
+++ b/third_party/blink/renderer/core/loader/link_loader.cc
@@ -133,10 +133,14 @@
 void LinkLoader::NotifyFinished() {
   DCHECK(finish_observer_);
   Resource* resource = finish_observer_->GetResource();
-  if (resource->ErrorOccurred())
+  if (resource->ErrorOccurred() ||
+      (resource->IsLinkPreload() &&
+       resource->IntegrityDisposition() ==
+           ResourceIntegrityDisposition::kFailed)) {
     client_->LinkLoadingErrored();
-  else
+  } else {
     client_->LinkLoaded();
+  }
 }
 
 // https://html.spec.whatwg.org/C/#link-type-modulepreload
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 fd4c72d..9d277ea4 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
@@ -675,7 +675,7 @@
     return false;
   }
 
-  std::string autoupgrade_mode = base::GetFieldTrialParamValueByFeature(
+  auto autoupgrade_mode = base::GetFieldTrialParamValueByFeature(
       blink::features::kMixedContentAutoupgrade,
       blink::features::kMixedContentAutoupgradeModeParamName);
 
diff --git a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
index eb8a42a6..7e2b9e6 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.h"
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
diff --git a/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.cc b/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.cc
index 2e5bb1f..9c563a6 100644
--- a/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.cc
+++ b/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.cc
@@ -7,7 +7,7 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_url_load_timing.h"
@@ -221,7 +221,7 @@
 }
 
 void PrefetchedSignedExchangeManager::TriggerLoad() {
-  std::vector<WebNavigationParams::PrefetchedSignedExchange*>
+  Vector<WebNavigationParams::PrefetchedSignedExchange*>
       maching_prefetched_exchanges;
   for (auto loader : loaders_) {
     if (!loader) {
diff --git a/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h b/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h
index 834a906..9cad373 100644
--- a/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h
+++ b/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PREFETCHED_SIGNED_EXCHANGE_MANAGER_H_
 
 #include "base/macros.h"
-#include "services/network/public/mojom/url_loader_factory.mojom-shared.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
 #include "third_party/blink/public/web/web_navigation_params.h"
 #include "third_party/blink/renderer/core/loader/preload_helper.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index fe6abc8..aba3ed59 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -295,6 +295,18 @@
     link_fetch_params.SetCrossOriginAccessControl(document.GetSecurityOrigin(),
                                                   params.cross_origin);
   }
+
+  const String& integrity_attr = params.integrity;
+  if (!integrity_attr.IsEmpty()) {
+    IntegrityMetadataSet metadata_set;
+    SubresourceIntegrity::ParseIntegrityAttribute(
+        integrity_attr, SubresourceIntegrityHelper::GetFeatures(&document),
+        metadata_set);
+    link_fetch_params.SetIntegrityMetadata(metadata_set);
+    link_fetch_params.MutableResourceRequest().SetFetchIntegrity(
+        integrity_attr);
+  }
+
   link_fetch_params.SetContentSecurityPolicyNonce(params.nonce);
   Settings* settings = document.GetSettings();
   if (settings && settings->GetLogPreload()) {
diff --git a/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc b/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
index cd42884..005274e 100644
--- a/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
+++ b/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
@@ -9,7 +9,7 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
@@ -50,7 +50,8 @@
 
   subresource_patterns_to_block_usage_.Fill(
       false, subresource_patterns_to_block.size());
-  blocked_resource_load_priority_counts_.fill(0);
+  blocked_resource_load_priority_counts_.Fill(
+      0, static_cast<int>(ResourceLoadPriority::kHighest) + 1);
 
   // Populate which specific resource types are eligible for blocking.
   // Certain resource types are blocked by default since their blocking
diff --git a/third_party/blink/renderer/core/loader/previews_resource_loading_hints.h b/third_party/blink/renderer/core/loader/previews_resource_loading_hints.h
index dd15727..32d42d7 100644
--- a/third_party/blink/renderer/core/loader/previews_resource_loading_hints.h
+++ b/third_party/blink/renderer/core/loader/previews_resource_loading_hints.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PREVIEWS_RESOURCE_LOADING_HINTS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PREVIEWS_RESOURCE_LOADING_HINTS_H_
 
-#include <vector>
-
 #include "base/feature_list.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -79,8 +77,7 @@
 
   // |blocked_resource_load_priority_counts_| records the total number of
   // resources blocked at each ResourceLoadPriority.
-  mutable std::array<int, static_cast<int>(ResourceLoadPriority::kHighest) + 1>
-      blocked_resource_load_priority_counts_;
+  mutable Vector<int> blocked_resource_load_priority_counts_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/previews_resource_loading_hints_receiver_impl.cc b/third_party/blink/renderer/core/loader/previews_resource_loading_hints_receiver_impl.cc
index cea75407..08c0d20 100644
--- a/third_party/blink/renderer/core/loader/previews_resource_loading_hints_receiver_impl.cc
+++ b/third_party/blink/renderer/core/loader/previews_resource_loading_hints_receiver_impl.cc
@@ -4,8 +4,6 @@
 
 #include "third_party/blink/renderer/core/loader/previews_resource_loading_hints_receiver_impl.h"
 
-#include <vector>
-
 #include "base/metrics/histogram_macros.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/loader/previews_resource_loading_hints.h"
diff --git a/third_party/blink/renderer/core/loader/private/prerender_handle.cc b/third_party/blink/renderer/core/loader/private/prerender_handle.cc
index dfc2381..490c0d6 100644
--- a/third_party/blink/renderer/core/loader/private/prerender_handle.cc
+++ b/third_party/blink/renderer/core/loader/private/prerender_handle.cc
@@ -30,7 +30,7 @@
 
 #include "third_party/blink/renderer/core/loader/private/prerender_handle.h"
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
diff --git a/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc b/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
index 9ec575f..9d3ade3 100644
--- a/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
+++ b/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
@@ -37,13 +37,12 @@
   }
 
  protected:
-  void RegisterMockedHttpURLLoad(const std::string& file_name) {
+  void RegisterMockedHttpURLLoad(const String& file_name) {
     url_test_helpers::RegisterMockedURLLoadFromBase(
-        WebString::FromUTF8(base_url_), test::CoreTestDataPath(),
-        WebString::FromUTF8(file_name));
+        WebString(base_url_), test::CoreTestDataPath(), WebString(file_name));
   }
 
-  std::string base_url_;
+  String base_url_;
 };
 
 TEST_F(ProgrammaticScrollTest, RestoreScrollPositionAndViewStateWithScale) {
@@ -51,7 +50,7 @@
 
   frame_test_helpers::WebViewHelper web_view_helper;
   WebViewImpl* web_view =
-      web_view_helper.InitializeAndLoad(base_url_ + "long_scroll.html");
+      web_view_helper.InitializeAndLoad(base_url_.Utf8() + "long_scroll.html");
   web_view->MainFrameWidget()->Resize(WebSize(1000, 1000));
   web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
       WebWidget::LifecycleUpdateReason::kTest);
@@ -84,7 +83,7 @@
 
   frame_test_helpers::WebViewHelper web_view_helper;
   WebViewImpl* web_view =
-      web_view_helper.InitializeAndLoad(base_url_ + "long_scroll.html");
+      web_view_helper.InitializeAndLoad(base_url_.Utf8() + "long_scroll.html");
   web_view->MainFrameWidget()->Resize(WebSize(1000, 1000));
   web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
       WebWidget::LifecycleUpdateReason::kTest);
@@ -114,7 +113,7 @@
 
   frame_test_helpers::WebViewHelper web_view_helper;
   WebViewImpl* web_view =
-      web_view_helper.InitializeAndLoad(base_url_ + "long_scroll.html");
+      web_view_helper.InitializeAndLoad(base_url_.Utf8() + "long_scroll.html");
   web_view->MainFrameWidget()->Resize(WebSize(1000, 1000));
   web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
       WebWidget::LifecycleUpdateReason::kTest);
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index 85b72c95..c03c194 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -7,7 +7,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/renderer/core/css/css_font_face_src_value.h"
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.cc b/third_party/blink/renderer/core/loader/resource/image_resource.cc
index c2e6ae6..e7f549e 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.cc
@@ -394,9 +394,7 @@
     // Observers are notified via ImageResource::finish().
     // TODO(hiroshige): Do not call didFinishLoading() directly.
     Loader()->AbortResponseBodyLoading();
-    Loader()->DidFinishLoading(
-        CurrentTimeTicks(), size, size, size, false,
-        std::vector<network::cors::PreflightTimingInfo>());
+    Loader()->DidFinishLoading(CurrentTimeTicks(), size, size, size, false, {});
   } else {
     auto result = GetContent()->UpdateImage(
         nullptr, GetStatus(),
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 67d1a28..cdb3bc8 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
@@ -33,7 +33,7 @@
 #include <memory>
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -232,8 +232,7 @@
       WrappedResourceResponse(resource_response));
   image_resource->Loader()->DidReceiveData(data, kDataLength);
   image_resource->Loader()->DidFinishLoading(
-      base::TimeTicks(), kDataLength, kDataLength, kDataLength, false,
-      std::vector<network::cors::PreflightTimingInfo>());
+      base::TimeTicks(), kDataLength, kDataLength, kDataLength, false, {});
 
   // Checks |imageResource|'s status after reloading.
   EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
@@ -291,8 +290,7 @@
   image_resource->Loader()->DidFinishLoading(
       base::TimeTicks(), kJpegImageSubrangeWithDimensionsLength,
       kJpegImageSubrangeWithDimensionsLength,
-      kJpegImageSubrangeWithDimensionsLength, false,
-      std::vector<network::cors::PreflightTimingInfo>());
+      kJpegImageSubrangeWithDimensionsLength, false, {});
 
   // Checks that |imageResource| is successfully loaded, showing a placeholder.
   EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
@@ -334,8 +332,7 @@
       reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
   image_resource->Loader()->DidFinishLoading(
       base::TimeTicks(), sizeof(kJpegImage), sizeof(kJpegImage),
-      sizeof(kJpegImage), false,
-      std::vector<network::cors::PreflightTimingInfo>());
+      sizeof(kJpegImage), false, {});
 
   // Checks that |imageResource| is successfully loaded,
   // showing a non-placeholder image.
@@ -432,9 +429,8 @@
 
   // This part finishes. The image is created, callbacks are sent, and the data
   // buffer is cleared.
-  image_resource->Loader()->DidFinishLoading(
-      base::TimeTicks(), 0, 0, 0, false,
-      std::vector<network::cors::PreflightTimingInfo>());
+  image_resource->Loader()->DidFinishLoading(base::TimeTicks(), 0, 0, 0, false,
+                                             {});
   EXPECT_TRUE(image_resource->ResourceBuffer());
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -478,9 +474,8 @@
   image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
                              sizeof(kJpegImage));
   image_resource->AppendData(kBoundary, strlen(kBoundary));
-  image_resource->Loader()->DidFinishLoading(
-      base::TimeTicks(), 0, 0, 0, false,
-      std::vector<network::cors::PreflightTimingInfo>());
+  image_resource->Loader()->DidFinishLoading(base::TimeTicks(), 0, 0, 0, false,
+                                             {});
   EXPECT_TRUE(image_resource->GetContent()->HasImage());
   EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
   EXPECT_TRUE(image_resource->GetContent()
@@ -1044,9 +1039,8 @@
   EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
   EXPECT_EQ(0, observer->ImageChangedCount());
 
-  image_resource->Loader()->DidFinishLoading(
-      base::TimeTicks(), 0, 0, 0, false,
-      std::vector<network::cors::PreflightTimingInfo>());
+  image_resource->Loader()->DidFinishLoading(base::TimeTicks(), 0, 0, 0, false,
+                                             {});
 
   EXPECT_EQ(ResourceStatus::kDecodeError, image_resource->GetStatus());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
@@ -1093,8 +1087,7 @@
   image_resource->Loader()->DidFinishLoading(
       base::TimeTicks(), kJpegImageSubrangeWithoutDimensionsLength,
       kJpegImageSubrangeWithoutDimensionsLength,
-      kJpegImageSubrangeWithoutDimensionsLength, false,
-      std::vector<network::cors::PreflightTimingInfo>());
+      kJpegImageSubrangeWithoutDimensionsLength, false, {});
 
   EXPECT_EQ(ResourceStatus::kDecodeError, image_resource->GetStatus());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
@@ -1322,8 +1315,7 @@
     image_resource->Loader()->DidFinishLoading(
         base::TimeTicks(), kJpegImageSubrangeWithoutDimensionsLength,
         kJpegImageSubrangeWithoutDimensionsLength,
-        kJpegImageSubrangeWithoutDimensionsLength, false,
-        std::vector<network::cors::PreflightTimingInfo>());
+        kJpegImageSubrangeWithoutDimensionsLength, false, {});
 
     EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
     EXPECT_EQ(2, observer->ImageChangedCount());
@@ -1464,8 +1456,7 @@
         reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
     image_resource->Loader()->DidFinishLoading(
         base::TimeTicks(), sizeof(kJpegImage), sizeof(kJpegImage),
-        sizeof(kJpegImage), false,
-        std::vector<network::cors::PreflightTimingInfo>());
+        sizeof(kJpegImage), false, {});
 
     EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
     EXPECT_EQ(sizeof(kJpegImage), image_resource->EncodedSize());
diff --git a/third_party/blink/renderer/core/loader/subresource_integrity_helper.cc b/third_party/blink/renderer/core/loader/subresource_integrity_helper.cc
index 01782e6..ccb865d14e 100644
--- a/third_party/blink/renderer/core/loader/subresource_integrity_helper.cc
+++ b/third_party/blink/renderer/core/loader/subresource_integrity_helper.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/loader/subresource_integrity_helper.h"
 
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
diff --git a/third_party/blink/renderer/core/loader/text_track_loader.cc b/third_party/blink/renderer/core/loader/text_track_loader.cc
index 48a27ed..0c16de8 100644
--- a/third_party/blink/renderer/core/loader/text_track_loader.cc
+++ b/third_party/blink/renderer/core/loader/text_track_loader.cc
@@ -25,7 +25,7 @@
 
 #include "third_party/blink/renderer/core/loader/text_track_loader.h"
 
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
diff --git a/third_party/blink/renderer/core/loader/threaded_icon_loader.cc b/third_party/blink/renderer/core/loader/threaded_icon_loader.cc
new file mode 100644
index 0000000..668d44a1
--- /dev/null
+++ b/third_party/blink/renderer/core/loader/threaded_icon_loader.cc
@@ -0,0 +1,183 @@
+// Copyright 2019 The Chromium 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/loader/threaded_icon_loader.h"
+
+#include <algorithm>
+
+#include "base/metrics/histogram_macros.h"
+#include "skia/ext/image_operations.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+
+namespace blink {
+
+namespace {
+
+// Because including base::ClampToRange would be a dependency violation.
+int ClampToRange(const int value, const int min, const int max) {
+  return std::min(std::max(value, min), max);
+}
+
+}  // namespace
+
+void ThreadedIconLoader::Start(ExecutionContext* execution_context,
+                               const ResourceRequest& resource_request,
+                               const base::Optional<WebSize>& resize_dimensions,
+                               IconCallback callback) {
+  DCHECK(!stopped_);
+  DCHECK(resource_request.Url().IsValid());
+  DCHECK_EQ(resource_request.GetRequestContext(),
+            mojom::RequestContextType::IMAGE);
+  DCHECK(!icon_callback_);
+
+  icon_callback_ = std::move(callback);
+  resize_dimensions_ = resize_dimensions;
+
+  ResourceLoaderOptions resource_loader_options;
+  if (execution_context->IsWorkerGlobalScope())
+    resource_loader_options.request_initiator_context = kWorkerContext;
+
+  threadable_loader_ = MakeGarbageCollected<ThreadableLoader>(
+      *execution_context, this, resource_loader_options);
+  threadable_loader_->SetTimeout(resource_request.TimeoutInterval());
+  threadable_loader_->Start(resource_request);
+
+  start_time_ = base::TimeTicks::Now();
+}
+
+void ThreadedIconLoader::Stop() {
+  stopped_ = true;
+  if (threadable_loader_) {
+    threadable_loader_->Cancel();
+    threadable_loader_ = nullptr;
+  }
+}
+
+void ThreadedIconLoader::DidReceiveData(const char* data, unsigned length) {
+  if (!data_)
+    data_ = SharedBuffer::Create();
+  data_->Append(data, length);
+}
+
+void ThreadedIconLoader::DidFinishLoading(uint64_t resource_identifier) {
+  if (stopped_)
+    return;
+
+  if (!data_) {
+    std::move(icon_callback_).Run(SkBitmap(), -1);
+    return;
+  }
+
+  UMA_HISTOGRAM_MEDIUM_TIMES("Blink.ThreadedIconLoader.LoadTime",
+                             base::TimeTicks::Now() - start_time_);
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      Thread::Current()->GetTaskRunner();
+
+  worker_pool::PostTask(
+      FROM_HERE,
+      CrossThreadBindOnce(
+          &ThreadedIconLoader::DecodeAndResizeImageOnBackgroundThread,
+          WrapCrossThreadPersistent(this), std::move(task_runner),
+          SegmentReader::CreateFromSharedBuffer(std::move(data_))));
+}
+
+void ThreadedIconLoader::DecodeAndResizeImageOnBackgroundThread(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    scoped_refptr<SegmentReader> data) {
+  DCHECK(task_runner);
+  DCHECK(data);
+
+  auto notify_complete = [&](double refactor_scale) {
+    PostCrossThreadTask(
+        *task_runner, FROM_HERE,
+        CrossThreadBindOnce(&ThreadedIconLoader::OnBackgroundTaskComplete,
+                            WrapCrossThreadPersistent(this), refactor_scale));
+  };
+
+  std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
+      std::move(data), /* data_complete= */ true,
+      ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth,
+      ColorBehavior::TransformToSRGB());
+
+  if (!decoder) {
+    notify_complete(-1.0);
+    return;
+  }
+
+  ImageFrame* image_frame = decoder->DecodeFrameBufferAtIndex(0);
+
+  if (!image_frame) {
+    notify_complete(-1.0);
+    return;
+  }
+
+  decoded_icon_ = image_frame->Bitmap();
+  if (!resize_dimensions_) {
+    notify_complete(1.0);
+    return;
+  }
+
+  // If the icon is larger than |resize_dimensions_| permits, we need to resize
+  // it as well. This can be done synchronously given that we're on a
+  // background thread already.
+  double scale = std::min(
+      static_cast<double>(resize_dimensions_->width) / decoded_icon_.width(),
+      static_cast<double>(resize_dimensions_->height) / decoded_icon_.height());
+
+  if (scale >= 1.0) {
+    notify_complete(1.0);
+    return;
+  }
+
+  int resized_width =
+      ClampToRange(static_cast<int>(scale * decoded_icon_.width()), 1,
+                   resize_dimensions_->width);
+  int resized_height =
+      ClampToRange(static_cast<int>(scale * decoded_icon_.height()), 1,
+                   resize_dimensions_->height);
+
+  // Use the RESIZE_GOOD quality allowing the implementation to pick an
+  // appropriate method for the resize. Can be increased to RESIZE_BETTER
+  // or RESIZE_BEST if the quality looks poor.
+  decoded_icon_ = skia::ImageOperations::Resize(
+      decoded_icon_, skia::ImageOperations::RESIZE_GOOD, resized_width,
+      resized_height);
+
+  notify_complete(scale);
+}
+
+void ThreadedIconLoader::OnBackgroundTaskComplete(double resize_scale) {
+  if (stopped_)
+    return;
+  std::move(icon_callback_).Run(std::move(decoded_icon_), resize_scale);
+}
+
+void ThreadedIconLoader::DidFail(const ResourceError& error) {
+  if (stopped_)
+    return;
+  std::move(icon_callback_).Run(SkBitmap(), -1);
+}
+
+void ThreadedIconLoader::DidFailRedirectCheck() {
+  if (stopped_)
+    return;
+  std::move(icon_callback_).Run(SkBitmap(), -1);
+}
+
+void ThreadedIconLoader::Trace(blink::Visitor* visitor) {
+  visitor->Trace(threadable_loader_);
+  ThreadableLoaderClient::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/threaded_icon_loader.h b/third_party/blink/renderer/core/loader/threaded_icon_loader.h
new file mode 100644
index 0000000..2b8b9fdd
--- /dev/null
+++ b/third_party/blink/renderer/core/loader/threaded_icon_loader.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_THREADED_ICON_LOADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_THREADED_ICON_LOADER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/loader/threadable_loader.h"
+#include "third_party/blink/renderer/core/loader/threadable_loader_client.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace blink {
+
+class ResourceRequest;
+class SegmentReader;
+
+// Utility class for loading, decoding, and potentially rescaling an icon on a
+// background thread. Note that icons are only downscaled and never upscaled.
+class CORE_EXPORT ThreadedIconLoader final
+    : public GarbageCollectedFinalized<ThreadedIconLoader>,
+      public ThreadableLoaderClient {
+  USING_GARBAGE_COLLECTED_MIXIN(ThreadedIconLoader);
+
+ public:
+  // On failure, |callback| is called with a null SkBitmap and |resize_scale|
+  // set to -1. On success, the icon is provided with a |resize_scale| <= 1.
+  using IconCallback =
+      base::OnceCallback<void(SkBitmap icon, double resize_scale)>;
+
+  // Starts a background task to download and decode the icon.
+  // If |resize_dimensions| is provided, the icon will will be downscaled to
+  // those dimensions.
+  void Start(ExecutionContext* execution_context,
+             const ResourceRequest& resource_request,
+             const base::Optional<WebSize>& resize_dimensions,
+             IconCallback callback);
+
+  // Stops the background task. The provided callback will not be run if
+  // `Stop` is called.
+  void Stop();
+
+  // ThreadableLoaderClient interface.
+  void DidReceiveData(const char* data, unsigned length) override;
+  void DidFinishLoading(uint64_t resource_identifier) override;
+  void DidFail(const ResourceError& error) override;
+  void DidFailRedirectCheck() override;
+
+  void Trace(blink::Visitor* visitor) override;
+
+ private:
+  void DecodeAndResizeImageOnBackgroundThread(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      scoped_refptr<SegmentReader> data);
+
+  void OnBackgroundTaskComplete(double resize_scale);
+
+  Member<ThreadableLoader> threadable_loader_;
+
+  // Data received from |threadable_loader_|. Will be invalidated when decoding
+  // of the image data starts.
+  scoped_refptr<SharedBuffer> data_;
+
+  // Accessed from main thread and background thread.
+  base::Optional<WebSize> resize_dimensions_;
+  SkBitmap decoded_icon_;
+
+  IconCallback icon_callback_;
+
+  base::TimeTicks start_time_;
+
+  bool stopped_ = false;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_THREADED_ICON_LOADER_H_
diff --git a/third_party/blink/renderer/core/loader/threaded_icon_loader_test.cc b/third_party/blink/renderer/core/loader/threaded_icon_loader_test.cc
new file mode 100644
index 0000000..18f20c13
--- /dev/null
+++ b/third_party/blink/renderer/core/loader/threaded_icon_loader_test.cc
@@ -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.
+
+#include "third_party/blink/renderer/core/loader/threaded_icon_loader.h"
+
+#include "base/optional.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+namespace {
+
+constexpr char kIconLoaderBaseUrl[] = "http://test.com/";
+constexpr char kIconLoaderBaseDir[] = "notifications/";
+constexpr char kIconLoaderIcon100x100[] = "100x100.png";
+constexpr char kIconLoaderInvalidIcon[] = "file.txt";
+
+class ThreadedIconLoaderTest : public PageTestBase {
+ public:
+  void SetUp() override {
+    PageTestBase::SetUp(IntSize());
+    GetDocument().SetBaseURLOverride(KURL(kIconLoaderBaseUrl));
+  }
+
+  void TearDown() override {
+    platform_->GetURLLoaderMockFactory()
+        ->UnregisterAllURLsAndClearMemoryCache();
+  }
+
+  // Registers a mocked url. When fetched, |fileName| will be loaded from the
+  // test data directory.
+  KURL RegisterMockedURL(const String& file_name) {
+    return url_test_helpers::RegisterMockedURLLoadFromBase(
+        kIconLoaderBaseUrl, test::CoreTestDataPath(kIconLoaderBaseDir),
+        file_name, "image/png");
+  }
+
+  std::pair<SkBitmap, double> LoadIcon(
+      const KURL& url,
+      base::Optional<WebSize> resize_dimensions = base::nullopt) {
+    auto* icon_loader = MakeGarbageCollected<ThreadedIconLoader>();
+
+    ResourceRequest resource_request(url);
+    resource_request.SetRequestContext(mojom::RequestContextType::IMAGE);
+    resource_request.SetPriority(ResourceLoadPriority::kMedium);
+
+    SkBitmap icon;
+    double resize_scale;
+    base::RunLoop run_loop;
+    icon_loader->Start(
+        &GetDocument(), resource_request, resize_dimensions,
+        WTF::Bind(&ThreadedIconLoaderTest::DidGetIcon, WTF::Unretained(this),
+                  run_loop.QuitClosure(), WTF::Unretained(&icon),
+                  WTF::Unretained(&resize_scale)));
+    platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+    run_loop.Run();
+
+    return {icon, resize_scale};
+  }
+
+ private:
+  void DidGetIcon(base::OnceClosure quit_closure,
+                  SkBitmap* out_icon,
+                  double* out_resize_scale,
+                  SkBitmap icon,
+                  double resize_scale) {
+    *out_icon = std::move(icon);
+    *out_resize_scale = resize_scale;
+    std::move(quit_closure).Run();
+  }
+
+  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
+};
+
+TEST_F(ThreadedIconLoaderTest, LoadIcon) {
+  auto result = LoadIcon(RegisterMockedURL(kIconLoaderIcon100x100));
+  const SkBitmap& icon = result.first;
+  double resize_scale = result.second;
+
+  ASSERT_FALSE(icon.isNull());
+  EXPECT_FALSE(icon.drawsNothing());
+  EXPECT_EQ(icon.width(), 100);
+  EXPECT_EQ(icon.height(), 100);
+  EXPECT_EQ(resize_scale, 1.0);
+}
+
+TEST_F(ThreadedIconLoaderTest, LoadAndDownscaleIcon) {
+  WebSize dimensions = {50, 50};
+  auto result = LoadIcon(RegisterMockedURL(kIconLoaderIcon100x100), dimensions);
+  const SkBitmap& icon = result.first;
+  double resize_scale = result.second;
+
+  ASSERT_FALSE(icon.isNull());
+  EXPECT_FALSE(icon.drawsNothing());
+  EXPECT_EQ(icon.width(), 50);
+  EXPECT_EQ(icon.height(), 50);
+  EXPECT_EQ(resize_scale, 0.5);
+}
+
+TEST_F(ThreadedIconLoaderTest, LoadIconAndUpscaleIgnored) {
+  WebSize dimensions = {500, 500};
+  auto result = LoadIcon(RegisterMockedURL(kIconLoaderIcon100x100), dimensions);
+  const SkBitmap& icon = result.first;
+  double resize_scale = result.second;
+
+  ASSERT_FALSE(icon.isNull());
+  EXPECT_FALSE(icon.drawsNothing());
+  EXPECT_EQ(icon.width(), 100);
+  EXPECT_EQ(icon.height(), 100);
+  EXPECT_EQ(resize_scale, 1.0);
+}
+
+TEST_F(ThreadedIconLoaderTest, InvalidResourceReturnsNullIcon) {
+  auto result = LoadIcon(RegisterMockedURL(kIconLoaderInvalidIcon));
+  const SkBitmap& icon = result.first;
+  double resize_scale = result.second;
+
+  ASSERT_TRUE(icon.isNull());
+  EXPECT_EQ(resize_scale, -1.0);
+}
+
+TEST_F(ThreadedIconLoaderTest, LoadTimeRecordedByUMA) {
+  HistogramTester histogram_tester;
+  LoadIcon(RegisterMockedURL(kIconLoaderIcon100x100));
+  histogram_tester.ExpectTotalCount("Blink.ThreadedIconLoader.LoadTime", 1);
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index 53baa47..17be769 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -32,7 +32,7 @@
 #include "cc/trees/paint_holding_commit_trigger.h"
 #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/platform/blame_context.h"
 #include "third_party/blink/public/platform/web_drag_operation.h"
 #include "third_party/blink/public/platform/web_focus_type.h"
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index de4ba86..548fa54 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -212,8 +212,9 @@
   if (!ContextMenuAllowedScope::IsContextMenuAllowed())
     return false;
 
-  HitTestRequest::HitTestRequestType type =
-      HitTestRequest::kReadOnly | HitTestRequest::kActive;
+  HitTestRequest::HitTestRequestType type = HitTestRequest::kReadOnly |
+                                            HitTestRequest::kActive |
+                                            HitTestRequest::kRetargetForInert;
   HitTestLocation location(point);
   HitTestResult result(type, location);
   if (frame)
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index 42286ef..676a8aa4 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -364,7 +364,8 @@
 // This can return null if an empty document is loaded.
 static Element* ElementUnderMouse(Document* document_under_mouse,
                                   const PhysicalOffset& point) {
-  HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
+  HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive |
+                         HitTestRequest::kRetargetForInert);
   HitTestLocation location(point);
   HitTestResult result(request, location);
   document_under_mouse->GetLayoutView()->HitTest(location, result);
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
index 050bb15..2b799e9 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
@@ -240,13 +240,18 @@
   return document->ActiveElement();
 }
 
-void SpatialNavigationController::DidDetachFrameView() {
+void SpatialNavigationController::DidDetachFrameView(
+    const LocalFrameView& view) {
   // If the interested element's view was lost (frame detached, navigated,
   // etc.) then reset navigation.
   if (interest_element_ && !interest_element_->GetDocument().View())
     interest_element_ = nullptr;
-  // TODO(crbug.com/956209): should be checked via an integration test.
-  ResetMojoBindings();
+
+  // TODO(bokan): This still needs a test. crbug.com/976892.
+  if (view.GetFrame().IsMainFrame()) {
+    // TODO(crbug.com/956209): should be checked via an integration test.
+    ResetMojoBindings();
+  }
 }
 
 void SpatialNavigationController::Trace(blink::Visitor* visitor) {
@@ -547,6 +552,15 @@
   }
 }
 
+void SpatialNavigationController::FullscreenStateChanged(Element* element) {
+  if (!RuntimeEnabledFeatures::FocuslessSpatialNavigationEnabled())
+    return;
+  if (IsHTMLMediaElement(element)) {
+    element->focus(FocusParams(SelectionBehaviorOnFocus::kReset,
+                               kWebFocusTypeSpatialNavigation, nullptr));
+  }
+}
+
 void SpatialNavigationController::UpdateSpatialNavigationState(
     Element* element) {
   bool change = false;
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.h b/third_party/blink/renderer/core/page/spatial_navigation_controller.h
index e748a5e5..63380b0f 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.h
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.h
@@ -14,6 +14,7 @@
 
 struct FocusCandidate;
 class KeyboardEvent;
+class LocalFrameView;
 class Node;
 class Page;
 struct PhysicalRect;
@@ -40,10 +41,11 @@
   // currently indicated to the user.
   Element* GetInterestedElement() const;
 
-  void DidDetachFrameView();
+  void DidDetachFrameView(const LocalFrameView&);
 
   void OnSpatialNavigationSettingChanged();
   void FocusedNodeChanged(Document*);
+  void FullscreenStateChanged(Element* element);
 
   void Trace(blink::Visitor*);
 
diff --git a/third_party/blink/renderer/core/paint/block_painter.cc b/third_party/blink/renderer/core/paint/block_painter.cc
index 180d375..6c12d13c 100644
--- a/third_party/blink/renderer/core/paint/block_painter.cc
+++ b/third_party/blink/renderer/core/paint/block_painter.cc
@@ -73,7 +73,10 @@
   if (original_phase == PaintPhase::kMask) {
     layout_block_.PaintObject(local_paint_info, paint_offset);
   } else if (original_phase != PaintPhase::kSelfBlockBackgroundOnly &&
-             original_phase != PaintPhase::kSelfOutlineOnly) {
+             original_phase != PaintPhase::kSelfOutlineOnly &&
+             // For now all scrollers with overlay scrollbars are self-painting
+             // layers, so we don't need to traverse descendants here.
+             original_phase != PaintPhase::kOverlayScrollbars) {
     ScopedBoxContentsPaintState contents_paint_state(paint_state,
                                                      layout_block_);
     layout_block_.PaintObject(contents_paint_state.GetPaintInfo(),
@@ -107,22 +110,12 @@
     layout_block_.PaintObject(local_paint_info, paint_offset);
   }
 
-  // Our scrollbar widgets paint exactly when we tell them to, so that they work
-  // properly with z-index. We paint after we painted the background/border, so
-  // that the scrollbars will sit above the background/border.
+  // We paint scrollbars after we painted the other things, so that the
+  // scrollbars will sit above them.
   local_paint_info.phase = original_phase;
-  PaintOverflowControlsIfNeeded(local_paint_info, paint_offset);
-}
-
-void BlockPainter::PaintOverflowControlsIfNeeded(
-    const PaintInfo& paint_info,
-    const PhysicalOffset& paint_offset) {
-  if (layout_block_.HasOverflowClip() &&
-      layout_block_.StyleRef().Visibility() == EVisibility::kVisible &&
-      ShouldPaintSelfBlockBackground(paint_info.phase)) {
-    ScrollableAreaPainter(*layout_block_.Layer()->GetScrollableArea())
-        .PaintOverflowControls(paint_info, RoundedIntPoint(paint_offset),
-                               false /* painting_overlay_controls */);
+  if (auto* scrollable_area = layout_block_.GetScrollableArea()) {
+    ScrollableAreaPainter(*scrollable_area)
+        .PaintOverflowControls(local_paint_info, RoundedIntPoint(paint_offset));
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/block_painter.h b/third_party/blink/renderer/core/paint/block_painter.h
index 76b51d3..1c9e74725 100644
--- a/third_party/blink/renderer/core/paint/block_painter.h
+++ b/third_party/blink/renderer/core/paint/block_painter.h
@@ -32,8 +32,6 @@
   void PaintContents(const PaintInfo&, const PhysicalOffset& paint_offset);
   void PaintChildren(const PaintInfo&);
   void PaintChild(const LayoutBox&, const PaintInfo&);
-  void PaintOverflowControlsIfNeeded(const PaintInfo&,
-                                     const PhysicalOffset& paint_offset);
 
   // See ObjectPainter::PaintAllPhasesAtomically().
   void PaintAllChildPhasesAtomically(const LayoutBox&, const PaintInfo&);
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 d9796621..e80b7a31 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
@@ -2955,12 +2955,7 @@
 }
 
 bool CompositedLayerMapping::NeedsToReparentOverflowControls() const {
-  const ComputedStyle& style = owning_layer_.GetLayoutObject().StyleRef();
-  return owning_layer_.GetScrollableArea() &&
-         owning_layer_.GetScrollableArea()->HasOverlayScrollbars() &&
-         !style.IsStackingContext() &&
-         (style.IsStacked() ||
-          owning_layer_.IsNonStackedWithInFlowStackedDescendant());
+  return owning_layer_.NeedsReorderOverlayScrollbars();
 }
 
 GraphicsLayer* CompositedLayerMapping::DetachLayerForOverflowControls() {
@@ -3178,13 +3173,6 @@
         paint_info.paint_layer->SubpixelAccumulation());
     PaintLayerPainter(*paint_info.paint_layer)
         .PaintLayerContents(context, painting_info, paint_layer_flags);
-
-    if (paint_info.paint_layer->ContainsDirtyOverlayScrollbars()) {
-      PaintLayerPainter(*paint_info.paint_layer)
-          .PaintLayerContents(
-              context, painting_info,
-              paint_layer_flags | kPaintLayerPaintingOverlayScrollbars);
-    }
   } else {
     PaintLayerPaintingInfo painting_info(
         paint_info.paint_layer, CullRect(dirty_rect), kGlobalPaintNormalPhase,
diff --git a/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc b/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc
index d27f2aa..084bd945 100644
--- a/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc
@@ -166,9 +166,8 @@
     pending_reparents.Set(&layer, child_layers.size());
 
   // Set or overwrite the entry in |pending_reparents| for this scroller.
-  // Overlay controls need to paint on top of all content under the
-  // scroller, so keep overwriting if we find a PaintLayer that is
-  // later in paint order.
+  // Overlay scrollbars need to paint on top of all content under the scroller,
+  // so keep overwriting if we find a PaintLayer that is later in paint order.
   const PaintLayer* scroll_parent = layer.ScrollParent();
   if (style.IsStacked() && scroll_parent &&
       scroll_parent->HasCompositedLayerMapping() &&
diff --git a/third_party/blink/renderer/core/paint/frame_painter.cc b/third_party/blink/renderer/core/paint/frame_painter.cc
index b1b9a2e..a3cbfbc 100644
--- a/third_party/blink/renderer/core/paint/frame_painter.cc
+++ b/third_party/blink/renderer/core/paint/frame_painter.cc
@@ -103,11 +103,6 @@
   layer_painter.Paint(context, cull_rect, updated_global_paint_flags,
                       root_layer_paint_flags);
 
-  if (root_layer->ContainsDirtyOverlayScrollbars()) {
-    layer_painter.PaintOverlayScrollbars(context, cull_rect,
-                                         updated_global_paint_flags);
-  }
-
   // Regions may have changed as a result of the visibility/z-index of element
   // changing.
   if (document->AnnotatedRegionsDirty())
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
index 00c4e74c..406da55 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
@@ -58,6 +58,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
 #include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/skia/include/core/SkMatrix44.h"
@@ -71,9 +72,19 @@
 
 namespace {
 
-float HighlightTargetOpacity() {
-  // For web tests we don't fade out.
-  return WebTestSupport::IsRunningWebTest() ? kStartOpacity : 0;
+EffectPaintPropertyNode::State LinkHighlightEffectNodeState(
+    float opacity,
+    CompositorElementId element_id) {
+  EffectPaintPropertyNode::State state;
+  state.opacity = opacity;
+  state.local_transform_space = &TransformPaintPropertyNode::Root();
+  state.compositor_element_id = element_id;
+  state.direct_compositing_reasons = CompositingReason::kActiveOpacityAnimation;
+  // EffectPaintPropertyNode::Update does not pay attention to changes in
+  // has_active_opacity_animation so we assume that the effect node is
+  // always animating.
+  state.has_active_opacity_animation = true;
+  return state;
 }
 
 }  // namespace
@@ -111,14 +122,9 @@
   compositor_animation_->AttachElement(element_id_);
   geometry_needs_update_ = true;
 
-  EffectPaintPropertyNode::State state;
-  state.opacity = HighlightTargetOpacity();
-  state.local_transform_space = &TransformPaintPropertyNode::Root();
-  state.compositor_element_id = element_id_;
-  state.direct_compositing_reasons = CompositingReason::kActiveOpacityAnimation;
-  state.has_active_opacity_animation = true;
-  effect_ = EffectPaintPropertyNode::Create(EffectPaintPropertyNode::Root(),
-                                            std::move(state));
+  effect_ = EffectPaintPropertyNode::Create(
+      EffectPaintPropertyNode::Root(),
+      LinkHighlightEffectNodeState(kStartOpacity, element_id_));
 #if DCHECK_IS_ON()
   effect_->SetDebugName("LinkHighlightEffect");
 #endif
@@ -354,6 +360,14 @@
   const auto& timing_function = *CubicBezierTimingFunction::Preset(
       CubicBezierTimingFunction::EaseType::EASE);
 
+  float target_opacity = WebTestSupport::IsRunningWebTest() ? kStartOpacity : 0;
+
+  // Since the notification about the animation finishing may not arrive in
+  // time to remove the link highlight before it's drawn without an animation
+  // we set the opacity to the final target opacity to avoid a flash of the
+  // initial opacity. https://crbug.com/974160
+  UpdateOpacity(target_opacity);
+
   curve->AddKeyframe(
       CompositorFloatKeyframe(0, kStartOpacity, timing_function));
   // Make sure we have displayed for at least minPreFadeDuration before starting
@@ -366,8 +380,8 @@
         extra_duration_required.InSecondsF(), kStartOpacity, timing_function));
   }
   curve->AddKeyframe(CompositorFloatKeyframe(
-      (kFadeDuration + extra_duration_required).InSecondsF(),
-      HighlightTargetOpacity(), timing_function));
+      (kFadeDuration + extra_duration_required).InSecondsF(), target_opacity,
+      timing_function));
 
   auto keyframe_model = std::make_unique<CompositorKeyframeModel>(
       *curve, compositor_target_property::OPACITY, 0, 0);
@@ -391,6 +405,10 @@
   // release resources as soon as possible.
   ClearGraphicsLayerLinkHighlightPointer();
   ReleaseResources();
+
+  // Reset the link highlight opacity to clean up after the animation now that
+  // we have removed the node and it won't be displayed.
+  UpdateOpacity(kStartOpacity);
 }
 
 void LinkHighlightImpl::UpdateGeometry() {
@@ -534,4 +552,9 @@
   }
 }
 
+void LinkHighlightImpl::UpdateOpacity(float opacity) {
+  effect_->Update(EffectPaintPropertyNode::Root(),
+                  LinkHighlightEffectNodeState(opacity, element_id_));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.h b/third_party/blink/renderer/core/paint/link_highlight_impl.h
index 8ff77311..cc2ff98 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.h
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.h
@@ -116,6 +116,7 @@
       const LayoutBoxModelObject&);
 
   void SetPaintArtifactCompositorNeedsUpdate();
+  void UpdateOpacity(float opacity);
 
   class LinkHighlightFragment : private cc::ContentLayerClient {
    public:
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 0c7b4de..b5ff2d0a 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
@@ -56,6 +56,7 @@
 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -294,6 +295,8 @@
       !RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
     return;
 
+  bool was_running_web_test = WebTestSupport::IsRunningWebTest();
+  WebTestSupport::SetIsRunningWebTest(false);
   int page_width = 640;
   int page_height = 480;
   WebViewImpl* web_view_impl = web_view_helper_.GetWebView();
@@ -317,7 +320,7 @@
   // The highlight should create one additional layer.
   EXPECT_EQ(layer_count_before_highlight + 1, ContentLayerCount());
 
-  const auto& highlights = web_view_impl->GetPage()->GetLinkHighlights();
+  auto& highlights = web_view_impl->GetPage()->GetLinkHighlights();
   auto* highlight = highlights.link_highlights_.at(0).get();
   ASSERT_TRUE(highlight);
 
@@ -336,12 +339,25 @@
   // node.
   EXPECT_EQ(highlight->Effect().GetCompositorElementId(),
             highlight->ElementIdForTesting());
+
+  // Initially the highlight node has full opacity as it is expected to remain
+  // visible until the user completes a tap. See https://crbug.com/974631
+  EXPECT_EQ(1.f, highlight->Effect().Opacity());
+  EXPECT_TRUE(highlight->Effect().HasActiveOpacityAnimation());
+
+  // After starting the highlight animation the effect node's opacity should
+  // be 0.f as it will be overridden bt the animation but may become visible
+  // before the animation is destructed. See https://crbug.com/974160
+  highlights.StartHighlightAnimationIfNeeded();
+  EXPECT_EQ(0.f, highlight->Effect().Opacity());
   EXPECT_TRUE(highlight->Effect().HasActiveOpacityAnimation());
 
   touch_node->remove(IGNORE_EXCEPTION_FOR_TESTING);
   UpdateAllLifecyclePhases();
   // Removing the highlight layer should drop the cc layer count by one.
   EXPECT_EQ(layer_count_before_highlight, ContentLayerCount());
+
+  WebTestSupport::SetIsRunningWebTest(was_running_web_test);
 }
 
 TEST_P(LinkHighlightImplTest, MultiColumn) {
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 ed73f2e..cd0e6f9 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
@@ -58,8 +58,7 @@
                                      const HitTestRequest& request) {
   return fragment.Style().Visibility() == EVisibility::kVisible &&
          (request.IgnorePointerEventsNone() ||
-          fragment.Style().PointerEvents() != EPointerEvents::kNone) &&
-         !(fragment.GetNode() && fragment.GetNode()->IsInert());
+          fragment.Style().PointerEvents() != EPointerEvents::kNone);
 }
 
 // Hit tests inline ancestor elements of |fragment| who do not have their own
@@ -185,11 +184,13 @@
     PaintObject(info, paint_offset);
   }
 
-  // Our scrollbar widgets paint exactly when we tell them to, so that they work
-  // properly with z-index. We paint after we painted the background/border, so
-  // that the scrollbars will sit above the background/border.
+  // We paint scrollbars after we painted other things, so that the scrollbars
+  // will sit above them.
   info.phase = original_phase;
-  PaintOverflowControlsIfNeeded(info, paint_offset);
+  if (box_fragment_.HasOverflowClip()) {
+    ScrollableAreaPainter(*PhysicalFragment().Layer()->GetScrollableArea())
+        .PaintOverflowControls(info, RoundedIntPoint(paint_offset));
+  }
 }
 
 void NGBoxFragmentPainter::RecordHitTestData(
@@ -880,19 +881,6 @@
          box_fragment_.GetLayoutObject() == paint_info.PaintContainer();
 }
 
-// Clone of BlockPainter::PaintOverflowControlsIfNeeded
-void NGBoxFragmentPainter::PaintOverflowControlsIfNeeded(
-    const PaintInfo& paint_info,
-    const PhysicalOffset& paint_offset) {
-  if (box_fragment_.HasOverflowClip() &&
-      box_fragment_.Style().Visibility() == EVisibility::kVisible &&
-      ShouldPaintSelfBlockBackground(paint_info.phase)) {
-    ScrollableAreaPainter(*PhysicalFragment().Layer()->GetScrollableArea())
-        .PaintOverflowControls(paint_info, RoundedIntPoint(paint_offset),
-                               false /* painting_overlay_controls */);
-  }
-}
-
 bool NGBoxFragmentPainter::ShouldPaint(
     const ScopedPaintState& paint_state) const {
   // TODO(layout-dev): Add support for scrolling, see BlockPainter::ShouldPaint.
@@ -986,6 +974,7 @@
                                        const HitTestLocation& hit_test_location,
                                        const PhysicalOffset& physical_offset,
                                        HitTestAction action) {
+  const NGPhysicalBoxFragment& fragment = PhysicalFragment();
   const PhysicalSize& size = box_fragment_.Size();
   const ComputedStyle& style = box_fragment_.Style();
 
@@ -1039,6 +1028,10 @@
       bounds_rect = box_fragment_.SelfInkOverflow();
       bounds_rect.Move(physical_offset);
     }
+    // TODO(kojii): Don't have good explanation why only inline box needs to
+    // snap, but matches to legacy and fixes crbug.com/976606.
+    if (fragment.IsInlineBox())
+      bounds_rect = PhysicalRect(PixelSnappedIntRect(bounds_rect));
     if (hit_test_location.Intersects(bounds_rect)) {
       Node* node = box_fragment_.NodeForHitTest();
       if (!result.InnerNode() && node) {
@@ -1136,6 +1129,21 @@
   if (!hit_test_location.Intersects(bounds_rect))
     return false;
 
+  // Floats will be hit-tested in |kHitTestFloat| phase, but
+  // |LayoutObject::HitTestAllPhases| does not try it if |kHitTestForeground|
+  // succeeds. Pretend the location is not in this linebox if it hits floating
+  // descendants. TODO(kojii): Computing this is redundant, consider
+  // restructuring. Changing the caller logic isn't easy because currently
+  // floats are in the bounds of line boxes only in NG.
+  const auto& line = To<NGPhysicalLineBoxFragment>(fragment.PhysicalFragment());
+  if (line.HasFloatingDescendants()) {
+    DCHECK_NE(action, kHitTestFloat);
+    if (HitTestChildren(result, fragment.Children(), hit_test_location,
+                        physical_offset, kHitTestFloat)) {
+      return false;
+    }
+  }
+
   Node* node = fragment.NodeForHitTest();
   if (!result.InnerNode() && node) {
     const PhysicalOffset point = hit_test_location.Point() - physical_offset +
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
index 88e9030..91e7ace 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
@@ -94,8 +94,6 @@
   void PaintFloatingChildren(NGPaintFragment::ChildList, const PaintInfo&);
   void PaintFloats(const PaintInfo&);
   void PaintMask(const PaintInfo&, const PhysicalOffset& paint_offset);
-  void PaintOverflowControlsIfNeeded(const PaintInfo&,
-                                     const PhysicalOffset& paint_offset);
   void PaintAtomicInline(const PaintInfo&);
   void PaintBackground(const PaintInfo&,
                        const PhysicalRect&,
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index e73a815..9722ce3 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -147,7 +147,6 @@
       needs_position_update_(!IsRootLayer()),
 #endif
       has3d_transformed_descendant_(false),
-      contains_dirty_overlay_scrollbars_(false),
       needs_ancestor_dependent_compositing_inputs_update_(
           !RuntimeEnabledFeatures::CompositeAfterPaintEnabled()),
       child_needs_compositing_inputs_update_(
@@ -177,7 +176,7 @@
       needs_compositing_layer_assignment_(false),
       descendant_needs_compositing_layer_assignment_(false),
       has_self_painting_layer_descendant_(false),
-      is_non_stacked_with_in_flow_stacked_descendant_(false),
+      needs_reorder_overlay_scrollbars_(false),
 #if DCHECK_IS_ON()
       layer_list_mutation_allowed_(true),
 #endif
@@ -223,6 +222,12 @@
     ClearCompositedLayerMapping(true);
   }
 
+  // Reset this flag before disposing scrollable_area_ to prevent
+  // PaintLayerScrollableArea::WillRemoveScrollbar() from dirtying the z-order
+  // list of the stacking context. If this layer is removed from the parent,
+  // the z-order list should have been invalidated in RemoveChild().
+  needs_reorder_overlay_scrollbars_ = false;
+
   if (scrollable_area_)
     scrollable_area_->Dispose();
 
@@ -662,14 +667,10 @@
     has_non_contained_absolute_position_descendant_ = false;
     has_stacked_descendant_in_current_stacking_context_ = false;
     has_self_painting_layer_descendant_ = false;
-    is_non_stacked_with_in_flow_stacked_descendant_ = false;
 
     bool can_contain_abs =
         GetLayoutObject().CanContainAbsolutePositionObjects();
 
-    const ComputedStyle& style = GetLayoutObject().StyleRef();
-    bool is_stacked = style.IsStacked();
-
     for (PaintLayer* child = FirstChild(); child;
          child = child->NextSibling()) {
       const ComputedStyle& child_style = child->GetLayoutObject().StyleRef();
@@ -713,14 +714,6 @@
           has_self_painting_layer_descendant_ ||
           child->HasSelfPaintingLayerDescendant() ||
           child->IsSelfPaintingLayer();
-
-      if (!is_stacked) {
-        if (child->IsNonStackedWithInFlowStackedDescendant())
-          is_non_stacked_with_in_flow_stacked_descendant_ = true;
-        else if (child_style.IsStacked() &&
-                 !child->GetLayoutObject().IsOutOfFlowPositioned())
-          is_non_stacked_with_in_flow_stacked_descendant_ = true;
-      }
     }
 
     UpdateStackingNode();
@@ -1369,7 +1362,7 @@
     // ancestorStackingContextNode() can be null in the case where we're
     // building up generated content layers. This is ok, since the lists will
     // start off dirty in that case anyway.
-    PaintLayerStackingNode::DirtyStackingContextZOrderLists(*child);
+    child->DirtyStackingContextZOrderLists();
     MarkAncestorChainForFlagsUpdate();
   }
 
@@ -1412,7 +1405,7 @@
         Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
     }
     // Dirty the z-order list in which we are contained.
-    PaintLayerStackingNode::DirtyStackingContextZOrderLists(*old_child);
+    old_child->DirtyStackingContextZOrderLists();
     SetNeedsCompositingInputsUpdate();
   }
 
@@ -1445,7 +1438,7 @@
     return;
 
   if (old_style && old_style->IsStacked()) {
-    PaintLayerStackingNode::DirtyStackingContextZOrderLists(*this);
+    DirtyStackingContextZOrderLists();
     MarkAncestorChainForFlagsUpdate();
   }
 
@@ -2187,6 +2180,7 @@
       // to 'result' if we know we're frontmost.
       HitTestResult temp_result(result.GetHitTestRequest(),
                                 recursion_data.original_location);
+      temp_result.SetInertNode(result.InertNode());
       bool inside_fragment_foreground_rect = false;
 
       if (HitTestContentsForFragments(
@@ -2206,6 +2200,8 @@
       } else if (inside_fragment_foreground_rect &&
                  result.GetHitTestRequest().ListBased()) {
         result.Append(temp_result);
+      } else if (result.GetHitTestRequest().RetargetForInert()) {
+        result.SetInertNode(temp_result.InertNode());
       }
     }
   }
@@ -2229,6 +2225,7 @@
   if (recursion_data.intersects_location && IsSelfPaintingLayer()) {
     HitTestResult temp_result(result.GetHitTestRequest(),
                               recursion_data.original_location);
+    temp_result.SetInertNode(result.InertNode());
     bool inside_fragment_background_rect = false;
     if (HitTestContentsForFragments(*layer_fragments, offset, temp_result,
                                     recursion_data.location, kHitTestSelf,
@@ -2240,6 +2237,8 @@
       else
         result = temp_result;
       return this;
+    } else if (result.GetHitTestRequest().RetargetForInert()) {
+      result.SetInertNode(temp_result.InertNode());
     }
     if (inside_fragment_background_rect &&
         result.GetHitTestRequest().ListBased())
@@ -2453,6 +2452,7 @@
     PaintLayer* hit_layer = nullptr;
     HitTestResult temp_result(result.GetHitTestRequest(),
                               recursion_data.original_location);
+    temp_result.SetInertNode(result.InertNode());
     hit_layer = child_layer->HitTestLayer(
         root_layer, this, temp_result, recursion_data, false, transform_state,
         z_offset_for_descendants);
@@ -2469,6 +2469,8 @@
         result = temp_result;
       if (!depth_sort_descendants)
         break;
+    } else if (result.GetHitTestRequest().RetargetForInert()) {
+      result.SetInertNode(temp_result.InertNode());
     }
   }
 
@@ -3537,6 +3539,23 @@
   return nullptr;
 }
 
+void PaintLayer::DirtyStackingContextZOrderLists() {
+  auto* stacking_context = AncestorStackingContext();
+  if (!stacking_context)
+    return;
+
+  // This invalidation code intentionally refers to stale state.
+  DisableCompositingQueryAsserts disabler;
+
+  // Changes of stacking may result in graphics layers changing size
+  // due to new contents painting into them.
+  if (auto* mapping = stacking_context->GetCompositedLayerMapping())
+    mapping->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
+
+  if (stacking_context->StackingNode())
+    stacking_context->StackingNode()->DirtyZOrderLists();
+}
+
 DisableCompositingQueryAsserts::DisableCompositingQueryAsserts()
     : disabler_(&g_compositing_query_mode, kCompositingQueriesAreAllowed) {}
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index 36921b3..1c727d4 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -615,13 +615,6 @@
   bool BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect&,
                                          bool should_check_children) const;
 
-  bool ContainsDirtyOverlayScrollbars() const {
-    return contains_dirty_overlay_scrollbars_;
-  }
-  void SetContainsDirtyOverlayScrollbars(bool dirty_scrollbars) {
-    contains_dirty_overlay_scrollbars_ = dirty_scrollbars;
-  }
-
   // If the input CompositorFilterOperation is not empty, it will be populated
   // only if |filter_on_effect_node_dirty_| is true or the reference box has
   // changed. Otherwise it will be populated unconditionally.
@@ -887,9 +880,10 @@
     DCHECK(!needs_descendant_dependent_flags_update_);
     return has_self_painting_layer_descendant_;
   }
-  bool IsNonStackedWithInFlowStackedDescendant() const {
-    DCHECK(!needs_descendant_dependent_flags_update_);
-    return is_non_stacked_with_in_flow_stacked_descendant_;
+
+  // See PaintLayerStackingNode::layer_to_overlay_scrollbars_painting_after_.
+  bool NeedsReorderOverlayScrollbars() const {
+    return needs_reorder_overlay_scrollbars_;
   }
 
   // Returns true if there is a descendant with blend-mode that is
@@ -1116,6 +1110,8 @@
     return descendant_needs_compositing_layer_assignment_;
   }
 
+  void DirtyStackingContextZOrderLists();
+
  private:
   void SetNeedsCompositingInputsUpdateInternal();
 
@@ -1281,6 +1277,10 @@
   // PaintLayerPaintOrderIterator.
   PaintLayerStackingNode* StackingNode() const { return stacking_node_.get(); }
 
+  void SetNeedsReorderOverlayScrollbars(bool b) {
+    needs_reorder_overlay_scrollbars_ = b;
+  }
+
   // Self-painting layer is an optimization where we avoid the heavy Layer
   // painting machinery for a Layer allocated only to handle the overflow clip
   // case.
@@ -1305,8 +1305,6 @@
   // in a preserves3D hierarchy. Hint to do 3D-aware hit testing.
   unsigned has3d_transformed_descendant_ : 1;
 
-  unsigned contains_dirty_overlay_scrollbars_ : 1;
-
   unsigned needs_ancestor_dependent_compositing_inputs_update_ : 1;
   unsigned child_needs_compositing_inputs_update_ : 1;
 
@@ -1361,7 +1359,8 @@
   unsigned descendant_needs_compositing_layer_assignment_ : 1;
 
   unsigned has_self_painting_layer_descendant_ : 1;
-  unsigned is_non_stacked_with_in_flow_stacked_descendant_ : 1;
+
+  unsigned needs_reorder_overlay_scrollbars_ : 1;
 
 #if DCHECK_IS_ON()
   mutable unsigned layer_list_mutation_allowed_ : 1;
diff --git a/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h b/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h
index 32ee38c..5bd84d94 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h
@@ -57,6 +57,14 @@
 
   PaintLayer* Next();
 
+  const Vector<PaintLayer*>* LayersPaintingOverlayScrollbarsAfter(
+      const PaintLayer* layer) const {
+    return root_.stacking_node_
+               ? root_.stacking_node_->LayersPaintingOverlayScrollbarsAfter(
+                     layer)
+               : nullptr;
+  }
+
  private:
   const PaintLayer& root_;
   unsigned remaining_children_;
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
index 0f91221..3341c7874 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
 #include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
@@ -491,13 +492,13 @@
          (is_painting_root_layer &&
           !(paint_flags & kPaintLayerPaintingSkipRootBackground)));
     bool should_paint_neg_z_order_list =
-        (is_painting_scrolling_content && is_painting_overflow_contents) ||
-        (!is_painting_scrolling_content && is_painting_composited_background);
+        !is_painting_overlay_scrollbars &&
+        (is_painting_scrolling_content ? is_painting_overflow_contents
+                                       : is_painting_composited_background);
     bool should_paint_own_contents =
         is_painting_composited_foreground && should_paint_content;
     bool should_paint_normal_flow_and_pos_z_order_lists =
-        is_painting_composited_foreground;
-    bool should_paint_overlay_scrollbars = is_painting_overlay_scrollbars;
+        is_painting_composited_foreground && !is_painting_overlay_scrollbars;
     bool is_video = paint_layer_.GetLayoutObject().IsVideo();
 
     base::Optional<ScopedPaintChunkProperties>
@@ -556,9 +557,13 @@
         result = kMayBeClippedByCullRect;
     }
 
-    if (should_paint_overlay_scrollbars) {
-      PaintOverflowControlsForFragments(layer_fragments, context,
-                                        local_painting_info, paint_flags);
+    if (paint_layer_.GetScrollableArea() &&
+        paint_layer_.GetScrollableArea()->HasOverlayScrollbars()) {
+      if (is_painting_overlay_scrollbars ||
+          !paint_layer_.NeedsReorderOverlayScrollbars()) {
+        PaintOverlayScrollbarsForFragments(layer_fragments, context,
+                                           local_painting_info, paint_flags);
+      }
     }
 
     if (is_video && should_paint_self_outline) {
@@ -698,44 +703,44 @@
     if (PaintLayerPainter(*child).Paint(context, painting_info, paint_flags) ==
         kMayBeClippedByCullRect)
       result = kMayBeClippedByCullRect;
+
+    if (const auto* layers_painting_overlay_scrollbars_after =
+            iterator.LayersPaintingOverlayScrollbarsAfter(child)) {
+      for (auto* reparent_scrollbar_layer :
+           *layers_painting_overlay_scrollbars_after) {
+        DCHECK(reparent_scrollbar_layer->NeedsReorderOverlayScrollbars());
+        if (PaintLayerPainter(*reparent_scrollbar_layer)
+                .Paint(context, painting_info,
+                       kPaintLayerPaintingOverlayScrollbars) ==
+            kMayBeClippedByCullRect)
+          result = kMayBeClippedByCullRect;
+      }
+    }
   }
 
   return result;
 }
 
-void PaintLayerPainter::PaintOverflowControlsForFragments(
+void PaintLayerPainter::PaintOverlayScrollbarsForFragments(
     const PaintLayerFragments& layer_fragments,
     GraphicsContext& context,
     const PaintLayerPaintingInfo& painting_info,
     PaintLayerFlags paint_flags) {
-  PaintLayerScrollableArea* scrollable_area = paint_layer_.GetScrollableArea();
-  if (!scrollable_area)
+  DCHECK(paint_layer_.GetScrollableArea() &&
+         paint_layer_.GetScrollableArea()->HasOverlayScrollbars());
+
+  // We don't need to paint composited scrollbars.
+  if (paint_layer_.GetScrollableArea()->HasLayerForHorizontalScrollbar() ||
+      paint_layer_.GetScrollableArea()->HasLayerForVerticalScrollbar())
     return;
 
   ForAllFragments(
       context, layer_fragments, [&](const PaintLayerFragment& fragment) {
-        ScopedPaintChunkProperties fragment_paint_chunk_properties(
-            context.GetPaintController(),
-            fragment.fragment_data->LocalBorderBoxProperties(), paint_layer_,
-            DisplayItem::kOverflowControls);
-
-        // We need to apply the same clips and transforms that
-        // paintFragmentWithPhase would have.
-        PhysicalRect cull_rect = fragment.background_rect.Rect();
-        PaintInfo paint_info(
-            context, PixelSnappedIntRect(cull_rect),
-            PaintPhase::kSelfBlockBackgroundOnly,
-            painting_info.GetGlobalPaintFlags(), paint_flags,
-            &painting_info.root_layer->GetLayoutObject(),
-            fragment.fragment_data
-                ? fragment.fragment_data->LogicalTopInFlowThread()
-                : LayoutUnit());
-        // We pass IntPoint() as the paint offset here, because
-        // ScrollableArea::paintOverflowControls just ignores it and uses the
-        // offset found in a previous pass.
-        ScrollableAreaPainter(*scrollable_area)
-            .PaintOverflowControls(paint_info, IntPoint(),
-                                   true /* painting_overlay_controls */);
+        if (!fragment.background_rect.IsEmpty()) {
+          PaintFragmentWithPhase(PaintPhase::kOverlayScrollbars, fragment,
+                                 context, fragment.background_rect,
+                                 painting_info, paint_flags);
+        }
       });
 }
 
@@ -940,14 +945,9 @@
     GraphicsContext& context,
     const CullRect& cull_rect,
     const GlobalPaintFlags paint_flags) {
-  if (!paint_layer_.ContainsDirtyOverlayScrollbars())
-    return;
-
   PaintLayerPaintingInfo painting_info(&paint_layer_, cull_rect, paint_flags,
                                        PhysicalOffset());
   Paint(context, painting_info, kPaintLayerPaintingOverlayScrollbars);
-
-  paint_layer_.SetContainsDirtyOverlayScrollbars(false);
 }
 
 void PaintLayerPainter::FillMaskingFragment(GraphicsContext& context,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.h b/third_party/blink/renderer/core/paint/paint_layer_painter.h
index c16f278..03be2fc 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.h
@@ -94,10 +94,10 @@
                                     GraphicsContext&,
                                     const PaintLayerPaintingInfo&,
                                     PaintLayerFlags);
-  void PaintOverflowControlsForFragments(const PaintLayerFragments&,
-                                         GraphicsContext&,
-                                         const PaintLayerPaintingInfo&,
-                                         PaintLayerFlags);
+  void PaintOverlayScrollbarsForFragments(const PaintLayerFragments&,
+                                          GraphicsContext&,
+                                          const PaintLayerPaintingInfo&,
+                                          PaintLayerFlags);
   void PaintMaskForFragments(const PaintLayerFragments&,
                              GraphicsContext&,
                              const PaintLayerPaintingInfo&,
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 0417c27..17879b4 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
@@ -2624,9 +2624,25 @@
   return page->GetScrollbarTheme();
 }
 
+void PaintLayerScrollableArea::DidAddScrollbar(
+    Scrollbar& scrollbar,
+    ScrollbarOrientation orientation) {
+  // Z-order of reparented scrollbar is updated along with the z-order lists.
+  if (scrollbar.IsOverlayScrollbar())
+    layer_->DirtyStackingContextZOrderLists();
+
+  ScrollableArea::DidAddScrollbar(scrollbar, orientation);
+}
+
 void PaintLayerScrollableArea::WillRemoveScrollbar(
     Scrollbar& scrollbar,
     ScrollbarOrientation orientation) {
+  if (layer_->NeedsReorderOverlayScrollbars()) {
+    // Z-order of reparented scrollbar is updated along with the z-order lists.
+    DCHECK(scrollbar.IsOverlayScrollbar());
+    layer_->DirtyStackingContextZOrderLists();
+  }
+
   if (!scrollbar.IsCustomScrollbar() &&
       !(orientation == kHorizontalScrollbar ? LayerForHorizontalScrollbar()
                                             : LayerForVerticalScrollbar())) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index b9b0205c..a40d634 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -444,13 +444,6 @@
 
   LayoutScrollbarPart* Resizer() const { return resizer_; }
 
-  const IntPoint& CachedOverlayScrollbarOffset() {
-    return cached_overlay_scrollbar_offset_;
-  }
-  void SetCachedOverlayScrollbarOffset(const IntPoint& offset) {
-    cached_overlay_scrollbar_offset_ = offset;
-  }
-
   IntRect RectForHorizontalScrollbar(const IntRect& border_box_rect) const;
   IntRect RectForVerticalScrollbar(const IntRect& border_box_rect) const;
 
@@ -528,6 +521,7 @@
   // scrollbar.
   int HypotheticalScrollbarThickness(ScrollbarOrientation) const;
 
+  void DidAddScrollbar(Scrollbar&, ScrollbarOrientation) override;
   void WillRemoveScrollbar(Scrollbar&, ScrollbarOrientation) override;
 
   void InvalidatePaintOfScrollControlsIfNeeded(const PaintInvalidatorContext&);
@@ -678,8 +672,6 @@
   // This is the offset from the beginning of content flow.
   ScrollOffset scroll_offset_;
 
-  IntPoint cached_overlay_scrollbar_offset_;
-
   // LayoutObject to hold our custom scroll corner.
   LayoutScrollbarPart* scroll_corner_;
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc
index 7598786..6872d8c 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc
@@ -53,6 +53,7 @@
 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 
 namespace blink {
 
@@ -86,50 +87,83 @@
 
   pos_z_order_list_.clear();
   neg_z_order_list_.clear();
+
+  for (auto& entry : layer_to_overlay_scrollbars_painting_after_.Values()) {
+    for (PaintLayer* layer : entry)
+      layer->SetNeedsReorderOverlayScrollbars(false);
+  }
+  layer_to_overlay_scrollbars_painting_after_.clear();
+
   z_order_lists_dirty_ = true;
 
   if (!layer_.GetLayoutObject().DocumentBeingDestroyed() && Compositor())
     Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
 }
 
-void PaintLayerStackingNode::DirtyStackingContextZOrderLists(
-    PaintLayer& layer) {
-  auto* stacking_context = layer.AncestorStackingContext();
-  if (!stacking_context)
-    return;
-
-  // This invalidation code intentionally refers to stale state.
-  DisableCompositingQueryAsserts disabler;
-
-  // Changes of stacking may result in graphics layers changing size
-  // due to new contents painting into them.
-  if (auto* mapping = stacking_context->GetCompositedLayerMapping())
-    mapping->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
-
-  if (stacking_context->StackingNode())
-    stacking_context->StackingNode()->DirtyZOrderLists();
+static bool ZIndexLessThan(const PaintLayer* first, const PaintLayer* second) {
+  DCHECK(first->GetLayoutObject().StyleRef().IsStacked());
+  DCHECK(second->GetLayoutObject().StyleRef().IsStacked());
+  return first->GetLayoutObject().StyleRef().ZIndex() <
+         second->GetLayoutObject().StyleRef().ZIndex();
 }
 
+static void SetIfHigher(const PaintLayer*& first, const PaintLayer* second) {
+  if (!second)
+    return;
+  DCHECK_GE(second->GetLayoutObject().StyleRef().ZIndex(), 0);
+  // |second| appears later in the tree, so it's higher than |first| if its
+  // z-index >= |first|'s z-index.
+  if (!first || !ZIndexLessThan(second, first))
+    first = second;
+}
+
+// For finding the proper z-order of reparented overlay scrollbars.
+struct PaintLayerStackingNode::HighestLayers {
+  const PaintLayer* highest_absolute_position = nullptr;
+  const PaintLayer* highest_fixed_position = nullptr;
+  const PaintLayer* highest_in_flow_stacked = nullptr;
+
+  void Update(const PaintLayer& layer) {
+    const auto& style = layer.GetLayoutObject().StyleRef();
+    // We only need to consider zero or positive z-index stacked child for
+    // candidates of causing reparent of overlay scrollbars of ancestors.
+    // A negative z-index child will not cause reparent of overlay scrollbars
+    // because the ancestor scroller either has auto z-index which is above
+    // the child or has negative z-index which is a stacking context.
+    if (!style.IsStacked() || style.ZIndex() < 0)
+      return;
+
+    if (style.GetPosition() == EPosition::kAbsolute)
+      SetIfHigher(highest_absolute_position, &layer);
+    else if (style.GetPosition() == EPosition::kFixed)
+      SetIfHigher(highest_fixed_position, &layer);
+    else
+      SetIfHigher(highest_in_flow_stacked, &layer);
+  }
+
+  void Merge(HighestLayers& child) {
+    SetIfHigher(highest_absolute_position, child.highest_absolute_position);
+    SetIfHigher(highest_fixed_position, child.highest_fixed_position);
+    SetIfHigher(highest_in_flow_stacked, child.highest_in_flow_stacked);
+  }
+};
+
 void PaintLayerStackingNode::RebuildZOrderLists() {
 #if DCHECK_IS_ON()
   DCHECK(layer_.LayerListMutationAllowed());
 #endif
   DCHECK(z_order_lists_dirty_);
 
+  layer_.SetNeedsReorderOverlayScrollbars(false);
   for (PaintLayer* child = layer_.FirstChild(); child;
        child = child->NextSibling())
-    CollectLayers(*child);
-
-  auto CompareZIndex = [](PaintLayer* first, PaintLayer* second) {
-    return first->GetLayoutObject().StyleRef().ZIndex() <
-           second->GetLayoutObject().StyleRef().ZIndex();
-  };
+    CollectLayers(*child, nullptr);
 
   // Sort the two lists.
   std::stable_sort(pos_z_order_list_.begin(), pos_z_order_list_.end(),
-                   CompareZIndex);
+                   ZIndexLessThan);
   std::stable_sort(neg_z_order_list_.begin(), neg_z_order_list_.end(),
-                   CompareZIndex);
+                   ZIndexLessThan);
 
   // Append layers for top layer elements after normal layer collection, to
   // ensure they are on top regardless of z-indexes.  The layoutObjects of top
@@ -163,11 +197,18 @@
   z_order_lists_dirty_ = false;
 }
 
-void PaintLayerStackingNode::CollectLayers(PaintLayer& paint_layer) {
+void PaintLayerStackingNode::CollectLayers(PaintLayer& paint_layer,
+                                           HighestLayers* highest_layers) {
+  paint_layer.SetNeedsReorderOverlayScrollbars(false);
+
   if (paint_layer.IsInTopLayer())
     return;
 
-  const ComputedStyle& style = paint_layer.GetLayoutObject().StyleRef();
+  if (highest_layers)
+    highest_layers->Update(paint_layer);
+
+  const auto& object = paint_layer.GetLayoutObject();
+  const auto& style = object.StyleRef();
 
   if (style.IsStacked()) {
     auto& list = style.ZIndex() >= 0 ? pos_z_order_list_ : neg_z_order_list_;
@@ -177,9 +218,41 @@
   if (style.IsStackingContext())
     return;
 
+  base::Optional<HighestLayers> subtree_highest_layers;
+  bool has_overlay_scrollbars =
+      paint_layer.GetScrollableArea() &&
+      paint_layer.GetScrollableArea()->HasOverlayScrollbars();
+  if (has_overlay_scrollbars)
+    subtree_highest_layers.emplace();
+
   for (PaintLayer* child = paint_layer.FirstChild(); child;
-       child = child->NextSibling())
-    CollectLayers(*child);
+       child = child->NextSibling()) {
+    CollectLayers(*child, subtree_highest_layers ? &*subtree_highest_layers
+                                                 : highest_layers);
+  }
+
+  if (has_overlay_scrollbars) {
+    const PaintLayer* layer_to_paint_overlay_scrollbars_after =
+        subtree_highest_layers->highest_in_flow_stacked;
+    if (object.CanContainFixedPositionObjects()) {
+      SetIfHigher(layer_to_paint_overlay_scrollbars_after,
+                  subtree_highest_layers->highest_fixed_position);
+    }
+    if (object.CanContainAbsolutePositionObjects()) {
+      SetIfHigher(layer_to_paint_overlay_scrollbars_after,
+                  subtree_highest_layers->highest_absolute_position);
+    }
+    if (layer_to_paint_overlay_scrollbars_after) {
+      layer_to_overlay_scrollbars_painting_after_
+          .insert(layer_to_paint_overlay_scrollbars_after, PaintLayers())
+          .stored_value->value.push_back(&paint_layer);
+    }
+    paint_layer.SetNeedsReorderOverlayScrollbars(
+        !!layer_to_paint_overlay_scrollbars_after);
+
+    if (highest_layers)
+      highest_layers->Merge(*subtree_highest_layers);
+  }
 }
 
 #if DCHECK_IS_ON()
@@ -214,7 +287,7 @@
 
   // Need to force requirements update, due to change of stacking order.
   paint_layer.SetNeedsCompositingRequirementsUpdate();
-  DirtyStackingContextZOrderLists(paint_layer);
+  paint_layer.DirtyStackingContextZOrderLists();
 
   if (paint_layer.StackingNode())
     paint_layer.StackingNode()->DirtyZOrderLists();
diff --git a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
index fb9288f4..7f0f3c24 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -96,8 +97,8 @@
   explicit PaintLayerStackingNode(PaintLayer&);
   ~PaintLayerStackingNode();
 
+  void DirtyZOrderLists();
   void UpdateZOrderLists();
-  static void DirtyStackingContextZOrderLists(PaintLayer&);
 
   // Returns whether a relevant style changed.
   static bool StyleDidChange(PaintLayer& paint_layer,
@@ -114,11 +115,21 @@
     return neg_z_order_list_;
   }
 
+  const PaintLayers* LayersPaintingOverlayScrollbarsAfter(
+      const PaintLayer* layer) const {
+    DCHECK(!z_order_lists_dirty_);
+    auto it = layer_to_overlay_scrollbars_painting_after_.find(layer);
+    return it == layer_to_overlay_scrollbars_painting_after_.end() ? nullptr
+                                                                   : &it->value;
+  }
+
+  void ClearNeedsReorderOverlayScrollbars();
+
  private:
-  void DirtyZOrderLists();
   void RebuildZOrderLists();
 
-  void CollectLayers(PaintLayer&);
+  struct HighestLayers;
+  void CollectLayers(PaintLayer&, HighestLayers*);
 
 #if DCHECK_IS_ON()
   void UpdateStackingParentForZOrderLists(
@@ -135,6 +146,48 @@
   // Holds descendants within our stacking context with negative z-indices.
   PaintLayers neg_z_order_list_;
 
+  // Overlay scrollbars need to be painted above all scrollable contents, even
+  // if the contents are stacked in a stacking context which is an ancestor of
+  // the scrolling layer, for example:
+  //   <div id="stacking-context" style="opacity: 0.5">
+  //     <div id="other" style="position: relative; z-index: 10></div>
+  //     <div id="scroller" style="overflow: scroll">
+  //       <div id="child" style="position: relative">CHILD</div>
+  //     </div>
+  //   </div>
+  // and
+  //   <div id="stacking-context" style="opacity: 0.5">
+  //     <div id="other" style="position: relative; z-index: 10></div>
+  //     <div id="scroller" style="overflow: scroll; position: relative">
+  //       <div id="child" style="position: absolute; z-index: 5">CHILD</div>
+  //     </div>
+  //   </div>
+  //
+  // The paint order without reordering overlay scrollbars would be:
+  //            stacking-context
+  //               /    |    \
+  //         scroller child  other
+  //            |
+  //    overlay scrollbars
+  // where the overlay scrollbars would be painted incorrectly below |child|
+  // which is scrollable by |scroller|.
+  //
+  // To paint the overlay scrollbars above all scrollable contents, we need to
+  // reorder the z-order of overlay scrollbars in the stacking context:
+  //            stacking-context
+  //             /    |    |   \
+  //       scroller child  |   other
+  //                       |
+  //                overlay scrollbars
+  //
+  // This map records which PaintLayers (the values of the map) have overlay
+  // scrollbars which should paint after the given PaintLayer (the key of the
+  // map). The value of the map is a list of PaintLayers because there may be
+  // more than one scroller in the same stacking context with overlay
+  // scrollbars.
+  HashMap<const PaintLayer*, PaintLayers>
+      layer_to_overlay_scrollbars_painting_after_;
+
   // Indicates whether the z-order lists above are dirty.
   bool z_order_lists_dirty_ : 1;
 
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 2bc5d6b..af74551 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 
+#include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
@@ -16,6 +17,9 @@
 
 namespace blink {
 
+using ::testing::ElementsAre;
+using ::testing::Pointee;
+
 class PaintLayerTest : public PaintTestConfigurations, public RenderingTest {
  public:
   PaintLayerTest()
@@ -445,79 +449,333 @@
   EXPECT_FALSE(child->HasSelfPaintingLayerDescendant());
 }
 
-TEST_P(PaintLayerTest, NonStackedWithInFlowDescendant) {
-  SetBodyInnerHTML(R"HTML(
-    <div id='parent' style='overflow: auto'>
-      <div id='child' style='position: relative'>
-        <div></div>
-      </div>
-    </div>
-  )HTML");
-  PaintLayer* parent = GetPaintLayerByElementId("parent");
-  PaintLayer* child = GetPaintLayerByElementId("child");
-
-  EXPECT_TRUE(parent->IsNonStackedWithInFlowStackedDescendant());
-  EXPECT_FALSE(child->IsNonStackedWithInFlowStackedDescendant());
+static const Vector<PaintLayer*>* LayersPaintingOverlayScrollbarsAfter(
+    const PaintLayer* layer) {
+  return PaintLayerPaintOrderIterator(*layer->AncestorStackingContext(),
+                                      kPositiveZOrderChildren)
+      .LayersPaintingOverlayScrollbarsAfter(layer);
 }
 
-TEST_P(PaintLayerTest, NonStackedWithOutOfFlowDescendant) {
+TEST_P(PaintLayerTest, ReorderOverlayScrollbars_StackedWithInFlowDescendant) {
   SetBodyInnerHTML(R"HTML(
-    <div id='parent' style='overflow: auto'>
-      <div id='child' style='position: absolute'>
-        <div></div>
-      </div>
+    <div id='parent' style='overflow: auto; position: relative;
+                            width: 100px; height: 100px'>
+      <div id='child' style='position: relative; height: 200px'></div>
     </div>
   )HTML");
-  PaintLayer* parent = GetPaintLayerByElementId("parent");
-  PaintLayer* child = GetPaintLayerByElementId("child");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
 
-  EXPECT_FALSE(parent->IsNonStackedWithInFlowStackedDescendant());
-  EXPECT_FALSE(child->IsNonStackedWithInFlowStackedDescendant());
+  GetDocument().getElementById("child")->setAttribute(
+      html_names::kStyleAttr, "position: relative; height: 80px");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(child));
+
+  GetDocument().getElementById("child")->setAttribute(
+      html_names::kStyleAttr, "position: relative; width: 200px; height: 80px");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
+
+  GetDocument().getElementById("child")->setAttribute(
+      html_names::kStyleAttr, "width: 200px; height: 80px");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+
+  GetDocument().getElementById("child")->setAttribute(
+      html_names::kStyleAttr, "position: relative; width: 200px; height: 80px");
+  UpdateAllLifecyclePhasesForTest();
+  child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
 }
 
-TEST_P(PaintLayerTest, NonStackedWithNonStackedDescendant) {
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_StackedWithOutOfFlowDescendant) {
   SetBodyInnerHTML(R"HTML(
-    <div id='parent' style='overflow: auto'>
-      <div id='child' style='overflow: auto'>
-        <div></div>
-      </div>
+    <style>#child { width: 200px; height: 200px; }</style>
+    <div id='parent' style='overflow: auto; position: relative; height: 100px'>
+      <div id='child' style='position: absolute'></div>
     </div>
   )HTML");
-  PaintLayer* parent = GetPaintLayerByElementId("parent");
-  PaintLayer* child = GetPaintLayerByElementId("child");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
 
-  EXPECT_FALSE(parent->IsNonStackedWithInFlowStackedDescendant());
-  EXPECT_FALSE(child->IsNonStackedWithInFlowStackedDescendant());
+  GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr,
+                                                      "");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+
+  GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr,
+                                                      "position: absolute");
+  UpdateAllLifecyclePhasesForTest();
+  child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
 }
 
-TEST_P(PaintLayerTest, NonStackedWithInFlowStackedGrandchild) {
+TEST_P(PaintLayerTest, ReorderOverlayScrollbars_StackedWithZIndexDescendant) {
   SetBodyInnerHTML(R"HTML(
-    <div id='parent' style='overflow: auto'>
-      <div id='child' style='overflow: auto'>
-        <div style='position: relative'></div>
-      </div>
+    <style>#child { position: absolute; width: 200px; height: 200px; }</style>
+    <div id='parent' style='overflow: auto; position: relative; height: 100px'>
+      <div id='child' style='position: absolute; z-index: 1;
+                             width: 200px; height: 200px'></div>
     </div>
   )HTML");
-  PaintLayer* parent = GetPaintLayerByElementId("parent");
-  PaintLayer* child = GetPaintLayerByElementId("child");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
 
-  EXPECT_TRUE(parent->IsNonStackedWithInFlowStackedDescendant());
-  EXPECT_TRUE(child->IsNonStackedWithInFlowStackedDescendant());
+  GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr,
+                                                      "z-index: -1");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(child));
+
+  GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr,
+                                                      "z-index: 2");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
 }
 
-TEST_P(PaintLayerTest, NonStackedWithOutOfFlowStackedGrandchild) {
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_NestedStackedWithInFlowStackedChild) {
   SetBodyInnerHTML(R"HTML(
-    <div id='parent' style='overflow: auto'>
-      <div id='child' style='overflow: auto'>
-        <div style='position: absolute'></div>
+    <div id='ancestor'
+         style='overflow: auto; position: relative; height: 100px'>
+      <div id='parent' style='overflow: auto; height: 200px'>
+        <div id="child" style='position: relative; height: 300px'></div>
       </div>
     </div>
   )HTML");
-  PaintLayer* parent = GetPaintLayerByElementId("parent");
-  PaintLayer* child = GetPaintLayerByElementId("child");
+  auto* ancestor = GetPaintLayerByElementId("ancestor");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(ancestor->NeedsReorderOverlayScrollbars());
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent, ancestor)));
+}
 
-  EXPECT_FALSE(parent->IsNonStackedWithInFlowStackedDescendant());
-  EXPECT_FALSE(child->IsNonStackedWithInFlowStackedDescendant());
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_NestedStackedWithOutOfFlowStackedChild) {
+  SetBodyInnerHTML(R"HTML(
+    <div id='ancestor'
+         style='overflow: auto; position: relative; height: 100px'>
+      <div id='parent' style='overflow: auto; position: absolute;
+                             width: 200px; height: 200px'>
+        <div id="child" style='position: absolute; width: 300px; height: 300px'>
+        </div>
+      </div>
+    </div>
+  )HTML");
+  auto* ancestor = GetPaintLayerByElementId("ancestor");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(ancestor->NeedsReorderOverlayScrollbars());
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent, ancestor)));
+}
+
+TEST_P(PaintLayerTest, ReorderOverlayScrollbars_MultipleChildren) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      div { width: 200px; height: 200px; }
+      #parent { overflow: auto; width: 100px; height: 100px; }
+    </style>
+    <div id='parent'>
+      <div id="low-child" style='position: absolute; z-index: 1'></div>
+      <div id="middle-child" style='position: relative; z-index: 2'></div>
+      <div id="high-child" style='position: absolute; z-index: 3'></div>
+    </div>
+  )HTML");
+
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* low_child = GetPaintLayerByElementId("low-child");
+  auto* middle_child = GetPaintLayerByElementId("middle-child");
+  auto* high_child = GetPaintLayerByElementId("high-child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(low_child));
+  // The highest contained child by parent is middle_child because the
+  // absolute-position children are not contained.
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(middle_child),
+              Pointee(ElementsAre(parent)));
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(high_child));
+
+  GetDocument().getElementById("parent")->setAttribute(
+      html_names::kStyleAttr, "position: absolute; z-index: 1");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(low_child));
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(middle_child));
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(high_child));
+
+  GetDocument().getElementById("parent")->setAttribute(html_names::kStyleAttr,
+                                                       "position: absolute");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(low_child));
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(middle_child));
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(high_child),
+              Pointee(ElementsAre(parent)));
+}
+
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_NonStackedWithInFlowDescendant) {
+  SetBodyInnerHTML(R"HTML(
+    <div id='parent' style='overflow: auto; width: 100px; height: 100px'>
+      <div id='child' style='position: relative; height: 200px'></div>
+    </div>
+  )HTML");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
+
+  GetDocument().getElementById("child")->setAttribute(
+      html_names::kStyleAttr, "position: relative; height: 80px");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(child));
+
+  GetDocument().getElementById("child")->setAttribute(
+      html_names::kStyleAttr, "position: relative; width: 200px; height: 80px");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
+
+  GetDocument().getElementById("child")->setAttribute(
+      html_names::kStyleAttr, "width: 200px; height: 80px");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+
+  GetDocument().getElementById("child")->setAttribute(
+      html_names::kStyleAttr, "position: relative; width: 200px; height: 80px");
+  UpdateAllLifecyclePhasesForTest();
+  child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
+}
+
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_NonStackedWithZIndexInFlowDescendant) {
+  SetBodyInnerHTML(R"HTML(
+    <style>#child { position: relative; height: 200px; }</style>
+    <div id='parent' style='overflow: auto; height: 100px'>
+      <div id='child' style='z-index: 1'>
+      </div>
+    </div>
+  )HTML");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
+
+  GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr,
+                                                      "z-index: -1");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(child));
+
+  GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr,
+                                                      "z-index: 2");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent)));
+}
+
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_NonStackedWithOutOfFlowDescendant) {
+  SetBodyInnerHTML(R"HTML(
+    <div id='parent' style='overflow: auto; height: 100px'>
+      <div id='child' style='position: absolute;
+                             width: 200px; height: 200px'></div>
+    </div>
+  )HTML");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(child));
+}
+
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_NonStackedWithNonStackedDescendant) {
+  SetBodyInnerHTML(R"HTML(
+    <div id='parent' style='overflow: auto'>
+      <div id='child' style='overflow: auto'></div>
+    </div>
+  )HTML");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(child));
+}
+
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_NestedNonStackedWithInFlowStackedChild) {
+  SetBodyInnerHTML(R"HTML(
+    <div id='ancestor' style='overflow: auto; height: 100px'>
+      <div id='parent' style='overflow: auto; height: 200px'>
+        <div id="child" style='position: relative; height: 300px'></div>
+      </div>
+    </div>
+  )HTML");
+  auto* ancestor = GetPaintLayerByElementId("ancestor");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_TRUE(ancestor->NeedsReorderOverlayScrollbars());
+  EXPECT_TRUE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_THAT(LayersPaintingOverlayScrollbarsAfter(child),
+              Pointee(ElementsAre(parent, ancestor)));
+}
+
+TEST_P(PaintLayerTest,
+       ReorderOverlayScrollbars_NestedNonStackedWithOutOfFlowStackedChild) {
+  SetBodyInnerHTML(R"HTML(
+    <div id='ancestor' style='overflow: auto; height: 100px'>
+      <div id='parent' style='overflow: auto; height: 200px'>
+        <div id="child" style='position: absolute; width: 300px; height: 300px'>
+        </div>
+      </div>
+    </div>
+  )HTML");
+  auto* ancestor = GetPaintLayerByElementId("ancestor");
+  auto* parent = GetPaintLayerByElementId("parent");
+  auto* child = GetPaintLayerByElementId("child");
+  EXPECT_FALSE(ancestor->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(parent->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(child->NeedsReorderOverlayScrollbars());
+  EXPECT_FALSE(LayersPaintingOverlayScrollbarsAfter(child));
 }
 
 TEST_P(PaintLayerTest, SubsequenceCachingStackingContexts) {
diff --git a/third_party/blink/renderer/core/paint/paint_phase.h b/third_party/blink/renderer/core/paint/paint_phase.h
index bf07703..34e57bf4 100644
--- a/third_party/blink/renderer/core/paint/paint_phase.h
+++ b/third_party/blink/renderer/core/paint/paint_phase.h
@@ -45,7 +45,7 @@
   // Background phase
   //
   // Paint background of the current object and non-self-painting descendants.
-  kBlockBackground = 0,
+  kBlockBackground,
   //
   // The following two values are added besides the normal
   // kBlockBackground to distinguish backgrounds for the object itself
@@ -53,39 +53,40 @@
   // different scroll offsets and clips.
   //
   // Paint background of the current object only.
-  kSelfBlockBackgroundOnly = 1,
+  kSelfBlockBackgroundOnly,
   // Paint backgrounds of non-self-painting descendants only. The painter should
   // call each non-self-painting child's paint method by passing
   // paintInfo.forDescendants() which converts kDescendantBlockBackgroundsOnly
   // to kBlockBackground.
-  kDescendantBlockBackgroundsOnly = 2,
+  kDescendantBlockBackgroundsOnly,
 
   // Float phase
-  kFloat = 3,
+  kFloat,
 
   // Foreground phase
-  kForeground = 4,
+  kForeground,
 
   // Outline phase
   //
   // Paint outline for the current object and non-self-painting descendants.
-  kOutline = 5,
+  kOutline,
   //
   // Similar to the background phase, the following two values are added for
   // painting outlines of the object itself and for descendants.
   //
   // Paint outline for the current object only.
-  kSelfOutlineOnly = 6,
+  kSelfOutlineOnly,
   // Paint outlines of non-self-painting descendants only. The painter should
   // call each non-self-painting child's paint method by passing
   // paintInfo.forDescendants() which converts kDescendantOutlinesOnly to
   // kOutline.
-  kDescendantOutlinesOnly = 7,
+  kDescendantOutlinesOnly,
 
   // The below are auxiliary phases which are used to paint special effects.
-  kSelection = 8,
-  kTextClip = 9,
-  kMask = 10,
+  kOverlayScrollbars,
+  kSelection,
+  kTextClip,
+  kMask,
 
   kMax = kMask,
   // These values must be kept in sync with DisplayItem::Type and
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 36c4cde..2d08ffa 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -169,6 +169,7 @@
  private:
   ALWAYS_INLINE void UpdatePaintOffset();
   ALWAYS_INLINE void UpdateForPaintOffsetTranslation(base::Optional<IntPoint>&);
+  ALWAYS_INLINE bool IsAffectedByOuterViewportBoundsDelta() const;
   ALWAYS_INLINE void UpdatePaintOffsetTranslation(
       const base::Optional<IntPoint>&);
   ALWAYS_INLINE void SetNeedsPaintPropertyUpdateIfNeeded();
@@ -456,6 +457,24 @@
   context_.current.paint_offset = subpixel_accumulation;
 }
 
+bool FragmentPaintPropertyTreeBuilder::IsAffectedByOuterViewportBoundsDelta()
+    const {
+  if (object_.StyleRef().GetPosition() != EPosition::kFixed ||
+      !object_.StyleRef().IsFixedToBottom() ||
+      !object_.GetFrame()->IsMainFrame())
+    return false;
+  // It's not affected by viewport if the container is not the LayoutView.
+  if (context_.current.transform != object_.View()
+                                        ->FirstFragment()
+                                        .PaintProperties()
+                                        ->PaintOffsetTranslation()) {
+    DCHECK_NE(object_.ContainerForFixedPosition(), object_.View());
+    return false;
+  }
+  DCHECK_EQ(object_.ContainerForFixedPosition(), object_.View());
+  return true;
+}
+
 void FragmentPaintPropertyTreeBuilder::UpdatePaintOffsetTranslation(
     const base::Optional<IntPoint>& paint_offset_translation) {
   DCHECK(properties_);
@@ -465,11 +484,8 @@
         FloatSize(ToIntSize(*paint_offset_translation))};
     state.flattens_inherited_transform =
         context_.current.should_flatten_inherited_transform;
-
     state.affected_by_outer_viewport_bounds_delta =
-        object_.StyleRef().GetPosition() == EPosition::kFixed &&
-        object_.StyleRef().IsFixedToBottom();
-
+        IsAffectedByOuterViewportBoundsDelta();
     if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
         RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
       state.rendering_context_id = context_.current.rendering_context_id;
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 4592534..8d6fbfd 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
@@ -6795,4 +6795,45 @@
   }
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, IsAffectedByOuterViewportBoundsDelta) {
+  SetBodyInnerHTML(R"HTML(
+    <style>div { will-change: transform; position: fixed; }</style>
+    <div id="fixed1"></div>
+    <div id="fixed2" style="right: 0"></div>
+    <div id="fixed3" style="bottom: 0"></div>
+    <div id="fixed4" style="bottom: 20px"></div>
+    <div style="transform: translateX(100px)">
+      <div id="fixed5" style="bottom: 0"></div>
+    </div>
+    <iframe></iframe>
+  )HTML");
+  SetChildFrameHTML(R"HTML(
+     <div id="fixed"
+          style="will-change: transform; position: fixed; bottom: 0"></div>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  auto check_result = [&](const ObjectPaintProperties* properties,
+                          bool expected) {
+    ASSERT_TRUE(properties);
+    ASSERT_TRUE(properties->PaintOffsetTranslation());
+    EXPECT_EQ(expected, properties->PaintOffsetTranslation()
+                            ->IsAffectedByOuterViewportBoundsDelta());
+  };
+
+  check_result(PaintPropertiesForElement("fixed1"), false);
+  check_result(PaintPropertiesForElement("fixed2"), false);
+  check_result(PaintPropertiesForElement("fixed3"), true);
+  check_result(PaintPropertiesForElement("fixed4"), true);
+  check_result(PaintPropertiesForElement("fixed5"), false);
+
+  // Fixed elements in subframes are not affected by viewport.
+  check_result(ChildDocument()
+                   .getElementById("fixed")
+                   ->GetLayoutObject()
+                   ->FirstFragment()
+                   .PaintProperties(),
+               false);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
index 2dc2294f..22a3963 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
@@ -124,56 +124,23 @@
 
 void ScrollableAreaPainter::PaintOverflowControls(
     const PaintInfo& paint_info,
-    const IntPoint& paint_offset,
-    bool painting_overlay_controls) {
+    const IntPoint& paint_offset) {
   // Don't do anything if we have no overflow.
-  if (!GetScrollableArea().GetLayoutBox()->HasOverflowClip())
+  const auto& box = *GetScrollableArea().GetLayoutBox();
+  if (!box.HasOverflowClip() ||
+      box.StyleRef().Visibility() != EVisibility::kVisible)
     return;
 
-  IntPoint adjusted_paint_offset = paint_offset;
-  if (painting_overlay_controls)
-    adjusted_paint_offset = GetScrollableArea().CachedOverlayScrollbarOffset();
-
-  CullRect adjusted_cull_rect = paint_info.GetCullRect();
-  adjusted_cull_rect.MoveBy(-adjusted_paint_offset);
-  // Overlay scrollbars paint in a second pass through the layer tree so that
-  // they will paint on top of everything else. If this is the normal painting
-  // pass, paintingOverlayControls will be false, and we should just tell the
-  // root layer that there are overlay scrollbars that need to be painted. That
-  // will cause the second pass through the layer tree to run, and we'll paint
-  // the scrollbars then. In the meantime, cache tx and ty so that the second
-  // pass doesn't need to re-enter the LayoutTree to get it right.
-  if (GetScrollableArea().HasOverlayScrollbars() &&
-      !painting_overlay_controls) {
-    GetScrollableArea().SetCachedOverlayScrollbarOffset(paint_offset);
-    // It's not necessary to do the second pass if the scrollbars paint into
-    // layers.
-    if ((GetScrollableArea().HorizontalScrollbar() &&
-         GetScrollableArea().LayerForHorizontalScrollbar()) ||
-        (GetScrollableArea().VerticalScrollbar() &&
-         GetScrollableArea().LayerForVerticalScrollbar()))
+  // Overlay scrollbars are painted in the dedicated paint phase, and normal
+  // scrollbars are painted in the background paint phase.
+  if (GetScrollableArea().HasOverlayScrollbars()) {
+    if (paint_info.phase != PaintPhase::kOverlayScrollbars)
       return;
-    if (!OverflowControlsIntersectRect(adjusted_cull_rect))
-      return;
-
-    LayoutView* layout_view = GetScrollableArea().GetLayoutBox()->View();
-
-    PaintLayer* painting_root =
-        GetScrollableArea().Layer()->EnclosingLayerWithCompositedLayerMapping(
-            kIncludeSelf);
-    if (!painting_root)
-      painting_root = layout_view->Layer();
-
-    painting_root->SetContainsDirtyOverlayScrollbars(true);
+  } else if (!ShouldPaintSelfBlockBackground(paint_info.phase)) {
     return;
   }
 
-  // This check is required to avoid painting custom CSS scrollbars twice.
-  if (painting_overlay_controls && !GetScrollableArea().HasOverlayScrollbars())
-    return;
-
   GraphicsContext& context = paint_info.context;
-  const auto& box = *GetScrollableArea().GetLayoutBox();
   const auto* fragment = paint_info.FragmentToPaint(box);
   if (!fragment)
     return;
@@ -191,6 +158,9 @@
     }
   }
 
+  CullRect adjusted_cull_rect = paint_info.GetCullRect();
+  adjusted_cull_rect.MoveBy(-paint_offset);
+
   if (GetScrollableArea().HorizontalScrollbar() &&
       !GetScrollableArea().LayerForHorizontalScrollbar()) {
     GetScrollableArea().HorizontalScrollbar()->Paint(context,
@@ -200,51 +170,27 @@
       !GetScrollableArea().LayerForVerticalScrollbar()) {
     GetScrollableArea().VerticalScrollbar()->Paint(context, adjusted_cull_rect);
   }
+
   if (!GetScrollableArea().LayerForScrollCorner()) {
     // We fill our scroll corner with white if we have a scrollbar that doesn't
     // run all the way up to the edge of the box.
-    PaintScrollCorner(context, adjusted_paint_offset, paint_info.GetCullRect());
+    PaintScrollCorner(context, paint_offset, paint_info.GetCullRect());
 
     // Paint our resizer last, since it sits on top of the scroll corner.
-    PaintResizer(context, adjusted_paint_offset, paint_info.GetCullRect());
+    PaintResizer(context, paint_offset, paint_info.GetCullRect());
   }
 }
 
-bool ScrollableAreaPainter::OverflowControlsIntersectRect(
-    const CullRect& cull_rect) const {
-  const IntRect border_box =
-      GetScrollableArea().GetLayoutBox()->PixelSnappedBorderBoxRect(
-          GetScrollableArea().Layer()->SubpixelAccumulation());
-
-  if (cull_rect.Intersects(
-          GetScrollableArea().RectForHorizontalScrollbar(border_box)))
-    return true;
-
-  if (cull_rect.Intersects(
-          GetScrollableArea().RectForVerticalScrollbar(border_box)))
-    return true;
-
-  if (cull_rect.Intersects(GetScrollableArea().ScrollCornerRect()))
-    return true;
-
-  if (cull_rect.Intersects(GetScrollableArea().ResizerCornerRect(
-          border_box, kResizerForPointer)))
-    return true;
-
-  return false;
-}
-
-void ScrollableAreaPainter::PaintScrollCorner(
-    GraphicsContext& context,
-    const IntPoint& paint_offset,
-    const CullRect& adjusted_cull_rect) {
+void ScrollableAreaPainter::PaintScrollCorner(GraphicsContext& context,
+                                              const IntPoint& paint_offset,
+                                              const CullRect& cull_rect) {
   IntRect abs_rect = GetScrollableArea().ScrollCornerRect();
   if (abs_rect.IsEmpty())
     return;
   abs_rect.MoveBy(paint_offset);
 
   if (const auto* scroll_corner = GetScrollableArea().ScrollCorner()) {
-    if (!adjusted_cull_rect.Intersects(abs_rect))
+    if (!cull_rect.Intersects(abs_rect))
       return;
     ScrollbarPainter::PaintIntoRect(*scroll_corner, context,
                                     PhysicalOffset(paint_offset),
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.h b/third_party/blink/renderer/core/paint/scrollable_area_painter.h
index d6c884a..86346c3 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.h
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.h
@@ -26,10 +26,7 @@
       PaintLayerScrollableArea& paint_layer_scrollable_area)
       : scrollable_area_(&paint_layer_scrollable_area) {}
 
-  void PaintOverflowControls(const PaintInfo&,
-                             const IntPoint& paint_offset,
-                             bool painting_overlay_controls);
-
+  void PaintOverflowControls(const PaintInfo&, const IntPoint& paint_offset);
   void PaintResizer(GraphicsContext&,
                     const IntPoint& paint_offset,
                     const CullRect&);
@@ -39,7 +36,6 @@
 
  private:
   void DrawPlatformResizerImage(GraphicsContext&, IntRect resizer_corner_rect);
-  bool OverflowControlsIntersectRect(const CullRect&) const;
 
   PaintLayerScrollableArea& GetScrollableArea() const;
   const DisplayItemClient& DisplayItemClientForCorner() const;
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 76affd2..e573d12 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.cc
+++ b/third_party/blink/renderer/core/script/classic_pending_script.cc
@@ -250,10 +250,13 @@
 
     // It is possible to get back a script resource with integrity metadata
     // for a request with an empty integrity attribute. In that case, the
-    // integrity check should be skipped, so this check ensures that the
-    // integrity attribute isn't empty in addition to checking if the
-    // resource has empty integrity metadata.
-    if (!element->IntegrityAttributeValue().IsEmpty()) {
+    // integrity check should be skipped, as the integrity may not have been
+    // "meant" for this specific request. If the resource is being served from
+    // the preload cache however, we know any associated integrity metadata and
+    // checks were destined for this request, so we cannot skip the integrity
+    // check.
+    if (!element->IntegrityAttributeValue().IsEmpty() ||
+        GetResource()->IsLinkPreload()) {
       integrity_failure_ = GetResource()->IntegrityDisposition() !=
                            ResourceIntegrityDisposition::kPassed;
     }
diff --git a/third_party/blink/renderer/core/script/document_write_intervention.cc b/third_party/blink/renderer/core/script/document_write_intervention.cc
index c2df36e..e4bb7522 100644
--- a/third_party/blink/renderer/core/script/document_write_intervention.cc
+++ b/third_party/blink/renderer/core/script/document_write_intervention.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/script/document_write_intervention.h"
 
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
diff --git a/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h b/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h
index c4df60f1..a01b4a1 100644
--- a/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h
+++ b/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_FETCH_CLIENT_SETTINGS_OBJECT_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_FETCH_CLIENT_SETTINGS_OBJECT_IMPL_H_
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
diff --git a/third_party/blink/renderer/core/script/generate_lapi_grdp.py b/third_party/blink/renderer/core/script/generate_lapi_grdp.py
index 99f20ed..fcb15ba 100755
--- a/third_party/blink/renderer/core/script/generate_lapi_grdp.py
+++ b/third_party/blink/renderer/core/script/generate_lapi_grdp.py
@@ -44,7 +44,10 @@
     # A list of (name, path)
     modules = []
     for root, _, filenames in sorted(os.walk(input_path)):
-        if 'index.mjs' in filenames:
+        # A directory represents a built-in module if
+        #  - it contains index.mjs (web-exposed module) or
+        #  - the directory name is 'internal' (private module)
+        if 'index.mjs' in filenames or re.search(r'\binternal$', root):
             # Get e.g. "kKvStorage" for kv-storage.
             module_name = os.path.relpath(root, input_path)
             module_name = "k" + re.sub(r'\W', '', module_name.title())
diff --git a/third_party/blink/renderer/core/script/layered_api_module.h b/third_party/blink/renderer/core/script/layered_api_module.h
index be89823..7be3348 100644
--- a/third_party/blink/renderer/core/script/layered_api_module.h
+++ b/third_party/blink/renderer/core/script/layered_api_module.h
@@ -15,6 +15,7 @@
 
 enum class Module {
   kBlank,
+  kElementsInternal,
   kElementsSwitch,
   kElementsToast,
   kKvStorage,
diff --git a/third_party/blink/renderer/core/script/layered_api_resources.h b/third_party/blink/renderer/core/script/layered_api_resources.h
index 291f2e2..dc7e79e 100644
--- a/third_party/blink/renderer/core/script/layered_api_resources.h
+++ b/third_party/blink/renderer/core/script/layered_api_resources.h
@@ -29,12 +29,14 @@
 const LayeredAPIResource kLayeredAPIResources[] = {
     {"blank/index.mjs", IDR_LAYERED_API_BLANK_INDEX_MJS, Module::kBlank},
 
+    {"elements/internal/reflection.mjs",
+     IDR_LAYERED_API_ELEMENTS_INTERNAL_REFLECTION_MJS,
+     Module::kElementsInternal},
+
     {"elements/switch/face_utils.mjs",
      IDR_LAYERED_API_ELEMENTS_SWITCH_FACE_UTILS_MJS, Module::kElementsSwitch},
     {"elements/switch/index.mjs", IDR_LAYERED_API_ELEMENTS_SWITCH_INDEX_MJS,
      Module::kElementsSwitch},
-    {"elements/switch/reflection.mjs",
-     IDR_LAYERED_API_ELEMENTS_SWITCH_REFLECTION_MJS, Module::kElementsSwitch},
     {"elements/switch/style.mjs", IDR_LAYERED_API_ELEMENTS_SWITCH_STYLE_MJS,
      Module::kElementsSwitch},
     {"elements/switch/track.mjs", IDR_LAYERED_API_ELEMENTS_SWITCH_TRACK_MJS,
diff --git a/third_party/blink/renderer/core/script/modulator.h b/third_party/blink/renderer/core/script/modulator.h
index 6024d69f..9f67b4e 100644
--- a/third_party/blink/renderer/core/script/modulator.h
+++ b/third_party/blink/renderer/core/script/modulator.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_MODULATOR_H_
 
 #include "base/single_thread_task_runner.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/module_record.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
diff --git a/third_party/blink/renderer/core/script/modulator_impl_base.cc b/third_party/blink/renderer/core/script/modulator_impl_base.cc
index acf6567..0673857b 100644
--- a/third_party/blink/renderer/core/script/modulator_impl_base.cc
+++ b/third_party/blink/renderer/core/script/modulator_impl_base.cc
@@ -64,6 +64,9 @@
     case layered_api::Module::kKvStorage:
       return RuntimeEnabledFeatures::BuiltInModuleKvStorageEnabled(
           GetExecutionContext());
+    case layered_api::Module::kElementsInternal:
+      // Union of conditions of KElementsSwitch and kElementsToast.
+      return RuntimeEnabledFeatures::BuiltInModuleSwitchElementEnabled();
     case layered_api::Module::kElementsSwitch:
       return RuntimeEnabledFeatures::BuiltInModuleSwitchElementEnabled();
     case layered_api::Module::kElementsToast:
@@ -78,6 +81,8 @@
   switch (module) {
     case layered_api::Module::kBlank:
       break;
+    case layered_api::Module::kElementsInternal:
+      break;
     case layered_api::Module::kElementsSwitch:
       UseCounter::Count(GetExecutionContext(),
                         WebFeature::kBuiltInModuleSwitchImported);
diff --git a/third_party/blink/renderer/core/script/pending_script.cc b/third_party/blink/renderer/core/script/pending_script.cc
index a549f857..4eaebf8 100644
--- a/third_party/blink/renderer/core/script/pending_script.cc
+++ b/third_party/blink/renderer/core/script/pending_script.cc
@@ -25,7 +25,7 @@
 
 #include "third_party/blink/renderer/core/script/pending_script.h"
 
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_parser_timing.h"
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/README.md b/third_party/blink/renderer/core/script/resources/layered_api/README.md
index 97186d6b..479818e 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/README.md
+++ b/third_party/blink/renderer/core/script/resources/layered_api/README.md
@@ -22,9 +22,27 @@
 
 ## Which files are bundled
 
-All files under sub-directories which have 'index.mjs' will be included
-in the grdp and thus bundled in the Chromium binary, except for files
-starting with '.', 'README', or 'OWNERS'.
+All files under
+
+- Sub-directories which have 'index.mjs' or
+- Directories of which last path component is 'internal'
+
+will be included in the grdp and thus bundled in the Chrome binary,
+except for files starting with '.', 'README', or 'OWNERS'.
 
 So be careful about binary size increase when you add new files or add more
 contents to existing files.
+
+## What are exposed
+
+All bundled resources are mapped to `std-internal://path-relative-to-here`, and
+`std-internal:` resources are not accessible from the web.  Resources loaded as
+`std-internal:` can import other `std-internal:` resources.
+
+For example, `layered_api/foo/bar/baz.mjs` is mapped to
+`std-internal://foo/bar/baz.mjs`.
+
+All `index.mjs` resources are mapped to `std:directory-name-relative-to-here`
+too, and they are web-exposed.  For example,
+`layered_api/elements/toast/index.mjs` is mapped to `std:elements/toast` as
+well as `std-internal://elements/toast/index.mjs`.
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/reflection.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/internal/reflection.mjs
similarity index 100%
rename from third_party/blink/renderer/core/script/resources/layered_api/elements/switch/reflection.mjs
rename to third_party/blink/renderer/core/script/resources/layered_api/elements/internal/reflection.mjs
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs
index f8e4685d..8db1f41 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs
@@ -6,7 +6,7 @@
  * @file Utilities for form-associated custom elements
  */
 
-import * as reflection from './reflection.mjs';
+import * as reflection from '../internal/reflection.mjs';
 
 function installGetter(proto, propName, getter) {
   Object.defineProperty(
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
index cda06d3e..f0be08f 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 import * as face from './face_utils.mjs';
-import * as reflection from './reflection.mjs';
+import * as reflection from '../internal/reflection.mjs';
 import { SwitchTrack } from './track.mjs';
-import { styleSheetFactory } from './style.mjs';
+import * as style from './style.mjs';
 
 // https://github.com/tkent-google/std-switch/issues/2
 const STATE_ATTR = 'on';
@@ -15,6 +15,7 @@
 const _internals = Symbol();
 const _track = Symbol();
 const _rippleElement = Symbol();
+const _containerElement = Symbol();
 
 export class StdSwitchElement extends HTMLElement {
   // TODO(tkent): The following should be |static fooBar = value;|
@@ -34,6 +35,8 @@
     }
     this[_internals] = this.attachInternals();
     this._initializeDOM();
+
+    this.addEventListener('click', this._onClick);
   }
 
   attributeChangedCallback(attrName, oldValue, newValue) {
@@ -42,25 +45,43 @@
     }
   }
 
+  connectedCallback() {
+    // TODO(tkent): We should not add tabindex attribute.
+    // https://github.com/w3c/webcomponents/issues/762
+    if (!this.hasAttribute('tabindex')) {
+      this.setAttribute('tabindex', '0');
+    }
+  }
+
   // TODO(tkent): Make this private.
   _initializeDOM() {
     let factory = this.ownerDocument;
     let root = this.attachShadow({mode: 'closed'});
-    let container = factory.createElement('span');
-    container.id = 'container';
-    root.appendChild(container);
+    this[_containerElement] = factory.createElement('span');
+    this[_containerElement].id = 'container';
+    root.appendChild(this[_containerElement]);
 
     this[_track] = new SwitchTrack(factory);
-    container.appendChild(this[_track].element);
+    this[_containerElement].appendChild(this[_track].element);
     this[_track].value = this.on;
 
-    let thumbElement = container.appendChild(factory.createElement('span'));
+    let thumbElement = this[_containerElement].appendChild(factory.createElement('span'));
     thumbElement.id = 'thumb';
 
     this[_rippleElement] = thumbElement.appendChild(factory.createElement('span'));
     this[_rippleElement].id = 'ripple';
 
-    root.adoptedStyleSheets = [styleSheetFactory()()];
+    root.adoptedStyleSheets = [style.styleSheetFactory()()];
+  }
+
+  // TODO(tkent): Make this private.
+  _onClick(event) {
+    for (let element of this[_containerElement].querySelectorAll('*')) {
+      style.markTransition(element);
+    }
+    this.on = !this.on;
+    this.dispatchEvent(new Event('input', {bubbles: true}));
+    this.dispatchEvent(new Event('change', {bubbles: true}));
   }
 }
 
@@ -82,3 +103,4 @@
 delete StdSwitchElement.formAssociated;
 delete StdSwitchElement.observedAttributes;
 delete StdSwitchElement.prototype.attributeChangedCallback;
+delete StdSwitchElement.prototype.connectedCallback;
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/style.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/style.mjs
index aba9b45..567e3d2 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/style.mjs
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/style.mjs
@@ -46,6 +46,9 @@
   margin-inline-start: calc(-100% + ${THUMB_MARGIN_START});
   max-inline-size: ${THUMB_WIDTH};
   min-inline-size: ${THUMB_WIDTH};
+}
+
+#thumb.transitioning {
   transition: all linear 0.1s;
 }
 
@@ -74,15 +77,82 @@
   box-sizing: border-box;
   display: inline-block;
   inline-size: 0%;
-  transition: all linear 0.1s;
   vertical-align: top;
 }
 
+#trackFill.transitioning {
+  transition: all linear 0.1s;
+}
+
 :host([on]) #track {
   border: ${TRACK_BORDER_WIDTH} solid ${COLOR_ON};
 }
+
+:host(:focus) {
+  outline-offset: 4px;
+}
+
+:host(:focus) #track {
+  box-shadow: 0 0 0 2px #f8f8f8;
+}
+
+:host([on]:focus) #track {
+  box-shadow: 0 0 0 2px #dddddd;
+}
+
+:host(:focus) #thumb {
+  border: 2px solid black;
+}
+
+:host([on]:focus) #thumb {
+  border: 2px solid ${COLOR_ON};
+}
+
+:host(:not(:focus-visible):focus) {
+  outline: none;
+}
 `);
     }
     return styleSheet;
   };
 }
+
+/**
+ * Add 'transitioning' class to the element, and remove it on 'transitionend'
+ * event.
+ *
+ * TODO(tkent): This doesn't work well with customization by web authors because
+ * class of a shadow element is invisible for web authors. We should apply
+ * custom state.
+ *
+ * @param {!Element} element
+ */
+export function markTransition(element) {
+  const CLASS_NAME = 'transitioning';
+  element.classList.add(CLASS_NAME);
+  let durations = element.computedStyleMap().getAll('transition-duration');
+  if (!durations.some(duration => duration.value >= 0)) {
+    // If the element will have no transitions, we remove CLASS_NAME
+    // immediately.
+    element.classList.remove(CLASS_NAME);
+    return;
+  }
+  // If the element will have transitions, initialize counters and listeners
+  // only once.
+  if (element.runningTransitions !== undefined) {
+    return;
+  }
+  element.runningTransitions = 0;
+  element.addEventListener('transitionrun', e => {
+    if (e.target === element) {
+      ++element.runningTransitions;
+    }
+  });
+  let handleEndOrCancel = e => {
+    if (e.target === element && --element.runningTransitions === 0) {
+      element.classList.remove(CLASS_NAME);
+    }
+  };
+  element.addEventListener('transitionend', handleEndOrCancel);
+  element.addEventListener('transitioncancel', handleEndOrCancel);
+}
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs
index e9c0bb4..a7e0541 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs
@@ -10,6 +10,8 @@
  * @package
  */
 
+import * as reflection from '../internal/reflection.mjs';
+
 const DEFAULT_DURATION = 3000;
 
 function stylesheetFactory() {
@@ -61,6 +63,13 @@
     }
   }
 
+  connectedCallback() {
+    if (!this.hasAttribute('role')) {
+      this.setAttribute('role', 'status');
+    }
+    // TODO(jacksteinberg): use https://github.com/whatwg/html/pull/4658 when implemented
+  }
+
   show({duration = DEFAULT_DURATION} = {}) {
     this.setAttribute('open', '');
     clearTimeout(this.#timeoutID);
@@ -77,14 +86,6 @@
     this.toggleAttribute('open', force);
   }
 
-  get open() {
-    return this.hasAttribute('open');
-  }
-
-  set open(val) {
-    this.toggleAttribute('open', Boolean(val));
-  }
-
   attributeChangedCallback(name, oldValue, newValue) {
     switch (name) {
       case 'open':
@@ -99,6 +100,9 @@
     }
   }
 }
+
+reflection.installBool(StdToastElement.prototype, 'open');
+
 customElements.define('std-toast', StdToastElement);
 
 delete StdToastElement.prototype.attributeChangedCallback;
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/resources.grdp b/third_party/blink/renderer/core/script/resources/layered_api/resources.grdp
index 0be152c3..e883432 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/resources.grdp
+++ b/third_party/blink/renderer/core/script/resources/layered_api/resources.grdp
@@ -7,9 +7,9 @@
        third_party/blink/public/blink_resources.grd.
     -->
   <include name="IDR_LAYERED_API_BLANK_INDEX_MJS" file="../renderer/core/script/resources/layered_api/blank/index.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
+  <include name="IDR_LAYERED_API_ELEMENTS_INTERNAL_REFLECTION_MJS" file="../renderer/core/script/resources/layered_api/elements/internal/reflection.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_ELEMENTS_SWITCH_FACE_UTILS_MJS" file="../renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_ELEMENTS_SWITCH_INDEX_MJS" file="../renderer/core/script/resources/layered_api/elements/switch/index.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
-  <include name="IDR_LAYERED_API_ELEMENTS_SWITCH_REFLECTION_MJS" file="../renderer/core/script/resources/layered_api/elements/switch/reflection.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_ELEMENTS_SWITCH_STYLE_MJS" file="../renderer/core/script/resources/layered_api/elements/switch/style.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_ELEMENTS_SWITCH_TRACK_MJS" file="../renderer/core/script/resources/layered_api/elements/switch/track.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_ELEMENTS_TOAST_INDEX_MJS" file="../renderer/core/script/resources/layered_api/elements/toast/index.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
diff --git a/third_party/blink/renderer/core/scroll/scroll_animator_mac.mm b/third_party/blink/renderer/core/scroll/scroll_animator_mac.mm
index c0d33d0..0d198d97 100644
--- a/third_party/blink/renderer/core/scroll/scroll_animator_mac.mm
+++ b/third_party/blink/renderer/core/scroll/scroll_animator_mac.mm
@@ -332,7 +332,7 @@
 
     double fraction = delta / duration_;
     fraction = clampTo(fraction, 0.0, 1.0);
-    double progress = timing_function_->Evaluate(fraction, 0.001);
+    double progress = timing_function_->Evaluate(fraction);
     [animation_ setCurrentProgress:progress];
   }
 
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index ac26470..32d310ec 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -239,7 +239,7 @@
       (old_style->IsDisplayFlexibleOrGridBox() ||
        old_style->IsDisplayLayoutCustomBox() ||
        new_style->IsDisplayFlexibleOrGridBox() ||
-       old_style->IsDisplayLayoutCustomBox())) {
+       new_style->IsDisplayLayoutCustomBox())) {
     return Difference::kDisplayAffectingDescendantStyles;
   }
   if (!old_style->NonIndependentInheritedEqual(*new_style))
diff --git a/third_party/blink/renderer/core/svg/svg_length.cc b/third_party/blink/renderer/core/svg/svg_length.cc
index 6271a18..1329dda 100644
--- a/third_party/blink/renderer/core/svg/svg_length.cc
+++ b/third_party/blink/renderer/core/svg/svg_length.cc
@@ -179,6 +179,21 @@
   return result;
 }
 
+namespace {
+
+const CSSParserContext* GetSVGAttributeParserContext() {
+  // NOTE(ikilpatrick): We will always parse SVG lengths in the insecure
+  // context mode. If a function/unit/etc will require a secure context check
+  // in the future, plumbing will need to be added.
+  DEFINE_STATIC_LOCAL(
+      const Persistent<CSSParserContext>, svg_parser_context,
+      (MakeGarbageCollected<CSSParserContext>(
+          kSVGAttributeMode, SecureContextMode::kInsecureContext)));
+  return svg_parser_context;
+}
+
+}  // namespace
+
 SVGParsingError SVGLength::SetValueAsString(const String& string) {
   // TODO(fs): Preferably we wouldn't need to special-case the null
   // string (which we'll get for example for removeAttribute.)
@@ -189,13 +204,8 @@
     return SVGParseStatus::kNoError;
   }
 
-  // NOTE(ikilpatrick): We will always parse svg lengths in the insecure
-  // context mode. If a function/unit/etc will require a secure context check
-  // in the future, plumbing will need to be added.
-  auto* svg_parser_context = MakeGarbageCollected<CSSParserContext>(
-      kSVGAttributeMode, SecureContextMode::kInsecureContext);
   const CSSValue* parsed = CSSParser::ParseSingleValue(
-      CSSPropertyID::kX, string, svg_parser_context);
+      CSSPropertyID::kX, string, GetSVGAttributeParserContext());
   const auto* new_value = DynamicTo<CSSPrimitiveValue>(parsed);
   if (!new_value)
     return SVGParseStatus::kExpectedLength;
diff --git a/third_party/blink/renderer/core/testing/data/notifications/file.txt b/third_party/blink/renderer/core/testing/data/notifications/file.txt
new file mode 100644
index 0000000..6f96b7b
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/data/notifications/file.txt
@@ -0,0 +1 @@
+notifications
\ No newline at end of file
diff --git a/third_party/blink/renderer/core/testing/sim/sim_network.cc b/third_party/blink/renderer/core/testing/sim/sim_network.cc
index 5caecb4..637f3e9 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_network.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_network.cc
@@ -84,8 +84,7 @@
   if (!current_request_) {
     client->DidFinishLoading(finish_time, total_encoded_data_length,
                              total_encoded_body_length,
-                             total_decoded_body_length, false,
-                             std::vector<network::cors::PreflightTimingInfo>());
+                             total_decoded_body_length, false, {});
     return;
   }
   current_request_ = nullptr;
diff --git a/third_party/blink/renderer/core/testing/sim/sim_request.cc b/third_party/blink/renderer/core/testing/sim/sim_request.cc
index ea81814..29a12ec 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_request.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_request.cc
@@ -98,10 +98,9 @@
       navigation_body_loader_->Finish();
     } else {
       // TODO(esprehn): Is claiming a request time of 0 okay for tests?
-      client_->DidFinishLoading(
-          base::TimeTicks(), total_encoded_data_length_,
-          total_encoded_data_length_, total_encoded_data_length_, false,
-          std::vector<network::cors::PreflightTimingInfo>());
+      client_->DidFinishLoading(base::TimeTicks(), total_encoded_data_length_,
+                                total_encoded_data_length_,
+                                total_encoded_data_length_, false, {});
     }
   }
   Reset();
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
index d087bdc..2574683 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h"
 
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_trusted_html.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_trusted_script.h"
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
index 3cc03c1..a27005b5 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
@@ -6,7 +6,7 @@
 
 [
     Exposed=(Window, Worker),
-    OriginTrialEnabled=TrustedDOMTypes
+    RuntimeEnabled=TrustedDOMTypes
 ] interface TrustedTypePolicyFactory {
     [RaisesException, Unforgeable] TrustedTypePolicy createPolicy(DOMString policyName, TrustedTypePolicyOptions policyOptions, optional boolean exposed = false);
     [Unforgeable] TrustedTypePolicy getExposedPolicy(DOMString policyName);
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
index 8c51ceb1..45f084de 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -43,7 +43,6 @@
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
-#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
@@ -150,6 +149,16 @@
   DCHECK(context->IsContextThread());
   DCHECK(script_request_url_.IsValid());
   DCHECK(context_proxy_);
+
+  // For nested workers, ensure the inside ResourceFetcher because it may not
+  // have been used yet.
+  // For documents, the ResourceFetcher is always already valid.
+  if (auto* scope = DynamicTo<WorkerGlobalScope>(*context))
+    scope->EnsureFetcher();
+
+  outside_fetch_client_settings_object_ =
+      MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
+          context->Fetcher()->GetProperties().GetFetchClientSettingsObject());
 }
 
 DedicatedWorker::~DedicatedWorker() {
@@ -209,8 +218,6 @@
   // calling into the debugger can cause a breakpoint.
   v8_stack_trace_id_ = ThreadDebugger::From(GetExecutionContext()->GetIsolate())
                            ->StoreCurrentStackTrace("Worker Created");
-  if (auto* scope = DynamicTo<WorkerGlobalScope>(*GetExecutionContext()))
-    scope->EnsureFetcher();
   if (blink::features::IsPlzDedicatedWorkerEnabled()) {
     // For classic script, always use "same-origin" credentials mode.
     // https://html.spec.whatwg.org/C/#fetch-a-classic-worker-script
@@ -234,7 +241,12 @@
     factory_client_->CreateWorkerHost(
         script_request_url_,
         WebSecurityOrigin(GetExecutionContext()->GetSecurityOrigin()),
-        credentials_mode, blob_url_token.PassInterface().PassHandle());
+        credentials_mode,
+        WebSecurityOrigin(
+            outside_fetch_client_settings_object_->GetSecurityOrigin()),
+        outside_fetch_client_settings_object_->GetReferrerPolicy(),
+        KURL(outside_fetch_client_settings_object_->GetOutgoingReferrer()),
+        blob_url_token.PassInterface().PassHandle());
     // Continue in OnScriptLoadStarted() or OnScriptLoadStartFailed().
     return;
   }
@@ -410,17 +422,11 @@
     network::mojom::ReferrerPolicy referrer_policy,
     base::Optional<mojom::IPAddressSpace> response_address_space,
     const String& source_code) {
-  auto* outside_settings_object =
-      MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
-          GetExecutionContext()
-              ->Fetcher()
-              ->GetProperties()
-              .GetFetchClientSettingsObject());
   context_proxy_->StartWorkerGlobalScope(
       CreateGlobalScopeCreationParams(script_url, off_main_thread_fetch_option,
                                       referrer_policy, response_address_space),
-      options_, script_url, *outside_settings_object, v8_stack_trace_id_,
-      source_code);
+      options_, script_url, *outside_fetch_client_settings_object_,
+      v8_stack_trace_id_, source_code);
 }
 
 std::unique_ptr<GlobalScopeCreationParams>
@@ -525,8 +531,9 @@
 }
 
 void DedicatedWorker::Trace(blink::Visitor* visitor) {
-  visitor->Trace(context_proxy_);
   visitor->Trace(options_);
+  visitor->Trace(outside_fetch_client_settings_object_);
+  visitor->Trace(context_proxy_);
   visitor->Trace(classic_script_loader_);
   AbstractWorker::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.h b/third_party/blink/renderer/core/workers/dedicated_worker.h
index a305ae0..1a9de84 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.h
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/worker_options.h"
 #include "third_party/blink/renderer/platform/graphics/begin_frame_provider.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "v8/include/v8-inspector.h"
@@ -142,6 +143,8 @@
 
   const KURL script_request_url_;
   Member<const WorkerOptions> options_;
+  Member<const FetchClientSettingsObjectSnapshot>
+      outside_fetch_client_settings_object_;
   const Member<DedicatedWorkerMessagingProxy> context_proxy_;
 
   Member<WorkerClassicScriptLoader> classic_script_loader_;
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
index 12bd236..03063be 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h"
 
 #include <memory>
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_cache_options.h"
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
index 4e963e8..0347fe0d 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/messaging/transferable_message.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
diff --git a/third_party/blink/renderer/core/workers/global_scope_creation_params.h b/third_party/blink/renderer/core/workers/global_scope_creation_params.h
index 0a1aad3..1e844d51 100644
--- a/third_party/blink/renderer/core/workers/global_scope_creation_params.h
+++ b/third_party/blink/renderer/core/workers/global_scope_creation_params.h
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/unguessable_token.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom-blink.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
diff --git a/third_party/blink/renderer/core/workers/shared_worker_client_holder.cc b/third_party/blink/renderer/core/workers/shared_worker_client_holder.cc
index 08633f82..6f72eb4 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_client_holder.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_client_holder.cc
@@ -35,6 +35,7 @@
 #include "base/logging.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
+#include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom-blink.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_info.mojom-blink.h"
 #include "third_party/blink/public/platform/web_content_security_policy.h"
 #include "third_party/blink/public/platform/web_string.h"
@@ -45,6 +46,9 @@
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/workers/shared_worker.h"
 #include "third_party/blink/renderer/core/workers/shared_worker_client.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
 
 namespace blink {
 
@@ -102,8 +106,19 @@
                          mojo::MakeRequest(&client_ptr, task_runner_),
                          task_runner_);
 
+  auto* outside_fetch_client_settings_object =
+      MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
+          worker->GetExecutionContext()
+              ->Fetcher()
+              ->GetProperties()
+              .GetFetchClientSettingsObject());
+
   connector_->Connect(
-      std::move(info), std::move(client_ptr),
+      std::move(info),
+      mojom::blink::FetchClientSettingsObject::New(
+          outside_fetch_client_settings_object->GetReferrerPolicy(),
+          KURL(outside_fetch_client_settings_object->GetOutgoingReferrer())),
+      std::move(client_ptr),
       worker->GetExecutionContext()->IsSecureContext()
           ? mojom::SharedWorkerCreationContextType::kSecure
           : mojom::SharedWorkerCreationContextType::kNonsecure,
diff --git a/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h b/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h
index a1a7417..b5c0674 100644
--- a/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h
+++ b/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_THREADED_MESSAGING_PROXY_BASE_H_
 
 #include "base/optional.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/web_feature_forward.h"
 #include "third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h"
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index 4a2b32fb..4a8ad69 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -28,7 +28,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_WORKER_GLOBAL_SCOPE_H_
 
 #include <memory>
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom-blink.h"
 #include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.idl b/third_party/blink/renderer/core/workers/worker_global_scope.idl
index 8c27b9af..4523572 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.idl
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.idl
@@ -79,7 +79,7 @@
     readonly attribute FontFaceSet fonts;
 
     // TrustedTypes API: http://github.com/wicg/trusted-types
-    [OriginTrialEnabled=TrustedDOMTypes, Unforgeable] readonly attribute TrustedTypePolicyFactory TrustedTypesWorkers;
+    [RuntimeEnabled=TrustedDOMTypes, Unforgeable] readonly attribute TrustedTypePolicyFactory TrustedTypesWorkers;
 };
 
 WorkerGlobalScope includes WindowOrWorkerGlobalScope;
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
index 7d69a20..2184df6 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -7,7 +7,7 @@
 
 #include <bitset>
 #include "base/single_thread_task_runner.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_cache_options.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
index 239d436..d1ecd27 100644
--- a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
+++ b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
@@ -32,7 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_WORKER_REPORTING_PROXY_H_
 
 #include <memory>
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/web_feature_forward.h"
diff --git a/third_party/blink/renderer/core/workers/worker_thread.h b/third_party/blink/renderer/core/workers/worker_thread.h
index d6cddf3f..45790f6 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.h
+++ b/third_party/blink/renderer/core/workers/worker_thread.h
@@ -35,7 +35,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/thread_annotations.h"
 #include "base/unguessable_token.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_thread_type.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/workers/worklet.cc b/third_party/blink/renderer/core/workers/worklet.cc
index f8aff6c5..7dde393 100644
--- a/third_party/blink/renderer/core/workers/worklet.cc
+++ b/third_party/blink/renderer/core/workers/worklet.cc
@@ -5,8 +5,8 @@
 #include "third_party/blink/renderer/core/workers/worklet.h"
 
 #include "base/single_thread_task_runner.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
diff --git a/third_party/blink/renderer/devtools/front_end/audits/AuditsStartView.js b/third_party/blink/renderer/devtools/front_end/audits/AuditsStartView.js
index e042fe6..4e454f2 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/AuditsStartView.js
+++ b/third_party/blink/renderer/devtools/front_end/audits/AuditsStartView.js
@@ -81,18 +81,19 @@
     const deviceIcon = UI.Icon.create('largeicon-phone');
     const categoriesIcon = UI.Icon.create('largeicon-checkmark');
     const throttlingIcon = UI.Icon.create('largeicon-settings-gear');
+    const auditsDescription = ls
+    `Identify and fix common problems that affect your site's performance, accessibility, and user experience.`;  // crbug.com/972969
 
     const fragment = UI.Fragment.build`
       <div class="vbox audits-start-view">
         <header>
           <div class="audits-logo"></div>
           <div class="audits-start-view-text">
-            <h2>${ls`Audits`}</h2>
-            <p>
-              ${ls`Identify and fix common problems that affect your site's performance,
-                accessibility, and user experience.`}
-              <span class="link" $="learn-more">${ls`Learn more`}</a>
-            </p>
+          <h2>${ls`Audits`}</h2>
+          <p>
+            <span class="text">${auditsDescription}</span>
+            <span class="link" $="learn-more">${ls`Learn more`}</a>
+          </p>
           </div>
         </header>
         <form>
diff --git a/third_party/blink/renderer/devtools/front_end/audits/auditsStartView.css b/third_party/blink/renderer/devtools/front_end/audits/auditsStartView.css
index caae2e4..14ca27d 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/auditsStartView.css
+++ b/third_party/blink/renderer/devtools/front_end/audits/auditsStartView.css
@@ -41,6 +41,11 @@
   margin-top: 0;
 }
 
+.audits-start-view-text .text {
+  display: inline-block;
+  margin-right: 5px;
+}
+
 .audits-start-view form {
   padding: 0 16px;
 }
diff --git a/third_party/blink/renderer/devtools/front_end/audits/audits_strings.grdp b/third_party/blink/renderer/devtools/front_end/audits/audits_strings.grdp
index 41e6503..cef6e70 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/audits_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/audits/audits_strings.grdp
@@ -117,10 +117,6 @@
   <message name="IDS_DEVTOOLS_c2ffe02d76c4f089648f1647b43e4ee5" desc="">
     Best practices
   </message>
-  <message name="IDS_DEVTOOLS_c3669fa42d4becdd2831b9685cf607bb" desc="">
-    Identify and fix common problems that affect your site&apos;s performance,
-                accessibility, and user experience.
-  </message>
   <message name="IDS_DEVTOOLS_c91c7b93c28cd18741b71f727ee81ee3" desc="">
     Reports
   </message>
@@ -130,6 +126,9 @@
   <message name="IDS_DEVTOOLS_d88946b678e4c2f251d4e292e8142291" desc="">
     SEO
   </message>
+  <message name="IDS_DEVTOOLS_deeacee140c7d3a451440dd0e206e256" desc="">
+    Identify and fix common problems that affect your site&apos;s performance, accessibility, and user experience.
+  </message>
   <message name="IDS_DEVTOOLS_e0ac20adce6ffee48c7151b070aa5737" desc="">
     Device
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/axe_core_test_runner/AxeCoreTestRunner.js b/third_party/blink/renderer/devtools/front_end/axe_core_test_runner/AxeCoreTestRunner.js
index 1901d8b..66d2e11 100644
--- a/third_party/blink/renderer/devtools/front_end/axe_core_test_runner/AxeCoreTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/axe_core_test_runner/AxeCoreTestRunner.js
@@ -26,7 +26,7 @@
       helpUrl: rule.helpUrl,
       ruleId: rule.id,
       impact: rule.impact,
-      faildedNodes: AxeCoreTestRunner.processAxeResultNodesArray(rule.nodes)
+      failedNodes: AxeCoreTestRunner.processAxeResultNodesArray(rule.nodes)
     };
   });
   return JSON.stringify(result, undefined, 2);
diff --git a/third_party/blink/renderer/devtools/front_end/changes/changes_strings.grdp b/third_party/blink/renderer/devtools/front_end/changes/changes_strings.grdp
index 2da2df97..8bba0fe 100644
--- a/third_party/blink/renderer/devtools/front_end/changes/changes_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/changes/changes_strings.grdp
@@ -1,36 +1,36 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
-  <message name="IDS_DEVTOOLS_025502db49a6cf61a8245998129bd973" desc="">
-    <ph name="DELETIONS">$1s</ph> deletions (-)
+  <message name="IDS_DEVTOOLS_025502db49a6cf61a8245998129bd973" desc="Deletion text in Changes View of the Changes tab">
+    <ph name="DELETIONS">$1s<ex>2</ex></ph> deletions (-)
   </message>
-  <message name="IDS_DEVTOOLS_5c2892cb638ec999fe22612587cd4a00" desc="">
+  <message name="IDS_DEVTOOLS_5c2892cb638ec999fe22612587cd4a00" desc="Text in Changes View of the Changes tab">
     No changes
   </message>
-  <message name="IDS_DEVTOOLS_70c85e43e99bc0cfcbb656123306eb19" desc="">
-    <ph name="INSERTIONS">$1s</ph> insertion (+),
+  <message name="IDS_DEVTOOLS_70c85e43e99bc0cfcbb656123306eb19" desc="Insertion text in Changes View of the Changes tab">
+    <ph name="INSERTIONS">$1s<ex>1</ex></ph> insertion (+),
   </message>
-  <message name="IDS_DEVTOOLS_af2c230293677261fe33ba6d3fbf02e3" desc="">
-    <ph name="INSERTIONS">$1s</ph> insertions (+),
+  <message name="IDS_DEVTOOLS_af2c230293677261fe33ba6d3fbf02e3" desc="Insertion text in Changes View of the Changes tab">
+    <ph name="INSERTIONS">$1s<ex>2</ex></ph> insertions (+),
   </message>
-  <message name="IDS_DEVTOOLS_c112bb3542e98308d12d5ecb10a67abc" desc="">
+  <message name="IDS_DEVTOOLS_c112bb3542e98308d12d5ecb10a67abc" desc="Title of the 'Changes' tool in the bottom drawer">
     Changes
   </message>
-  <message name="IDS_DEVTOOLS_c73e4e53c15d9971d9293fcff6c0d8c0" desc="">
-    ( … Skipping <ph name="LINES_LENGTH___PADDINGLINES____">$1d</ph> matching lines … )
+  <message name="IDS_DEVTOOLS_c73e4e53c15d9971d9293fcff6c0d8c0" desc="Text in Changes View of the Changes tab">
+    ( … Skipping <ph name="LINES_LENGTH___PADDINGLINES____">$1d<ex>2</ex></ph> matching lines … )
   </message>
-  <message name="IDS_DEVTOOLS_ca012662d53a8bde28b4d82722aaff7e" desc="">
+  <message name="IDS_DEVTOOLS_ca012662d53a8bde28b4d82722aaff7e" desc="The UI destination when right clicking an item that can be revealed">
     Changes drawer
   </message>
-  <message name="IDS_DEVTOOLS_df5f7764a0991b372d30c55eb57f6a55" desc="">
+  <message name="IDS_DEVTOOLS_df5f7764a0991b372d30c55eb57f6a55" desc="Tooltip text that appears when hovering over the largeicon undo button in the Changes View of the Changes tab">
     Revert all changes
   </message>
-  <message name="IDS_DEVTOOLS_e0e5ea9203300d2eb8ced39dd88c214e" desc="">
-    <ph name="DELETIONS">$1s</ph> deletion (-)
+  <message name="IDS_DEVTOOLS_e0e5ea9203300d2eb8ced39dd88c214e" desc="Deletion text in Changes View of the Changes tab">
+    <ph name="DELETIONS">$1s<ex>1</ex></ph> deletion (-)
   </message>
-  <message name="IDS_DEVTOOLS_f550ec70278cc72604795d91ff8dcd30" desc="">
+  <message name="IDS_DEVTOOLS_f550ec70278cc72604795d91ff8dcd30" desc="Text in Changes View of the Changes tab">
     Binary data
   </message>
-  <message name="IDS_DEVTOOLS_fa6711f918fe2018131a4ad0380b9e56" desc="">
-    <ph name="THIS_UISOURCECODE_DISPLAYNAME__">$1s</ph> (from source map)
+  <message name="IDS_DEVTOOLS_fa6711f918fe2018131a4ad0380b9e56" desc="Tooltip in Changes Sidebar of the Changes tab">
+    <ph name="THIS_UISOURCECODE_DISPLAYNAME__">$1s<ex>compile.html</ex></ph> (from source map)
   </message>
 </grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/common/ParsedURL.js b/third_party/blink/renderer/devtools/front_end/common/ParsedURL.js
index 2cd4637..eea78c5 100644
--- a/third_party/blink/renderer/devtools/front_end/common/ParsedURL.js
+++ b/third_party/blink/renderer/devtools/front_end/common/ParsedURL.js
@@ -215,7 +215,8 @@
   static completeURL(baseURL, href) {
     // Return special URLs as-is.
     const trimmedHref = href.trim();
-    if (trimmedHref.startsWith('data:') || trimmedHref.startsWith('blob:') || trimmedHref.startsWith('javascript:'))
+    if (trimmedHref.startsWith('data:') || trimmedHref.startsWith('blob:') || trimmedHref.startsWith('javascript:') ||
+        trimmedHref.startsWith('mailto:'))
       return href;
 
     // Return absolute URLs as-is.
diff --git a/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeElement.js b/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeElement.js
index 9c9c058..7cb30775 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeElement.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeElement.js
@@ -287,16 +287,18 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
-    this.treeOutline.populateTreeElement(this);
+  async onpopulate() {
+    return this.treeOutline.populateTreeElement(this);
   }
 
   /**
    * @override
    */
-  expandRecursively() {
-    this._node.getSubtree(-1, true).then(UI.TreeElement.prototype.expandRecursively.bind(this, Number.MAX_VALUE));
+  async expandRecursively() {
+    await this._node.getSubtree(-1, true);
+    await super.expandRecursively(Number.MAX_VALUE);
   }
 
   /**
@@ -524,9 +526,13 @@
       section.appendItem(Common.UIString('Copy selector'), this._copyCSSPath.bind(this));
       section.appendItem(
           Common.UIString('Copy JS path'), this._copyJSPath.bind(this), !Elements.DOMPath.canGetJSPath(this._node));
+      section.appendItem(ls`Copy styles`, this._copyStyles.bind(this));
     }
-    if (!isShadowRoot)
+    if (!isShadowRoot) {
       section.appendItem(Common.UIString('Copy XPath'), this._copyXPath.bind(this));
+      section.appendItem(ls`Copy full XPath`, this._copyFullXPath.bind(this));
+    }
+
     if (!isShadowRoot) {
       menuItem = copyMenu.clipboardSection().appendItem(
           Common.UIString('Cut element'), treeOutline.performCopyOrCut.bind(treeOutline, true, this._node),
@@ -1632,6 +1638,34 @@
     InspectorFrontendHost.copyText(Elements.DOMPath.xPath(this._node, true));
   }
 
+  _copyFullXPath() {
+    InspectorFrontendHost.copyText(Elements.DOMPath.xPath(this._node, false));
+  }
+
+  async _copyStyles() {
+    const node = this._node;
+    const cssModel = node.domModel().cssModel();
+    const cascade = await cssModel.cachedMatchedCascadeForNode(node);
+    if (!cascade)
+      return;
+    /** @type {!Array<string>} */
+    const lines = [];
+    for (const style of cascade.nodeStyles().reverse()) {
+      for (const property of style.leadingProperties()) {
+        if (!property.parsedOk || property.disabled || !property.activeInStyle() || property.implicit)
+          continue;
+        if (cascade.isInherited(style) && !SDK.cssMetadata().isPropertyInherited(property.name))
+          continue;
+        if (style.parentRule && style.parentRule.isUserAgent())
+          continue;
+        if (cascade.propertyState(property) !== SDK.CSSMatchedStyles.PropertyState.Active)
+          continue;
+        lines.push(`${property.name}: ${property.value};`);
+      }
+    }
+    InspectorFrontendHost.copyText(lines.join('\n'));
+  }
+
   _highlightSearchResults() {
     if (!this._searchQuery || !this._searchHighlightsVisible)
       return;
diff --git a/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js b/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js
index 27390a41..9380824a 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js
@@ -1156,13 +1156,18 @@
 
   /**
    * @param {!Elements.ElementsTreeElement} treeElement
+   * @returns {!Promise}
    */
   populateTreeElement(treeElement) {
     if (treeElement.childCount() || !treeElement.isExpandable())
-      return;
-    treeElement.node().getChildNodes(() => {
-      treeElement.populated = true;
-      this._updateModifiedParentNode(treeElement.node());
+      return Promise.resolve();
+
+    return new Promise(resolve => {
+      treeElement.node().getChildNodes(() => {
+        treeElement.populated = true;
+        this._updateModifiedParentNode(treeElement.node());
+        resolve();
+      });
     });
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/elements/StylePropertyTreeElement.js b/third_party/blink/renderer/devtools/front_end/elements/StylePropertyTreeElement.js
index 11f5e46..24b71b95 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/StylePropertyTreeElement.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/StylePropertyTreeElement.js
@@ -341,8 +341,9 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     // Only populate once and if this property is a shorthand.
     if (this.childCount() || !this.isShorthand)
       return;
diff --git a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
index 86f70e3..f057d55 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
@@ -99,6 +99,9 @@
   <message name="IDS_DEVTOOLS_4757fe07fd492a8be0ea6a760d683d6e" desc="">
     position
   </message>
+  <message name="IDS_DEVTOOLS_4a27b8d01ae86c80074e4c57be48f8c6" desc="">
+    Copy styles
+  </message>
   <message name="IDS_DEVTOOLS_4c1c7d945fcd04d68bd3b3f1d99a55a1" desc="">
     Styles
   </message>
@@ -195,6 +198,9 @@
   <message name="IDS_DEVTOOLS_91d17eff6a1529e8a4f67a5aa4cabec7" desc="">
     No matching property
   </message>
+  <message name="IDS_DEVTOOLS_97d28fbc573f9f331f5b1886b5c1eeef" desc="">
+    Copy full XPath
+  </message>
   <message name="IDS_DEVTOOLS_9a0364b9e99bb480dd25e1f0284c8555" desc="">
     content
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js b/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
index 6a8699e..0d23a25 100644
--- a/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
+++ b/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
@@ -267,8 +267,9 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     const properties = [];
     const eventListener = this._eventListener;
     const runtimeModel = eventListener.domDebuggerModel().runtimeModel();
diff --git a/third_party/blink/renderer/devtools/front_end/layer_viewer/PaintProfilerView.js b/third_party/blink/renderer/devtools/front_end/layer_viewer/PaintProfilerView.js
index 011753a..9d8c6105 100644
--- a/third_party/blink/renderer/devtools/front_end/layer_viewer/PaintProfilerView.js
+++ b/third_party/blink/renderer/devtools/front_end/layer_viewer/PaintProfilerView.js
@@ -450,8 +450,9 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     for (const param in this._logItem.params)
       LayerViewer.LogPropertyTreeElement._appendLogPropertyItem(this, param, this._logItem.params[param]);
   }
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 4c273e9b..69e4ead 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -135,7 +135,8 @@
     const enabledExperiments = Runtime.queryParam('enabledExperiments');
     if (enabledExperiments)
       Runtime.experiments.setServerEnabledExperiments(enabledExperiments.split(';'));
-    Runtime.experiments.setDefaultExperiments(['backgroundServices']);
+    Runtime.experiments.setDefaultExperiments(
+        ['backgroundServices', 'backgroundServicesNotifications', 'backgroundServicesPushMessaging']);
 
     if (Host.isUnderTest() && Runtime.queryParam('test').includes('live-line-level-heap-profile.js'))
       Runtime.experiments.enableForTest('liveHeapProfile');
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js b/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
index 81b20b2..485d8fd 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
@@ -123,6 +123,7 @@
         Object.values(Common.resourceCategories)
             .map(category => ({name: category.title, label: category.shortTitle, title: category.title}));
     this._resourceCategoryFilterUI = new UI.NamedBitSetFilterUI(filterItems, this._networkResourceTypeFiltersSetting);
+    UI.ARIAUtils.setAccessibleName(this._resourceCategoryFilterUI.element(), ls`Resource types to include`);
     this._resourceCategoryFilterUI.addEventListener(
         UI.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
     filterBar.addFilter(this._resourceCategoryFilterUI);
diff --git a/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp b/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
index ab6a4dd..1449e27 100644
--- a/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
@@ -201,6 +201,9 @@
   <message name="IDS_DEVTOOLS_4dea2ac78e9450e62e96c166ef9f5d4c" desc="">
     Manage Header Columns…
   </message>
+  <message name="IDS_DEVTOOLS_4dec8df20c6bc162fd77420b6f17edc5" desc="">
+    Resource types to include
+  </message>
   <message name="IDS_DEVTOOLS_4e88c13fb97f356a81c48bf2644bef51" desc="">
     Connection Start
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js b/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
index c099b59..1dbd573 100644
--- a/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
+++ b/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
@@ -497,9 +497,10 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
-    ObjectUI.ObjectPropertyTreeElement._populate(
+  async onpopulate() {
+    return ObjectUI.ObjectPropertyTreeElement._populate(
         this, this._object, !!this.treeOutline._skipProto, this._linkifier, this._emptyPlaceholder,
         this._ignoreHasOwnProperty, this._extraProperties);
   }
@@ -728,13 +729,14 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     const propertyValue = /** @type {!SDK.RemoteObject} */ (this.property.value);
     console.assert(propertyValue);
     const skipProto = this.treeOutline ? this.treeOutline._skipProto : true;
     const targetValue = this.property.name !== '__proto__' ? propertyValue : this.property.parentObject;
-    ObjectUI.ObjectPropertyTreeElement._populate(
+    await ObjectUI.ObjectPropertyTreeElement._populate(
         this, propertyValue, skipProto, this._linkifier, undefined, undefined, undefined, targetValue);
   }
 
@@ -930,8 +932,9 @@
 
   /**
    * @param {string} originalContent
+   * @returns {!Promise}
    */
-  _editingCommitted(originalContent) {
+  async _editingCommitted(originalContent) {
     const userInput = this._prompt.text();
     if (userInput === originalContent) {
       this._editingCancelled();  // nothing changed, so cancel
@@ -939,7 +942,7 @@
     }
 
     this._editingEnded();
-    this._applyExpression(userInput);
+    await this._applyExpression(userInput);
   }
 
   /**
@@ -1059,9 +1062,10 @@
    * @param {number} fromIndex
    * @param {number} toIndex
    * @param {!Components.Linkifier=} linkifier
+   * @returns {!Promise}
    */
-  static _populateArray(treeNode, object, fromIndex, toIndex, linkifier) {
-    ObjectUI.ArrayGroupingTreeElement._populateRanges(treeNode, object, fromIndex, toIndex, true, linkifier);
+  static async _populateArray(treeNode, object, fromIndex, toIndex, linkifier) {
+    await ObjectUI.ArrayGroupingTreeElement._populateRanges(treeNode, object, fromIndex, toIndex, true, linkifier);
   }
 
   /**
@@ -1072,17 +1076,16 @@
    * @param {boolean} topLevel
    * @param {!Components.Linkifier=} linkifier
    * @this {ObjectUI.ArrayGroupingTreeElement}
+   * @returns {!Promise}
    */
-  static _populateRanges(treeNode, object, fromIndex, toIndex, topLevel, linkifier) {
-    object
-        .callFunctionJSON(
-            packRanges,
-            [
-              {value: fromIndex}, {value: toIndex}, {value: ObjectUI.ArrayGroupingTreeElement._bucketThreshold},
-              {value: ObjectUI.ArrayGroupingTreeElement._sparseIterationThreshold},
-              {value: ObjectUI.ArrayGroupingTreeElement._getOwnPropertyNamesThreshold}
-            ])
-        .then(callback);
+  static async _populateRanges(treeNode, object, fromIndex, toIndex, topLevel, linkifier) {
+    const jsonValue = await object.callFunctionJSON(packRanges, [
+      {value: fromIndex}, {value: toIndex}, {value: ObjectUI.ArrayGroupingTreeElement._bucketThreshold},
+      {value: ObjectUI.ArrayGroupingTreeElement._sparseIterationThreshold},
+      {value: ObjectUI.ArrayGroupingTreeElement._getOwnPropertyNamesThreshold}
+    ]);
+
+    await callback(jsonValue);
 
     /**
      * Note: must declare params as optional.
@@ -1160,25 +1163,28 @@
       return {ranges: ranges, skipGetOwnPropertyNames: skipGetOwnPropertyNames};
     }
 
-    function callback(result) {
+    async function callback(result) {
       if (!result)
         return;
       const ranges = /** @type {!Array.<!Array.<number>>} */ (result.ranges);
       if (ranges.length === 1) {
-        ObjectUI.ArrayGroupingTreeElement._populateAsFragment(treeNode, object, ranges[0][0], ranges[0][1], linkifier);
+        await ObjectUI.ArrayGroupingTreeElement._populateAsFragment(
+            treeNode, object, ranges[0][0], ranges[0][1], linkifier);
       } else {
         for (let i = 0; i < ranges.length; ++i) {
           const fromIndex = ranges[i][0];
           const toIndex = ranges[i][1];
           const count = ranges[i][2];
-          if (fromIndex === toIndex)
-            ObjectUI.ArrayGroupingTreeElement._populateAsFragment(treeNode, object, fromIndex, toIndex, linkifier);
-          else
+          if (fromIndex === toIndex) {
+            await ObjectUI.ArrayGroupingTreeElement._populateAsFragment(
+                treeNode, object, fromIndex, toIndex, linkifier);
+          } else {
             treeNode.appendChild(new ObjectUI.ArrayGroupingTreeElement(object, fromIndex, toIndex, count, linkifier));
+          }
         }
       }
       if (topLevel) {
-        ObjectUI.ArrayGroupingTreeElement._populateNonIndexProperties(
+        await ObjectUI.ArrayGroupingTreeElement._populateNonIndexProperties(
             treeNode, object, result.skipGetOwnPropertyNames, linkifier);
       }
     }
@@ -1293,14 +1299,15 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     if (this._propertyCount >= ObjectUI.ArrayGroupingTreeElement._bucketThreshold) {
-      ObjectUI.ArrayGroupingTreeElement._populateRanges(
+      await ObjectUI.ArrayGroupingTreeElement._populateRanges(
           this, this._object, this._fromIndex, this._toIndex, false, this._linkifier);
       return;
     }
-    ObjectUI.ArrayGroupingTreeElement._populateAsFragment(
+    await ObjectUI.ArrayGroupingTreeElement._populateAsFragment(
         this, this._object, this._fromIndex, this._toIndex, this._linkifier);
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotDataGrids.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotDataGrids.js
index 71e007b..51e47f2 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotDataGrids.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotDataGrids.js
@@ -69,6 +69,7 @@
     this._nodeFilter = new HeapSnapshotModel.NodeFilter();
     this.addEventListener(Profiler.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this);
     this.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this.sortingChanged, this);
+    this.setRowContextMenuCallback(this._populateContextMenu.bind(this));
   }
 
   /**
@@ -134,13 +135,10 @@
 
   /**
    * @param {!UI.ContextMenu} contextMenu
-   * @param {!Event} event
+   * @param {!DataGrid.DataGridNode} gridNode
    */
-  populateContextMenu(contextMenu, event) {
-    const td = event.target.enclosingNodeOrSelfWithNodeName('td');
-    if (!td)
-      return;
-    const node = td.heapSnapshotNode;
+  _populateContextMenu(contextMenu, gridNode) {
+    const node = /** @type {!Profiler.HeapSnapshotGridNode} */ (gridNode);
     contextMenu.revealSection().appendItem(ls`Reveal in Summary view`, () => {
       this._dataDisplayDelegate.showObject(node.snapshotNodeId, ls`Summary`);
     });
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotView.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotView.js
index 8cad326..2922767 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotView.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotView.js
@@ -164,8 +164,6 @@
     this._populate();
     this._searchThrottler = new Common.Throttler(0);
 
-    this.element.addEventListener('contextmenu', this._handleContextMenuEvent.bind(this), true);
-
     for (const existingProfile of this._profiles())
       existingProfile.addEventListener(Profiler.ProfileHeader.Events.ProfileTitleChanged, this._updateControls, this);
   }
@@ -359,16 +357,6 @@
   }
 
   /**
-   * @param {!Event} event
-   */
-  _handleContextMenuEvent(event) {
-    const contextMenu = new UI.ContextMenu(event);
-    if (this._dataGrid)
-      this._dataGrid.populateContextMenu(contextMenu, event);
-    contextMenu.show();
-  }
-
-  /**
    * @override
    * @param {!UI.SearchableView.SearchConfig} searchConfig
    * @param {boolean} shouldJump
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ResourcesSection.js b/third_party/blink/renderer/devtools/front_end/resources/ResourcesSection.js
index d1b37fb..40fe01a 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ResourcesSection.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ResourcesSection.js
@@ -264,8 +264,9 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     this._populated = true;
     for (const child of this._frame.childFrames)
       this._section._frameAdded(child);
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js b/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
index a167c7e..f465d82f 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
@@ -1018,6 +1018,13 @@
 
   /**
    * @override
+   * @param {string} mode
+   */
+  fileChooserOpened(mode) {
+  }
+
+  /**
+   * @override
    * @param {!Protocol.Page.FrameId} frameId
    * @param {string} url
    */
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/ScreenCaptureModel.js b/third_party/blink/renderer/devtools/front_end/sdk/ScreenCaptureModel.js
index 64064d8..6c2fecd 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/ScreenCaptureModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/ScreenCaptureModel.js
@@ -230,6 +230,13 @@
 
   /**
    * @override
+   * @param {string} mode
+   */
+  fileChooserOpened(mode) {
+  }
+
+  /**
+   * @override
    * @param {string} url
    * @param {string} data
    */
diff --git a/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js b/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js
index c5cc6e33..188ba138 100644
--- a/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js
@@ -44,11 +44,13 @@
    * @return {!Element}
    */
   static createCertificateViewerButtonForOrigin(text, origin) {
-    return UI.createTextButton(text, async e => {
+    const certificateButton = UI.createTextButton(text, async e => {
       e.consume();
       const names = await SDK.multitargetNetworkManager.getCertificate(origin);
       InspectorFrontendHost.showCertificateViewer(names);
     }, 'origin-button');
+    UI.ARIAUtils.markAsMenuButton(certificateButton);
+    return certificateButton;
   }
 
   /**
@@ -57,10 +59,12 @@
    * @return {!Element}
    */
   static createCertificateViewerButtonForCert(text, names) {
-    return UI.createTextButton(text, e => {
+    const certificateButton = UI.createTextButton(text, e => {
       e.consume();
       InspectorFrontendHost.showCertificateViewer(names);
     }, 'security-certificate-button');
+    UI.ARIAUtils.markAsMenuButton(certificateButton);
+    return certificateButton;
   }
 
   /**
@@ -598,8 +602,9 @@
         this.contentElement.createChild('div', 'security-explanation-list security-explanations-extra');
 
     // Fill the security summary section.
-    this._summarySection.createChild('div', 'security-summary-section-title').textContent =
-        Common.UIString('Security overview');
+    const summaryDiv = this._summarySection.createChild('div', 'security-summary-section-title');
+    summaryDiv.textContent = ls`Security overview`;
+    UI.ARIAUtils.markAsHeading(summaryDiv, 1);
 
     const lockSpectrum = this._summarySection.createChild('div', 'lock-spectrum');
     lockSpectrum.createChild('div', 'lock-icon lock-icon-secure').title = Common.UIString('Secure');
@@ -611,6 +616,7 @@
         .createChild('div', 'triangle-pointer');
 
     this._summaryText = this._summarySection.createChild('div', 'security-summary-text');
+    UI.ARIAUtils.markAsHeading(this._summaryText, 2);
   }
 
   /**
@@ -665,10 +671,10 @@
     this._securityState = newSecurityState;
     this._summarySection.classList.add('security-summary-' + this._securityState);
     const summaryExplanationStrings = {
-      'unknown': Common.UIString('The security of this page is unknown.'),
-      'insecure': Common.UIString('This page is not secure (broken HTTPS).'),
-      'neutral': Common.UIString('This page is not secure.'),
-      'secure': Common.UIString('This page is secure (valid HTTPS).')
+      'unknown': ls`The security of this page is unknown.`,
+      'insecure': ls`This page is not secure (broken HTTPS).`,
+      'neutral': ls`This page is not secure.`,
+      'secure': ls`This page is secure (valid HTTPS).`
     };
 
     // Use override summary if present, otherwise use base explanation
@@ -736,14 +742,19 @@
       return;
     }
 
-    const requestsAnchor = element.createChild('div', 'security-mixed-content link');
+    const requestsAnchor = element.createChild('div', 'security-mixed-content devtools-link');
+    UI.ARIAUtils.markAsLink(requestsAnchor);
+    requestsAnchor.tabIndex = 0;
     if (filterRequestCount === 1)
       requestsAnchor.textContent = Common.UIString('View %d request in Network Panel', filterRequestCount);
     else
       requestsAnchor.textContent = Common.UIString('View %d requests in Network Panel', filterRequestCount);
 
-    requestsAnchor.href = '';
     requestsAnchor.addEventListener('click', this.showNetworkFilter.bind(this, filterKey));
+    requestsAnchor.addEventListener('keydown', event => {
+      if (isEnterKey(event))
+        this.showNetworkFilter(filterKey, event);
+    });
   }
 
   /**
@@ -776,7 +787,9 @@
     this.registerRequiredCSS('security/lockIcon.css');
 
     const titleSection = this.element.createChild('div', 'title-section');
-    titleSection.createChild('div', 'title-section-header').textContent = ls`Origin`;
+    const titleDiv = titleSection.createChild('div', 'title-section-header');
+    titleDiv.textContent = ls`Origin`;
+    UI.ARIAUtils.markAsHeading(titleDiv, 1);
 
     const originDisplay = titleSection.createChild('div', 'origin-display');
     this._originLockIcon = originDisplay.createChild('span', 'security-property');
@@ -784,19 +797,23 @@
 
     originDisplay.appendChild(Security.SecurityPanel.createHighlightedUrl(origin, originState.securityState));
 
-    const originNetworkButton = titleSection.createChild('div', 'view-network-button');
-    originNetworkButton.appendChild(UI.createTextButton('View requests in Network Panel', e => {
+    const originNetworkDiv = titleSection.createChild('div', 'view-network-button');
+    const originNetworkButton = UI.createTextButton('View requests in Network Panel', e => {
       e.consume();
       const parsedURL = new Common.ParsedURL(origin);
       Network.NetworkPanel.revealAndFilter([
         {filterType: Network.NetworkLogView.FilterType.Domain, filterValue: parsedURL.host},
         {filterType: Network.NetworkLogView.FilterType.Scheme, filterValue: parsedURL.scheme}
       ]);
-    }, 'origin-button'));
+    }, 'origin-button');
+    UI.ARIAUtils.markAsLink(originNetworkButton);
+    originNetworkDiv.appendChild(originNetworkButton);
 
-    if (originState.securityDetails && originState.securityDetails.protocol.length > 0) {
+    if (originState.securityDetails) {
       const connectionSection = this.element.createChild('div', 'origin-view-section');
-      connectionSection.createChild('div', 'origin-view-section-title').textContent = Common.UIString('Connection');
+      const connectionDiv = connectionSection.createChild('div', 'origin-view-section-title');
+      connectionDiv.textContent = ls`Connection`;
+      UI.ARIAUtils.markAsHeading(connectionDiv, 2);
 
       let table = new Security.SecurityDetailsTable();
       connectionSection.appendChild(table.element());
@@ -812,7 +829,9 @@
 
       // Create the certificate section outside the callback, so that it appears in the right place.
       const certificateSection = this.element.createChild('div', 'origin-view-section');
-      certificateSection.createChild('div', 'origin-view-section-title').textContent = Common.UIString('Certificate');
+      const certificateDiv = certificateSection.createChild('div', 'origin-view-section-title');
+      certificateDiv.textContent = ls`Certificate`;
+      UI.ARIAUtils.markAsHeading(certificateDiv, 2);
 
       const sctListLength = originState.securityDetails.signedCertificateTimestampList.length;
       const ctCompliance = originState.securityDetails.certificateTransparencyCompliance;
@@ -820,8 +839,9 @@
       if (sctListLength || ctCompliance !== Protocol.Network.CertificateTransparencyCompliance.Unknown) {
         // Create the Certificate Transparency section outside the callback, so that it appears in the right place.
         sctSection = this.element.createChild('div', 'origin-view-section');
-        sctSection.createChild('div', 'origin-view-section-title').textContent =
-            Common.UIString('Certificate Transparency');
+        const sctDiv = sctSection.createChild('div', 'origin-view-section-title');
+        sctDiv.textContent = ls`Certificate Transparency`;
+        UI.ARIAUtils.markAsHeading(sctDiv, 2);
       }
 
       const sanDiv = this._createSanDiv(originState.securityDetails.sanList);
@@ -873,19 +893,18 @@
 
       // Add link to toggle between displaying of the summary of the SCT(s) and the detailed SCT(s).
       if (sctListLength) {
-        const toggleSctsDetailsLink = sctSection.createChild('div', 'link');
-        toggleSctsDetailsLink.classList.add('sct-toggle');
-        toggleSctsDetailsLink.textContent = Common.UIString('Show full details');
         function toggleSctDetailsDisplay() {
           const isDetailsShown = !sctTableWrapper.classList.contains('hidden');
           if (isDetailsShown)
-            toggleSctsDetailsLink.textContent = Common.UIString('Show full details');
+            toggleSctsDetailsLink.textContent = ls`Show full details`;
           else
-            toggleSctsDetailsLink.textContent = Common.UIString('Hide full details');
+            toggleSctsDetailsLink.textContent = ls`Hide full details`;
           sctSummaryTable.element().classList.toggle('hidden');
           sctTableWrapper.classList.toggle('hidden');
         }
-        toggleSctsDetailsLink.addEventListener('click', toggleSctDetailsDisplay, false);
+        const toggleSctsDetailsLink =
+            UI.createTextButton(ls`Show full details`, toggleSctDetailsDisplay, 'details-toggle');
+        sctSection.appendChild(toggleSctsDetailsLink);
       }
 
       switch (ctCompliance) {
@@ -913,17 +932,22 @@
       // this means that the origin is a non-cryptographic secure origin, e.g.
       // chrome:// or about:.
       const secureSection = this.element.createChild('div', 'origin-view-section');
-      secureSection.createChild('div', 'origin-view-section-title').textContent = Common.UIString('Secure');
-      secureSection.createChild('div').textContent = Common.UIString('This origin is a non-HTTPS secure origin.');
+      const secureDiv = secureSection.createChild('div', 'origin-view-section-title');
+      secureDiv.textContent = ls`Secure`;
+      UI.ARIAUtils.markAsHeading(secureDiv, 2);
+      secureSection.createChild('div').textContent = ls`This origin is a non-HTTPS secure origin.`;
     } else if (originState.securityState !== Protocol.Security.SecurityState.Unknown) {
       const notSecureSection = this.element.createChild('div', 'origin-view-section');
-      notSecureSection.createChild('div', 'origin-view-section-title').textContent = Common.UIString('Not secure');
+      const notSecureDiv = notSecureSection.createChild('div', 'origin-view-section-title');
+      notSecureDiv.textContent = ls`Not secure`;
+      UI.ARIAUtils.markAsHeading(notSecureDiv, 2);
       notSecureSection.createChild('div').textContent =
           Common.UIString('Your connection to this origin is not secure.');
     } else {
       const noInfoSection = this.element.createChild('div', 'origin-view-section');
-      noInfoSection.createChild('div', 'origin-view-section-title').textContent =
-          Common.UIString('No security information');
+      const noInfoDiv = noInfoSection.createChild('div', 'origin-view-section-title');
+      noInfoDiv.textContent = ls`No security information`;
+      UI.ARIAUtils.markAsHeading(noInfoDiv, 2);
       noInfoSection.createChild('div').textContent =
           Common.UIString('No security details are available for this origin.');
     }
@@ -931,7 +955,7 @@
 
   /**
    * @param {!Array<string>} sanList
-   * *return {!Element}
+   * @return {!Element}
    */
   _createSanDiv(sanList) {
     const sanDiv = createElement('div');
@@ -948,19 +972,17 @@
           span.classList.add('truncated-entry');
       }
       if (listIsTruncated) {
-        const truncatedSANToggle = sanDiv.createChild('div', 'link');
-        truncatedSANToggle.href = '';
-
         function toggleSANTruncation() {
           if (sanDiv.classList.contains('truncated-san')) {
             sanDiv.classList.remove('truncated-san');
-            truncatedSANToggle.textContent = Common.UIString('Show less');
+            truncatedSANToggle.textContent = ls`Show less`;
           } else {
             sanDiv.classList.add('truncated-san');
-            truncatedSANToggle.textContent = Common.UIString('Show more (%d total)', sanList.length);
+            truncatedSANToggle.textContent = ls`Show more (${sanList.length} total)`;
           }
         }
-        truncatedSANToggle.addEventListener('click', toggleSANTruncation, false);
+        const truncatedSANToggle = UI.createTextButton(ls`Show more (${sanList.length} total)`, toggleSANTruncation);
+        sanDiv.appendChild(truncatedSANToggle);
         toggleSANTruncation();
       }
     }
diff --git a/third_party/blink/renderer/devtools/front_end/security/mainView.css b/third_party/blink/renderer/devtools/front_end/security/mainView.css
index f06c287..e3e17aa96 100644
--- a/third_party/blink/renderer/devtools/front_end/security/mainView.css
+++ b/third_party/blink/renderer/devtools/front_end/security/mainView.css
@@ -3,6 +3,10 @@
  * found in the LICENSE file.
  */
 
+.devtools-link {
+  display: inline-block;
+}
+
 .security-main-view {
     -webkit-user-select: text;
     overflow-x: hidden;
diff --git a/third_party/blink/renderer/devtools/front_end/security/originView.css b/third_party/blink/renderer/devtools/front_end/security/originView.css
index 299e100..b5495d0f 100644
--- a/third_party/blink/renderer/devtools/front_end/security/originView.css
+++ b/third_party/blink/renderer/devtools/front_end/security/originView.css
@@ -95,8 +95,12 @@
     padding-bottom: 0;
 }
 
+.security-origin-view .details-toggle {
+  margin-left: 126px;
+}
+
 .security-origin-view .sct-toggle {
-    padding-left: 143px;
+    margin-left: 145px;
     padding-top: 5px;
 }
 
diff --git a/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp b/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp
index 46bc61e..a4b6c5c 100644
--- a/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp
@@ -60,9 +60,6 @@
   <message name="IDS_DEVTOOLS_5a70e1c7b4fa71a69851e44cda14dff7" desc="">
     Secure origins
   </message>
-  <message name="IDS_DEVTOOLS_628b50e6d53ec0d0e9b62971872e6b8b" desc="">
-    Show more (<ph name="SANLIST_LENGTH">$1d</ph> total)
-  </message>
   <message name="IDS_DEVTOOLS_62a0282d39568be094470486eaf70c4f" desc="">
     SAN
   </message>
@@ -114,6 +111,9 @@
   <message name="IDS_DEVTOOLS_a7ec70e97f31322dc2e9aaa805b102ab" desc="">
     View <ph name="FILTERREQUESTCOUNT">$1d</ph> request in Network Panel
   </message>
+  <message name="IDS_DEVTOOLS_aa043eca48e5c05602ee440521a2d8fc" desc="">
+    Show more (<ph name="SANLIST_LENGTH">$1s</ph> total)
+  </message>
   <message name="IDS_DEVTOOLS_b92f649b203ca680d444432324771186" desc="">
     This request does not comply with Chrome&apos;s Certificate Transparency policy.
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/source_frame/XMLView.js b/third_party/blink/renderer/devtools/front_end/source_frame/XMLView.js
index ad6a40a..75feb5c3 100644
--- a/third_party/blink/renderer/devtools/front_end/source_frame/XMLView.js
+++ b/third_party/blink/renderer/devtools/front_end/source_frame/XMLView.js
@@ -367,8 +367,9 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     SourceFrame.XMLView.Node.populate(this, this._node, this._xmlView);
     this.appendChild(new SourceFrame.XMLView.Node(this._node, true, this._xmlView));
   }
diff --git a/third_party/blink/renderer/devtools/front_end/sources/NavigatorView.js b/third_party/blink/renderer/devtools/front_end/sources/NavigatorView.js
index 28b251b..19a8d09 100644
--- a/third_party/blink/renderer/devtools/front_end/sources/NavigatorView.js
+++ b/third_party/blink/renderer/devtools/front_end/sources/NavigatorView.js
@@ -920,8 +920,9 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     this._node.populate();
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js b/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js
index 955255e..4a78921 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js
@@ -1939,8 +1939,9 @@
 
   /**
    * @override
+   * @returns {!Promise}
    */
-  onpopulate() {
+  async onpopulate() {
     const content = createElementWithClass('div', 'content');
 
     const first = this._invalidations[0];
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js b/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
index 1fa34a3..21b70d0 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
@@ -56,6 +56,14 @@
 /**
  * @param {!Element} element
  */
+UI.ARIAUtils.markAsMenuButton = function(element) {
+  UI.ARIAUtils.markAsButton(element);
+  element.setAttribute('aria-haspopup', true);
+};
+
+/**
+ * @param {!Element} element
+ */
 UI.ARIAUtils.markAsTab = function(element) {
   element.setAttribute('role', 'tab');
 };
@@ -104,6 +112,13 @@
 };
 
 /**
+ * @param {!Element} element
+ */
+UI.ARIAUtils.markAsMultiSelectable = function(element) {
+  element.setAttribute('aria-multiselectable', 'true');
+};
+
+/**
  * Must be contained in, or owned by, an element with the role listbox.
  * @param {!Element} element
  */
diff --git a/third_party/blink/renderer/devtools/front_end/ui/FilterBar.js b/third_party/blink/renderer/devtools/front_end/ui/FilterBar.js
index a6935638..8393d077 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/FilterBar.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/FilterBar.js
@@ -263,13 +263,17 @@
   constructor(items, setting) {
     super();
     this._filtersElement = createElementWithClass('div', 'filter-bitset-filter');
+    UI.ARIAUtils.markAsListBox(this._filtersElement);
+    UI.ARIAUtils.markAsMultiSelectable(this._filtersElement);
     this._filtersElement.title = Common.UIString(
         '%sClick to select multiple types',
         UI.KeyboardShortcut.shortcutToString('', UI.KeyboardShortcut.Modifiers.CtrlOrMeta));
 
     this._allowedTypes = {};
-    this._typeFilterElements = {};
+    /** @type {!Array.<!Element>} */
+    this._typeFilterElements = [];
     this._addBit(UI.NamedBitSetFilterUI.ALL_TYPES, Common.UIString('All'));
+    this._typeFilterElements[0].tabIndex = 0;
     this._filtersElement.createChild('div', 'filter-bitset-filter-divider');
 
     for (let i = 0; i < items.length; ++i)
@@ -315,9 +319,9 @@
   _settingChanged() {
     const allowedTypes = this._setting.get();
     this._allowedTypes = {};
-    for (const typeName in this._typeFilterElements) {
-      if (allowedTypes[typeName])
-        this._allowedTypes[typeName] = true;
+    for (const element of this._typeFilterElements) {
+      if (allowedTypes[element.typeName])
+        this._allowedTypes[element.typeName] = true;
     }
     this._update();
   }
@@ -327,8 +331,12 @@
       this._allowedTypes = {};
       this._allowedTypes[UI.NamedBitSetFilterUI.ALL_TYPES] = true;
     }
-    for (const typeName in this._typeFilterElements)
-      this._typeFilterElements[typeName].classList.toggle('selected', !!this._allowedTypes[typeName]);
+    for (const element of this._typeFilterElements) {
+      const typeName = element.typeName;
+      const active = !!this._allowedTypes[typeName];
+      element.classList.toggle('selected', active);
+      UI.ARIAUtils.setSelected(element, active);
+    }
     this.dispatchEventToListeners(UI.FilterUI.Events.FilterChanged, null);
   }
 
@@ -339,12 +347,15 @@
    */
   _addBit(name, label, title) {
     const typeFilterElement = this._filtersElement.createChild('span', name);
+    typeFilterElement.tabIndex = -1;
     typeFilterElement.typeName = name;
     typeFilterElement.createTextChild(label);
+    UI.ARIAUtils.markAsOption(typeFilterElement);
     if (title)
       typeFilterElement.title = title;
     typeFilterElement.addEventListener('click', this._onTypeFilterClicked.bind(this), false);
-    this._typeFilterElements[name] = typeFilterElement;
+    typeFilterElement.addEventListener('keydown', this._onTypeFilterKeydown.bind(this), false);
+    this._typeFilterElements.push(typeFilterElement);
   }
 
   /**
@@ -360,6 +371,50 @@
   }
 
   /**
+   * @param {!Event} event
+   */
+  _onTypeFilterKeydown(event) {
+    const element = /** @type {?Element} */ (event.target);
+    if (!element)
+      return;
+    if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
+      if (this._keyFocusNextBit(element, true)) {
+        event.consume(true);
+        return;
+      }
+    } else if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
+      if (this._keyFocusNextBit(element, false)) {
+        event.consume(true);
+        return;
+      }
+    } else if (!(event.key === ' ' || isEnterKey(event))) {
+      return;
+    }
+
+    this._onTypeFilterClicked(event);
+  }
+
+  /**
+   * @param {!Element} target
+   * @param {boolean} selectPrevious
+   * @returns {!boolean}
+   */
+  _keyFocusNextBit(target, selectPrevious) {
+    const index = this._typeFilterElements.indexOf(target);
+    if (index === -1)
+      return false;
+    const nextIndex = selectPrevious ? index - 1 : index + 1;
+    if (nextIndex < 0 || nextIndex >= this._typeFilterElements.length)
+      return false;
+
+    const nextElement = this._typeFilterElements[nextIndex];
+    nextElement.tabIndex = 0;
+    target.tabIndex = -1;
+    nextElement.focus();
+    return true;
+  }
+
+  /**
    * @param {string} typeName
    * @param {boolean} allowMultiSelect
    */
diff --git a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
index 39dc017..a9e56d4 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
@@ -1466,6 +1466,7 @@
     super();
     const root = UI.createShadowRootWithCoreStyles(this, 'ui/closeButton.css');
     this._buttonElement = root.createChild('div', 'close-button');
+    UI.ARIAUtils.setAccessibleName(this._buttonElement, ls`Close`);
     UI.ARIAUtils.markAsButton(this._buttonElement);
     const regularIcon = UI.Icon.create('smallicon-cross', 'default-icon');
     this._hoverIcon = UI.Icon.create('mediumicon-red-cross-hover', 'hover-icon');
diff --git a/third_party/blink/renderer/devtools/front_end/ui/filter.css b/third_party/blink/renderer/devtools/front_end/ui/filter.css
index eb39ece..83e2132 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/filter.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/filter.css
@@ -66,6 +66,10 @@
     overflow: hidden;
 }
 
+.filter-bitset-filter span[data-keyboard-focus="true"] {
+    outline: -webkit-focus-ring-color auto 5px;
+}
+
 .filter-bitset-filter-divider {
     background-color: #ccc;
     height: 16px;
diff --git a/third_party/blink/renderer/devtools/front_end/ui/treeoutline.js b/third_party/blink/renderer/devtools/front_end/ui/treeoutline.js
index 81aa692..14282899 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/treeoutline.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/treeoutline.js
@@ -929,8 +929,9 @@
 
   /**
    * @param {number=} maxDepth
+   * @returns {!Promise}
    */
-  expandRecursively(maxDepth) {
+  async expandRecursively(maxDepth) {
     let item = this;
     const info = {};
     let depth = 0;
@@ -942,8 +943,11 @@
       maxDepth = 3;
 
     while (item) {
+      await item._populateIfNeeded();
+
       if (depth < maxDepth)
         item.expand();
+
       item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
       depth += info.depthChange;
     }
@@ -1135,14 +1139,20 @@
     }
   }
 
-  _populateIfNeeded() {
+  /**
+   * @returns {!Promise}
+   */
+  async _populateIfNeeded() {
     if (this.treeOutline && this._expandable && !this._children) {
       this._children = [];
-      this.onpopulate();
+      await this.onpopulate();
     }
   }
 
-  onpopulate() {
+  /**
+   * @return {!Promise}
+   */
+  async onpopulate() {
     // Overridden by subclasses.
   }
 
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 09a6c481..d31d9b9 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -279,6 +279,7 @@
     "canvas/canvas2d/canvas_rendering_context_2d_test.cc",
     "canvas/htmlcanvas/html_canvas_element_module_test.cc",
     "canvas/offscreencanvas/offscreen_canvas_test.cc",
+    "content_index/content_description_type_converter_test.cc",
     "credentialmanager/credentials_container_test.cc",
     "credentialmanager/password_credential_test.cc",
     "csspaint/document_paint_definition_test.cc",
@@ -345,7 +346,6 @@
     "mediastream/webaudio_media_stream_audio_sink_test.cc",
     "nfc/nfc_proxy_test.cc",
     "notifications/notification_data_test.cc",
-    "notifications/notification_image_loader_test.cc",
     "notifications/notification_resources_loader_test.cc",
     "payments/abort_test.cc",
     "payments/can_make_payment_test.cc",
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 31e073b..ee0cf22 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -1696,7 +1696,8 @@
 
   PaintLayer* layer = ToLayoutBox(layout_object_)->Layer();
 
-  HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
+  HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive |
+                         HitTestRequest::kRetargetForInert);
   HitTestLocation location(point);
   HitTestResult hit_test_result = HitTestResult(request, location);
   layer->HitTest(location, hit_test_result,
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
index 00c9489..7861b5b1 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
@@ -71,8 +71,8 @@
     const String& name,
     WorkletAnimationOptions options,
     scoped_refptr<SerializedScriptValue> serialized_state,
-    const std::vector<base::Optional<base::TimeDelta>>& local_times,
-    const WTF::Vector<Timing>& timings) {
+    const Vector<base::Optional<base::TimeDelta>>& local_times,
+    const Vector<Timing>& timings) {
   DCHECK(!animators_.at(animation_id));
   Animator* animator =
       CreateInstance(name, options, serialized_state, local_times, timings);
@@ -107,13 +107,13 @@
     }
 
     // Down casting to blink type
-    WTF::Vector<Timing> timings = (static_cast<WorkletAnimationEffectTimings*>(
-                                       animation.effect_timings.get()))
-                                      ->GetTimings()
-                                      ->data;
+    Vector<Timing> timings = (static_cast<WorkletAnimationEffectTimings*>(
+                                  animation.effect_timings.get()))
+                                 ->GetTimings()
+                                 ->data;
     DCHECK_GE(timings.size(), 1u);
 
-    std::vector<base::Optional<base::TimeDelta>> local_times(
+    Vector<base::Optional<base::TimeDelta>> local_times(
         static_cast<int>(timings.size()), base::nullopt);
 
     CreateAnimatorFor(id, name, options, nullptr /* serialized_state */,
@@ -163,7 +163,7 @@
 
     AnimationWorkletDispatcherOutput::AnimationState animation_output(
         worklet_animation_id);
-    animation_output.local_times = animator->GetLocalTimes();
+    animator->GetLocalTimes(animation_output.local_times);
     output->animations.push_back(animation_output);
   }
 }
@@ -243,8 +243,8 @@
     const String& name,
     WorkletAnimationOptions options,
     scoped_refptr<SerializedScriptValue> serialized_state,
-    const std::vector<base::Optional<base::TimeDelta>>& local_times,
-    const WTF::Vector<Timing>& timings) {
+    const Vector<base::Optional<base::TimeDelta>>& local_times,
+    const Vector<Timing>& timings) {
   DCHECK(IsContextThread());
   AnimatorDefinition* definition = animator_definitions_.at(name);
   if (!definition)
@@ -328,9 +328,11 @@
       }
     }
 
+    Vector<base::Optional<base::TimeDelta>> local_times;
+    animator->GetLocalTimes(local_times);
     target_global_scope->CreateAnimatorFor(
         animation_id, animator->name(), animator->options(), serialized_state,
-        animator->GetLocalTimes(), animator->GetTimings());
+        std::move(local_times), animator->GetTimings());
   }
   animators_.clear();
 }
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
index 54e98d0..f03f90e 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
@@ -67,14 +67,14 @@
       const String& name,
       WorkletAnimationOptions options,
       scoped_refptr<SerializedScriptValue> serialized_state,
-      const std::vector<base::Optional<base::TimeDelta>>& local_times,
+      const Vector<base::Optional<base::TimeDelta>>& local_times,
       const Vector<Timing>& timings);
   Animator* CreateAnimatorFor(
       int animation_id,
       const String& name,
       WorkletAnimationOptions options,
       scoped_refptr<SerializedScriptValue> serialized_state,
-      const std::vector<base::Optional<base::TimeDelta>>& local_times,
+      const Vector<base::Optional<base::TimeDelta>>& local_times,
       const Vector<Timing>& timings);
   typedef HeapHashMap<String, Member<AnimatorDefinition>> DefinitionMap;
   DefinitionMap animator_definitions_;
diff --git a/third_party/blink/renderer/modules/animationworklet/animator.cc b/third_party/blink/renderer/modules/animationworklet/animator.cc
index f43a7c7..1956a7c 100644
--- a/third_party/blink/renderer/modules/animationworklet/animator.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animator.cc
@@ -14,14 +14,13 @@
 
 namespace blink {
 
-Animator::Animator(
-    v8::Isolate* isolate,
-    AnimatorDefinition* definition,
-    v8::Local<v8::Value> instance,
-    const String& name,
-    WorkletAnimationOptions options,
-    const std::vector<base::Optional<base::TimeDelta>>& local_times,
-    const Vector<Timing>& timings)
+Animator::Animator(v8::Isolate* isolate,
+                   AnimatorDefinition* definition,
+                   v8::Local<v8::Value> instance,
+                   const String& name,
+                   WorkletAnimationOptions options,
+                   const Vector<base::Optional<base::TimeDelta>>& local_times,
+                   const Vector<Timing>& timings)
     : definition_(definition),
       instance_(isolate, instance),
       name_(name),
@@ -64,19 +63,10 @@
     return false;
   }
 
-  output->local_times = GetLocalTimes();
+  GetLocalTimes(output->local_times);
   return true;
 }
 
-std::vector<base::Optional<base::TimeDelta>> Animator::GetLocalTimes() const {
-  std::vector<base::Optional<base::TimeDelta>> local_times;
-  local_times.reserve(group_effect_->getChildren().size());
-  for (const auto& effect : group_effect_->getChildren()) {
-    local_times.push_back(effect->local_time());
-  }
-  return local_times;
-}
-
 Vector<Timing> Animator::GetTimings() const {
   Vector<Timing> timings;
   timings.ReserveInitialCapacity(group_effect_->getChildren().size());
diff --git a/third_party/blink/renderer/modules/animationworklet/animator.h b/third_party/blink/renderer/modules/animationworklet/animator.h
index f41fb97..c60aa77 100644
--- a/third_party/blink/renderer/modules/animationworklet/animator.h
+++ b/third_party/blink/renderer/modules/animationworklet/animator.h
@@ -31,7 +31,7 @@
            v8::Local<v8::Value> instance,
            const String& name,
            WorkletAnimationOptions options,
-           const std::vector<base::Optional<base::TimeDelta>>& local_times,
+           const Vector<base::Optional<base::TimeDelta>>& local_times,
            const Vector<Timing>& timings);
   ~Animator();
   void Trace(blink::Visitor*);
@@ -44,7 +44,19 @@
                double current_time,
                AnimationWorkletDispatcherOutput::AnimationState* output);
   v8::Local<v8::Value> State(v8::Isolate*, ExceptionState&);
-  std::vector<base::Optional<base::TimeDelta>> GetLocalTimes() const;
+
+  template <typename T>
+  void GetLocalTimes(T& local_times) const {
+    local_times.clear();
+
+    const auto& children = group_effect_->getChildren();
+    local_times.resize(children.size());
+
+    for (wtf_size_t i = 0; i < children.size(); i++) {
+      local_times[i] = children[i]->local_time();
+    }
+  }
+
   Vector<Timing> GetTimings() const;
   bool IsStateful() const;
 
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_group_effect.cc b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect.cc
index 680d19e..fc42f92d 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_group_effect.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect.cc
@@ -7,7 +7,7 @@
 namespace blink {
 
 WorkletGroupEffect::WorkletGroupEffect(
-    const std::vector<base::Optional<base::TimeDelta>>& local_times,
+    const Vector<base::Optional<base::TimeDelta>>& local_times,
     const Vector<Timing>& timings) {
   DCHECK_GE(local_times.size(), 1u);
   DCHECK_EQ(local_times.size(), timings.size());
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_group_effect.h b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect.h
index c42b0d7b..e1f205f 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_group_effect.h
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect.h
@@ -18,7 +18,7 @@
 
  public:
   explicit WorkletGroupEffect(
-      const std::vector<base::Optional<base::TimeDelta>>& local_times,
+      const Vector<base::Optional<base::TimeDelta>>& local_times,
       const Vector<Timing>& timings);
   const HeapVector<Member<WorkletAnimationEffect>>& getChildren() {
     return effects_;
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
index 3bfc5b37..bd0161d9 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
@@ -4,26 +4,17 @@
 
 #include "third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h"
 
-#include "skia/ext/image_operations.h"
+#include "base/time/time.h"
 #include "third_party/blink/public/common/manifest/manifest_icon_selector.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader.h"
 #include "third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h"
 #include "third_party/blink/renderer/modules/manifest/image_resource.h"
 #include "third_party/blink/renderer/modules/manifest/image_resource_type_converters.h"
-#include "third_party/blink/renderer/platform/graphics/color_behavior.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
-#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
-#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
-#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
-#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/threading.h"
@@ -32,28 +23,19 @@
 
 namespace {
 
-constexpr uint32_t kIconFetchTimeoutInMs = 30000;
+constexpr base::TimeDelta kIconFetchTimeout = base::TimeDelta::FromSeconds(30);
 constexpr int kMinimumIconSizeInPx = 0;
 
-// Because including base::ClampToRange would be a dependency violation.
-int ClampToRange(const int value, const int min, const int max) {
-  return std::min(std::max(value, min), max);
-}
-
 }  // namespace
 
-BackgroundFetchIconLoader::BackgroundFetchIconLoader() = default;
-BackgroundFetchIconLoader::~BackgroundFetchIconLoader() {
-  // We should've called Stop() before the destructor is invoked.
-  DCHECK(stopped_ || icon_callback_.is_null());
-}
+BackgroundFetchIconLoader::BackgroundFetchIconLoader()
+    : threaded_icon_loader_(MakeGarbageCollected<ThreadedIconLoader>()) {}
 
 void BackgroundFetchIconLoader::Start(
     BackgroundFetchBridge* bridge,
     ExecutionContext* execution_context,
     HeapVector<Member<ManifestImageResource>> icons,
     IconCallback icon_callback) {
-  DCHECK(!stopped_);
   DCHECK_GE(icons.size(), 1u);
   DCHECK(bridge);
 
@@ -68,17 +50,16 @@
     ExecutionContext* execution_context,
     IconCallback icon_callback,
     const WebSize& icon_display_size_pixels) {
-  icon_display_size_pixels_ = icon_display_size_pixels;
-
-  // If |icon_display_size_pixels_| is empty then no image will be displayed by
+  // If |icon_display_size_pixels| is empty then no image will be displayed by
   // the UI powering Background Fetch. Bail out immediately.
-  if (icon_display_size_pixels_.IsEmpty()) {
+  if (icon_display_size_pixels.IsEmpty()) {
     std::move(icon_callback)
         .Run(SkBitmap(), -1 /* ideal_to_chosen_icon_size_times_hundred */);
     return;
   }
 
-  KURL best_icon_url = PickBestIconForDisplay(execution_context);
+  KURL best_icon_url = PickBestIconForDisplay(execution_context,
+                                              icon_display_size_pixels.height);
   if (best_icon_url.IsEmpty()) {
     // None of the icons provided was suitable.
     std::move(icon_callback)
@@ -88,10 +69,6 @@
 
   icon_callback_ = std::move(icon_callback);
 
-  ResourceLoaderOptions resource_loader_options;
-  if (execution_context->IsWorkerGlobalScope())
-    resource_loader_options.request_initiator_context = kWorkerContext;
-
   ResourceRequest resource_request(best_icon_url);
   resource_request.SetRequestContext(mojom::RequestContextType::IMAGE);
   resource_request.SetPriority(ResourceLoadPriority::kMedium);
@@ -100,16 +77,17 @@
   resource_request.SetCredentialsMode(
       network::mojom::CredentialsMode::kInclude);
   resource_request.SetSkipServiceWorker(true);
+  resource_request.SetTimeoutInterval(kIconFetchTimeout);
 
-  threadable_loader_ = MakeGarbageCollected<ThreadableLoader>(
-      *execution_context, this, resource_loader_options);
-  threadable_loader_->SetTimeout(
-      base::TimeDelta::FromMilliseconds(kIconFetchTimeoutInMs));
-  threadable_loader_->Start(resource_request);
+  threaded_icon_loader_->Start(execution_context, resource_request,
+                               icon_display_size_pixels,
+                               WTF::Bind(&BackgroundFetchIconLoader::DidGetIcon,
+                                         WrapWeakPersistent(this)));
 }
 
 KURL BackgroundFetchIconLoader::PickBestIconForDisplay(
-    ExecutionContext* execution_context) {
+    ExecutionContext* execution_context,
+    int ideal_size_pixels) {
   std::vector<Manifest::ImageResource> icons;
   for (auto& icon : icons_) {
     // Update the src of |icon| to include the base URL in case relative paths
@@ -128,120 +106,27 @@
   }
 
   return KURL(ManifestIconSelector::FindBestMatchingSquareIcon(
-      std::move(icons), icon_display_size_pixels_.height, kMinimumIconSizeInPx,
+      std::move(icons), ideal_size_pixels, kMinimumIconSizeInPx,
       Manifest::ImageResource::Purpose::ANY));
 }
 
 void BackgroundFetchIconLoader::Stop() {
-  if (stopped_)
-    return;
-
-  stopped_ = true;
-  if (threadable_loader_) {
-    threadable_loader_->Cancel();
-    threadable_loader_ = nullptr;
-  }
+  threaded_icon_loader_->Stop();
 }
 
-void BackgroundFetchIconLoader::DidReceiveData(const char* data,
-                                               unsigned length) {
-  if (!data_)
-    data_ = SharedBuffer::Create();
-  data_->Append(data, length);
-}
-
-void BackgroundFetchIconLoader::DidFinishLoading(uint64_t resource_identifier) {
-  if (stopped_)
-    return;
-
-  if (!data_) {
-    RunCallbackWithEmptyBitmap();
+void BackgroundFetchIconLoader::DidGetIcon(SkBitmap icon, double resize_scale) {
+  if (icon.isNull()) {
+    std::move(icon_callback_).Run(icon, -1);
     return;
   }
 
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      Thread::Current()->GetTaskRunner();
-
-  worker_pool::PostTask(
-      FROM_HERE,
-      CrossThreadBindOnce(
-          &BackgroundFetchIconLoader::DecodeAndResizeImageOnBackgroundThread,
-          WrapCrossThreadPersistent(this), std::move(task_runner),
-          SegmentReader::CreateFromSharedBuffer(std::move(data_))));
+  int ideal_to_chosen_icon_size_times_hundred = std::round(resize_scale) * 100;
+  std::move(icon_callback_).Run(icon, ideal_to_chosen_icon_size_times_hundred);
 }
 
-void BackgroundFetchIconLoader::DecodeAndResizeImageOnBackgroundThread(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    scoped_refptr<SegmentReader> data) {
-  DCHECK(task_runner);
-  DCHECK(data);
-
-  std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
-      std::move(data), /* data_complete= */ true,
-      ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth,
-      ColorBehavior::TransformToSRGB());
-
-  int64_t ideal_to_chosen_icon_size_times_hundred = -1;
-  if (decoder) {
-    ImageFrame* image_frame = decoder->DecodeFrameBufferAtIndex(0);
-    if (image_frame) {
-      decoded_icon_ = image_frame->Bitmap();
-
-      int width = decoded_icon_.width();
-      int height = decoded_icon_.height();
-
-      // If the |decoded_icon_| is larger than |icon_display_size_pixels_|
-      // permits, we need to resize it as well. This can be done synchronously
-      // given that we're on a background thread already.
-      double scale = std::min(
-          static_cast<double>(icon_display_size_pixels_.width) / width,
-          static_cast<double>(icon_display_size_pixels_.height) / height);
-
-      ideal_to_chosen_icon_size_times_hundred = std::round(scale * 100.0);
-
-      if (scale < 1) {
-        width = ClampToRange(scale * width, 1, icon_display_size_pixels_.width);
-        height =
-            ClampToRange(scale * height, 1, icon_display_size_pixels_.height);
-
-        // Use the RESIZE_GOOD quality allowing the implementation to pick an
-        // appropriate method for the resize. Can be increased to RESIZE_BETTER
-        // or RESIZE_BEST if the quality looks poor.
-        decoded_icon_ = skia::ImageOperations::Resize(
-            decoded_icon_, skia::ImageOperations::RESIZE_GOOD, width, height);
-      }
-    }
-  }
-
-  PostCrossThreadTask(
-      *task_runner, FROM_HERE,
-      CrossThreadBindOnce(&BackgroundFetchIconLoader::RunCallback,
-                          WrapCrossThreadPersistent(this),
-                          ideal_to_chosen_icon_size_times_hundred));
-}
-
-void BackgroundFetchIconLoader::DidFail(const ResourceError& error) {
-  RunCallbackWithEmptyBitmap();
-}
-
-void BackgroundFetchIconLoader::DidFailRedirectCheck() {
-  RunCallbackWithEmptyBitmap();
-}
-
-void BackgroundFetchIconLoader::RunCallback(
-    int64_t ideal_to_chosen_icon_size_times_hundred) {
-  // If this has been stopped it is not desirable to trigger further work,
-  // there is a shutdown of some sort in progress.
-  if (stopped_)
-    return;
-
-  std::move(icon_callback_)
-      .Run(decoded_icon_, ideal_to_chosen_icon_size_times_hundred);
-}
-
-void BackgroundFetchIconLoader::RunCallbackWithEmptyBitmap() {
-  DCHECK(decoded_icon_.isNull());
-  RunCallback(-1 /* ideal_to_chosen_icon_size_times_hundred */);
+void BackgroundFetchIconLoader::Trace(Visitor* visitor) {
+  visitor->Trace(icons_);
+  visitor->Trace(threaded_icon_loader_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h
index 8e18843..c6ee8d86 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h
@@ -5,26 +5,16 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BACKGROUND_FETCH_BACKGROUND_FETCH_ICON_LOADER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BACKGROUND_FETCH_BACKGROUND_FETCH_ICON_LOADER_H_
 
-#include <memory>
-
-#include "third_party/blink/renderer/core/loader/threadable_loader.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader_client.h"
+#include "third_party/blink/renderer/core/loader/threaded_icon_loader.h"
 #include "third_party/blink/renderer/modules/background_fetch/background_fetch_type_converters.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/shared_buffer.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 
 namespace blink {
 
 class BackgroundFetchBridge;
-class SegmentReader;
-struct WebSize;
 
 class MODULES_EXPORT BackgroundFetchIconLoader final
-    : public GarbageCollectedFinalized<BackgroundFetchIconLoader>,
-      public ThreadableLoaderClient {
-  USING_GARBAGE_COLLECTED_MIXIN(BackgroundFetchIconLoader);
-
+    : public GarbageCollectedFinalized<BackgroundFetchIconLoader> {
  public:
   // The bitmap may be empty if the request failed or the image data could not
   // be decoded. The int64_t returned is the scale of the ideal to chosen icon,
@@ -33,7 +23,6 @@
   using IconCallback = base::OnceCallback<void(const SkBitmap&, int64_t)>;
 
   BackgroundFetchIconLoader();
-  ~BackgroundFetchIconLoader() override;
 
   // Asynchronously download an icon from the given url, decodes the loaded
   // data, and passes the bitmap to the given callback.
@@ -46,62 +35,28 @@
   // be run.
   void Stop();
 
-  // ThreadableLoaderClient interface.
-  void DidReceiveData(const char* data, unsigned length) override;
-  void DidFinishLoading(uint64_t resource_identifier) override;
-  void DidFail(const ResourceError& error) override;
-  void DidFailRedirectCheck() override;
-
-  void Trace(blink::Visitor* visitor) override {
-    visitor->Trace(threadable_loader_);
-    visitor->Trace(icons_);
-    ThreadableLoaderClient::Trace(visitor);
-  }
+  void Trace(Visitor* visitor);
 
  private:
   friend class BackgroundFetchIconLoaderTest;
 
-  void RunCallback(int64_t ideal_to_chosen_icon_size_times_hundred);
-  void RunCallbackWithEmptyBitmap();
-
   // Callback for BackgroundFetchBridge::GetIconDisplaySize()
   void DidGetIconDisplaySizeIfSoLoadIcon(
       ExecutionContext* execution_context,
       IconCallback callback,
       const WebSize& icon_display_size_pixels);
 
+  void DidGetIcon(SkBitmap icon, double resize_scale);
+
   // Picks the best icon from the list of developer provided icons, for current
-  // display, given the ideal |icon_display_size_pixels_|, and returns its KURL.
+  // display, given the |ideal_size_pixels|, and returns its KURL.
   // If none of the icons is appropriate, this returns an empty URL.
-  KURL PickBestIconForDisplay(ExecutionContext* execution_context);
-
-  // Decodes the |data| to a SkBitmap using the image decoders and resizes it to
-  // be no larger than |icon_display_size_pixels_|.
-  void DecodeAndResizeImageOnBackgroundThread(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      scoped_refptr<SegmentReader> data);
-
-  // Called when the image has been decoded and resized on a background thread.
-  void DidFinishDecoding();
+  KURL PickBestIconForDisplay(ExecutionContext* execution_context,
+                              int ideal_size_pixels);
 
   HeapVector<Member<ManifestImageResource>> icons_;
+  Member<ThreadedIconLoader> threaded_icon_loader_;
   IconCallback icon_callback_;
-
-  Member<ThreadableLoader> threadable_loader_;
-
-  // Size at which the icon will be presented to the user.
-  WebSize icon_display_size_pixels_;
-
-  // Data received from the ThreadableLoader. Will be invalidated when decoding
-  // of the image data starts.
-  scoped_refptr<SharedBuffer> data_;
-
-  // Result of decoding the icon on the background thread.
-  SkBitmap decoded_icon_;
-
-  // Whether the icon loading operation has been stopped. The process should
-  // be aborted then, and |icon_callback_| must not be invoked anymore.
-  bool stopped_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader_test.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader_test.cc
index d521526..c9a1a02a 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader_test.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader_test.cc
@@ -91,9 +91,9 @@
   KURL PickRightIcon(HeapVector<Member<ManifestImageResource>> icons,
                      const WebSize& ideal_display_size) {
     loader_->icons_ = std::move(icons);
-    loader_->icon_display_size_pixels_ = ideal_display_size;
 
-    return loader_->PickBestIconForDisplay(GetContext());
+    return loader_->PickBestIconForDisplay(GetContext(),
+                                           ideal_display_size.height);
   }
 
   void LoadIcon(const KURL& url,
diff --git a/third_party/blink/renderer/modules/content_index/BUILD.gn b/third_party/blink/renderer/modules/content_index/BUILD.gn
index b244445f..42ca7af 100644
--- a/third_party/blink/renderer/modules/content_index/BUILD.gn
+++ b/third_party/blink/renderer/modules/content_index/BUILD.gn
@@ -6,6 +6,8 @@
 
 blink_modules_sources("content_index") {
   sources = [
+    "content_description_type_converter.cc",
+    "content_description_type_converter.h",
     "content_index.cc",
     "content_index.h",
     "service_worker_registration_content_index.cc",
diff --git a/third_party/blink/renderer/modules/content_index/OWNERS b/third_party/blink/renderer/modules/content_index/OWNERS
index 3871b892..67eed44 100644
--- a/third_party/blink/renderer/modules/content_index/OWNERS
+++ b/third_party/blink/renderer/modules/content_index/OWNERS
@@ -2,5 +2,8 @@
 peter@chromium.org
 rayankans@chromium.org
 
+per-file *_type_converter*.*=set noparent
+per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
+
 # TEAM: platform-capabilities@chromium.org
 # COMPONENT: Blink>ContentIndexing
diff --git a/third_party/blink/renderer/modules/content_index/content_description.idl b/third_party/blink/renderer/modules/content_index/content_description.idl
index f128cad..84ee066d3 100644
--- a/third_party/blink/renderer/modules/content_index/content_description.idl
+++ b/third_party/blink/renderer/modules/content_index/content_description.idl
@@ -12,10 +12,10 @@
 };
 
 dictionary ContentDescription {
-    DOMString id;
-    DOMString title;
-    DOMString description;
-    ContentCategory category;
-    USVString iconUrl;
-    USVString launchUrl;
+    required DOMString id;
+    required DOMString title;
+    required DOMString description;
+    required ContentCategory category;
+    required USVString iconUrl;
+    required USVString launchUrl;
 };
diff --git a/third_party/blink/renderer/modules/content_index/content_description_type_converter.cc b/third_party/blink/renderer/modules/content_index/content_description_type_converter.cc
new file mode 100644
index 0000000..d5aedb52
--- /dev/null
+++ b/third_party/blink/renderer/modules/content_index/content_description_type_converter.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 "third_party/blink/renderer/modules/content_index/content_description_type_converter.h"
+
+#include "third_party/blink/public/mojom/content_index/content_index.mojom-blink.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace mojo {
+
+namespace {
+
+blink::mojom::ContentCategory GetContentCategory(const WTF::String& category) {
+  if (category == "homepage")
+    return blink::mojom::ContentCategory::HOME_PAGE;
+  if (category == "article")
+    return blink::mojom::ContentCategory::ARTICLE;
+  if (category == "video")
+    return blink::mojom::ContentCategory::VIDEO;
+  if (category == "audio")
+    return blink::mojom::ContentCategory::AUDIO;
+
+  NOTREACHED();
+  return blink::mojom::ContentCategory::ARTICLE;
+}
+
+WTF::String GetContentCategory(blink::mojom::ContentCategory category) {
+  switch (category) {
+    case blink::mojom::ContentCategory::HOME_PAGE:
+      return "homepage";
+    case blink::mojom::ContentCategory::ARTICLE:
+      return "article";
+    case blink::mojom::ContentCategory::VIDEO:
+      return "video";
+    case blink::mojom::ContentCategory::AUDIO:
+      return "audio";
+  }
+}
+
+}  // namespace
+
+blink::mojom::blink::ContentDescriptionPtr TypeConverter<
+    blink::mojom::blink::ContentDescriptionPtr,
+    const blink::ContentDescription*>::Convert(const blink::ContentDescription*
+                                                   description) {
+  auto result = blink::mojom::blink::ContentDescription::New();
+  result->id = description->id();
+  result->title = description->title();
+  result->description = description->description();
+  result->category = GetContentCategory(description->category());
+  result->icon_url = description->iconUrl();
+  result->launch_url = description->launchUrl();
+
+  return result;
+}
+
+blink::ContentDescription*
+TypeConverter<blink::ContentDescription*,
+              blink::mojom::blink::ContentDescriptionPtr>::
+    Convert(const blink::mojom::blink::ContentDescriptionPtr& description) {
+  auto* result = blink::MakeGarbageCollected<blink::ContentDescription>();
+  result->setId(description->id);
+  result->setTitle(description->title);
+  result->setDescription(description->description);
+  result->setCategory(GetContentCategory(description->category));
+  result->setIconUrl(description->icon_url);
+  result->setLaunchUrl(description->launch_url);
+  return result;
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/renderer/modules/content_index/content_description_type_converter.h b/third_party/blink/renderer/modules/content_index/content_description_type_converter.h
new file mode 100644
index 0000000..4acc056
--- /dev/null
+++ b/third_party/blink/renderer/modules/content_index/content_description_type_converter.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CONTENT_INDEX_CONTENT_DESCRIPTION_TYPE_CONVERTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_CONTENT_INDEX_CONTENT_DESCRIPTION_TYPE_CONVERTER_H_
+
+#include "third_party/blink/public/mojom/content_index/content_index.mojom-blink-forward.h"
+#include "third_party/blink/renderer/modules/content_index/content_description.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+
+namespace mojo {
+
+template <>
+struct MODULES_EXPORT TypeConverter<blink::mojom::blink::ContentDescriptionPtr,
+                                    const blink::ContentDescription*> {
+  static blink::mojom::blink::ContentDescriptionPtr Convert(
+      const blink::ContentDescription* description);
+};
+
+template <>
+struct MODULES_EXPORT
+    TypeConverter<blink::ContentDescription*,
+                  blink::mojom::blink::ContentDescriptionPtr> {
+  static blink::ContentDescription* Convert(
+      const blink::mojom::blink::ContentDescriptionPtr& description);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_CONTENT_INDEX_CONTENT_DESCRIPTION_TYPE_CONVERTER_H_
diff --git a/third_party/blink/renderer/modules/content_index/content_description_type_converter_test.cc b/third_party/blink/renderer/modules/content_index/content_description_type_converter_test.cc
new file mode 100644
index 0000000..6611cee
--- /dev/null
+++ b/third_party/blink/renderer/modules/content_index/content_description_type_converter_test.cc
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/content_index/content_description_type_converter.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/content_index/content_index.mojom-blink.h"
+#include "third_party/blink/renderer/modules/content_index/content_description.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+const blink::ContentDescription* CreateDescription(const WTF::String& category,
+                                                   const WTF::String& url) {
+  auto* description = blink::MakeGarbageCollected<blink::ContentDescription>();
+  description->setId("id");
+  description->setTitle("title");
+  description->setDescription("description");
+  description->setCategory(category);
+  description->setIconUrl(url);
+  description->setLaunchUrl(url);
+  return description;
+}
+
+bool operator==(const ContentDescription& cd1, const ContentDescription& cd2) {
+  return cd1.id() == cd2.id() && cd1.title() == cd2.title() &&
+         cd1.description() == cd2.description() &&
+         cd1.category() == cd2.category() && cd1.iconUrl() == cd2.iconUrl() &&
+         cd1.launchUrl() == cd2.launchUrl();
+}
+
+TEST(ContentDescriptionConversionTest, RoundTrip) {
+  auto* description = CreateDescription("homepage", "https://example.com/");
+  auto mojo_description = mojom::blink::ContentDescription::From(description);
+  ASSERT_TRUE(mojo_description);
+  auto* round_trip_description =
+      mojo_description.To<blink::ContentDescription*>();
+  EXPECT_EQ(*description, *round_trip_description);
+}
+
+TEST(ContentDescriptionConversionTest, EnumRoundTrip) {
+  WTF::Vector<WTF::String> categories = {"homepage", "article", "video",
+                                         "audio"};
+  for (const auto& category : categories) {
+    auto* description = CreateDescription(category, "https://example.com/");
+    auto* round_trip_description =
+        mojom::blink::ContentDescription::From(description)
+            .To<blink::ContentDescription*>();
+    EXPECT_EQ(*description, *round_trip_description);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/content_index/content_index.cc b/third_party/blink/renderer/modules/content_index/content_index.cc
index 85496ea..4660ebd8 100644
--- a/third_party/blink/renderer/modules/content_index/content_index.cc
+++ b/third_party/blink/renderer/modules/content_index/content_index.cc
@@ -4,16 +4,51 @@
 
 #include "third_party/blink/renderer/modules/content_index/content_index.h"
 
+#include "base/optional.h"
+#include "base/time/time.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/loader/threaded_icon_loader.h"
+#include "third_party/blink/renderer/modules/content_index/content_description_type_converter.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 
 namespace blink {
 
+namespace {
+
+constexpr base::TimeDelta kIconFetchTimeout = base::TimeDelta::FromSeconds(30);
+
+// Validates |description|. If there is an error, an error message to be passed
+// to a TypeError is passed. Otherwise a null string is returned.
+WTF::String ValidateDescription(const ContentDescription& description,
+                                ExecutionContext* execution_context) {
+  if (description.id().IsEmpty())
+    return "ID cannot be empty";
+
+  if (description.title().IsEmpty())
+    return "Title cannot be empty";
+
+  if (description.description().IsEmpty())
+    return "Description cannot be empty";
+
+  if (description.iconUrl().IsEmpty())
+    return "Invalid icon URL provided";
+
+  if (description.launchUrl().IsEmpty())
+    return "Invalid launch URL provided";
+
+  // TODO(crbug.com/973844): Add origin checks.
+
+  return WTF::String();
+}
+
+}  // namespace
+
 ContentIndex::ContentIndex(ServiceWorkerRegistration* registration,
                            scoped_refptr<base::SequencedTaskRunner> task_runner)
     : registration_(registration), task_runner_(std::move(task_runner)) {
@@ -32,12 +67,74 @@
                                           "the ServiceWorkerRegistration."));
   }
 
-  // TODO(crbug.com/973844): Fetch the icon and add a struct trait to convert
-  // from `ContentDescription` to `mojom::blink::ContentDescriptionPtr`.
-  return ScriptPromise::RejectWithDOMException(
-      script_state, MakeGarbageCollected<DOMException>(
-                        DOMExceptionCode::kNotSupportedError,
-                        "ContentIndex::add is not yet implemented"));
+  WTF::String description_error =
+      ValidateDescription(*description, registration_->GetExecutionContext());
+  if (!description_error.IsNull()) {
+    return ScriptPromise::Reject(
+        script_state, V8ThrowException::CreateTypeError(
+                          script_state->GetIsolate(), description_error));
+  }
+
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise promise = resolver->Promise();
+
+  KURL icon_url =
+      registration_->GetExecutionContext()->CompleteURL(description->iconUrl());
+  ResourceRequest resource_request(icon_url);
+  resource_request.SetRequestContext(mojom::RequestContextType::IMAGE);
+  resource_request.SetPriority(ResourceLoadPriority::kMedium);
+  resource_request.SetTimeoutInterval(kIconFetchTimeout);
+
+  auto* threaded_icon_loader = MakeGarbageCollected<ThreadedIconLoader>();
+  // TODO(crbug.com/973844): Find the ideal icon dimensions.
+  threaded_icon_loader->Start(
+      registration_->GetExecutionContext(), resource_request,
+      /* resize_dimensions= */ base::nullopt,
+      WTF::Bind(&ContentIndex::DidGetIcon, WrapPersistent(this),
+                WrapPersistent(resolver), WrapPersistent(threaded_icon_loader),
+                mojom::blink::ContentDescription::From(description)));
+
+  return promise;
+}
+
+void ContentIndex::DidGetIcon(ScriptPromiseResolver* resolver,
+                              ThreadedIconLoader* loader,
+                              mojom::blink::ContentDescriptionPtr description,
+                              SkBitmap icon,
+                              double resize_scale) {
+  ScriptState* script_state = resolver->GetScriptState();
+  ScriptState::Scope scope(script_state);
+
+  if (icon.isNull()) {
+    resolver->Reject(V8ThrowException::CreateTypeError(
+        script_state->GetIsolate(), "Icon could not be loaded"));
+    return;
+  }
+
+  GetService()->Add(registration_->RegistrationId(), std::move(description),
+                    icon,
+                    WTF::Bind(&ContentIndex::DidAdd, WrapPersistent(this),
+                              WrapPersistent(resolver)));
+}
+
+void ContentIndex::DidAdd(ScriptPromiseResolver* resolver,
+                          mojom::blink::ContentIndexError error) {
+  ScriptState* script_state = resolver->GetScriptState();
+  ScriptState::Scope scope(script_state);
+
+  switch (error) {
+    case mojom::blink::ContentIndexError::NONE:
+      resolver->Resolve();
+      return;
+    case mojom::blink::ContentIndexError::STORAGE_ERROR:
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError,
+          "Failed to add description due to I/O error."));
+      return;
+    case mojom::blink::ContentIndexError::SERVICE_WORKER_UNAVAILABLE:
+      NOTREACHED();
+      return;
+  }
 }
 
 ScriptPromise ContentIndex::deleteDescription(ScriptState* script_state,
@@ -66,9 +163,19 @@
   ScriptState* script_state = resolver->GetScriptState();
   ScriptState::Scope scope(script_state);
 
-  resolver->Reject(MakeGarbageCollected<DOMException>(
-      DOMExceptionCode::kNotSupportedError,
-      "ContentIndex::delete is not yet implemented"));
+  switch (error) {
+    case mojom::blink::ContentIndexError::NONE:
+      resolver->Resolve();
+      return;
+    case mojom::blink::ContentIndexError::STORAGE_ERROR:
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError,
+          "Failed to delete description due to I/O error."));
+      return;
+    case mojom::blink::ContentIndexError::SERVICE_WORKER_UNAVAILABLE:
+      NOTREACHED();
+      return;
+  }
 }
 
 ScriptPromise ContentIndex::getDescriptions(ScriptState* script_state) {
@@ -98,9 +205,24 @@
   ScriptState* script_state = resolver->GetScriptState();
   ScriptState::Scope scope(script_state);
 
-  resolver->Reject(MakeGarbageCollected<DOMException>(
-      DOMExceptionCode::kNotSupportedError,
-      "ContentIndex::getDescriptions is not yet implemented"));
+  HeapVector<Member<ContentDescription>> blink_descriptions;
+  blink_descriptions.ReserveCapacity(descriptions.size());
+  for (const auto& description : descriptions)
+    blink_descriptions.push_back(description.To<blink::ContentDescription*>());
+
+  switch (error) {
+    case mojom::blink::ContentIndexError::NONE:
+      resolver->Resolve(std::move(blink_descriptions));
+      return;
+    case mojom::blink::ContentIndexError::STORAGE_ERROR:
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError,
+          "Failed to get descriptions due to I/O error."));
+      return;
+    case mojom::blink::ContentIndexError::SERVICE_WORKER_UNAVAILABLE:
+      NOTREACHED();
+      return;
+  }
 }
 
 void ContentIndex::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/content_index/content_index.h b/third_party/blink/renderer/modules/content_index/content_index.h
index eb57c2917..8ffc627 100644
--- a/third_party/blink/renderer/modules/content_index/content_index.h
+++ b/third_party/blink/renderer/modules/content_index/content_index.h
@@ -18,6 +18,7 @@
 class ScriptPromiseResolver;
 class ScriptState;
 class ServiceWorkerRegistration;
+class ThreadedIconLoader;
 
 class ContentIndex final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
@@ -39,6 +40,13 @@
   mojom::blink::ContentIndexService* GetService();
 
   // Callbacks.
+  void DidGetIcon(ScriptPromiseResolver* resolver,
+                  ThreadedIconLoader* loader,
+                  mojom::blink::ContentDescriptionPtr description,
+                  SkBitmap icon,
+                  double resize_scale);
+  void DidAdd(ScriptPromiseResolver* resolver,
+              mojom::blink::ContentIndexError error);
   void DidDeleteDescription(ScriptPromiseResolver* resolver,
                             mojom::blink::ContentIndexError error);
   void DidGetDescriptions(
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
index ef16513..3fab4d4 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
@@ -101,8 +101,6 @@
                                          const ImageResourceObserver& observer,
                                          const FloatSize& container_size,
                                          const CSSStyleValueVector* data) {
-  DCHECK(!RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled());
-
   if (!document_definition_map_.Contains(name))
     return nullptr;
 
@@ -234,10 +232,13 @@
 
 WorkletGlobalScopeProxy* PaintWorklet::CreateGlobalScope() {
   DCHECK(NeedsToCreateGlobalScope());
-  // It does not matter which thread creates its global scopes first, here we
-  // choose to have the worker thread global scopes created first.
+  // The main thread global scopes must be created first so that they are at the
+  // front of the vector.  This is because SelectNewGlobalScope selects global
+  // scopes from the beginning of the vector.  If this code is changed to put
+  // the main thread global scopes at the end, then SelectNewGlobalScope must
+  // also be changed.
   if (!RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled() ||
-      GetNumberOfGlobalScopes() >= kNumGlobalScopesPerThread) {
+      GetNumberOfGlobalScopes() < kNumGlobalScopesPerThread) {
     return MakeGarbageCollected<PaintWorkletGlobalScopeProxy>(
         To<Document>(GetExecutionContext())->GetFrame(), ModuleResponsesMap(),
         GetNumberOfGlobalScopes() + 1);
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
index c9fd510..fcd47429 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
@@ -201,6 +201,203 @@
   EXPECT_TRUE(generator->HasAlpha());
 }
 
+TEST_F(PaintWorkletTest, ConsistentGlobalScopeOnMainThread) {
+  PaintWorklet* paint_worklet_to_test =
+      PaintWorklet::From(*GetFrame().GetDocument()->domWindow());
+
+  Vector<Persistent<PaintWorkletGlobalScope>> global_scopes;
+  for (size_t i = 0; i < PaintWorklet::kNumGlobalScopesPerThread; ++i) {
+    paint_worklet_to_test->AddGlobalScopeForTesting();
+    global_scopes.push_back(
+        PaintWorkletGlobalScopeProxy::From(
+            paint_worklet_to_test->GetGlobalScopesForTesting()[i])
+            ->global_scope());
+  }
+
+  String foo0 = R"JS(registerPaint('foo', class {
+        static get inputProperties() { return ['--foo0']; }
+        paint() {}
+      });)JS";
+  String foo1 = R"JS(registerPaint('foo', class {
+        static get inputProperties() { return ['--foo1']; }
+        paint() {}
+      });)JS";
+  String bar = R"JS(registerPaint('bar', class {
+        static get inputProperties() { return ['--bar']; }
+        paint() {}
+      });)JS";
+
+  global_scopes[0]->ScriptController()->Evaluate(
+      ScriptSourceCode(foo0), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_TRUE(global_scopes[0]->FindDefinition("foo"));
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
+
+  global_scopes[1]->ScriptController()->Evaluate(
+      ScriptSourceCode(foo1), SanitizeScriptErrors::kDoNotSanitize);
+
+  // foo0 and foo1 have the same name but different definitions, therefore
+  // this definition must become invalid.
+  EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
+
+  global_scopes[0]->ScriptController()->Evaluate(
+      ScriptSourceCode(bar), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_TRUE(global_scopes[0]->FindDefinition("bar"));
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
+
+  global_scopes[1]->ScriptController()->Evaluate(
+      ScriptSourceCode(bar), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
+}
+
+TEST_F(PaintWorkletTest, ConsistentGlobalScopeCrossThread) {
+  PaintWorklet* paint_worklet_to_test =
+      PaintWorklet::From(*GetFrame().GetDocument()->domWindow());
+
+  Vector<Persistent<PaintWorkletGlobalScope>> global_scopes;
+  for (size_t i = 0; i < PaintWorklet::kNumGlobalScopesPerThread; ++i) {
+    paint_worklet_to_test->AddGlobalScopeForTesting();
+    global_scopes.push_back(
+        PaintWorkletGlobalScopeProxy::From(
+            paint_worklet_to_test->GetGlobalScopesForTesting()[i])
+            ->global_scope());
+  }
+
+  String foo0 = R"JS(registerPaint('foo', class {
+        static get inputProperties() { return ['--foo0']; }
+        paint() {}
+      });)JS";
+  String foo1 = R"JS(registerPaint('foo', class {
+        static get inputProperties() { return ['--foo1']; }
+        paint() {}
+      });)JS";
+  String bar0 = R"JS(registerPaint('bar', class {
+        static get inputProperties() { return ['--bar0']; }
+        paint() {}
+      });)JS";
+  String loo0 = R"JS(registerPaint('loo', class {
+        static get inputProperties() { return ['--loo0']; }
+        paint() {}
+      });)JS";
+  String loo1 = R"JS(registerPaint('loo', class {
+        static get inputProperties() { return ['--loo1']; }
+        paint() {}
+      });)JS";
+  String gar0 = R"JS(registerPaint('gar', class {
+        static get inputProperties() { return ['--gar0']; }
+        paint() {}
+      });)JS";
+
+  // Definition invalidated before cross thread check
+  global_scopes[0]->ScriptController()->Evaluate(
+      ScriptSourceCode(foo0), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_TRUE(global_scopes[0]->FindDefinition("foo"));
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
+
+  global_scopes[1]->ScriptController()->Evaluate(
+      ScriptSourceCode(foo1), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
+
+  CSSPaintDefinition* definition = global_scopes[0]->FindDefinition("foo");
+  Vector<String> foo_custom_properties;
+  for (const auto& s : definition->CustomInvalidationProperties()) {
+    foo_custom_properties.push_back(s);
+  }
+
+  paint_worklet_to_test->RegisterMainThreadDocumentPaintDefinition(
+      "foo", definition->NativeInvalidationProperties(), foo_custom_properties,
+      definition->InputArgumentTypes(),
+      definition->GetPaintRenderingContext2DSettings()->alpha());
+
+  EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
+
+  // Definition invalidated by cross thread check
+  global_scopes[0]->ScriptController()->Evaluate(
+      ScriptSourceCode(bar0), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_TRUE(global_scopes[0]->FindDefinition("bar"));
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
+
+  global_scopes[1]->ScriptController()->Evaluate(
+      ScriptSourceCode(bar0), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
+
+  definition = global_scopes[0]->FindDefinition("bar");
+
+  // Manually change the custom properties
+  Vector<String> bar_custom_properties({"--bar1"});
+
+  paint_worklet_to_test->RegisterMainThreadDocumentPaintDefinition(
+      "bar", definition->NativeInvalidationProperties(), bar_custom_properties,
+      definition->InputArgumentTypes(),
+      definition->GetPaintRenderingContext2DSettings()->alpha());
+
+  // Although the main thread definitions were the same, the definition sent
+  // cross thread differed from the main thread definitions so it must become
+  // invalid.
+  EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
+
+  // Definition invalidated by second main thread call after cross thread check
+  global_scopes[0]->ScriptController()->Evaluate(
+      ScriptSourceCode(loo0), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_TRUE(global_scopes[0]->FindDefinition("loo"));
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("loo"));
+
+  definition = global_scopes[0]->FindDefinition("loo");
+  Vector<String> loo_custom_properties;
+  for (const auto& s : definition->CustomInvalidationProperties()) {
+    loo_custom_properties.push_back(s);
+  }
+
+  paint_worklet_to_test->RegisterMainThreadDocumentPaintDefinition(
+      "loo", definition->NativeInvalidationProperties(), loo_custom_properties,
+      definition->InputArgumentTypes(),
+      definition->GetPaintRenderingContext2DSettings()->alpha());
+
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("loo"));
+
+  global_scopes[1]->ScriptController()->Evaluate(
+      ScriptSourceCode(loo1), SanitizeScriptErrors::kDoNotSanitize);
+
+  // Although the first main thread call and the cross thread definition are the
+  // same, the second main thread call differs so the definition must become
+  // invalid
+  EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("loo"));
+
+  // Definition invalidated by cross thread check before second main thread call
+  global_scopes[0]->ScriptController()->Evaluate(
+      ScriptSourceCode(gar0), SanitizeScriptErrors::kDoNotSanitize);
+
+  EXPECT_TRUE(global_scopes[0]->FindDefinition("gar"));
+  EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("gar"));
+
+  definition = global_scopes[0]->FindDefinition("gar");
+
+  // Manually change custom properties
+  Vector<String> gar_custom_properties({"--gar1"});
+
+  paint_worklet_to_test->RegisterMainThreadDocumentPaintDefinition(
+      "gar", definition->NativeInvalidationProperties(), gar_custom_properties,
+      definition->InputArgumentTypes(),
+      definition->GetPaintRenderingContext2DSettings()->alpha());
+
+  EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("gar"));
+
+  global_scopes[1]->ScriptController()->Evaluate(
+      ScriptSourceCode(gar0), SanitizeScriptErrors::kDoNotSanitize);
+
+  // Although the main thread definitions were the same, the definition sent
+  // cross thread differed from the main thread definitions so it must stay
+  // invalid.
+  EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("gar"));
+}
+
 class OffThreadPaintWorkletTest : public PageTestBase,
                                   public ::testing::WithParamInterface<bool> {
  public:
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index 442dbd1..9b20712d 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -34,7 +34,7 @@
 #include <utility>
 #include "base/feature_list.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
diff --git a/third_party/blink/renderer/modules/filesystem/async_callback_helper.h b/third_party/blink/renderer/modules/filesystem/async_callback_helper.h
index cfa1ec0..f893ec66 100644
--- a/third_party/blink/renderer/modules/filesystem/async_callback_helper.h
+++ b/third_party/blink/renderer/modules/filesystem/async_callback_helper.h
@@ -29,12 +29,10 @@
       return base::OnceCallback<void(CallbackParam*)>();
 
     auto success_callback_wrapper = WTF::Bind(
-        [](V8PersistentCallbackInterface<V8Callback>* persistent_callback,
-           CallbackParam* param) {
+        [](V8Callback* persistent_callback, CallbackParam* param) {
           persistent_callback->InvokeAndReportException(nullptr, param);
         },
-        WrapPersistentIfNeeded(
-            ToV8PersistentCallbackInterface(success_callback)));
+        WrapPersistent(success_callback));
     return success_callback_wrapper;
   }
 
@@ -44,13 +42,11 @@
       return base::OnceCallback<void(base::File::Error)>();
 
     return WTF::Bind(
-        [](V8PersistentCallbackInterface<V8ErrorCallback>* persistent_callback,
-           base::File::Error error) {
+        [](V8ErrorCallback* persistent_callback, base::File::Error error) {
           persistent_callback->InvokeAndReportException(
               nullptr, file_error::CreateDOMException(error));
         },
-        WrapPersistentIfNeeded(
-            ToV8PersistentCallbackInterface(error_callback)));
+        WrapPersistent(error_callback));
   }
 
   // The method below is not templatized, to be used exclusively for
@@ -61,11 +57,10 @@
       return VoidCallbacks::SuccessCallback();
 
     auto success_callback_wrapper = WTF::Bind(
-        [](V8PersistentCallbackInterface<V8VoidCallback>* persistent_callback) {
+        [](V8VoidCallback* persistent_callback) {
           persistent_callback->InvokeAndReportException(nullptr);
         },
-        WrapPersistentIfNeeded(
-            ToV8PersistentCallbackInterface(success_callback)));
+        WrapPersistent(success_callback));
     return success_callback_wrapper;
   }
 };
diff --git a/third_party/blink/renderer/modules/filesystem/directory_reader.cc b/third_party/blink/renderer/modules/filesystem/directory_reader.cc
index 20f5f22..9599edc 100644
--- a/third_party/blink/renderer/modules/filesystem/directory_reader.cc
+++ b/third_party/blink/renderer/modules/filesystem/directory_reader.cc
@@ -39,9 +39,7 @@
 
 namespace {
 
-void RunEntriesCallback(
-    V8PersistentCallbackInterface<V8EntriesCallback>* callback,
-    EntryHeapVector* entries) {
+void RunEntriesCallback(V8EntriesCallback* callback, EntryHeapVector* entries) {
   callback->InvokeAndReportException(nullptr, *entries);
 }
 
@@ -87,15 +85,13 @@
         MakeGarbageCollected<EntryHeapVector>(std::move(entries_));
     DOMFileSystem::ScheduleCallback(
         Filesystem()->GetExecutionContext(),
-        WTF::Bind(
-            &RunEntriesCallback,
-            WrapPersistent(ToV8PersistentCallbackInterface(entries_callback)),
-            WrapPersistent(entries)));
+        WTF::Bind(&RunEntriesCallback, WrapPersistent(entries_callback),
+                  WrapPersistent(entries)));
     return;
   }
 
-  entries_callback_ = ToV8PersistentCallbackInterface(entries_callback);
-  error_callback_ = ToV8PersistentCallbackInterface(error_callback);
+  entries_callback_ = entries_callback;
+  error_callback_ = error_callback;
 }
 
 void DirectoryReader::AddEntries(const EntryHeapVector& entries_to_add) {
diff --git a/third_party/blink/renderer/modules/filesystem/directory_reader.h b/third_party/blink/renderer/modules/filesystem/directory_reader.h
index 010be5d..d515d6f 100644
--- a/third_party/blink/renderer/modules/filesystem/directory_reader.h
+++ b/third_party/blink/renderer/modules/filesystem/directory_reader.h
@@ -63,8 +63,8 @@
   bool is_reading_;
   EntryHeapVector entries_;
   base::File::Error error_ = base::File::FILE_OK;
-  Member<V8PersistentCallbackInterface<V8EntriesCallback>> entries_callback_;
-  Member<V8PersistentCallbackInterface<V8ErrorCallback>> error_callback_;
+  Member<V8EntriesCallback> entries_callback_;
+  Member<V8ErrorCallback> error_callback_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/filesystem/file_entry.cc b/third_party/blink/renderer/modules/filesystem/file_entry.cc
index b88a311..ca7101c 100644
--- a/third_party/blink/renderer/modules/filesystem/file_entry.cc
+++ b/third_party/blink/renderer/modules/filesystem/file_entry.cc
@@ -46,15 +46,13 @@
   auto success_callback_wrapper =
       success_callback
           ? WTF::Bind(
-                [](V8PersistentCallbackInterface<V8FileWriterCallback>*
-                       persistent_callback,
+                [](V8FileWriterCallback* persistent_callback,
                    FileWriterBase* file_writer) {
                   // The call sites must pass a FileWriter in |file_writer|.
                   persistent_callback->InvokeAndReportException(
                       nullptr, static_cast<FileWriter*>(file_writer));
                 },
-                WrapPersistentIfNeeded(
-                    ToV8PersistentCallbackInterface(success_callback)))
+                WrapPersistent(success_callback))
           : FileWriterCallbacks::SuccessCallback();
   auto error_callback_wrapper =
       AsyncCallbackHelper::ErrorCallback(error_callback);
diff --git a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
index a99fce0f..8c1c23d 100644
--- a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
+++ b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
@@ -737,7 +737,7 @@
   }
 
   if (configuration->type() == "record") {
-    if (auto handler = MediaRecorderHandler::Create(
+    if (auto* handler = MediaRecorderHandler::Create(
             ExecutionContext::From(script_state)
                 ->GetTaskRunner(TaskType::kInternalMediaRealTime))) {
       handler->EncodingInfo(ToWebMediaConfiguration(configuration),
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.cc
index acd1b6f..4b8272c5 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.cc
@@ -91,7 +91,7 @@
               this))) {
   setType(input_type_names::kRange);
   setAttribute(html_names::kStepAttr, "any");
-  resize_observer_->observe(this);
+  OnControlsShown();
 }
 
 Element& MediaControlSliderElement::GetTrackElement() {
@@ -170,4 +170,12 @@
   MediaControlInputElement::Trace(visitor);
 }
 
+void MediaControlSliderElement::OnControlsShown() {
+  resize_observer_->observe(this);
+}
+
+void MediaControlSliderElement::OnControlsHidden() {
+  resize_observer_->disconnect();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.h
index 72d4372..a2fbc9559 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.h
@@ -33,6 +33,9 @@
   // simplicity; deliberately ignores pinch zoom's pageScaleFactor).
   int TrackWidth();
 
+  void OnControlsShown();
+  void OnControlsHidden();
+
  protected:
   friend class MediaControlsImplTest;
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
index 059c6411..e53fa0a 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
@@ -6,7 +6,6 @@
 
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_screen_info.h"
-#include "third_party/blink/renderer/core/css/css_style_declaration.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
@@ -14,7 +13,6 @@
 #include "third_party/blink/renderer/core/events/touch_event.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_div_element.h"
-#include "third_party/blink/renderer/core/html/html_style_element.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
 #include "third_party/blink/renderer/core/html/time_ranges.h"
@@ -24,12 +22,10 @@
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
-#include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_current_time_display_element.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_remaining_time_display_element.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
-#include "third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -57,24 +53,10 @@
 // MediaControlTimelineElement
 //   (-webkit-media-controls-timeline)
 // +-div#thumb (created by the HTMLSliderElement)
-//
-// +-HTMLStyleElement
 MediaControlTimelineElement::MediaControlTimelineElement(
     MediaControlsImpl& media_controls)
     : MediaControlSliderElement(media_controls) {
   SetShadowPseudoId(AtomicString("-webkit-media-controls-timeline"));
-
-  Element& track = GetTrackElement();
-
-  // TODO(851144): This stylesheet no longer contains animations, so should
-  // be re-combined with the UA sheet.
-  // This stylesheet element contains rules that cannot be present in the UA
-  // stylesheet (e.g. animations).
-  auto* style = MakeGarbageCollected<HTMLStyleElement>(GetDocument(),
-                                                       CreateElementFlags());
-  style->setTextContent(
-      MediaControlsResourceLoader::GetShadowTimelineStyleSheet());
-  track.ParserAppendChild(style);
 }
 
 bool MediaControlTimelineElement::WillRespondToMouseClickEvents() {
@@ -271,10 +253,12 @@
 
   // End scrubbing state.
   is_touching_ = false;
+  MediaControlSliderElement::OnControlsHidden();
 }
 
 void MediaControlTimelineElement::OnControlsShown() {
   controls_hidden_ = false;
+  MediaControlSliderElement::OnControlsShown();
 }
 
 bool MediaControlTimelineElement::EndScrubbingEvent(Event& event) {
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
index 993c52a9..1dffb0a0 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
@@ -938,6 +938,7 @@
     loading_panel_->OnControlsShown();
 
   timeline_->OnControlsShown();
+  volume_slider_->OnControlsShown();
   UpdateCSSClassFromState();
   UpdateActingAsAudioControls();
 }
@@ -961,6 +962,7 @@
     EndScrubbing();
   }
   timeline_->OnControlsHidden();
+  volume_slider_->OnControlsHidden();
 
   UpdateCSSClassFromState();
 
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
index 4b09d747..e20b68f 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
@@ -13,10 +13,12 @@
 #include "third_party/blink/public/platform/web_mouse_event.h"
 #include "third_party/blink/public/platform/web_screen_info.h"
 #include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/document_style_environment_variables.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/document_parser.h"
 #include "third_party/blink/renderer/core/dom/dom_token_list.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
@@ -1136,6 +1138,33 @@
 }
 
 TEST_F(MediaControlsImplTest,
+       RemovingFromDocumentWhenResettingSrcAllowsReclamation) {
+  // Regression test: https://crbug.com/918064
+  //
+  // Test ensures that unified heap garbage collections are able to collect
+  // detached HTMLVideoElements. The tricky part is that ResizeObserver's are
+  // treated as roots as long as they have observations which prevent the video
+  // element from being collected.
+
+  auto page_holder = std::make_unique<DummyPageHolder>();
+  page_holder->GetDocument().write("<video controls>");
+  page_holder->GetDocument().Parser()->Finish();
+
+  HTMLVideoElement& video =
+      ToHTMLVideoElement(*page_holder->GetDocument().QuerySelector("video"));
+  WeakPersistent<HTMLMediaElement> weak_persistent_video = &video;
+  video.remove();
+
+  test::RunPendingTasks();
+
+  // Needs to call into V8's GC here to trigger a unified garbage collection.
+  V8GCController::CollectAllGarbageForTesting(
+      ThreadState::Current()->GetIsolate(),
+      v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
+  EXPECT_EQ(nullptr, weak_persistent_video);
+}
+
+TEST_F(MediaControlsImplTest,
        ReInsertingInDocumentRestoresListenersAndCallbacks) {
   auto page_holder = std::make_unique<DummyPageHolder>();
 
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.cc b/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.cc
index 6120df8..c6549b9 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.cc
@@ -39,12 +39,6 @@
 }
 
 // static
-String MediaControlsResourceLoader::GetShadowTimelineStyleSheet() {
-  return UncompressResourceAsString(
-      IDR_SHADOWSTYLE_MEDIA_CONTROLS_TIMELINE_CSS);
-}
-
-// static
 String MediaControlsResourceLoader::GetShadowLoadingStyleSheet() {
   return UncompressResourceAsString(IDR_SHADOWSTYLE_MEDIA_CONTROLS_LOADING_CSS);
 }
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h b/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h
index 40d99ac..3c2e5a65 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h
@@ -17,10 +17,6 @@
  public:
   static void InjectMediaControlsUAStyleSheet();
 
-  // The timeline specific stylesheet is inserted into the timeline DOM tree
-  // and contains loading animations specific to the timeline.
-  static String GetShadowTimelineStyleSheet();
-
   // The loading specific stylesheet is inserted into the loading panel DOM
   // tree and contains styles specific to the loading panel.
   static String GetShadowLoadingStyleSheet();
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc b/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
index 2fe44542..752f81e 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.h"
 
 #include <cmath>
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
diff --git a/third_party/blink/renderer/modules/media_controls/resources/mediaControls_timeline.css b/third_party/blink/renderer/modules/media_controls/resources/mediaControls_timeline.css
deleted file mode 100644
index 0ec0df6..0000000
--- a/third_party/blink/renderer/modules/media_controls/resources/mediaControls_timeline.css
+++ /dev/null
@@ -1,11 +0,0 @@
-/* Copyright (c) 2017 The Chromium Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style license that can be
-   found in the LICENSE file.*/
-
-/**
- * Scrubbing
- */
-
-#thumb {
-  position: relative;
-}
diff --git a/third_party/blink/renderer/modules/media_controls/resources/media_controls_resources.grd b/third_party/blink/renderer/modules/media_controls/resources/media_controls_resources.grd
index 37c1511d..11352af0 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/media_controls_resources.grd
+++ b/third_party/blink/renderer/modules/media_controls/resources/media_controls_resources.grd
@@ -15,7 +15,6 @@
     </structures>
     <includes>
       <include name="IDR_UASTYLE_MEDIA_CONTROLS_ANDROID_CSS" file="mediaControls_Android.css" type="BINDATA" compress="gzip" />
-      <include name="IDR_SHADOWSTYLE_MEDIA_CONTROLS_TIMELINE_CSS" file="mediaControls_timeline.css" type="BINDATA" compress="gzip" />
       <include name="IDR_SHADOWSTYLE_MEDIA_CONTROLS_ANIMATED_ARROW_CSS" file="mediaControls_animated_arrow.css" type="BINDATA" compress="gzip" />
       <include name="IDR_SHADOWSTYLE_MEDIA_CONTROLS_SCRUBBING_MESSAGE_CSS" file="mediaControls_scrubbing_message.css" type="BINDATA" compress="gzip" />
       <include name="IDR_MEDIA_CONTROLS_JUMP_SVG" file="jump_image.svg" type="BINDATA" compress="gzip" />
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
index fd6b6b77..ef0ec06 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
@@ -48,6 +48,10 @@
 
 const char kInlineCSSClass[] = "inline";
 
+const char kNoSourceCSSClass[] = "state-no-source";
+
+const char kUseDefaultPosterCSSClass[] = "use-default-poster";
+
 }  // namespace
 
 enum class MediaControlsTouchlessImpl::ArrowDirection {
@@ -57,6 +61,12 @@
   kRight,
 };
 
+enum class MediaControlsTouchlessImpl::ControlsState {
+  kNoSource,
+  kPreReady,
+  kReady,
+};
+
 MediaControlsTouchlessImpl::MediaControlsTouchlessImpl(
     HTMLMediaElement& media_element)
     : HTMLDivElement(media_element.GetDocument()),
@@ -87,6 +97,9 @@
       MakeGarbageCollected<MediaControlsTouchlessVolumeContainerElement>(
           *controls);
 
+  MediaControlElementsHelper::CreateDiv(
+      "-internal-media-controls-touchless-info", controls);
+
   controls->ParserAppendChild(controls->bottom_container_);
   controls->ParserAppendChild(controls->overlay_);
   controls->ParserAppendChild(controls->volume_container_);
@@ -111,6 +124,8 @@
   if (!media_element.IsFullscreen())
     controls->classList().Add(kInlineCSSClass);
 
+  controls->UpdateCSSFromState();
+
   shadow_root.ParserAppendChild(controls);
   return controls;
 }
@@ -144,6 +159,10 @@
   SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kNone);
 }
 
+void MediaControlsTouchlessImpl::NetworkStateChanged() {
+  UpdateCSSFromState();
+}
+
 LayoutObject* MediaControlsTouchlessImpl::PanelLayoutObject() {
   return nullptr;
 }
@@ -437,6 +456,63 @@
   MediaElement().setCurrentTime(new_time);
 }
 
+MediaControlsTouchlessImpl::ControlsState MediaControlsTouchlessImpl::State() {
+  HTMLMediaElement::NetworkState network_state =
+      MediaElement().getNetworkState();
+  HTMLMediaElement::ReadyState ready_state = MediaElement().getReadyState();
+
+  switch (network_state) {
+    case HTMLMediaElement::kNetworkEmpty:
+    case HTMLMediaElement::kNetworkNoSource:
+      return ControlsState::kNoSource;
+    case HTMLMediaElement::kNetworkLoading:
+      if (ready_state == HTMLMediaElement::kHaveNothing)
+        return ControlsState::kPreReady;
+      else
+        return ControlsState::kReady;
+    case HTMLMediaElement::kNetworkIdle:
+      if (ready_state == HTMLMediaElement::kHaveNothing)
+        return ControlsState::kPreReady;
+      break;
+  }
+
+  return ControlsState::kReady;
+}
+
+void MediaControlsTouchlessImpl::UpdateCSSFromState() {
+  ControlsState state = State();
+
+  if (state == ControlsState::kNoSource)
+    classList().Add(kNoSourceCSSClass);
+  else
+    classList().Remove(kNoSourceCSSClass);
+
+  if (!MediaElement().IsHTMLVideoElement())
+    return;
+
+  if (MediaElement().ShouldShowControls() &&
+      !VideoElement().HasAvailableVideoFrame() &&
+      VideoElement().PosterImageURL().IsEmpty() &&
+      state <= ControlsState::kPreReady) {
+    classList().Add(kUseDefaultPosterCSSClass);
+  } else {
+    classList().Remove(kUseDefaultPosterCSSClass);
+  }
+}
+
+HTMLVideoElement& MediaControlsTouchlessImpl::VideoElement() {
+  DCHECK(MediaElement().IsHTMLVideoElement());
+  return *ToHTMLVideoElement(&MediaElement());
+}
+
+void MediaControlsTouchlessImpl::OnError() {
+  UpdateCSSFromState();
+}
+
+void MediaControlsTouchlessImpl::OnLoadedMetadata() {
+  UpdateCSSFromState();
+}
+
 void MediaControlsTouchlessImpl::Trace(blink::Visitor* visitor) {
   visitor->Trace(bottom_container_);
   visitor->Trace(overlay_);
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
index b3d86bd5..6601987 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
@@ -42,7 +42,7 @@
   void Reset() override {}
   void OnControlsListUpdated() override {}
   void OnTrackElementFailedToLoad() override {}
-  void NetworkStateChanged() override {}
+  void NetworkStateChanged() override;
   LayoutObject* PanelLayoutObject() override;
   LayoutObject* TimelineLayoutObject() override;
   LayoutObject* ButtonPanelLayoutObject() override;
@@ -62,8 +62,8 @@
   void OnPause() override;
   void OnEnterFullscreen() override;
   void OnExitFullscreen() override;
-  void OnError() override {}
-  void OnLoadedMetadata() override {}
+  void OnError() override;
+  void OnLoadedMetadata() override;
   void OnKeyPress(KeyboardEvent* event) override {}
   void OnKeyDown(KeyboardEvent* event) override;
   void OnKeyUp(KeyboardEvent* event) override {}
@@ -81,11 +81,15 @@
   friend class MediaControlsTouchlessImplTest;
 
   enum class ArrowDirection;
+  enum class ControlsState;
   ArrowDirection OrientArrowPress(ArrowDirection direction);
   void HandleOrientedArrowPress(ArrowDirection direction);
 
   WebScreenOrientationType GetOrientation();
 
+  ControlsState State();
+  void UpdateCSSFromState();
+
   void HandleTopButtonPress();
   void HandleBottomButtonPress();
   void HandleLeftButtonPress();
@@ -96,6 +100,8 @@
 
   void Download();
 
+  HTMLVideoElement& VideoElement();
+
   // Node
   bool IsMediaControls() const override { return true; }
 
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
index 01576a0..4efbda6 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
@@ -59,6 +59,8 @@
   bool has_audio_ = false;
 };
 
+}  // namespace
+
 class MockChromeClientForTouchlessImpl : public EmptyChromeClient {
  public:
   explicit MockChromeClientForTouchlessImpl()
@@ -124,6 +126,14 @@
     return test_media_controls_host_->GetMenuHostArgList();
   }
 
+  void SetNetworkState(HTMLMediaElement::NetworkState state) {
+    MediaElement().SetNetworkState(state);
+  }
+
+  void SetReadyState(HTMLMediaElement::ReadyState state) {
+    MediaElement().SetReadyState(state);
+  }
+
   void SimulateKeydownEvent(Element& element, int key_code) {
     KeyboardEventInit* keyboard_event_init = KeyboardEventInit::Create();
     keyboard_event_init->setKeyCode(key_code);
@@ -393,10 +403,12 @@
                                        UserGestureToken::kNewGesture);
   test::RunPendingTasks();
 
-  KeyboardEventInit* keyboard_event_init = KeyboardEventInit::Create();
-  keyboard_event_init->setKey("SoftRight");
-  Event* keyboard_event =
-      MakeGarbageCollected<KeyboardEvent>("keydown", keyboard_event_init);
+  WebKeyboardEvent web_keyboard_event(
+      WebInputEvent::kKeyUp, WebInputEvent::Modifiers::kNoModifiers,
+      WebInputEvent::GetStaticTimeStampForTests());
+  // TODO: Cleanup magic numbers once https://crbug.com/949766 lands.
+  web_keyboard_event.dom_key = 0x00200310;
+  Event* keyboard_event = KeyboardEvent::Create(web_keyboard_event, nullptr);
 
   // Test fullscreen function.
   SetMenuResponse(mojom::blink::MenuItem::FULLSCREEN);
@@ -449,6 +461,26 @@
   EXPECT_NE(track->mode(), TextTrack::ShowingKeyword());
 }
 
+TEST_F(MediaControlsTouchlessImplTest, NoSourceTest) {
+  EXPECT_TRUE(MediaControls().classList().contains("state-no-source"));
+
+  LoadMediaWithDuration(10);
+  EXPECT_FALSE(MediaControls().classList().contains("state-no-source"));
+}
+
+TEST_F(MediaControlsTouchlessImplTest, DefaultPosterTest) {
+  LoadMediaWithDuration(10);
+
+  SetNetworkState(HTMLMediaElement::NetworkState::kNetworkLoading);
+  test::RunPendingTasks();
+  EXPECT_TRUE(MediaControls().classList().contains("use-default-poster"));
+
+  SetNetworkState(HTMLMediaElement::NetworkState::kNetworkIdle);
+  SetReadyState(HTMLMediaElement::ReadyState::kHaveMetadata);
+  test::RunPendingTasks();
+  EXPECT_FALSE(MediaControls().classList().contains("use-default-poster"));
+}
+
 TEST_F(MediaControlsTouchlessImplTestWithMockScheduler,
        MidOverlayHideTimerTest) {
   Element* overlay =
@@ -568,6 +600,4 @@
   EXPECT_TRUE(IsControlsVisible(overlay));
 }
 
-}  // namespace
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/resources/ic_music_note.svg b/third_party/blink/renderer/modules/media_controls/touchless/resources/ic_music_note.svg
new file mode 100644
index 0000000..bcabd88
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/touchless/resources/ic_music_note.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 3l.01 10.55c-.59-.34-1.27-.55-2-.55C7.79 13 6 14.79 6 17s1.79 4 4.01 4S14 19.21 14 17V7h4V3h-6z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/resources/ic_no_source.svg b/third_party/blink/renderer/modules/media_controls/touchless/resources/ic_no_source.svg
new file mode 100644
index 0000000..49b7eaf
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/touchless/resources/ic_no_source.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="#000000">
+    <path fill="none" d="M0 0h24v24H0zm0 0h24v24H0zm21 19c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2"/>
+    <path fill="none" d="M0 0h24v24H0z"/>
+    <path d="M21 5v6.59l-3-3.01-4 4.01-4-4-4 4-3-3.01V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2zm-3 6.42l3 3.01V19c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2v-6.58l3 2.99 4-4 4 4 4-3.99z"/>
+</svg>
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css b/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
index 98516f3..67a1913 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
+++ b/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
@@ -2,6 +2,12 @@
    Use of this source code is governed by a BSD-style license that can be
    found in the LICENSE file. */
 
+audio {
+  width: 300px;
+  height: 180px;
+}
+
+audio::-webkit-media-controls-touchless,
 video::-webkit-media-controls-touchless {
   width: inherit;
   height: inherit;
@@ -18,6 +24,42 @@
   margin-bottom: env(safe-area-inset-bottom);
 }
 
+audio::-webkit-media-controls-touchless,
+video::-webkit-media-controls-touchless.use-default-poster {
+  background: #333;
+}
+
+audio::-internal-media-controls-touchless-info,
+video::-internal-media-controls-touchless-info {
+  display: none;
+  height: 72px;
+  width: 72px;
+  border-radius: 50%;
+  opacity: 0.3;
+  background-color: rgba(255, 255, 255, 0.9);
+  background-size: 50%;
+  background-position: center;
+  background-repeat: no-repeat;
+  position: absolute;
+  margin: auto;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+}
+
+audio::-internal-media-controls-touchless-info {
+  background-image: -webkit-image-set(url(ic_music_note.svg) 1x);
+  display: block;
+}
+
+audio::-webkit-media-controls-touchless.state-no-source [pseudo="-internal-media-controls-touchless-info"],
+video::-webkit-media-controls-touchless.state-no-source [pseudo="-internal-media-controls-touchless-info"] {
+  background-image: -webkit-image-set(url(ic_no_source.svg) 1x);
+  display: block;
+}
+
+audio::-internal-media-controls-touchless-overlay,
 video::-internal-media-controls-touchless-overlay {
   width: 104px;
   height: 104px;
@@ -35,15 +77,23 @@
   right: 0;
 }
 
+audio::-webkit-media-controls-touchless.state-no-source [pseudo="-internal-media-controls-touchless-overlay"],
+video::-webkit-media-controls-touchless.state-no-source [pseudo="-internal-media-controls-touchless-overlay"] {
+  display: none;
+}
+
+audio::-internal-media-controls-touchless-overlay.transparent,
 video::-internal-media-controls-touchless-overlay.transparent {
   opacity: 0;
   transition: opacity .5s;
 }
 
+audio::-internal-media-controls-touchless-overlay.transparent-immediate,
 video::-internal-media-controls-touchless-overlay.transparent-immediate {
   opacity: 0;
 }
 
+audio::-internal-media-controls-touchless-volume-up-button,
 video::-internal-media-controls-touchless-volume-up-button {
   width: 30px;
   height: 30px;
@@ -54,7 +104,7 @@
   background-repeat: no-repeat;
 }
 
-
+audio::-internal-media-controls-touchless-seek-backward-button,
 video::-internal-media-controls-touchless-seek-backward-button {
   width: 30px;
   height: 30px;
@@ -66,7 +116,7 @@
   background-repeat: no-repeat;
 }
 
-
+audio::-internal-media-controls-touchless-play-button,
 video::-internal-media-controls-touchless-play-button {
   width: 44px;
   height: 44px;
@@ -76,14 +126,17 @@
   float: left;
 }
 
+audio::-internal-media-controls-touchless-play-button.playing,
 video::-internal-media-controls-touchless-play-button.playing {
   background-image: -webkit-image-set(url(ic_pause.svg) 1x);
 }
 
+audio::-internal-media-controls-touchless-play-button.paused,
 video::-internal-media-controls-touchless-play-button.paused {
   background-image: -webkit-image-set(url(ic_play_arrow.svg) 1x);
 }
 
+audio::-internal-media-controls-touchless-seek-forward-button,
 video::-internal-media-controls-touchless-seek-forward-button {
   width: 30px;
   height: 30px;
@@ -95,6 +148,7 @@
   background-repeat: no-repeat;
 }
 
+audio::-internal-media-controls-touchless-volume-down-button,
 video::-internal-media-controls-touchless-volume-down-button {
   width: 30px;
   height: 30px;
@@ -107,6 +161,7 @@
   background-repeat: no-repeat;
 }
 
+audio::-internal-media-controls-touchless-bottom-container,
 video::-internal-media-controls-touchless-bottom-container {
   position: absolute;
   display: flex;
@@ -122,11 +177,13 @@
   background-size: auto 48px;
 }
 
+audio::-internal-media-controls-touchless-bottom-container.transparent,
 video::-internal-media-controls-touchless-bottom-container.transparent {
   opacity: 0;
   transition: opacity .5s;
 }
 
+audio::-internal-media-controls-touchless-time-display,
 video::-internal-media-controls-touchless-time-display {
   color: #ffffff;
   font-family: Roboto-Regular, Roboto, sans-serif;
@@ -137,6 +194,7 @@
   padding-bottom: 8px;
 }
 
+audio::-internal-media-controls-touchless-timeline,
 video::-internal-media-controls-touchless-timeline {
   width: 100%;
   bottom: auto;
@@ -144,18 +202,23 @@
   background-color: rgba(0, 0, 0, 0.2);
 }
 
+audio::-internal-media-controls-touchless-timeline-loaded,
 video::-internal-media-controls-touchless-timeline-loaded {
   height: 100%;
+  width: 0; /* We are using style attribute to override this. */
   border-radius: 0 2px 2px 0;
   background-color: rgba(255, 255, 255, 0.54);
 }
 
+audio::-internal-media-controls-touchless-timeline-progress,
 video::-internal-media-controls-touchless-timeline-progress {
   height: 100%;
+  width: 0; /* We are using style attribute to override this. */
   border-radius: 0 2px 2px 0;
   background-color: rgba(255, 255, 255, 1.0);
 }
 
+audio::-internal-media-controls-touchless-volume-container,
 video::-internal-media-controls-touchless-volume-container {
   width: 44px;
   max-height: 152px;
@@ -174,15 +237,18 @@
   justify-content: flex-end;
 }
 
+audio::-internal-media-controls-touchless-volume-container.transparent,
 video::-internal-media-controls-touchless-volume-container.transparent {
   opacity: 0;
   transition: opacity .5s;
 }
 
+audio::-internal-media-controls-touchless-volume-container.transparent-immediate,
 video::-internal-media-controls-touchless-volume-container.transparent-immediate {
   opacity: 0;
 }
 
+audio::-internal-media-controls-touchless-volume-bar-background,
 video::-internal-media-controls-touchless-volume-bar-background {
   width: 4px;
   margin-left: 20px;
@@ -196,15 +262,18 @@
   justify-content: flex-end;
 }
 
+audio::-webkit-media-controls-touchless.inline [pseudo="-internal-media-controls-touchless-volume-bar-background"],
 video::-webkit-media-controls-touchless.inline [pseudo="-internal-media-controls-touchless-volume-bar-background"] {
   margin-bottom: 12px;
 }
 
+audio::-internal-media-controls-touchless-volume-bar,
 video::-internal-media-controls-touchless-volume-bar {
   background-color: #FFFFFF;
   border-radius: 2px;
 }
 
+audio::-internal-media-controls-touchless-volume-icon,
 video::-internal-media-controls-touchless-volume-icon {
   width: 24px;
   height: 24px;
@@ -214,10 +283,12 @@
   background-image: -webkit-image-set(url(ic_volume_on.svg) 1x);
 }
 
+audio::-internal-media-controls-touchless-volume-icon.muted,
 video::-internal-media-controls-touchless-volume-icon.muted {
   background-image: -webkit-image-set(url(ic_volume_off.svg) 1x);
 }
 
+audio::-webkit-media-controls-touchless.inline div[pseudo="-internal-media-controls-touchless-volume-icon" i],
 video::-webkit-media-controls-touchless.inline div[pseudo="-internal-media-controls-touchless-volume-icon" i] {
   display: none;
 }
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.cc
index cb2230b9..d2a8051 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.cc
@@ -127,7 +127,6 @@
   const blink::WebSize resolution = web_media_player_->NaturalSize();
 
   if (!canvas_ || is_opaque_ != web_media_player_->IsOpaque()) {
-    LOG(ERROR) << " Change in opacity !!!";
     is_opaque_ = web_media_player_->IsOpaque();
     if (!bitmap_.tryAllocPixels(SkImageInfo::MakeN32(
             resolution.width, resolution.height,
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source_unittest.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source_unittest.cc
index ed8ef0f..99784b41 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source_unittest.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source_unittest.cc
@@ -91,6 +91,7 @@
     return;
   }
   bool IsOpaque() const override { return is_video_opaque_; }
+  bool HasAvailableVideoFrame() const override { return true; }
 
   base::WeakPtr<WebMediaPlayer> AsWeakPtr() override {
     return weak_factory_.GetWeakPtr();
diff --git a/third_party/blink/renderer/modules/mediarecorder/BUILD.gn b/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
index 61e1a244..832d6976 100644
--- a/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
@@ -27,7 +27,6 @@
     "media_recorder.h",
     "media_recorder_handler.cc",
     "media_recorder_handler.h",
-    "media_recorder_handler_client.h",
     "vea_encoder.cc",
     "vea_encoder.h",
     "video_track_recorder.cc",
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
index 88bcbf4..6e78644 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
@@ -12,6 +12,8 @@
 #include "third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.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/cross_thread_copier.h"
@@ -42,7 +44,7 @@
 }
 
 AudioTrackRecorder::AudioTrackRecorder(CodecId codec,
-                                       const WebMediaStreamTrack& track,
+                                       MediaStreamComponent* track,
                                        OnEncodedAudioCB on_encoded_audio_cb,
                                        int32_t bits_per_second)
     : track_(track),
@@ -53,17 +55,14 @@
           ThreadCreationParams(WebThreadType::kAudioEncoderThread))),
       encoder_task_runner_(encoder_thread_->GetTaskRunner()) {
   DCHECK(IsMainThread());
-  DCHECK(!track_.IsNull());
-  DCHECK(MediaStreamAudioTrack::From(track_));
+  DCHECK(track_);
+  DCHECK(track_->Source()->GetType() == MediaStreamSource::kTypeAudio);
 
   // Connect the source provider to the track as a sink.
-  WebMediaStreamAudioSink::AddToAudioTrack(this, track_);
+  ConnectToTrack();
 }
 
-AudioTrackRecorder::~AudioTrackRecorder() {
-  DCHECK(IsMainThread());
-  WebMediaStreamAudioSink::RemoveFromAudioTrack(this, track_);
-}
+AudioTrackRecorder::~AudioTrackRecorder() = default;
 
 // Creates an audio encoder from the codec. Returns nullptr if the codec is
 // invalid.
@@ -122,4 +121,30 @@
       CrossThreadBindOnce(&AudioTrackEncoder::set_paused, encoder_, false));
 }
 
+void AudioTrackRecorder::ConnectToTrack() {
+  auto* audio_track =
+      static_cast<MediaStreamAudioTrack*>(track_->GetPlatformTrack());
+  DCHECK(audio_track);
+  audio_track->AddSink(this);
+}
+
+void AudioTrackRecorder::DisconnectFromTrack() {
+  auto* audio_track =
+      static_cast<MediaStreamAudioTrack*>(track_->GetPlatformTrack());
+  DCHECK(audio_track);
+  audio_track->RemoveSink(this);
+}
+
+void AudioTrackRecorder::Trace(blink::Visitor* visitor) {
+  visitor->Trace(track_);
+}
+
+void AudioTrackRecorder::Prefinalize() {
+  // TODO(crbug.com/704136) : Remove this method when moving
+  // MediaStreamAudioTrack to Oilpan's heap.
+  DCHECK(IsMainThread());
+  DisconnectFromTrack();
+  track_ = nullptr;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h
index c714628b..e9eb1d02 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h
@@ -21,6 +21,7 @@
 namespace blink {
 
 class AudioTrackEncoder;
+class MediaStreamComponent;
 class Thread;
 
 // AudioTrackRecorder is a MediaStreamAudioSink that encodes the audio buses
@@ -30,7 +31,11 @@
 // the "capture thread"). It owns an internal thread to use for encoding, on
 // which lives an AudioTrackEncoder with its own threading subtleties, see the
 // implementation file.
-class MODULES_EXPORT AudioTrackRecorder : public WebMediaStreamAudioSink {
+class MODULES_EXPORT AudioTrackRecorder
+    : public GarbageCollectedFinalized<AudioTrackRecorder>,
+      public WebMediaStreamAudioSink {
+  USING_PRE_FINALIZER(AudioTrackRecorder, Prefinalize);
+
  public:
   enum class CodecId {
     // Do not change the order of codecs. Add new ones right before LAST.
@@ -47,7 +52,7 @@
   static CodecId GetPreferredCodecId();
 
   AudioTrackRecorder(CodecId codec,
-                     const WebMediaStreamTrack& track,
+                     MediaStreamComponent* track,
                      OnEncodedAudioCB on_encoded_audio_cb,
                      int32_t bits_per_second);
   ~AudioTrackRecorder() override;
@@ -60,6 +65,8 @@
   void Pause();
   void Resume();
 
+  void Trace(blink::Visitor*);
+
  private:
   // Creates an audio encoder from |codec|. Returns nullptr if the codec is
   // invalid.
@@ -68,12 +75,17 @@
       OnEncodedAudioCB on_encoded_audio_cb,
       int32_t bits_per_second);
 
+  void ConnectToTrack();
+  void DisconnectFromTrack();
+
+  void Prefinalize();
+
   // Used to check that MediaStreamAudioSink's methods are called on the
   // capture audio thread.
   THREAD_CHECKER(capture_thread_checker_);
 
   // We need to hold on to the Blink track to remove ourselves on destruction.
-  const WebMediaStreamTrack track_;
+  Member<MediaStreamComponent> track_;
 
   // Thin wrapper around OpusEncoder.
   // |encoder_| should be initialized before |encoder_thread_| such that
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder_unittest.cc
index 8ff2836..fe3230d1 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder_unittest.cc
@@ -109,11 +109,11 @@
         first_source_cache_pos_(0) {
     ResetDecoder(first_params_);
     PrepareBlinkTrack();
-    audio_track_recorder_.reset(new AudioTrackRecorder(
+    audio_track_recorder_ = MakeGarbageCollected<AudioTrackRecorder>(
         codec_, blink_track_,
         WTF::BindRepeating(&AudioTrackRecorderTest::OnEncodedAudio,
                            WTF::Unretained(this)),
-        0 /* bits_per_second */));
+        0 /* bits_per_second */);
   }
 
   ~AudioTrackRecorderTest() {
@@ -121,7 +121,7 @@
     opus_decoder_ = nullptr;
     blink_track_.Reset();
     WebHeap::CollectAllGarbageForTesting();
-    audio_track_recorder_.reset();
+    audio_track_recorder_ = nullptr;
     // Let the message loop run to finish destroying the recorder properly.
     base::RunLoop().RunUntilIdle();
   }
@@ -207,7 +207,7 @@
   }
 
   // ATR and WebMediaStreamTrack for fooling it.
-  std::unique_ptr<AudioTrackRecorder> audio_track_recorder_;
+  Persistent<AudioTrackRecorder> audio_track_recorder_;
   WebMediaStreamTrack blink_track_;
 
   // The codec we'll use for compression the audio.
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
index b3b34ac4..c3226c44 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
@@ -294,7 +294,7 @@
 
 bool MediaRecorder::isTypeSupported(ExecutionContext* context,
                                     const String& type) {
-  std::unique_ptr<MediaRecorderHandler> handler = MediaRecorderHandler::Create(
+  MediaRecorderHandler* handler = MediaRecorderHandler::Create(
       context->GetTaskRunner(TaskType::kInternalMediaRealTime));
   if (!handler)
     return false;
@@ -321,9 +321,12 @@
   if (stopped_)
     return;
 
+  WriteData(nullptr /* data */, 0 /* length */, true /* lastInSlice */,
+            WTF::CurrentTimeMS());
+
   stopped_ = true;
   stream_.Clear();
-  recorder_handler_.reset();
+  recorder_handler_ = nullptr;
 }
 
 void MediaRecorder::WriteData(const char* data,
@@ -399,6 +402,7 @@
 
 void MediaRecorder::Trace(blink::Visitor* visitor) {
   visitor->Trace(stream_);
+  visitor->Trace(recorder_handler_);
   visitor->Trace(scheduled_events_);
   EventTargetWithInlineData::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder.h b/third_party/blink/renderer/modules/mediarecorder/media_recorder.h
index 7ca2d702..c978e85 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder.h
@@ -10,7 +10,6 @@
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h"
-#include "third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_client.h"
 #include "third_party/blink/renderer/modules/mediarecorder/media_recorder_options.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -21,9 +20,8 @@
 class BlobData;
 class ExceptionState;
 
-class MODULES_EXPORT MediaRecorder final
+class MODULES_EXPORT MediaRecorder
     : public EventTargetWithInlineData,
-      public MediaRecorderHandlerClient,
       public ActiveScriptWrappable<MediaRecorder>,
       public ContextLifecycleObserver {
   USING_GARBAGE_COLLECTED_MIXIN(MediaRecorder);
@@ -78,12 +76,11 @@
   // ScriptWrappable
   bool HasPendingActivity() const final { return !stopped_; }
 
-  // MediaRecorderHandlerClient
-  void WriteData(const char* data,
-                 size_t length,
-                 bool last_in_slice,
-                 double timecode) override;
-  void OnError(const String& message) override;
+  virtual void WriteData(const char* data,
+                         size_t length,
+                         bool last_in_slice,
+                         double timecode);
+  virtual void OnError(const String& message);
 
   void Trace(blink::Visitor* visitor) override;
 
@@ -104,7 +101,7 @@
 
   std::unique_ptr<BlobData> blob_data_;
 
-  std::unique_ptr<MediaRecorderHandler> recorder_handler_;
+  Member<MediaRecorderHandler> recorder_handler_;
 
   HeapVector<Member<Event>> scheduled_events_;
 };
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
index ad9ab68..416dbb5a 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
@@ -18,12 +18,13 @@
 #include "media/muxers/webm_muxer.h"
 #include "third_party/blink/public/platform/modules/media_capabilities/web_media_capabilities_info.h"
 #include "third_party/blink/public/platform/modules/media_capabilities/web_media_configuration.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h"
-#include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_track.h"
 #include "third_party/blink/public/platform/modules/mediastream/webrtc_uma_histograms.h"
-#include "third_party/blink/public/platform/web_media_stream_source.h"
+#include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/modules/mediarecorder/buildflags.h"
-#include "third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_client.h"
+#include "third_party/blink/renderer/modules/mediarecorder/media_recorder.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
@@ -105,9 +106,9 @@
 
 }  // anonymous namespace
 
-std::unique_ptr<MediaRecorderHandler> MediaRecorderHandler::Create(
+MediaRecorderHandler* MediaRecorderHandler::Create(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  return std::make_unique<MediaRecorderHandler>(std::move(task_runner));
+  return MakeGarbageCollected<MediaRecorderHandler>(std::move(task_runner));
 }
 
 MediaRecorderHandler::MediaRecorderHandler(
@@ -117,19 +118,11 @@
       video_codec_id_(VideoTrackRecorder::CodecId::LAST),
       audio_codec_id_(AudioTrackRecorder::CodecId::LAST),
       recording_(false),
-      client_(nullptr),
+      recorder_(nullptr),
       task_runner_(std::move(task_runner)),
       weak_factory_(this) {}
 
-MediaRecorderHandler::~MediaRecorderHandler() {
-  DCHECK(IsMainThread());
-  // Send a |last_in_slice| to our |client_|.
-  if (client_) {
-    client_->WriteData(
-        nullptr, 0u, true,
-        (TimeTicks::Now() - base::TimeTicks::UnixEpoch()).InMillisecondsF());
-  }
-}
+MediaRecorderHandler::~MediaRecorderHandler() = default;
 
 bool MediaRecorderHandler::CanSupportMimeType(const String& type,
                                               const String& web_codecs) {
@@ -180,8 +173,8 @@
   return true;
 }
 
-bool MediaRecorderHandler::Initialize(MediaRecorderHandlerClient* client,
-                                      const WebMediaStream& media_stream,
+bool MediaRecorderHandler::Initialize(MediaRecorder* recorder,
+                                      MediaStreamDescriptor* media_stream,
                                       const String& type,
                                       const String& codecs,
                                       int32_t audio_bits_per_second,
@@ -217,8 +210,8 @@
       << static_cast<int>(audio_codec_id_);
 
   media_stream_ = media_stream;
-  DCHECK(client);
-  client_ = client;
+  DCHECK(recorder);
+  recorder_ = recorder;
 
   audio_bits_per_second_ = audio_bits_per_second;
   video_bits_per_second_ = video_bits_per_second;
@@ -228,28 +221,28 @@
 bool MediaRecorderHandler::Start(int timeslice) {
   DCHECK(IsMainThread());
   DCHECK(!recording_);
-  DCHECK(!media_stream_.IsNull());
+  DCHECK(media_stream_);
   DCHECK(timeslice_.is_zero());
   DCHECK(!webm_muxer_);
 
   timeslice_ = base::TimeDelta::FromMilliseconds(timeslice);
   slice_origin_timestamp_ = base::TimeTicks::Now();
 
-  video_tracks_ = media_stream_.VideoTracks();
-  audio_tracks_ = media_stream_.AudioTracks();
+  video_tracks_ = media_stream_->VideoComponents();
+  audio_tracks_ = media_stream_->AudioComponents();
 
-  if (video_tracks_.empty() && audio_tracks_.empty()) {
+  if (video_tracks_.IsEmpty() && audio_tracks_.IsEmpty()) {
     LOG(WARNING) << __func__ << ": no media tracks.";
     return false;
   }
 
   const bool use_video_tracks =
-      !video_tracks_.empty() && video_tracks_[0].Source().GetReadyState() !=
-                                    WebMediaStreamSource::kReadyStateEnded;
-  const bool use_audio_tracks = !audio_tracks_.empty() &&
-                                MediaStreamAudioTrack::From(audio_tracks_[0]) &&
-                                audio_tracks_[0].Source().GetReadyState() !=
-                                    WebMediaStreamSource::kReadyStateEnded;
+      !video_tracks_.IsEmpty() && video_tracks_[0]->Source()->GetReadyState() !=
+                                      MediaStreamSource::kReadyStateEnded;
+  const bool use_audio_tracks = !audio_tracks_.IsEmpty() &&
+                                audio_tracks_[0]->GetPlatformTrack() &&
+                                audio_tracks_[0]->Source()->GetReadyState() !=
+                                    MediaStreamSource::kReadyStateEnded;
 
   if (!use_video_tracks && !use_audio_tracks) {
     LOG(WARNING) << __func__ << ": no tracks to be recorded.";
@@ -269,16 +262,15 @@
     LOG_IF(WARNING, video_tracks_.size() > 1u)
         << "Recording multiple video tracks is not implemented. "
         << "Only recording first video track.";
-    const WebMediaStreamTrack& video_track = video_tracks_[0];
-    if (video_track.IsNull())
+    if (!video_tracks_[0])
       return false;
 
     const VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_cb =
         media::BindToCurrentLoop(WTF::BindRepeating(
             &MediaRecorderHandler::OnEncodedVideo, weak_factory_.GetWeakPtr()));
 
-    video_recorders_.emplace_back(new VideoTrackRecorder(
-        video_codec_id_, video_track, on_encoded_video_cb,
+    video_recorders_.emplace_back(MakeGarbageCollected<VideoTrackRecorder>(
+        video_codec_id_, video_tracks_[0], on_encoded_video_cb,
         video_bits_per_second_, task_runner_));
   }
 
@@ -288,16 +280,15 @@
     LOG_IF(WARNING, audio_tracks_.size() > 1u)
         << "Recording multiple audio"
         << " tracks is not implemented.  Only recording first audio track.";
-    const WebMediaStreamTrack& audio_track = audio_tracks_[0];
-    if (audio_track.IsNull())
+    if (!audio_tracks_[0])
       return false;
 
     const AudioTrackRecorder::OnEncodedAudioCB on_encoded_audio_cb =
         media::BindToCurrentLoop(base::Bind(
             &MediaRecorderHandler::OnEncodedAudio, weak_factory_.GetWeakPtr()));
 
-    audio_recorders_.emplace_back(new AudioTrackRecorder(
-        audio_codec_id_, audio_track, std::move(on_encoded_audio_cb),
+    audio_recorders_.emplace_back(MakeGarbageCollected<AudioTrackRecorder>(
+        audio_codec_id_, audio_tracks_[0], std::move(on_encoded_audio_cb),
         audio_bits_per_second_));
   }
 
@@ -397,10 +388,10 @@
 
 String MediaRecorderHandler::ActualMimeType() {
   DCHECK(IsMainThread());
-  DCHECK(client_) << __func__ << " should be called after Initialize()";
+  DCHECK(recorder_) << __func__ << " should be called after Initialize()";
 
-  const bool has_video_tracks = !media_stream_.VideoTracks().empty();
-  const bool has_audio_tracks = !media_stream_.AudioTracks().empty();
+  const bool has_video_tracks = media_stream_->NumberOfVideoComponents();
+  const bool has_audio_tracks = media_stream_->NumberOfAudioComponents();
   if (!has_video_tracks && !has_audio_tracks)
     return String();
 
@@ -470,7 +461,7 @@
   DCHECK(IsMainThread());
 
   if (UpdateTracksAndCheckIfChanged()) {
-    client_->OnError("Amount of tracks in MediaStream has changed.");
+    recorder_->OnError("Amount of tracks in MediaStream has changed.");
     return;
   }
   if (!webm_muxer_)
@@ -479,7 +470,7 @@
                                    std::move(encoded_alpha), timestamp,
                                    is_key_frame)) {
     DLOG(ERROR) << "Error muxing video data";
-    client_->OnError("Error muxing video data");
+    recorder_->OnError("Error muxing video data");
   }
 }
 
@@ -490,7 +481,7 @@
   DCHECK(IsMainThread());
 
   if (UpdateTracksAndCheckIfChanged()) {
-    client_->OnError("Amount of tracks in MediaStream has changed.");
+    recorder_->OnError("Amount of tracks in MediaStream has changed.");
     return;
   }
   if (!webm_muxer_)
@@ -498,7 +489,7 @@
   if (!webm_muxer_->OnEncodedAudio(params, std::move(encoded_data),
                                    timestamp)) {
     DLOG(ERROR) << "Error muxing audio data";
-    client_->OnError("Error muxing audio data");
+    recorder_->OnError("Error muxing audio data");
   }
 }
 
@@ -507,8 +498,9 @@
   const base::TimeTicks now = base::TimeTicks::Now();
   // Non-buffered mode does not need to check timestamps.
   if (timeslice_.is_zero()) {
-    client_->WriteData(data.data(), data.length(), true /* lastInSlice */,
-                       (now - base::TimeTicks::UnixEpoch()).InMillisecondsF());
+    recorder_->WriteData(
+        data.data(), data.length(), true /* lastInSlice */,
+        (now - base::TimeTicks::UnixEpoch()).InMillisecondsF());
     return;
   }
 
@@ -516,31 +508,30 @@
   DVLOG_IF(1, last_in_slice) << "Slice finished @ " << now;
   if (last_in_slice)
     slice_origin_timestamp_ = now;
-  client_->WriteData(data.data(), data.length(), last_in_slice,
-                     (now - base::TimeTicks::UnixEpoch()).InMillisecondsF());
+  recorder_->WriteData(data.data(), data.length(), last_in_slice,
+                       (now - base::TimeTicks::UnixEpoch()).InMillisecondsF());
 }
 
 bool MediaRecorderHandler::UpdateTracksAndCheckIfChanged() {
   DCHECK(IsMainThread());
 
-  WebVector<WebMediaStreamTrack> video_tracks, audio_tracks;
-  video_tracks = media_stream_.VideoTracks();
-  audio_tracks = media_stream_.AudioTracks();
+  const auto video_tracks = media_stream_->VideoComponents();
+  const auto audio_tracks = media_stream_->AudioComponents();
 
   bool video_tracks_changed = video_tracks_.size() != video_tracks.size();
   bool audio_tracks_changed = audio_tracks_.size() != audio_tracks.size();
 
   if (!video_tracks_changed) {
-    for (size_t i = 0; i < video_tracks.size(); ++i) {
-      if (video_tracks_[i].Id() != video_tracks[i].Id()) {
+    for (wtf_size_t i = 0; i < video_tracks.size(); ++i) {
+      if (video_tracks_[i]->Id() != video_tracks[i]->Id()) {
         video_tracks_changed = true;
         break;
       }
     }
   }
   if (!video_tracks_changed && !audio_tracks_changed) {
-    for (size_t i = 0; i < audio_tracks.size(); ++i) {
-      if (audio_tracks_[i].Id() != audio_tracks[i].Id()) {
+    for (wtf_size_t i = 0; i < audio_tracks.size(); ++i) {
+      if (audio_tracks_[i]->Id() != audio_tracks[i]->Id()) {
         audio_tracks_changed = true;
         break;
       }
@@ -575,4 +566,13 @@
     recorder->OnSetFormat(params);
 }
 
+void MediaRecorderHandler::Trace(blink::Visitor* visitor) {
+  visitor->Trace(media_stream_);
+  visitor->Trace(video_tracks_);
+  visitor->Trace(audio_tracks_);
+  visitor->Trace(recorder_);
+  visitor->Trace(video_recorders_);
+  visitor->Trace(audio_recorders_);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
index cc8b605..b368325 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
@@ -12,11 +12,11 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_piece.h"
 #include "base/threading/thread_checker.h"
-#include "third_party/blink/public/platform/web_media_stream.h"
-#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -29,9 +29,10 @@
 
 namespace blink {
 
+class MediaRecorder;
+class MediaStreamDescriptor;
 struct WebMediaCapabilitiesInfo;
 struct WebMediaConfiguration;
-class MediaRecorderHandlerClient;
 
 // MediaRecorderHandler orchestrates the creation, lifetime management and
 // mapping between:
@@ -42,9 +43,10 @@
 // All methods are called on the same thread as construction and destruction,
 // i.e. the Main Render thread. (Note that a BindToCurrentLoop is used to
 // guarantee this, since VideoTrackRecorder sends back frames on IO thread.)
-class MODULES_EXPORT MediaRecorderHandler {
+class MODULES_EXPORT MediaRecorderHandler
+    : public GarbageCollectedFinalized<MediaRecorderHandler> {
  public:
-  static std::unique_ptr<MediaRecorderHandler> Create(
+  static MediaRecorderHandler* Create(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   explicit MediaRecorderHandler(
@@ -59,8 +61,8 @@
   // encoding."
   // [1] https://w3c.github.io/mediacapture-record/MediaRecorder.html#methods
   bool CanSupportMimeType(const String& type, const String& web_codecs);
-  bool Initialize(MediaRecorderHandlerClient* client,
-                  const WebMediaStream& media_stream,
+  bool Initialize(MediaRecorder* client,
+                  MediaStreamDescriptor* media_stream,
                   const String& type,
                   const String& codecs,
                   int32_t audio_bits_per_second,
@@ -78,6 +80,9 @@
                     OnMediaCapabilitiesEncodingInfoCallback cb);
   String ActualMimeType();
 
+  EAGERLY_FINALIZE();
+  void Trace(blink::Visitor*);
+
  private:
   friend class MediaRecorderHandlerTest;
 
@@ -115,22 +120,22 @@
   // Audio Codec, OPUS is used by default.
   AudioTrackRecorder::CodecId audio_codec_id_;
 
-  // |client_| has no notion of time, thus may configure us via start(timeslice)
-  // to notify it after a certain |timeslice_| has passed. We use a moving
-  // |slice_origin_timestamp_| to track those time chunks.
+  // |recorder_| has no notion of time, thus may configure us via
+  // start(timeslice) to notify it after a certain |timeslice_| has passed. We
+  // use a moving |slice_origin_timestamp_| to track those time chunks.
   base::TimeDelta timeslice_;
   base::TimeTicks slice_origin_timestamp_;
 
   bool recording_;
-  WebMediaStream media_stream_;  // The MediaStream being recorded.
-  WebVector<WebMediaStreamTrack> video_tracks_;
-  WebVector<WebMediaStreamTrack> audio_tracks_;
+  // The MediaStream being recorded.
+  Member<MediaStreamDescriptor> media_stream_;
+  HeapVector<Member<MediaStreamComponent>> video_tracks_;
+  HeapVector<Member<MediaStreamComponent>> audio_tracks_;
 
-  // |client_| is a weak pointer, and is valid for the lifetime of this object.
-  MediaRecorderHandlerClient* client_;
+  Member<MediaRecorder> recorder_;
 
-  Vector<std::unique_ptr<VideoTrackRecorder>> video_recorders_;
-  Vector<std::unique_ptr<AudioTrackRecorder>> audio_recorders_;
+  HeapVector<Member<VideoTrackRecorder>> video_recorders_;
+  HeapVector<Member<AudioTrackRecorder>> audio_recorders_;
 
   // Worker class doing the actual Webm Muxing work.
   std::unique_ptr<media::WebmMuxer> webm_muxer_;
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_client.h b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_client.h
deleted file mode 100644
index 9346dcb..0000000
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_client.h
+++ /dev/null
@@ -1,27 +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_MODULES_MEDIARECORDER_MEDIA_RECORDER_HANDLER_CLIENT_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_MEDIA_RECORDER_HANDLER_CLIENT_H_
-
-namespace WTF {
-class String;
-}
-
-namespace blink {
-
-// Interface used by a MediaRecorder to get errors and recorded data delivered.
-class MediaRecorderHandlerClient {
- public:
-  virtual void WriteData(const char* data,
-                         size_t length,
-                         bool last_inslice,
-                         double timecode) = 0;
-
-  virtual void OnError(const String& message) = 0;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIARECORDER_MEDIA_RECORDER_HANDLER_CLIENT_H_
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
index daab2c80..ab04baf 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
@@ -16,8 +16,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/modules/mediastream/mock_media_stream_registry.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/modules/mediarecorder/media_recorder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h"
-#include "third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_client.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -70,11 +72,41 @@
     {true, true, "video/webm", "vp9,opus", true},
 };
 
-class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams>,
-                                 public MediaRecorderHandlerClient {
+MediaStream* CreateMediaStream(V8TestingScope& scope) {
+  auto* source = MakeGarbageCollected<MediaStreamSource>(
+      "sourceId", MediaStreamSource::kTypeAudio, "sourceName", false);
+  auto* component =
+      MakeGarbageCollected<MediaStreamComponent>("audioTrack", source);
+
+  auto* track =
+      MediaStreamTrack::Create(scope.GetExecutionContext(), component);
+
+  HeapVector<Member<MediaStreamTrack>> tracks;
+  tracks.push_back(track);
+
+  MediaStream* stream =
+      MediaStream::Create(scope.GetExecutionContext(), tracks);
+
+  return stream;
+}
+
+class MockMediaRecorder : public MediaRecorder {
+ public:
+  MockMediaRecorder(V8TestingScope& scope)
+      : MediaRecorder(scope.GetExecutionContext(),
+                      CreateMediaStream(scope),
+                      MediaRecorderOptions::Create(),
+                      scope.GetExceptionState()) {}
+  virtual ~MockMediaRecorder() = default;
+
+  MOCK_METHOD4(WriteData, void(const char*, size_t, bool, double));
+  MOCK_METHOD1(OnError, void(const String& message));
+};
+
+class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams> {
  public:
   MediaRecorderHandlerTest()
-      : media_recorder_handler_(new MediaRecorderHandler(
+      : media_recorder_handler_(MediaRecorderHandler::Create(
             scheduler::GetSingleThreadTaskRunnerForTesting())),
         audio_source_(kTestAudioChannels,
                       440 /* freq */,
@@ -89,9 +121,6 @@
     ThreadState::Current()->CollectAllGarbageForTesting();
   }
 
-  MOCK_METHOD4(WriteData, void(const char*, size_t, bool, double));
-  MOCK_METHOD1(OnError, void(const String& message));
-
   bool recording() const { return media_recorder_handler_->recording_; }
   bool hasVideoRecorders() const {
     return !media_recorder_handler_->video_recorders_.IsEmpty();
@@ -138,7 +167,7 @@
   MockMediaStreamRegistry registry_;
 
   // The Class under test. Needs to be scoped_ptr to force its destruction.
-  std::unique_ptr<MediaRecorderHandler> media_recorder_handler_;
+  Persistent<MediaRecorderHandler> media_recorder_handler_;
 
   // For generating test AudioBuses
   media::SineWaveAudioSource audio_source_;
@@ -203,10 +232,12 @@
 // Checks that the initialization-destruction sequence works fine.
 TEST_P(MediaRecorderHandlerTest, InitializeStartStop) {
   AddTracks();
+  V8TestingScope scope;
+  auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
   const String mime_type(GetParam().mime_type);
   const String codecs(GetParam().codecs);
-  EXPECT_TRUE(media_recorder_handler_->Initialize(this, registry_.test_stream(),
-                                                  mime_type, codecs, 0, 0));
+  EXPECT_TRUE(media_recorder_handler_->Initialize(
+      recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
   EXPECT_FALSE(recording());
   EXPECT_FALSE(hasVideoRecorders());
   EXPECT_FALSE(hasAudioRecorders());
@@ -223,8 +254,8 @@
   EXPECT_FALSE(hasAudioRecorders());
 
   // Expect a last call on destruction.
-  EXPECT_CALL(*this, WriteData(_, _, true, _)).Times(1);
-  media_recorder_handler_.reset();
+  EXPECT_CALL(*recorder, WriteData(_, _, true, _)).Times(1);
+  media_recorder_handler_ = nullptr;
 }
 
 // Sends 2 opaque frames and 1 transparent frame and expects them as WebM
@@ -236,10 +267,12 @@
 
   AddTracks();
 
+  V8TestingScope scope;
+  auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
   const String mime_type(GetParam().mime_type);
   const String codecs(GetParam().codecs);
-  EXPECT_TRUE(media_recorder_handler_->Initialize(this, registry_.test_stream(),
-                                                  mime_type, codecs, 0, 0));
+  EXPECT_TRUE(media_recorder_handler_->Initialize(
+      recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
   EXPECT_TRUE(media_recorder_handler_->Start(0));
 
   InSequence s;
@@ -252,9 +285,9 @@
     base::Closure quit_closure = run_loop.QuitClosure();
     // writeData() is pinged a number of times as the WebM header is written;
     // the last time it is called it has the encoded data.
-    EXPECT_CALL(*this, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
         .Times(AtLeast(1));
-    EXPECT_CALL(*this, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
         .Times(1)
         .WillOnce(RunClosure5(std::move(quit_closure)));
 
@@ -269,9 +302,9 @@
     base::Closure quit_closure = run_loop.QuitClosure();
     // The second time around writeData() is called a number of times to write
     // the WebM frame header, and then is pinged with the encoded data.
-    EXPECT_CALL(*this, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
         .Times(AtLeast(1));
-    EXPECT_CALL(*this, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
         .Times(1)
         .WillOnce(RunClosure5(std::move(quit_closure)));
 
@@ -289,15 +322,15 @@
     base::Closure quit_closure = run_loop.QuitClosure();
     // The second time around writeData() is called a number of times to write
     // the WebM frame header, and then is pinged with the encoded data.
-    EXPECT_CALL(*this, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
         .Times(AtLeast(1));
-    EXPECT_CALL(*this, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
         .Times(1)
         .WillOnce(RunClosure5(quit_closure));
     if (GetParam().encoder_supports_alpha) {
-      EXPECT_CALL(*this, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
+      EXPECT_CALL(*recorder, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
           .Times(AtLeast(1));
-      EXPECT_CALL(*this, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
+      EXPECT_CALL(*recorder, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
           .Times(1)
           .WillOnce(RunClosure5(std::move(quit_closure)));
     }
@@ -308,9 +341,9 @@
 
   media_recorder_handler_->Stop();
 
-  // Expect a last call on destruction, with size 0 and |lastInSlice| true.
-  EXPECT_CALL(*this, WriteData(nullptr, 0, true, _)).Times(1);
-  media_recorder_handler_.reset();
+  // Expect a last call on destruction.
+  EXPECT_CALL(*recorder, WriteData(_, _, true, _)).Times(1);
+  media_recorder_handler_ = nullptr;
 }
 
 INSTANTIATE_TEST_SUITE_P(,
@@ -326,10 +359,13 @@
 
   AddTracks();
 
+  V8TestingScope scope;
+  auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
+
   const String mime_type(GetParam().mime_type);
   const String codecs(GetParam().codecs);
-  EXPECT_TRUE(media_recorder_handler_->Initialize(this, registry_.test_stream(),
-                                                  mime_type, codecs, 0, 0));
+  EXPECT_TRUE(media_recorder_handler_->Initialize(
+      recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
   EXPECT_TRUE(media_recorder_handler_->Start(0));
 
   InSequence s;
@@ -348,9 +384,9 @@
     base::Closure quit_closure = run_loop.QuitClosure();
     // writeData() is pinged a number of times as the WebM header is written;
     // the last time it is called it has the encoded data.
-    EXPECT_CALL(*this, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
         .Times(AtLeast(1));
-    EXPECT_CALL(*this, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
         .Times(1)
         .WillOnce(RunClosure5(std::move(quit_closure)));
 
@@ -365,9 +401,9 @@
     base::Closure quit_closure = run_loop.QuitClosure();
     // The second time around writeData() is called a number of times to write
     // the WebM frame header, and then is pinged with the encoded data.
-    EXPECT_CALL(*this, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Lt(kEncodedSizeThreshold), _, _))
         .Times(AtLeast(1));
-    EXPECT_CALL(*this, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
         .Times(1)
         .WillOnce(RunClosure5(std::move(quit_closure)));
 
@@ -378,9 +414,9 @@
 
   media_recorder_handler_->Stop();
 
-  // Expect a last call on destruction, with size 0 and |lastInSlice| true.
-  EXPECT_CALL(*this, WriteData(nullptr, 0, true, _)).Times(1);
-  media_recorder_handler_.reset();
+  // Expect a last call on destruction.
+  EXPECT_CALL(*recorder, WriteData(_, _, true, _)).Times(1);
+  media_recorder_handler_ = nullptr;
 }
 
 // Starts up recording and forces a WebmMuxer's libwebm error.
@@ -391,10 +427,13 @@
 
   AddTracks();
 
+  V8TestingScope scope;
+  auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
+
   const String mime_type(GetParam().mime_type);
   const String codecs(GetParam().codecs);
-  EXPECT_TRUE(media_recorder_handler_->Initialize(this, registry_.test_stream(),
-                                                  mime_type, codecs, 0, 0));
+  EXPECT_TRUE(media_recorder_handler_->Initialize(
+      recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
   EXPECT_TRUE(media_recorder_handler_->Start(0));
 
   InSequence s;
@@ -405,8 +444,8 @@
     const size_t kEncodedSizeThreshold = 16;
     base::RunLoop run_loop;
     base::Closure quit_closure = run_loop.QuitClosure();
-    EXPECT_CALL(*this, WriteData(_, _, _, _)).Times(AtLeast(1));
-    EXPECT_CALL(*this, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
+    EXPECT_CALL(*recorder, WriteData(_, _, _, _)).Times(AtLeast(1));
+    EXPECT_CALL(*recorder, WriteData(_, Gt(kEncodedSizeThreshold), _, _))
         .Times(1)
         .WillOnce(RunClosure5(std::move(quit_closure)));
 
@@ -419,8 +458,8 @@
   {
     base::RunLoop run_loop;
     base::Closure quit_closure = run_loop.QuitClosure();
-    EXPECT_CALL(*this, WriteData(_, _, _, _)).Times(0);
-    EXPECT_CALL(*this, OnError(_))
+    EXPECT_CALL(*recorder, WriteData(_, _, _, _)).Times(0);
+    EXPECT_CALL(*recorder, OnError(_))
         .Times(1)
         .WillOnce(RunClosure5(std::move(quit_closure)));
 
@@ -428,18 +467,22 @@
     run_loop.Run();
   }
 
-  // Expect a last call on destruction, with size 0 and |lastInSlice| true.
-  EXPECT_CALL(*this, WriteData(nullptr, 0, true, _)).Times(1);
-  media_recorder_handler_.reset();
+  // Expect a last call on destruction.
+  EXPECT_CALL(*recorder, WriteData(_, _, true, _)).Times(1);
+  media_recorder_handler_ = nullptr;
 }
 
 // Checks the ActualMimeType() versus the expected.
 TEST_P(MediaRecorderHandlerTest, ActualMimeType) {
   AddTracks();
+
+  V8TestingScope scope;
+  auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
+
   const String mime_type(GetParam().mime_type);
   const String codecs(GetParam().codecs);
-  EXPECT_TRUE(media_recorder_handler_->Initialize(this, registry_.test_stream(),
-                                                  mime_type, codecs, 0, 0));
+  EXPECT_TRUE(media_recorder_handler_->Initialize(
+      recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
 
   StringBuilder actual_mime_type;
   actual_mime_type.Append(GetParam().mime_type);
@@ -454,9 +497,9 @@
   EXPECT_EQ(media_recorder_handler_->ActualMimeType(),
             actual_mime_type.ToString());
 
-  // Expect a last call on destruction, with size 0 and |lastInSlice| true.
-  EXPECT_CALL(*this, WriteData(nullptr, 0, true, _)).Times(1);
-  media_recorder_handler_.reset();
+  // Expect a last call on destruction.
+  EXPECT_CALL(*recorder, WriteData(_, _, true, _)).Times(1);
+  media_recorder_handler_ = nullptr;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
index 10e725c..b993f06 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
@@ -19,9 +19,12 @@
 #include "skia/ext/platform_canvas.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/modules/mediarecorder/buildflags.h"
 #include "third_party/blink/renderer/modules/mediarecorder/vea_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/vpx_encoder.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.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/cross_thread_functional.h"
@@ -427,7 +430,7 @@
 
 VideoTrackRecorder::VideoTrackRecorder(
     CodecId codec,
-    const WebMediaStreamTrack& track,
+    MediaStreamComponent* track,
     const OnEncodedVideoCB& on_encoded_video_callback,
     int32_t bits_per_second,
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
@@ -436,26 +439,19 @@
       main_task_runner_(std::move(main_task_runner)),
       weak_ptr_factory_(this) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
-  DCHECK(!track_.IsNull());
-  DCHECK(track_.GetPlatformTrack());
+  DCHECK(track_);
+  DCHECK(track_->Source()->GetType() == MediaStreamSource::kTypeVideo);
 
   initialize_encoder_callback_ = WTF::BindRepeating(
       &VideoTrackRecorder::InitializeEncoder, weak_ptr_factory_.GetWeakPtr(),
       codec, on_encoded_video_callback, bits_per_second);
 
   // InitializeEncoder() will be called on Render Main thread.
-  MediaStreamVideoSink::ConnectToTrack(
-      track_,
-      media::BindToCurrentLoop(WTF::BindRepeating(
-          initialize_encoder_callback_, true /* allow_vea_encoder */)),
-      false);
+  ConnectToTrack(media::BindToCurrentLoop(WTF::BindRepeating(
+      initialize_encoder_callback_, true /* allow_vea_encoder */)));
 }
 
-VideoTrackRecorder::~VideoTrackRecorder() {
-  DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
-  MediaStreamVideoSink::DisconnectFromTrack();
-  track_.Reset();
-}
+VideoTrackRecorder::~VideoTrackRecorder() = default;
 
 void VideoTrackRecorder::Pause() {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
@@ -502,7 +498,7 @@
   if (encoder_)
     return;
 
-  MediaStreamVideoSink::DisconnectFromTrack();
+  DisconnectFromTrack();
 
   const gfx::Size& input_size = frame->visible_rect().size();
   if (allow_vea_encoder && CanUseAcceleratedEncoder(codec, input_size.width(),
@@ -539,11 +535,8 @@
     encoder_->SetPaused(should_pause_encoder_on_initialization_);
 
   // StartFrameEncode() will be called on Render IO thread.
-  MediaStreamVideoSink::ConnectToTrack(
-      track_,
-      ConvertToBaseCallback(CrossThreadBindRepeating(
-          &VideoTrackRecorder::Encoder::StartFrameEncode, encoder_)),
-      false);
+  ConnectToTrack(ConvertToBaseCallback(CrossThreadBindRepeating(
+      &VideoTrackRecorder::Encoder::StartFrameEncode, encoder_)));
 }
 
 void VideoTrackRecorder::OnError() {
@@ -552,13 +545,35 @@
 
   // InitializeEncoder() will be called to reinitialize encoder on Render Main
   // thread.
-  MediaStreamVideoSink::DisconnectFromTrack();
+  DisconnectFromTrack();
   encoder_ = nullptr;
-  MediaStreamVideoSink::ConnectToTrack(
-      track_,
-      media::BindToCurrentLoop(WTF::BindRepeating(initialize_encoder_callback_,
-                                                  false /*allow_vea_encoder*/)),
-      false);
+  ConnectToTrack(media::BindToCurrentLoop(WTF::BindRepeating(
+      initialize_encoder_callback_, false /*allow_vea_encoder*/)));
+}
+
+void VideoTrackRecorder::ConnectToTrack(
+    const VideoCaptureDeliverFrameCB& callback) {
+  auto* video_track =
+      static_cast<MediaStreamVideoTrack*>(track_->GetPlatformTrack());
+  video_track->AddSink(this, callback, false);
+}
+
+void VideoTrackRecorder::DisconnectFromTrack() {
+  auto* video_track =
+      static_cast<MediaStreamVideoTrack*>(track_->GetPlatformTrack());
+  video_track->RemoveSink(this);
+}
+
+void VideoTrackRecorder::Trace(blink::Visitor* visitor) {
+  visitor->Trace(track_);
+}
+
+void VideoTrackRecorder::Prefinalize() {
+  // TODO(crbug.com/704136) : Remove this method when moving
+  // MediaStreamVideoTrack to Oilpan's heap.
+  DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+  DisconnectFromTrack();
+  track_ = nullptr;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
index 3911420..88d99224 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
@@ -15,8 +15,9 @@
 #include "build/build_config.h"
 #include "media/muxers/webm_muxer.h"
 #include "media/video/video_encode_accelerator.h"
+#include "third_party/blink/public/common/media/video_capture.h"
+#include "third_party/blink/public/platform/modules/mediastream/web_media_stream_sink.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/blink/public/web/modules/mediastream/media_stream_video_sink.h"
 #include "third_party/blink/renderer/modules/mediarecorder/buildflags.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
@@ -61,6 +62,7 @@
 
 namespace blink {
 
+class MediaStreamVideoTrack;
 class Thread;
 
 // VideoTrackRecorder is a MediaStreamVideoSink that encodes the video frames
@@ -69,7 +71,11 @@
 // MediaStreamVideo* classes that are constructed/configured on Main Render
 // thread but that pass frames on Render IO thread. It has an internal Encoder
 // with its own threading subtleties, see the implementation file.
-class MODULES_EXPORT VideoTrackRecorder : public MediaStreamVideoSink {
+class MODULES_EXPORT VideoTrackRecorder
+    : public GarbageCollectedFinalized<VideoTrackRecorder>,
+      public WebMediaStreamSink {
+  USING_PRE_FINALIZER(VideoTrackRecorder, Prefinalize);
+
  public:
   // Do not change the order of codecs; add new ones right before LAST.
   enum class CodecId {
@@ -257,7 +263,7 @@
 
   VideoTrackRecorder(
       CodecId codec,
-      const WebMediaStreamTrack& track,
+      MediaStreamComponent* track,
       const OnEncodedVideoCB& on_encoded_video_cb,
       int32_t bits_per_second,
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
@@ -269,6 +275,8 @@
   void OnVideoFrameForTesting(scoped_refptr<media::VideoFrame> frame,
                               base::TimeTicks capture_time);
 
+  void Trace(blink::Visitor*);
+
  private:
   friend class VideoTrackRecorderTest;
   void InitializeEncoder(CodecId codec,
@@ -279,11 +287,16 @@
                          base::TimeTicks capture_time);
   void OnError();
 
+  void ConnectToTrack(const VideoCaptureDeliverFrameCB& callback);
+  void DisconnectFromTrack();
+
+  void Prefinalize();
+
   // Used to check that we are destroyed on the same thread we were created.
   THREAD_CHECKER(main_thread_checker_);
 
   // We need to hold on to the Blink track to remove ourselves on dtor.
-  WebMediaStreamTrack track_;
+  Member<MediaStreamComponent> track_;
 
   // Inner class to encode using whichever codec is configured.
   scoped_refptr<Encoder> encoder_;
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
index 14f9066..aed1633 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
@@ -83,18 +83,18 @@
   ~VideoTrackRecorderTest() {
     blink_track_.Reset();
     blink_source_.Reset();
-    video_track_recorder_.reset();
+    video_track_recorder_ = nullptr;
     WebHeap::CollectAllGarbageForTesting();
   }
 
   void InitializeRecorder(VideoTrackRecorder::CodecId codec) {
-    video_track_recorder_.reset(new VideoTrackRecorder(
+    video_track_recorder_ = MakeGarbageCollected<VideoTrackRecorder>(
         codec, blink_track_,
         ConvertToBaseCallback(
             CrossThreadBindRepeating(&VideoTrackRecorderTest::OnEncodedVideo,
                                      CrossThreadUnretained(this))),
         0 /* bits_per_second */,
-        scheduler::GetSingleThreadTaskRunnerForTesting()));
+        scheduler::GetSingleThreadTaskRunnerForTesting());
   }
 
   MOCK_METHOD5(DoOnEncodedVideo,
@@ -141,7 +141,7 @@
   MediaStreamVideoTrack* track_;
   WebMediaStreamTrack blink_track_;
 
-  std::unique_ptr<VideoTrackRecorder> video_track_recorder_;
+  Persistent<VideoTrackRecorder> video_track_recorder_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(VideoTrackRecorderTest);
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.cc b/third_party/blink/renderer/modules/mediasource/media_source.cc
index 7300809d..5489bc8 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.cc
+++ b/third_party/blink/renderer/modules/mediasource/media_source.cc
@@ -47,6 +47,7 @@
 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/network/mime/content_type.h"
@@ -58,6 +59,17 @@
 
 namespace blink {
 
+namespace {
+// These values are written to logs. New enum values can be added, but existing
+// ones must never be renumbered or deleted and reused.
+enum class MseExecutionContext {
+  kWindow = 0,
+  kDedicatedWorker = 1,
+  kSharedWorker = 2,
+  kMax = kSharedWorker
+};
+}  // namespace
+
 static bool ThrowExceptionIfClosed(bool is_open,
                                    ExceptionState& exception_state) {
   if (!is_open) {
@@ -123,6 +135,29 @@
       live_seekable_range_(MakeGarbageCollected<TimeRanges>()),
       added_to_registry_counter_(0) {
   DVLOG(1) << __func__ << " this=" << this;
+
+  DCHECK(RuntimeEnabledFeatures::MediaSourceInWorkersEnabled() ||
+         IsMainThread());
+
+  MseExecutionContext type = MseExecutionContext::kWindow;
+  if (!IsMainThread()) {
+    if (context->IsDedicatedWorkerGlobalScope())
+      type = MseExecutionContext::kDedicatedWorker;
+    else if (context->IsSharedWorkerGlobalScope())
+      type = MseExecutionContext::kSharedWorker;
+    else
+      CHECK(false) << "Invalid execution context for MSE usage";
+  }
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(
+      EnumerationHistogram, mse_execution_context_histogram,
+      ("Media.MSE.ExecutionContext",
+       static_cast<int>(MseExecutionContext::kMax) + 1));
+  mse_execution_context_histogram.Count(static_cast<int>(type));
+
+  // TODO(wolenetz): Actually enable experimental usage of MediaSource from
+  // dedicated and shared worker contexts. See https://crbug.com/878133.
+  CHECK(type == MseExecutionContext::kWindow)
+      << "MSE is not yet supported from workers";
 }
 
 MediaSource::~MediaSource() {
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.idl b/third_party/blink/renderer/modules/mediasource/media_source.idl
index b081da91..76a2b9b7 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.idl
+++ b/third_party/blink/renderer/modules/mediasource/media_source.idl
@@ -38,7 +38,8 @@
 [
     ActiveScriptWrappable,
     Constructor,
-    ConstructorCallWith=ExecutionContext
+    ConstructorCallWith=ExecutionContext,
+    Exposed(Window MediaSourceStable, DedicatedWorker MediaSourceInWorkers, SharedWorker MediaSourceInWorkers)
 ] interface MediaSource : EventTarget {
     // All the source buffers created by this object.
     readonly attribute SourceBufferList sourceBuffers;
diff --git a/third_party/blink/renderer/modules/mediastream/BUILD.gn b/third_party/blink/renderer/modules/mediastream/BUILD.gn
index 2a7136f..44a6c33 100644
--- a/third_party/blink/renderer/modules/mediastream/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediastream/BUILD.gn
@@ -73,6 +73,7 @@
     "//base/test:test_support",
     "//skia",
     "//testing/gmock",
+    "//third_party/blink/public:test_headers",
     "//third_party/blink/public/mojom:mojom_platform_blink_headers",
     "//third_party/webrtc_overrides:init_webrtc",
   ]
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index d6ca16c..a823184 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -82,6 +82,7 @@
 #include "third_party/blink/renderer/modules/webdatabase/database_client.h"
 #include "third_party/blink/renderer/modules/webdatabase/database_manager.h"
 #include "third_party/blink/renderer/modules/webdatabase/inspector_database_agent.h"
+#include "third_party/blink/renderer/modules/webdatabase/web_database_host.h"
 #include "third_party/blink/renderer/modules/webdatabase/web_database_impl.h"
 #include "third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread.h"
 #include "third_party/blink/renderer/modules/xr/navigator_xr.h"
@@ -120,6 +121,7 @@
   DraggedIsolatedFileSystem::Init(
       DraggedIsolatedFileSystemImpl::PrepareForDataObject);
   CSSPaintImageGenerator::Init(CSSPaintImageGeneratorImpl::Create);
+  WebDatabaseHost::GetInstance().Init();
 
   CoreInitializer::Initialize();
 
diff --git a/third_party/blink/renderer/modules/netinfo/network_information.cc b/third_party/blink/renderer/modules/netinfo/network_information.cc
index 58335db..fe023d9 100644
--- a/third_party/blink/renderer/modules/netinfo/network_information.cc
+++ b/third_party/blink/renderer/modules/netinfo/network_information.cc
@@ -6,7 +6,7 @@
 
 #include <algorithm>
 
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
diff --git a/third_party/blink/renderer/modules/notifications/BUILD.gn b/third_party/blink/renderer/modules/notifications/BUILD.gn
index daffd3a..51776ed 100644
--- a/third_party/blink/renderer/modules/notifications/BUILD.gn
+++ b/third_party/blink/renderer/modules/notifications/BUILD.gn
@@ -12,8 +12,6 @@
     "notification_data.h",
     "notification_event.cc",
     "notification_event.h",
-    "notification_image_loader.cc",
-    "notification_image_loader.h",
     "notification_manager.cc",
     "notification_manager.h",
     "notification_resources_loader.cc",
diff --git a/third_party/blink/renderer/modules/notifications/notification_image_loader.cc b/third_party/blink/renderer/modules/notifications/notification_image_loader.cc
deleted file mode 100644
index 51be9dfe..0000000
--- a/third_party/blink/renderer/modules/notifications/notification_image_loader.cc
+++ /dev/null
@@ -1,202 +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/modules/notifications/notification_image_loader.h"
-
-#include <memory>
-#include "base/numerics/safe_conversions.h"
-#include "base/time/default_tick_clock.h"
-#include "skia/ext/image_operations.h"
-#include "third_party/blink/public/platform/modules/notifications/web_notification_constants.h"
-#include "third_party/blink/public/platform/web_url_request.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/platform/histogram.h"
-#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
-#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/wtf/threading.h"
-#include "third_party/blink/renderer/platform/wtf/time.h"
-
-#define NOTIFICATION_PER_TYPE_HISTOGRAM_COUNTS(metric, type_name, value, max) \
-  case NotificationImageLoader::Type::k##type_name: {                         \
-    DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram,                     \
-                                    metric##type_name##Histogram,             \
-                                    ("Notifications." #metric "." #type_name, \
-                                     1 /* min */, max, 50 /* buckets */));    \
-    metric##type_name##Histogram.Count(value);                                \
-    break;                                                                    \
-  }
-
-#define NOTIFICATION_HISTOGRAM_COUNTS(metric, type, value, max)            \
-  switch (type) {                                                          \
-    NOTIFICATION_PER_TYPE_HISTOGRAM_COUNTS(metric, Image, value, max)      \
-    NOTIFICATION_PER_TYPE_HISTOGRAM_COUNTS(metric, Icon, value, max)       \
-    NOTIFICATION_PER_TYPE_HISTOGRAM_COUNTS(metric, Badge, value, max)      \
-    NOTIFICATION_PER_TYPE_HISTOGRAM_COUNTS(metric, ActionIcon, value, max) \
-  }
-
-namespace {
-
-// 99.9% of all images were fetched successfully in 90 seconds.
-const uint32_t kImageFetchTimeoutInMs = 90000;
-
-}  // namespace
-
-namespace blink {
-
-NotificationImageLoader::NotificationImageLoader(Type type,
-                                                 const base::TickClock* clock)
-    : type_(type), stopped_(false), clock_(clock) {}
-
-NotificationImageLoader::~NotificationImageLoader() = default;
-
-void NotificationImageLoader::Start(ExecutionContext* context,
-                                    const KURL& url,
-                                    ImageCallback image_callback) {
-  DCHECK(!stopped_);
-
-  start_time_ = clock_->NowTicks();
-  image_callback_ = std::move(image_callback);
-
-  // TODO(mvanouwerkerk): Add an entry for notifications to
-  // FetchInitiatorTypeNames and use it.
-  ResourceLoaderOptions resource_loader_options;
-  if (context->IsWorkerGlobalScope())
-    resource_loader_options.request_initiator_context = kWorkerContext;
-
-  ResourceRequest resource_request(url);
-  resource_request.SetRequestContext(mojom::RequestContextType::IMAGE);
-  resource_request.SetPriority(ResourceLoadPriority::kMedium);
-
-  threadable_loader_ = MakeGarbageCollected<ThreadableLoader>(
-      *context, this, resource_loader_options);
-  threadable_loader_->SetTimeout(
-      base::TimeDelta::FromMilliseconds(kImageFetchTimeoutInMs));
-  threadable_loader_->Start(resource_request);
-}
-
-void NotificationImageLoader::Stop() {
-  if (stopped_)
-    return;
-
-  stopped_ = true;
-  if (threadable_loader_) {
-    threadable_loader_->Cancel();
-    threadable_loader_ = nullptr;
-  }
-}
-
-void NotificationImageLoader::DidReceiveData(const char* data,
-                                             unsigned length) {
-  if (!data_)
-    data_ = SharedBuffer::Create();
-  data_->Append(data, length);
-}
-
-void NotificationImageLoader::DidFinishLoading(uint64_t resource_identifier) {
-  // If this has been stopped it is not desirable to trigger further work,
-  // there is a shutdown of some sort in progress.
-  if (stopped_)
-    return;
-
-  NOTIFICATION_HISTOGRAM_COUNTS(
-      LoadFinishTime, type_,
-      base::saturated_cast<base::HistogramBase::Sample>(
-          (clock_->NowTicks() - start_time_).InMilliseconds()),
-      1000 * 60 * 60 /* 1 hour max */);
-
-  if (data_) {
-    NOTIFICATION_HISTOGRAM_COUNTS(
-        LoadFileSize, type_,
-        base::saturated_cast<base::HistogramBase::Sample>(data_->size()),
-        10000000 /* ~10mb max */);
-
-    const bool data_complete = true;
-    std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
-        data_, data_complete, ImageDecoder::kAlphaPremultiplied,
-        ImageDecoder::kDefaultBitDepth, ColorBehavior::TransformToSRGB());
-    if (decoder) {
-      // The |ImageFrame*| is owned by the decoder.
-      ImageFrame* image_frame = decoder->DecodeFrameBufferAtIndex(0);
-      if (image_frame) {
-        std::move(image_callback_)
-            .Run(ScaleDownIfNeeded(image_frame->Bitmap()));
-        return;
-      }
-    }
-  }
-  RunCallbackWithEmptyBitmap();
-}
-
-void NotificationImageLoader::DidFail(const ResourceError& error) {
-  NOTIFICATION_HISTOGRAM_COUNTS(
-      LoadFailTime, type_,
-      base::saturated_cast<base::HistogramBase::Sample>(
-          (clock_->NowTicks() - start_time_).InMilliseconds()),
-      1000 * 60 * 60 /* 1 hour max */);
-
-  RunCallbackWithEmptyBitmap();
-}
-
-void NotificationImageLoader::DidFailRedirectCheck() {
-  RunCallbackWithEmptyBitmap();
-}
-
-void NotificationImageLoader::RunCallbackWithEmptyBitmap() {
-  // If this has been stopped it is not desirable to trigger further work,
-  // there is a shutdown of some sort in progress.
-  if (stopped_)
-    return;
-
-  std::move(image_callback_).Run(SkBitmap());
-}
-
-SkBitmap NotificationImageLoader::ScaleDownIfNeeded(const SkBitmap& image) {
-  int max_width_px = 0, max_height_px = 0;
-  switch (type_) {
-    case Type::kImage:
-      max_width_px = kWebNotificationMaxImageWidthPx;
-      max_height_px = kWebNotificationMaxImageHeightPx;
-      break;
-    case Type::kIcon:
-      max_width_px = kWebNotificationMaxIconSizePx;
-      max_height_px = kWebNotificationMaxIconSizePx;
-      break;
-    case Type::kBadge:
-      max_width_px = kWebNotificationMaxBadgeSizePx;
-      max_height_px = kWebNotificationMaxBadgeSizePx;
-      break;
-    case Type::kActionIcon:
-      max_width_px = kWebNotificationMaxActionIconSizePx;
-      max_height_px = kWebNotificationMaxActionIconSizePx;
-      break;
-  }
-  DCHECK_GT(max_width_px, 0);
-  DCHECK_GT(max_height_px, 0);
-  // TODO(peter): Explore doing the scaling on a background thread.
-  if (image.width() > max_width_px || image.height() > max_height_px) {
-    double scale =
-        std::min(static_cast<double>(max_width_px) / image.width(),
-                 static_cast<double>(max_height_px) / image.height());
-    base::TimeTicks start_time = CurrentTimeTicks();
-    // TODO(peter): Try using RESIZE_BETTER for large images.
-    SkBitmap scaled_image = skia::ImageOperations::Resize(
-        image, skia::ImageOperations::RESIZE_BEST,
-        static_cast<int>(std::lround(scale * image.width())),
-        static_cast<int>(std::lround(scale * image.height())));
-    NOTIFICATION_HISTOGRAM_COUNTS(
-        LoadScaleDownTime, type_,
-        base::saturated_cast<base::HistogramBase::Sample>(
-            (CurrentTimeTicks() - start_time).InMilliseconds()),
-        1000 * 10 /* 10 seconds max */);
-    return scaled_image;
-  }
-  return image;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/notifications/notification_image_loader.h b/third_party/blink/renderer/modules/notifications/notification_image_loader.h
deleted file mode 100644
index 4d2117a..0000000
--- a/third_party/blink/renderer/modules/notifications/notification_image_loader.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NOTIFICATIONS_NOTIFICATION_IMAGE_LOADER_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_NOTIFICATIONS_NOTIFICATION_IMAGE_LOADER_H_
-
-#include <memory>
-#include "base/memory/scoped_refptr.h"
-#include "base/time/default_tick_clock.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader_client.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/shared_buffer.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-#include "third_party/blink/renderer/platform/wtf/time.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace blink {
-
-class ExecutionContext;
-class KURL;
-class ResourceError;
-
-// Asynchronously downloads an image when given a url, decodes the loaded data,
-// and passes the bitmap to the given callback.
-class MODULES_EXPORT NotificationImageLoader final
-    : public GarbageCollectedFinalized<NotificationImageLoader>,
-      public ThreadableLoaderClient {
-  USING_GARBAGE_COLLECTED_MIXIN(NotificationImageLoader);
-
- public:
-  // Type names are used in UMAs, so do not rename.
-  enum class Type { kImage, kIcon, kBadge, kActionIcon };
-
-  // The bitmap may be empty if the request failed or the image data could not
-  // be decoded.
-  using ImageCallback = base::OnceCallback<void(const SkBitmap&)>;
-
-  NotificationImageLoader(
-      Type type,
-      const base::TickClock* clock = base::DefaultTickClock::GetInstance());
-  ~NotificationImageLoader() override;
-
-  // Asynchronously downloads an image from the given url, decodes the loaded
-  // data, and passes the bitmap to the callback. Times out if the load takes
-  // too long and ImageCallback is invoked with an empty bitmap.
-  void Start(ExecutionContext* context,
-             const KURL& url,
-             ImageCallback image_callback);
-
-  // Cancels the pending load, if there is one. The |m_imageCallback| will not
-  // be run.
-  void Stop();
-
-  // ThreadableLoaderClient interface.
-  void DidReceiveData(const char* data, unsigned length) override;
-  void DidFinishLoading(uint64_t resource_identifier) override;
-  void DidFail(const ResourceError& error) override;
-  void DidFailRedirectCheck() override;
-
-  void Trace(blink::Visitor* visitor) override {
-    visitor->Trace(threadable_loader_);
-    ThreadableLoaderClient::Trace(visitor);
-  }
-
- private:
-  // Scales down |image| according to its |type_| and returns result. If it is
-  // already small enough, |image| is returned unchanged.
-  SkBitmap ScaleDownIfNeeded(const SkBitmap& image);
-
-  void RunCallbackWithEmptyBitmap();
-
-  Type type_;
-  bool stopped_;
-  base::TimeTicks start_time_;
-  scoped_refptr<SharedBuffer> data_;
-  ImageCallback image_callback_;
-  Member<ThreadableLoader> threadable_loader_;
-  const base::TickClock* clock_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_NOTIFICATIONS_NOTIFICATION_IMAGE_LOADER_H_
diff --git a/third_party/blink/renderer/modules/notifications/notification_image_loader_test.cc b/third_party/blink/renderer/modules/notifications/notification_image_loader_test.cc
deleted file mode 100644
index 6b2fe636..0000000
--- a/third_party/blink/renderer/modules/notifications/notification_image_loader_test.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2016 Chromium 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/notifications/notification_image_loader.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/testing/page_test_base.h"
-#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
-#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
-#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
-#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace blink {
-namespace {
-
-enum class NotificationLoadState { kNotLoaded, kLoadFailed, kLoadSuccessful };
-
-constexpr char kNotificationImageLoaderBaseUrl[] = "http://test.com/";
-constexpr char kNotificationImageLoaderBaseDir[] = "notifications/";
-constexpr char kNotificationImageLoaderIcon500x500[] = "500x500.png";
-
-// This mirrors the definition in NotificationImageLoader.cpp.
-constexpr uint32_t kImageFetchTimeoutInMs = 90000;
-
-static_assert(kImageFetchTimeoutInMs > 1000.0,
-              "kImageFetchTimeoutInMs must be greater than 1000ms.");
-
-class NotificationImageLoaderTest : public PageTestBase {
- public:
-  NotificationImageLoaderTest() {
-    EnablePlatform();
-    // Use an arbitrary type, since it only affects which UMA bucket we use.
-    loader_ = MakeGarbageCollected<NotificationImageLoader>(
-        NotificationImageLoader::Type::kIcon,
-        platform()->test_task_runner()->GetMockTickClock());
-  }
-
-  ~NotificationImageLoaderTest() override {
-    loader_->Stop();
-    platform()
-        ->GetURLLoaderMockFactory()
-        ->UnregisterAllURLsAndClearMemoryCache();
-  }
-
-  void SetUp() override { PageTestBase::SetUp(IntSize()); }
-
-  // Registers a mocked URL. When fetched it will be loaded form the test data
-  // directory.
-  WebURL RegisterMockedURL(const String& file_name) {
-    WebURL registered_url = url_test_helpers::RegisterMockedURLLoadFromBase(
-        kNotificationImageLoaderBaseUrl,
-        test::CoreTestDataPath(kNotificationImageLoaderBaseDir), file_name,
-        "image/png");
-    return registered_url;
-  }
-
-  // Callback for the NotificationImageLoader. This will set the state of the
-  // load as either success or failed based on whether the bitmap is empty.
-  void ImageLoaded(const SkBitmap& bitmap) {
-    if (!bitmap.empty())
-      loaded_ = NotificationLoadState::kLoadSuccessful;
-    else
-      loaded_ = NotificationLoadState::kLoadFailed;
-  }
-
-  void LoadImage(const KURL& url) {
-    loader_->Start(
-        Context(), url,
-        Bind(&NotificationImageLoaderTest::ImageLoaded, WTF::Unretained(this)));
-  }
-
-  ExecutionContext* Context() const { return &GetDocument(); }
-  NotificationLoadState Loaded() const { return loaded_; }
-
- protected:
-  HistogramTester histogram_tester_;
-
- private:
-  Persistent<NotificationImageLoader> loader_;
-  NotificationLoadState loaded_ = NotificationLoadState::kNotLoaded;
-};
-
-TEST_F(NotificationImageLoaderTest, SuccessTest) {
-  KURL url = RegisterMockedURL(kNotificationImageLoaderIcon500x500);
-  LoadImage(url);
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFinishTime.Icon", 0);
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFileSize.Icon", 0);
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFailTime.Icon", 0);
-  platform()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-  EXPECT_EQ(NotificationLoadState::kLoadSuccessful, Loaded());
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFinishTime.Icon", 1);
-  histogram_tester_.ExpectUniqueSample("Notifications.LoadFileSize.Icon", 7439,
-                                       1);
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFailTime.Icon", 0);
-}
-
-TEST_F(NotificationImageLoaderTest, TimeoutTest) {
-  // To test for a timeout, this needs to override the clock in the platform.
-  // Just creating the mock platform will do everything to set it up.
-  KURL url = RegisterMockedURL(kNotificationImageLoaderIcon500x500);
-  LoadImage(url);
-
-  // Run the platform for kImageFetchTimeoutInMs-1 seconds. This should not
-  // result in a timeout.
-  platform()->RunForPeriodSeconds(kImageFetchTimeoutInMs / 1000 - 1);
-  EXPECT_EQ(NotificationLoadState::kNotLoaded, Loaded());
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFinishTime.Icon", 0);
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFileSize.Icon", 0);
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFailTime.Icon", 0);
-
-  // Now advance time until a timeout should be expected.
-  platform()->RunForPeriodSeconds(2);
-
-  // If the loader times out, it calls the callback and returns an empty bitmap.
-  EXPECT_EQ(NotificationLoadState::kLoadFailed, Loaded());
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFinishTime.Icon", 0);
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFileSize.Icon", 0);
-  // Should log a non-zero failure time.
-  histogram_tester_.ExpectTotalCount("Notifications.LoadFailTime.Icon", 1);
-  histogram_tester_.ExpectBucketCount("Notifications.LoadFailTime.Icon", 0, 0);
-}
-
-}  // namspace
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/notifications/notification_resources_loader.cc b/third_party/blink/renderer/modules/notifications/notification_resources_loader.cc
index 4fba768..89dccac1 100644
--- a/third_party/blink/renderer/modules/notifications/notification_resources_loader.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_resources_loader.cc
@@ -5,14 +5,40 @@
 #include "third_party/blink/renderer/modules/notifications/notification_resources_loader.h"
 
 #include <cmath>
+
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/modules/notifications/web_notification_constants.h"
+#include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/wtf/threading.h"
-#include "third_party/blink/renderer/platform/wtf/time.h"
 
 namespace blink {
 
+namespace {
+
+// 99.9% of all images were fetched successfully in 90 seconds.
+constexpr base::TimeDelta kImageFetchTimeout = base::TimeDelta::FromSeconds(90);
+
+enum class NotificationIconType { kImage, kIcon, kBadge, kActionIcon };
+
+WebSize GetIconDimensions(NotificationIconType type) {
+  switch (type) {
+    case NotificationIconType::kImage:
+      return {kWebNotificationMaxImageWidthPx,
+              kWebNotificationMaxImageHeightPx};
+    case NotificationIconType::kIcon:
+      return {kWebNotificationMaxIconSizePx, kWebNotificationMaxIconSizePx};
+    case NotificationIconType::kBadge:
+      return {kWebNotificationMaxBadgeSizePx, kWebNotificationMaxBadgeSizePx};
+    case NotificationIconType::kActionIcon:
+      return {kWebNotificationMaxActionIconSizePx,
+              kWebNotificationMaxActionIconSizePx};
+  }
+}
+
+}  // namespace
+
 NotificationResourcesLoader::NotificationResourcesLoader(
     CompletionCallback completion_callback)
     : started_(false),
@@ -36,25 +62,27 @@
 
   // TODO(johnme): ensure image is not loaded when it will not be used.
   // TODO(mvanouwerkerk): ensure no badge is loaded when it will not be used.
-  LoadImage(context, NotificationImageLoader::Type::kImage,
-            notification_data.image,
-            WTF::Bind(&NotificationResourcesLoader::DidLoadImage,
-                      WrapWeakPersistent(this)));
-  LoadImage(context, NotificationImageLoader::Type::kIcon,
-            notification_data.icon,
-            WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
-                      WrapWeakPersistent(this)));
-  LoadImage(context, NotificationImageLoader::Type::kBadge,
-            notification_data.badge,
-            WTF::Bind(&NotificationResourcesLoader::DidLoadBadge,
-                      WrapWeakPersistent(this)));
+  LoadIcon(context, notification_data.image,
+           GetIconDimensions(NotificationIconType::kImage),
+           WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
+                     WrapWeakPersistent(this), WTF::Unretained(&image_)));
+  LoadIcon(context, notification_data.icon,
+           GetIconDimensions(NotificationIconType::kIcon),
+           WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
+                     WrapWeakPersistent(this), WTF::Unretained(&icon_)));
+  LoadIcon(context, notification_data.badge,
+           GetIconDimensions(NotificationIconType::kBadge),
+           WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
+                     WrapWeakPersistent(this), WTF::Unretained(&badge_)));
 
-  action_icons_.resize(num_actions);
-  for (wtf_size_t i = 0; i < num_actions; i++)
-    LoadImage(context, NotificationImageLoader::Type::kActionIcon,
-              notification_data.actions.value()[i]->icon,
-              WTF::Bind(&NotificationResourcesLoader::DidLoadActionIcon,
-                        WrapWeakPersistent(this), i));
+  action_icons_.Grow(num_actions);
+  for (wtf_size_t i = 0; i < num_actions; i++) {
+    LoadIcon(context, notification_data.actions.value()[i]->icon,
+             GetIconDimensions(NotificationIconType::kActionIcon),
+             WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
+                       WrapWeakPersistent(this),
+                       WTF::Unretained(&action_icons_[i])));
+  }
 }
 
 mojom::blink::NotificationResourcesPtr
@@ -68,50 +96,39 @@
 }
 
 void NotificationResourcesLoader::Stop() {
-  for (auto image_loader : image_loaders_)
-    image_loader->Stop();
+  for (const auto& icon_loader : icon_loaders_)
+    icon_loader->Stop();
 }
 
 void NotificationResourcesLoader::Trace(blink::Visitor* visitor) {
-  visitor->Trace(image_loaders_);
+  visitor->Trace(icon_loaders_);
 }
 
-void NotificationResourcesLoader::LoadImage(
+void NotificationResourcesLoader::LoadIcon(
     ExecutionContext* context,
-    NotificationImageLoader::Type type,
     const KURL& url,
-    NotificationImageLoader::ImageCallback image_callback) {
+    const WebSize& resize_dimensions,
+    ThreadedIconLoader::IconCallback icon_callback) {
   if (url.IsNull() || url.IsEmpty() || !url.IsValid()) {
-    DidFinishRequest();
+    std::move(icon_callback).Run(SkBitmap(), -1.0);
     return;
   }
 
-  NotificationImageLoader* image_loader =
-      MakeGarbageCollected<NotificationImageLoader>(type);
-  image_loaders_.push_back(image_loader);
-  image_loader->Start(context, url, std::move(image_callback));
+  ResourceRequest resource_request(url);
+  resource_request.SetRequestContext(mojom::RequestContextType::IMAGE);
+  resource_request.SetPriority(ResourceLoadPriority::kMedium);
+  resource_request.SetTimeoutInterval(kImageFetchTimeout);
+
+  auto* icon_loader = MakeGarbageCollected<ThreadedIconLoader>();
+  icon_loaders_.push_back(icon_loader);
+  icon_loader->Start(context, resource_request, resize_dimensions,
+                     std::move(icon_callback));
 }
 
-void NotificationResourcesLoader::DidLoadImage(const SkBitmap& image) {
-  image_ = image;
-  DidFinishRequest();
-}
-
-void NotificationResourcesLoader::DidLoadIcon(const SkBitmap& image) {
-  icon_ = image;
-  DidFinishRequest();
-}
-
-void NotificationResourcesLoader::DidLoadBadge(const SkBitmap& image) {
-  badge_ = image;
-  DidFinishRequest();
-}
-
-void NotificationResourcesLoader::DidLoadActionIcon(wtf_size_t action_index,
-                                                    const SkBitmap& image) {
-  DCHECK_LT(action_index, action_icons_.size());
-
-  action_icons_[action_index] = image;
+void NotificationResourcesLoader::DidLoadIcon(SkBitmap* out_icon,
+                                              SkBitmap icon,
+                                              double resize_scale) {
+  *out_icon = std::move(icon);
   DidFinishRequest();
 }
 
diff --git a/third_party/blink/renderer/modules/notifications/notification_resources_loader.h b/third_party/blink/renderer/modules/notifications/notification_resources_loader.h
index 5568ef4..c9a70fa 100644
--- a/third_party/blink/renderer/modules/notifications/notification_resources_loader.h
+++ b/third_party/blink/renderer/modules/notifications/notification_resources_loader.h
@@ -6,9 +6,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_NOTIFICATIONS_NOTIFICATION_RESOURCES_LOADER_H_
 
 #include <memory>
+
 #include "third_party/blink/public/mojom/notifications/notification.mojom-blink.h"
+#include "third_party/blink/renderer/core/loader/threaded_icon_loader.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/modules/notifications/notification_image_loader.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
@@ -20,6 +21,7 @@
 namespace blink {
 
 class ExecutionContext;
+struct WebSize;
 
 // Fetches the resources specified in a given NotificationData. Uses a
 // callback to notify the caller when all fetches have finished.
@@ -56,23 +58,21 @@
   virtual void Trace(blink::Visitor* visitor);
 
  private:
-  void LoadImage(ExecutionContext* context,
-                 NotificationImageLoader::Type type,
-                 const KURL& url,
-                 NotificationImageLoader::ImageCallback image_callback);
-  void DidLoadImage(const SkBitmap& image);
-  void DidLoadIcon(const SkBitmap& image);
-  void DidLoadBadge(const SkBitmap& image);
-  void DidLoadActionIcon(wtf_size_t action_index, const SkBitmap& image);
+  void LoadIcon(ExecutionContext* context,
+                const KURL& url,
+                const WebSize& resize_dimensions,
+                ThreadedIconLoader::IconCallback icon_callback);
 
-  // Decrements |m_pendingRequestCount| and runs |m_completionCallback| if
+  void DidLoadIcon(SkBitmap* out_icon, SkBitmap icon, double resize_scale);
+
+  // Decrements |pending_request_count_| and runs |completion_callback_| if
   // there are no more pending requests.
   void DidFinishRequest();
 
   bool started_;
   CompletionCallback completion_callback_;
   int pending_request_count_;
-  HeapVector<Member<NotificationImageLoader>> image_loaders_;
+  HeapVector<Member<ThreadedIconLoader>> icon_loaders_;
   SkBitmap image_;
   SkBitmap icon_;
   SkBitmap badge_;
diff --git a/third_party/blink/renderer/modules/notifications/notification_resources_loader_test.cc b/third_party/blink/renderer/modules/notifications/notification_resources_loader_test.cc
index bafa987..35c6414 100644
--- a/third_party/blink/renderer/modules/notifications/notification_resources_loader_test.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_resources_loader_test.cc
@@ -57,6 +57,16 @@
 
   void DidFetchResources(NotificationResourcesLoader* loader) {
     resources_ = loader->GetResources();
+    std::move(resources_loaded_closure_).Run();
+  }
+
+  void StartAndWaitForResources(
+      const mojom::blink::NotificationData& notification_data) {
+    base::RunLoop run_loop;
+    resources_loaded_closure_ = run_loop.QuitClosure();
+    Loader()->Start(GetExecutionContext(), notification_data);
+    platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+    run_loop.Run();
   }
 
   // Registers a mocked url. When fetched, |fileName| will be loaded from the
@@ -79,6 +89,7 @@
   ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 
  private:
+  base::OnceClosure resources_loaded_closure_;
   Persistent<NotificationResourcesLoader> loader_;
   mojom::blink::NotificationResourcesPtr resources_;
 };
@@ -100,9 +111,7 @@
 
   ASSERT_FALSE(Resources());
 
-  Loader()->Start(GetExecutionContext(), *notification_data);
-  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-
+  StartAndWaitForResources(*notification_data);
   ASSERT_TRUE(Resources());
 
   ASSERT_FALSE(Resources()->image.drawsNothing());
@@ -135,9 +144,7 @@
 
   ASSERT_FALSE(Resources());
 
-  Loader()->Start(GetExecutionContext(), *notification_data);
-  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-
+  StartAndWaitForResources(*notification_data);
   ASSERT_TRUE(Resources());
 
   ASSERT_FALSE(Resources()->icon.drawsNothing());
@@ -162,9 +169,7 @@
 
   ASSERT_FALSE(Resources());
 
-  Loader()->Start(GetExecutionContext(), *notification_data);
-  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-
+  StartAndWaitForResources(*notification_data);
   ASSERT_TRUE(Resources());
 
   ASSERT_FALSE(Resources()->image.drawsNothing());
@@ -178,9 +183,7 @@
 
   ASSERT_FALSE(Resources());
 
-  Loader()->Start(GetExecutionContext(), *notification_data);
-  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-
+  StartAndWaitForResources(*notification_data);
   ASSERT_TRUE(Resources());
 
   ASSERT_FALSE(Resources()->image.drawsNothing());
@@ -194,9 +197,7 @@
 
   ASSERT_FALSE(Resources());
 
-  Loader()->Start(GetExecutionContext(), *notification_data);
-  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-
+  StartAndWaitForResources(*notification_data);
   ASSERT_TRUE(Resources());
 
   ASSERT_TRUE(Resources()->image.drawsNothing());
@@ -217,9 +218,7 @@
 
   ASSERT_FALSE(Resources());
 
-  Loader()->Start(GetExecutionContext(), *notification_data);
-  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-
+  StartAndWaitForResources(*notification_data);
   ASSERT_TRUE(Resources());
 
   // The test received resources but they are all empty. This ensures that a
@@ -238,9 +237,7 @@
 
   ASSERT_FALSE(Resources());
 
-  Loader()->Start(GetExecutionContext(), *notification_data);
-  platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
-
+  StartAndWaitForResources(*notification_data);
   ASSERT_TRUE(Resources());
 
   // The test received resources even though one image failed to load. This
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index 92dee9d..eb983d3 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -11,7 +11,7 @@
 #include "base/stl_util.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
diff --git a/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.cc b/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.cc
index 1806255..f0cb5b55 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.cc
@@ -17,6 +17,11 @@
 #include "v8/include/v8.h"
 
 namespace blink {
+namespace {
+
+using payments::mojom::blink::PaymentEventResponseType;
+
+}  // namespace
 
 PaymentRequestRespondWithObserver* PaymentRequestRespondWithObserver::Create(
     ExecutionContext* context,
@@ -30,11 +35,10 @@
     mojom::ServiceWorkerResponseError error) {
   PaymentHandlerUtils::ReportResponseError(GetExecutionContext(),
                                            "PaymentRequestEvent", error);
-
-  To<ServiceWorkerGlobalScope>(GetExecutionContext())
-      ->RespondToPaymentRequestEvent(
-          event_id_,
-          payments::mojom::blink::PaymentHandlerResponse::New("", ""));
+  Respond("", "",
+          error == mojom::ServiceWorkerResponseError::kPromiseRejected
+              ? PaymentEventResponseType::PAYMENT_EVENT_REJECT
+              : PaymentEventResponseType::PAYMENT_EVENT_INTERNAL_ERROR);
 }
 
 void PaymentRequestRespondWithObserver::OnResponseFulfilled(
@@ -55,14 +59,30 @@
   }
 
   // Check payment response validity.
-  if (!response->hasMethodName() || !response->hasDetails()) {
+  if (!response->hasMethodName() || response->methodName().IsEmpty() ||
+      !response->hasDetails() || response->details().IsNull() ||
+      !response->details().IsObject()) {
     GetExecutionContext()->AddConsoleMessage(
         ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript,
                                mojom::ConsoleMessageLevel::kError,
                                "'PaymentHandlerResponse.methodName' and "
                                "'PaymentHandlerResponse.details' must not "
                                "be empty in payment response."));
-    OnResponseRejected(mojom::ServiceWorkerResponseError::kUnknown);
+  }
+
+  if (!response->hasMethodName() || response->methodName().IsEmpty()) {
+    Respond("", "", PaymentEventResponseType::PAYMENT_METHOD_NAME_EMPTY);
+    return;
+  }
+
+  if (!response->hasDetails()) {
+    Respond("", "", PaymentEventResponseType::PAYMENT_DETAILS_ABSENT);
+    return;
+  }
+
+  if (response->details().IsNull() || !response->details().IsObject() ||
+      response->details().IsEmpty()) {
+    Respond("", "", PaymentEventResponseType::PAYMENT_DETAILS_NOT_OBJECT);
     return;
   }
 
@@ -75,21 +95,19 @@
         mojom::ConsoleMessageLevel::kError,
         "Failed to stringify PaymentHandlerResponse.details in payment "
         "response."));
-    OnResponseRejected(mojom::ServiceWorkerResponseError::kUnknown);
+    Respond("", "", PaymentEventResponseType::PAYMENT_DETAILS_STRINGIFY_ERROR);
     return;
   }
-  To<ServiceWorkerGlobalScope>(GetExecutionContext())
-      ->RespondToPaymentRequestEvent(
-          event_id_, payments::mojom::blink::PaymentHandlerResponse::New(
-                         response->methodName(), ToCoreString(details_value)));
+
+  String details = ToCoreString(details_value);
+  DCHECK(!details.IsEmpty());
+
+  Respond(response->methodName(), details,
+          PaymentEventResponseType::PAYMENT_EVENT_SUCCESS);
 }
 
 void PaymentRequestRespondWithObserver::OnNoResponse() {
-  DCHECK(GetExecutionContext());
-  To<ServiceWorkerGlobalScope>(GetExecutionContext())
-      ->RespondToPaymentRequestEvent(
-          event_id_,
-          payments::mojom::blink::PaymentHandlerResponse::New("", ""));
+  Respond("", "", PaymentEventResponseType::PAYMENT_EVENT_NO_RESPONSE);
 }
 
 PaymentRequestRespondWithObserver::PaymentRequestRespondWithObserver(
@@ -102,4 +120,15 @@
   RespondWithObserver::Trace(visitor);
 }
 
+void PaymentRequestRespondWithObserver::Respond(
+    const String& method_name,
+    const String& stringified_details,
+    PaymentEventResponseType response_type) {
+  DCHECK(GetExecutionContext());
+  To<ServiceWorkerGlobalScope>(GetExecutionContext())
+      ->RespondToPaymentRequestEvent(
+          event_id_, payments::mojom::blink::PaymentHandlerResponse::New(
+                         method_name, stringified_details, response_type));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.h b/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.h
index 558336f0..3f73d92 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.h
+++ b/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_PAYMENT_REQUEST_RESPOND_WITH_OBSERVER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_PAYMENT_REQUEST_RESPOND_WITH_OBSERVER_H_
 
+#include "third_party/blink/public/mojom/payments/payment_app.mojom-blink.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom-blink.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/service_worker/respond_with_observer.h"
@@ -38,6 +39,11 @@
   void OnNoResponse() override;
 
   void Trace(blink::Visitor*) override;
+
+ private:
+  void Respond(const String& method_name,
+               const String& stringified_details,
+               payments::mojom::blink::PaymentEventResponseType response_type);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h
index 8db89c3..7b0e6ca 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h
@@ -5,7 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_ADAPTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_ADAPTER_H_
 
+#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/webrtc/p2p/base/p2p_transport_channel.h"
 
 namespace blink {
@@ -64,14 +66,14 @@
   virtual void StartGathering(
       const cricket::IceParameters& local_parameters,
       const cricket::ServerAddresses& stun_servers,
-      const std::vector<cricket::RelayServerConfig>& turn_servers,
+      const WebVector<cricket::RelayServerConfig>& turn_servers,
       IceTransportPolicy policy) = 0;
 
   // Start ICE connectivity checks with the given initial remote candidates.
   virtual void Start(
       const cricket::IceParameters& remote_parameters,
       cricket::IceRole role,
-      const std::vector<cricket::Candidate>& initial_remote_candidates) = 0;
+      const Vector<cricket::Candidate>& initial_remote_candidates) = 0;
 
   // Handle a remote ICE restart. This changes the remote parameters and clears
   // all remote candidates.
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
index 6f2624b7..c8a537bd 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
@@ -83,13 +83,16 @@
 void IceTransportAdapterImpl::StartGathering(
     const cricket::IceParameters& local_parameters,
     const cricket::ServerAddresses& stun_servers,
-    const std::vector<cricket::RelayServerConfig>& turn_servers,
+    const WebVector<cricket::RelayServerConfig>& turn_servers,
     IceTransportPolicy policy) {
   if (port_allocator_) {
     port_allocator_->set_candidate_filter(GetCandidateFilterForPolicy(policy));
-    port_allocator_->SetConfiguration(stun_servers, turn_servers,
-                                      port_allocator_->candidate_pool_size(),
-                                      port_allocator_->prune_turn_ports());
+    port_allocator_->SetConfiguration(
+        stun_servers,
+        const_cast<WebVector<cricket::RelayServerConfig>&>(turn_servers)
+            .ReleaseVector(),
+        port_allocator_->candidate_pool_size(),
+        port_allocator_->prune_turn_ports());
   }
   if (!ice_transport_channel()) {
     LOG(ERROR) << "StartGathering called, but ICE transport released";
@@ -104,7 +107,7 @@
 void IceTransportAdapterImpl::Start(
     const cricket::IceParameters& remote_parameters,
     cricket::IceRole role,
-    const std::vector<cricket::Candidate>& initial_remote_candidates) {
+    const Vector<cricket::Candidate>& initial_remote_candidates) {
   if (!ice_transport_channel()) {
     LOG(ERROR) << "Start called, but ICE transport released";
     return;
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h
index e039f2f7..4af101d 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h
@@ -6,8 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_ADAPTER_IMPL_H_
 
 #include <memory>
-#include <vector>
 
+#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h"
 #include "third_party/webrtc/api/ice_transport_interface.h"
 
@@ -39,15 +39,14 @@
   ~IceTransportAdapterImpl() override;
 
   // IceTransportAdapter overrides.
-  void StartGathering(
-      const cricket::IceParameters& local_parameters,
-      const cricket::ServerAddresses& stun_servers,
-      const std::vector<cricket::RelayServerConfig>& turn_servers,
-      IceTransportPolicy policy) override;
-  void Start(const cricket::IceParameters& remote_parameters,
-             cricket::IceRole role,
-             const std::vector<cricket::Candidate>& initial_remote_candidates)
-      override;
+  void StartGathering(const cricket::IceParameters& local_parameters,
+                      const cricket::ServerAddresses& stun_servers,
+                      const WebVector<cricket::RelayServerConfig>& turn_servers,
+                      IceTransportPolicy policy) override;
+  void Start(
+      const cricket::IceParameters& remote_parameters,
+      cricket::IceRole role,
+      const Vector<cricket::Candidate>& initial_remote_candidates) override;
   void HandleRemoteRestart(
       const cricket::IceParameters& new_remote_parameters) override;
   void AddRemoteCandidate(const cricket::Candidate& candidate) override;
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc
index a793b39b..3f613278 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc
@@ -52,7 +52,7 @@
 void IceTransportHost::StartGathering(
     const cricket::IceParameters& local_parameters,
     const cricket::ServerAddresses& stun_servers,
-    const std::vector<cricket::RelayServerConfig>& turn_servers,
+    const WebVector<cricket::RelayServerConfig>& turn_servers,
     IceTransportPolicy policy) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   transport_->StartGathering(local_parameters, stun_servers, turn_servers,
@@ -62,7 +62,7 @@
 void IceTransportHost::Start(
     const cricket::IceParameters& remote_parameters,
     cricket::IceRole role,
-    const std::vector<cricket::Candidate>& initial_remote_candidates) {
+    const Vector<cricket::Candidate>& initial_remote_candidates) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   transport_->Start(remote_parameters, role, initial_remote_candidates);
 }
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h
index 75cfec0b..8b39c973 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h
@@ -52,14 +52,13 @@
   scoped_refptr<base::SingleThreadTaskRunner> proxy_thread() const;
   scoped_refptr<base::SingleThreadTaskRunner> host_thread() const;
 
-  void StartGathering(
-      const cricket::IceParameters& local_parameters,
-      const cricket::ServerAddresses& stun_servers,
-      const std::vector<cricket::RelayServerConfig>& turn_servers,
-      IceTransportPolicy policy);
+  void StartGathering(const cricket::IceParameters& local_parameters,
+                      const cricket::ServerAddresses& stun_servers,
+                      const WebVector<cricket::RelayServerConfig>& turn_servers,
+                      IceTransportPolicy policy);
   void Start(const cricket::IceParameters& remote_parameters,
              cricket::IceRole role,
-             const std::vector<cricket::Candidate>& initial_remote_candidates);
+             const Vector<cricket::Candidate>& initial_remote_candidates);
   void HandleRemoteRestart(const cricket::IceParameters& new_remote_parameters);
   void AddRemoteCandidate(const cricket::Candidate& candidate);
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc
index 176a3c26..2195a46c 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc
@@ -66,7 +66,7 @@
 void IceTransportProxy::StartGathering(
     const cricket::IceParameters& local_parameters,
     const cricket::ServerAddresses& stun_servers,
-    const std::vector<cricket::RelayServerConfig>& turn_servers,
+    const WebVector<cricket::RelayServerConfig>& turn_servers,
     IceTransportPolicy policy) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   PostCrossThreadTask(
@@ -79,7 +79,7 @@
 void IceTransportProxy::Start(
     const cricket::IceParameters& remote_parameters,
     cricket::IceRole role,
-    const std::vector<cricket::Candidate>& initial_remote_candidates) {
+    const Vector<cricket::Candidate>& initial_remote_candidates) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   PostCrossThreadTask(
       *host_thread_, FROM_HERE,
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h
index 1fdcabc3..f8f0814 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h
@@ -75,14 +75,13 @@
   scoped_refptr<base::SingleThreadTaskRunner> host_thread() const;
 
   // These methods are proxied to an IceTransportAdapter instance.
-  void StartGathering(
-      const cricket::IceParameters& local_parameters,
-      const cricket::ServerAddresses& stun_servers,
-      const std::vector<cricket::RelayServerConfig>& turn_servers,
-      IceTransportPolicy policy);
+  void StartGathering(const cricket::IceParameters& local_parameters,
+                      const cricket::ServerAddresses& stun_servers,
+                      const WebVector<cricket::RelayServerConfig>& turn_servers,
+                      IceTransportPolicy policy);
   void Start(const cricket::IceParameters& remote_parameters,
              cricket::IceRole role,
-             const std::vector<cricket::Candidate>& initial_remote_candidates);
+             const Vector<cricket::Candidate>& initial_remote_candidates);
   void HandleRemoteRestart(const cricket::IceParameters& new_remote_parameters);
   void AddRemoteCandidate(const cricket::Candidate& candidate);
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
index 0709185..2380ed7 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
@@ -53,8 +53,8 @@
  public:
   // A config used when starting the QUIC handshake.
   struct StartConfig final {
-    explicit StartConfig(std::vector<std::unique_ptr<rtc::SSLFingerprint>>
-                             remote_fingerprints_in)
+    explicit StartConfig(
+        Vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints_in)
         : remote_fingerprints(std::move(remote_fingerprints_in)) {
       DCHECK_GT(remote_fingerprints.size(), 0u);
     }
@@ -67,7 +67,7 @@
     // These fingerprints are used to verify the self signed remote certificate
     // used in the QUIC handshake. See:
     // https://w3c.github.io/webrtc-quic/#quic-transport*
-    std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints;
+    Vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints;
 
     // The pre shared key to be used in the handshake.
     const std::string pre_shared_key;
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
index ee49af3b..7c65328b 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
@@ -20,8 +20,7 @@
   // This object is only moveable.
   explicit P2PQuicTransportConfig(
       quic::Perspective perspective,
-      const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>
-          certificates_in,
+      const Vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates_in,
       uint32_t stream_delegate_read_buffer_size_in,
       uint32_t stream_write_buffer_size_in)
       : perspective(perspective),
@@ -38,7 +37,7 @@
   // blink::RTCCertificates: https://www.w3.org/TR/webrtc/#dom-rtccertificate
   // This can be empty if pre shared keys are being used to establish a
   // connection.
-  const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates;
+  const Vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates;
   // The amount that the delegate can store in its read buffer. This is a
   // mandatory field that must be set to ensure that the
   // P2PQuicStream::Delegate will not give the delegate more data than it can
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
index fa1b7ee5..3b965a8 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
@@ -284,7 +284,7 @@
   DCHECK(packet_transport_);
   DCHECK_GT(stream_delegate_read_buffer_size_, 0u);
   DCHECK_GT(stream_write_buffer_size_, 0u);
-  if (!p2p_transport_config.certificates.empty()) {
+  if (!p2p_transport_config.certificates.IsEmpty()) {
     // TODO(https://crbug.com/874296): The web API accepts multiple
     // certificates, and we might want to pass these down to let QUIC decide on
     // what to use.
@@ -331,7 +331,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Either the remote fingerprints are being verified or a pre shared key is
   // set.
-  DCHECK((certificate_ && !config.remote_fingerprints.empty()) ||
+  DCHECK((certificate_ && !config.remote_fingerprints.IsEmpty()) ||
          !config.pre_shared_key.empty());
   DCHECK(!crypto_stream_);
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
index 55d18f3..9932f5d 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
@@ -195,7 +195,7 @@
   // Crypto certificate information. Note that currently the handshake is
   // insecure and these are not used...
   rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
-  std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints_;
+  Vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints_;
 
   bool pre_shared_key_set_ = false;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
index 91714348..fcaa43a3 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
@@ -18,6 +18,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_crypto_config_factory_impl.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_crypto_stream_factory_impl.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
@@ -402,10 +403,10 @@
   quic::QuicReferenceCountedPointer<Chain> GetCertChain(
       const quic::QuicSocketAddress& server_address,
       const std::string& hostname) override {
-    std::vector<std::string> certs;
-    certs.push_back("Test cert");
+    WebVector<std::string> certs;
+    certs.emplace_back("Test cert");
     return quic::QuicReferenceCountedPointer<Chain>(
-        new ProofSource::Chain(certs));
+        new ProofSource::Chain(certs.ReleaseVector()));
   }
   void ComputeTlsSignature(
       const quic::QuicSocketAddress& server_address,
@@ -802,13 +803,13 @@
       .WillOnce(FireCallback(run_loop.CreateCallback()));
 
   // Start the handshake with the remote fingerprints.
-  std::vector<std::unique_ptr<rtc::SSLFingerprint>> server_fingerprints;
+  Vector<std::unique_ptr<rtc::SSLFingerprint>> server_fingerprints;
   server_fingerprints.push_back(rtc::SSLFingerprint::CreateUnique(
       "sha-256", *server_peer()->certificate()->identity()));
   server_peer()->quic_transport()->Start(
       P2PQuicTransport::StartConfig(std::move(server_fingerprints)));
 
-  std::vector<std::unique_ptr<rtc::SSLFingerprint>> client_fingerprints;
+  Vector<std::unique_ptr<rtc::SSLFingerprint>> client_fingerprints;
   client_fingerprints.push_back(rtc::SSLFingerprint::CreateUnique(
       "sha-256", *client_peer()->certificate()->identity()));
   client_peer()->quic_transport()->Start(
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h
index 4f48e714..a508b7d1 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_TRANSPORT_PROXY_H_
 
 #include <unordered_map>
-#include <vector>
 
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
@@ -16,6 +15,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_stats.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_ice_transport_adapter.h b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_ice_transport_adapter.h
index 424465a..3c6c2a0 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_ice_transport_adapter.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_ice_transport_adapter.h
@@ -28,12 +28,12 @@
   MOCK_METHOD4(StartGathering,
                void(const cricket::IceParameters&,
                     const cricket::ServerAddresses&,
-                    const std::vector<cricket::RelayServerConfig>&,
+                    const WebVector<cricket::RelayServerConfig>&,
                     IceTransportPolicy));
   MOCK_METHOD3(Start,
                void(const cricket::IceParameters&,
                     cricket::IceRole,
-                    const std::vector<cricket::Candidate>&));
+                    const Vector<cricket::Candidate>&));
   MOCK_METHOD1(HandleRemoteRestart, void(const cricket::IceParameters&));
   MOCK_METHOD1(AddRemoteCandidate, void(const cricket::Candidate&));
   MOCK_CONST_METHOD0(packet_transport, P2PQuicPacketTransport*());
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h b/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h
index 5d984c14a..ad0d976 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h
@@ -8,12 +8,12 @@
 // This file defines specializations for the CrossThreadCopier that allow WebRTC
 // types to be passed across threads using their copy constructors.
 
-#include <memory>
 #include <set>
-#include <vector>
 
+#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
 
 namespace cricket {
@@ -23,7 +23,6 @@
 }  // namespace cricket
 
 namespace rtc {
-class RTCCertificate;
 class SocketAddress;
 }
 
@@ -46,13 +45,6 @@
   STATIC_ONLY(CrossThreadCopier);
 };
 
-template <typename T, typename Allocator>
-struct CrossThreadCopier<std::vector<std::unique_ptr<T>, Allocator>> {
-  STATIC_ONLY(CrossThreadCopier);
-  using Type = std::vector<std::unique_ptr<T>, Allocator>;
-  static Type Copy(Type vector) { return std::move(vector); }
-};
-
 template <>
 struct CrossThreadCopier<cricket::IceParameters>
     : public CrossThreadCopierPassThrough<cricket::IceParameters> {
@@ -66,15 +58,15 @@
 };
 
 template <>
-struct CrossThreadCopier<std::vector<cricket::RelayServerConfig>>
+struct CrossThreadCopier<blink::WebVector<cricket::RelayServerConfig>>
     : public CrossThreadCopierPassThrough<
-          std::vector<cricket::RelayServerConfig>> {
+          blink::WebVector<cricket::RelayServerConfig>> {
   STATIC_ONLY(CrossThreadCopier);
 };
 
 template <>
-struct CrossThreadCopier<std::vector<cricket::Candidate>>
-    : public CrossThreadCopierPassThrough<std::vector<cricket::Candidate>> {
+struct CrossThreadCopier<Vector<cricket::Candidate>>
+    : public CrossThreadCopierPassThrough<Vector<cricket::Candidate>> {
   STATIC_ONLY(CrossThreadCopier);
 };
 
@@ -85,13 +77,6 @@
 };
 
 template <>
-struct CrossThreadCopier<std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>>
-    : public CrossThreadCopierPassThrough<
-          std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>> {
-  STATIC_ONLY(CrossThreadCopier);
-};
-
-template <>
 struct CrossThreadCopier<std::pair<cricket::Candidate, cricket::Candidate>>
     : public CrossThreadCopierPassThrough<
           std::pair<cricket::Candidate, cricket::Candidate>> {
diff --git a/third_party/blink/renderer/modules/peerconnection/call_setup_state_tracker_unittest.cc b/third_party/blink/renderer/modules/peerconnection/call_setup_state_tracker_unittest.cc
index fc0eca4..e629992 100644
--- a/third_party/blink/renderer/modules/peerconnection/call_setup_state_tracker_unittest.cc
+++ b/third_party/blink/renderer/modules/peerconnection/call_setup_state_tracker_unittest.cc
@@ -5,42 +5,43 @@
 #include "third_party/blink/renderer/modules/peerconnection/call_setup_state_tracker.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
 namespace {
 
 template <typename StateType>
-std::vector<StateType> GetAllCallSetupStates();
+Vector<StateType> GetAllCallSetupStates();
 
 template <>
-std::vector<OffererState> GetAllCallSetupStates() {
-  std::vector<OffererState> states = {OffererState::kNotStarted,
-                                      OffererState::kCreateOfferPending,
-                                      OffererState::kCreateOfferRejected,
-                                      OffererState::kCreateOfferResolved,
-                                      OffererState::kSetLocalOfferPending,
-                                      OffererState::kSetLocalOfferRejected,
-                                      OffererState::kSetLocalOfferResolved,
-                                      OffererState::kSetRemoteAnswerPending,
-                                      OffererState::kSetRemoteAnswerRejected,
-                                      OffererState::kSetRemoteAnswerResolved};
+Vector<OffererState> GetAllCallSetupStates() {
+  Vector<OffererState> states = {OffererState::kNotStarted,
+                                 OffererState::kCreateOfferPending,
+                                 OffererState::kCreateOfferRejected,
+                                 OffererState::kCreateOfferResolved,
+                                 OffererState::kSetLocalOfferPending,
+                                 OffererState::kSetLocalOfferRejected,
+                                 OffererState::kSetLocalOfferResolved,
+                                 OffererState::kSetRemoteAnswerPending,
+                                 OffererState::kSetRemoteAnswerRejected,
+                                 OffererState::kSetRemoteAnswerResolved};
   EXPECT_EQ(static_cast<size_t>(OffererState::kMaxValue) + 1u, states.size());
   return states;
 }
 
 template <>
-std::vector<AnswererState> GetAllCallSetupStates() {
-  std::vector<AnswererState> states = {AnswererState::kNotStarted,
-                                       AnswererState::kSetRemoteOfferPending,
-                                       AnswererState::kSetRemoteOfferRejected,
-                                       AnswererState::kSetRemoteOfferResolved,
-                                       AnswererState::kCreateAnswerPending,
-                                       AnswererState::kCreateAnswerRejected,
-                                       AnswererState::kCreateAnswerResolved,
-                                       AnswererState::kSetLocalAnswerPending,
-                                       AnswererState::kSetLocalAnswerRejected,
-                                       AnswererState::kSetLocalAnswerResolved};
+Vector<AnswererState> GetAllCallSetupStates() {
+  Vector<AnswererState> states = {AnswererState::kNotStarted,
+                                  AnswererState::kSetRemoteOfferPending,
+                                  AnswererState::kSetRemoteOfferRejected,
+                                  AnswererState::kSetRemoteOfferResolved,
+                                  AnswererState::kCreateAnswerPending,
+                                  AnswererState::kCreateAnswerRejected,
+                                  AnswererState::kCreateAnswerResolved,
+                                  AnswererState::kSetLocalAnswerPending,
+                                  AnswererState::kSetLocalAnswerRejected,
+                                  AnswererState::kSetLocalAnswerResolved};
   EXPECT_EQ(static_cast<size_t>(AnswererState::kMaxValue) + 1u, states.size());
   return states;
 }
@@ -69,7 +70,7 @@
 
   template <typename StateType>
   bool VerifyReachability(Reachability reachability,
-                          std::vector<StateType> states) const {
+                          Vector<StateType> states) const {
     bool expected_state_reached = (reachability == Reachability::kReachable);
     for (const auto& state : states) {
       bool did_reach_state;
@@ -88,12 +89,11 @@
   }
 
   template <typename StateType>
-  bool VerifyOnlyReachableStates(std::vector<StateType> reachable_states,
+  bool VerifyOnlyReachableStates(Vector<StateType> reachable_states,
                                  bool include_current = true) const {
     if (include_current)
       reachable_states.push_back(current_state<StateType>());
-    std::vector<StateType> unreachable_states =
-        GetAllCallSetupStates<StateType>();
+    Vector<StateType> unreachable_states = GetAllCallSetupStates<StateType>();
     for (const auto& reachable_state : reachable_states) {
       unreachable_states.erase(std::find(unreachable_states.begin(),
                                          unreachable_states.end(),
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
index 3e7ef3d7..d2a2c69 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
@@ -4,8 +4,6 @@
 
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.h"
 
-#include <vector>
-
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -318,11 +316,11 @@
   return converted_ice_parameters;
 }
 
-static std::vector<webrtc::PeerConnectionInterface::IceServer>
-ConvertIceServers(const HeapVector<Member<RTCIceServer>>& ice_servers) {
-  std::vector<webrtc::PeerConnectionInterface::IceServer> converted_ice_servers;
+static WebVector<webrtc::PeerConnectionInterface::IceServer> ConvertIceServers(
+    const HeapVector<Member<RTCIceServer>>& ice_servers) {
+  Vector<webrtc::PeerConnectionInterface::IceServer> converted_ice_servers;
   for (const RTCIceServer* ice_server : ice_servers) {
-    converted_ice_servers.push_back(ConvertIceServer(ice_server));
+    converted_ice_servers.emplace_back(ConvertIceServer(ice_server));
   }
   return converted_ice_servers;
 }
@@ -350,14 +348,14 @@
                                       "Can only call gather() once.");
     return;
   }
-  std::vector<webrtc::PeerConnectionInterface::IceServer> ice_servers;
+  WebVector<webrtc::PeerConnectionInterface::IceServer> ice_servers;
   if (options->hasIceServers()) {
     ice_servers = ConvertIceServers(options->iceServers());
   }
   cricket::ServerAddresses stun_servers;
   std::vector<cricket::RelayServerConfig> turn_servers;
-  webrtc::RTCErrorType error_type =
-      webrtc::ParseIceServers(ice_servers, &stun_servers, &turn_servers);
+  webrtc::RTCErrorType error_type = webrtc::ParseIceServers(
+      ice_servers.ReleaseVector(), &stun_servers, &turn_servers);
   if (error_type != webrtc::RTCErrorType::NONE) {
     ThrowExceptionFromRTCError(
         webrtc::RTCError(error_type, "Invalid ICE server URL(s)."),
@@ -417,7 +415,7 @@
     if (remote_candidates_.size() > 0) {
       state_ = webrtc::IceTransportState::kChecking;
     }
-    std::vector<cricket::Candidate> initial_remote_candidates;
+    Vector<cricket::Candidate> initial_remote_candidates;
     for (RTCIceCandidate* remote_candidate : remote_candidates_) {
       // This conversion is safe since we throw an exception in
       // addRemoteCandidate on malformed ICE candidates.
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h
index be17c4d..c2bae63 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h
@@ -54,7 +54,7 @@
  protected:
   scoped_refptr<base::TestSimpleTaskRunner> main_thread_;
   scoped_refptr<base::TestSimpleTaskRunner> worker_thread_;
-  std::vector<Persistent<MockEventListener>> mock_event_listeners_;
+  Vector<Persistent<MockEventListener>> mock_event_listeners_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 970a980b..e8a6a17 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -345,7 +345,7 @@
   }
 
   if (configuration->hasIceServers()) {
-    std::vector<webrtc::PeerConnectionInterface::IceServer> ice_servers;
+    WebVector<webrtc::PeerConnectionInterface::IceServer> ice_servers;
     for (const RTCIceServer* ice_server : configuration->iceServers()) {
       Vector<String> url_strings;
       if (ice_server->hasURLs()) {
@@ -393,25 +393,28 @@
               "required when the URL scheme is "
               "\"turn\" or \"turns\".");
         }
-        ice_servers.emplace_back();
-        auto& converted_ice_server = ice_servers.back();
+
+        auto converted_ice_server =
+            webrtc::PeerConnectionInterface::IceServer();
         converted_ice_server.urls.push_back(String(url).Utf8());
         converted_ice_server.username = username.Utf8();
         converted_ice_server.password = credential.Utf8();
+
+        ice_servers.emplace_back(std::move(converted_ice_server));
       }
     }
-    web_configuration.servers = ice_servers;
+    web_configuration.servers = ice_servers.ReleaseVector();
   }
 
   if (configuration->hasCertificates()) {
     const HeapVector<Member<RTCCertificate>>& certificates =
         configuration->certificates();
-    std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates_copy(
+    WebVector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates_copy(
         certificates.size());
     for (wtf_size_t i = 0; i < certificates.size(); ++i) {
       certificates_copy[i] = certificates[i]->Certificate();
     }
-    web_configuration.certificates = std::move(certificates_copy);
+    web_configuration.certificates = certificates_copy.ReleaseVector();
   }
 
   web_configuration.ice_candidate_pool_size =
@@ -864,7 +867,7 @@
           RTCCreateSessionDescriptionOperation::kCreateOffer, this,
           success_callback, error_callback);
 
-  std::vector<std::unique_ptr<WebRTCRtpTransceiver>> web_transceivers;
+  WebVector<std::unique_ptr<WebRTCRtpTransceiver>> web_transceivers;
   if (offer_options) {
     if (offer_options->OfferToReceiveAudio() != -1 ||
         offer_options->OfferToReceiveVideo() != -1) {
@@ -2802,7 +2805,7 @@
 }
 
 void RTCPeerConnection::DidModifyTransceivers(
-    std::vector<std::unique_ptr<WebRTCRtpTransceiver>> web_transceivers,
+    WebVector<std::unique_ptr<WebRTCRtpTransceiver>> web_transceivers,
     bool is_remote_description) {
   HeapVector<Member<MediaStreamTrack>> mute_tracks;
   HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index b94bc361..82d36814 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -33,7 +33,6 @@
 
 #include <memory>
 #include <utility>
-#include <vector>
 
 #include "third_party/blink/public/platform/web_media_constraints.h"
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
@@ -55,6 +54,7 @@
 #include "third_party/blink/renderer/platform/peerconnection/rtc_void_request.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -293,7 +293,7 @@
   void DidAddReceiverPlanB(std::unique_ptr<WebRTCRtpReceiver>) override;
   void DidRemoveReceiverPlanB(std::unique_ptr<WebRTCRtpReceiver>) override;
   void DidModifySctpTransport(WebRTCSctpTransportSnapshot) override;
-  void DidModifyTransceivers(std::vector<std::unique_ptr<WebRTCRtpTransceiver>>,
+  void DidModifyTransceivers(WebVector<std::unique_ptr<WebRTCRtpTransceiver>>,
                              bool is_remote_description) override;
   void DidAddRemoteDataChannel(
       scoped_refptr<webrtc::DataChannelInterface> channel) override;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
index 8b74078..4d92bfe 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
@@ -689,7 +689,7 @@
 
 class FakeWebRTCPeerConnectionHandler : public MockWebRTCPeerConnectionHandler {
  public:
-  std::vector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
+  WebVector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
       const WebRTCSessionDescriptionRequest& request,
       const WebMediaConstraints&) override {
     PostToCompleteRequest<WebRTCSessionDescriptionRequest>(
@@ -697,7 +697,7 @@
     return {};
   }
 
-  std::vector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
+  WebVector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
       const WebRTCSessionDescriptionRequest& request,
       const WebRTCOfferOptions&) override {
     PostToCompleteRequest<WebRTCSessionDescriptionRequest>(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
index 4113555..149fcd9c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
@@ -282,7 +282,7 @@
   remote_parameters_ = remote_parameters;
   start_reason_ = StartReason::kP2PWithRemoteFingerprints;
   if (transport_->IsStarted()) {
-    std::vector<std::unique_ptr<rtc::SSLFingerprint>> rtc_fingerprints;
+    Vector<std::unique_ptr<rtc::SSLFingerprint>> rtc_fingerprints;
     for (const RTCDtlsFingerprint* fingerprint :
          remote_parameters_->fingerprints()) {
       rtc_fingerprints.push_back(
@@ -302,7 +302,7 @@
   state_ = RTCQuicTransportState::kConnecting;
   // We don't create the underlying transports until we are starting
   // to connect.
-  std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> rtc_certificates;
+  Vector<rtc::scoped_refptr<rtc::RTCCertificate>> rtc_certificates;
   for (const auto& certificate : certificates_) {
     rtc_certificates.push_back(certificate->Certificate());
   }
@@ -322,7 +322,7 @@
   // since start() determines its quic::Perspective based upon ICE.
   if (start_reason_ == StartReason::kP2PWithRemoteFingerprints) {
     DCHECK(remote_parameters_);
-    std::vector<std::unique_ptr<rtc::SSLFingerprint>> rtc_fingerprints;
+    Vector<std::unique_ptr<rtc::SSLFingerprint>> rtc_fingerprints;
     for (const RTCDtlsFingerprint* fingerprint :
          remote_parameters_->fingerprints()) {
       rtc_fingerprints.push_back(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index f4a6925..3de725f6 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -8,7 +8,6 @@
 #include <string>
 #include <tuple>
 #include <utility>
-#include <vector>
 
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_rtc_dtmf_sender_handler.h"
@@ -240,12 +239,11 @@
   return result;
 }
 
-std::tuple<std::vector<webrtc::RtpEncodingParameters>,
-           webrtc::DegradationPreference>
+std::tuple<Vector<webrtc::RtpEncodingParameters>, webrtc::DegradationPreference>
 ToRtpParameters(const RTCRtpSendParameters* parameters) {
-  std::vector<webrtc::RtpEncodingParameters> encodings;
+  Vector<webrtc::RtpEncodingParameters> encodings;
   if (parameters->hasEncodings()) {
-    encodings.reserve(parameters->encodings().size());
+    encodings.ReserveCapacity(parameters->encodings().size());
 
     for (const auto& encoding : parameters->encodings()) {
       encodings.push_back(ToRtpEncodingParameters(encoding));
@@ -448,7 +446,7 @@
   // field and the degradationPreference field. We just forward those to the
   // native layer without having to transform all the other read-only
   // parameters.
-  std::vector<webrtc::RtpEncodingParameters> encodings;
+  Vector<webrtc::RtpEncodingParameters> encodings;
   webrtc::DegradationPreference degradation_preference;
   std::tie(encodings, degradation_preference) = ToRtpParameters(parameters);
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
index 619d443..993fa3b9 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
@@ -195,8 +195,8 @@
 void RTCRtpTransceiver::setCodecPreferences(
     const HeapVector<Member<RTCRtpCodecCapability>>& codecs,
     ExceptionState& exception_state) {
-  std::vector<webrtc::RtpCodecCapability> codec_preferences;
-  codec_preferences.reserve(codecs.size());
+  Vector<webrtc::RtpCodecCapability> codec_preferences;
+  codec_preferences.ReserveCapacity(codecs.size());
   for (const auto& codec : codecs) {
     codec_preferences.emplace_back();
     auto& webrtc_codec = codec_preferences.back();
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
index cfd2895..cd0dd0a 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
@@ -119,18 +119,18 @@
 
 }  // namespace
 
-std::vector<webrtc::NonStandardGroupId> GetExposedGroupIds(
+WebVector<webrtc::NonStandardGroupId> GetExposedGroupIds(
     const ScriptState* script_state) {
   const ExecutionContext* context = ExecutionContext::From(script_state);
   DCHECK(context->IsContextThread());
-  std::vector<webrtc::NonStandardGroupId> enabled_origin_trials;
+  WebVector<webrtc::NonStandardGroupId> enabled_origin_trials;
   if (RuntimeEnabledFeatures::RtcAudioJitterBufferMaxPacketsEnabled(context)) {
-    enabled_origin_trials.push_back(
+    enabled_origin_trials.emplace_back(
         webrtc::NonStandardGroupId::kRtcAudioJitterBufferMaxPackets);
   }
   if (RuntimeEnabledFeatures::RTCStatsRelativePacketArrivalDelayEnabled(
           context)) {
-    enabled_origin_trials.push_back(
+    enabled_origin_trials.emplace_back(
         webrtc::NonStandardGroupId::kRtcStatsRelativePacketArrivalDelay);
   }
   return enabled_origin_trials;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h
index 59c219c6..f6f0e15 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h
@@ -5,10 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_STATS_REPORT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_STATS_REPORT_H_
 
-#include <map>
-#include <vector>
-
 #include "third_party/blink/public/platform/web_rtc_stats.h"
+#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -19,7 +17,7 @@
 
 // Returns the group ids for non-standardized members which should be exposed
 // based on what Origin Trials are running.
-std::vector<webrtc::NonStandardGroupId> GetExposedGroupIds(
+WebVector<webrtc::NonStandardGroupId> GetExposedGroupIds(
     const ScriptState* script_state);
 
 // https://w3c.github.io/webrtc-pc/#rtcstatsreport-object
diff --git a/third_party/blink/renderer/modules/push_messaging/OWNERS b/third_party/blink/renderer/modules/push_messaging/OWNERS
index 98b8efe..2c516368 100644
--- a/third_party/blink/renderer/modules/push_messaging/OWNERS
+++ b/third_party/blink/renderer/modules/push_messaging/OWNERS
@@ -1,3 +1,6 @@
+knollr@chromium.org
+rayankans@chromium.org
+
 file://content/browser/push_messaging/OWNERS
 
 per-file *_type_converter*.*=set noparent
diff --git a/third_party/blink/renderer/modules/push_messaging/push_manager.cc b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
index bae0c24..c4c16b2a 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_manager.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_error.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_bridge.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_client.h"
@@ -66,6 +67,16 @@
   if (exception_state.HadException())
     return ScriptPromise();
 
+  if (!options->IsApplicationServerKeyVapid()) {
+    ExecutionContext::From(script_state)
+        ->AddConsoleMessage(ConsoleMessage::Create(
+            mojom::ConsoleMessageSource::kJavaScript,
+            mojom::ConsoleMessageLevel::kWarning,
+            "The provided application server key is not a VAPID key. Only "
+            "VAPID keys will be supported in the future. For more information "
+            "check https://crbug.com/979235."));
+  }
+
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc b/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
index 2a670c72..1db027ac 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
@@ -98,6 +98,13 @@
           application_server_key.data(),
           SafeCast<unsigned>(application_server_key.size()))) {}
 
+bool PushSubscriptionOptions::IsApplicationServerKeyVapid() const {
+  if (!application_server_key_)
+    return false;
+  return application_server_key_->ByteLength() == 65 &&
+         static_cast<uint8_t*>(application_server_key_->Data())[0] == 0x04;
+}
+
 void PushSubscriptionOptions::Trace(blink::Visitor* visitor) {
   visitor->Trace(application_server_key_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription_options.h b/third_party/blink/renderer/modules/push_messaging/push_subscription_options.h
index e0dc22a..3907c36 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription_options.h
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription_options.h
@@ -44,6 +44,9 @@
     return application_server_key_;
   }
 
+  // Whether the application server key follows the VAPID protocol.
+  bool IsApplicationServerKeyVapid() const;
+
   void Trace(blink::Visitor* visitor) override;
 
  private:
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.idl b/third_party/blink/renderer/modules/remoteplayback/remote_playback.idl
index 752ae0b..0f400e46 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.idl
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.idl
@@ -13,6 +13,7 @@
 callback RemotePlaybackAvailabilityCallback = void(boolean available);
 
 [
+    Exposed=Window,
     ActiveScriptWrappable,
     RuntimeEnabled=RemotePlayback
 ] interface RemotePlayback : EventTarget {
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl
index 3343e61..16cd1dbb 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl
@@ -22,6 +22,7 @@
     "landscape-secondary"
 };
 
+[Exposed=Window]
 interface ScreenOrientation : EventTarget {
     [MeasureAs=ScreenOrientationAngle] readonly attribute unsigned short angle;
     [MeasureAs=ScreenOrientationType] readonly attribute DOMString type;
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
index 080438f..d91687c 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
diff --git a/third_party/blink/renderer/modules/service_worker/respond_with_observer.h b/third_party/blink/renderer/modules/service_worker/respond_with_observer.h
index eacf6c7..4b9f8aff 100644
--- a/third_party/blink/renderer/modules/service_worker/respond_with_observer.h
+++ b/third_party/blink/renderer/modules/service_worker/respond_with_observer.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_RESPOND_WITH_OBSERVER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_RESPOND_WITH_OBSERVER_H_
 
-#include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_client.cc b/third_party/blink/renderer/modules/service_worker/service_worker_client.cc
index fa3d270..097af17 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_client.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_client.cc
@@ -40,6 +40,8 @@
   switch (type_) {
     case mojom::ServiceWorkerClientType::kWindow:
       return "window";
+    case mojom::ServiceWorkerClientType::kDedicatedWorker:
+      return "worker";
     case mojom::ServiceWorkerClientType::kSharedWorker:
       return "sharedworker";
     case mojom::ServiceWorkerClientType::kAll:
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc b/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
index f2c59e6..9c4ee806 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
@@ -29,6 +29,8 @@
 mojom::ServiceWorkerClientType GetClientType(const String& type) {
   if (type == "window")
     return mojom::ServiceWorkerClientType::kWindow;
+  if (type == "worker")
+    return mojom::ServiceWorkerClientType::kDedicatedWorker;
   if (type == "sharedworker")
     return mojom::ServiceWorkerClientType::kSharedWorker;
   if (type == "all")
@@ -54,6 +56,7 @@
     case mojom::ServiceWorkerClientType::kWindow:
       client = ServiceWorkerWindowClient::Create(*info);
       break;
+    case mojom::ServiceWorkerClientType::kDedicatedWorker:
     case mojom::ServiceWorkerClientType::kSharedWorker:
       client = ServiceWorkerClient::Create(*info);
       break;
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_barcode.h b/third_party/blink/renderer/modules/shapedetection/detected_barcode.h
index 6c213af5..d4eaeef 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_barcode.h
+++ b/third_party/blink/renderer/modules/shapedetection/detected_barcode.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SHAPEDETECTION_DETECTED_BARCODE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SHAPEDETECTION_DETECTED_BARCODE_H_
 
-#include "services/shape_detection/public/mojom/barcodedetection_provider.mojom-shared.h"
+#include "services/shape_detection/public/mojom/barcodedetection_provider.mojom-blink.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/modules/imagecapture/point_2d.h"
diff --git a/third_party/blink/renderer/modules/vr/vr_display.cc b/third_party/blink/renderer/modules/vr/vr_display.cc
index 43ecde1..1fcd9199 100644
--- a/third_party/blink/renderer/modules/vr/vr_display.cc
+++ b/third_party/blink/renderer/modules/vr/vr_display.cc
@@ -57,8 +57,8 @@
 device::mojom::blink::XRFrameDataPtr CreateIdentityFrameData() {
   auto data = device::mojom::blink::XRFrameData::New();
   data->pose = device::mojom::blink::VRPose::New();
-  data->pose->orientation.emplace({0.0f, 0.0f, 0.0f, 1.0f});
-  data->pose->position.emplace({0.0f, 0.0f, 0.0f});
+  data->pose->orientation = gfx::Quaternion();
+  data->pose->position = WebFloatPoint3D();
 
   return data;
 }
diff --git a/third_party/blink/renderer/modules/vr/vr_eye_parameters.cc b/third_party/blink/renderer/modules/vr/vr_eye_parameters.cc
index 82c5b00..39010d8 100644
--- a/third_party/blink/renderer/modules/vr/vr_eye_parameters.cc
+++ b/third_party/blink/renderer/modules/vr/vr_eye_parameters.cc
@@ -10,9 +10,9 @@
     const device::mojom::blink::VREyeParametersPtr& eye_parameters,
     double render_scale) {
   offset_ = DOMFloat32Array::Create(3);
-  offset_->Data()[0] = eye_parameters->offset[0];
-  offset_->Data()[1] = eye_parameters->offset[1];
-  offset_->Data()[2] = eye_parameters->offset[2];
+  offset_->Data()[0] = eye_parameters->offset.x();
+  offset_->Data()[1] = eye_parameters->offset.y();
+  offset_->Data()[2] = eye_parameters->offset.z();
 
   field_of_view_ = MakeGarbageCollected<VRFieldOfView>();
   field_of_view_->SetUpDegrees(eye_parameters->fieldOfView->upDegrees);
diff --git a/third_party/blink/renderer/modules/vr/vr_frame_data.cc b/third_party/blink/renderer/modules/vr/vr_frame_data.cc
index 211bf68..d3e0774 100644
--- a/third_party/blink/renderer/modules/vr/vr_frame_data.cc
+++ b/third_party/blink/renderer/modules/vr/vr_frame_data.cc
@@ -49,13 +49,13 @@
 // Create a matrix from a rotation and translation.
 void MatrixfromRotationTranslation(
     blink::DOMFloat32Array* out_array,
-    const base::Optional<WTF::Vector<float>>& rotation,
-    const base::Optional<WTF::Vector<float>>& translation) {
+    const base::Optional<gfx::Quaternion>& rotation,
+    const base::Optional<blink::WebFloatPoint3D>& translation) {
   // Quaternion math
-  float x = !rotation ? 0.0f : rotation.value()[0];
-  float y = !rotation ? 0.0f : rotation.value()[1];
-  float z = !rotation ? 0.0f : rotation.value()[2];
-  float w = !rotation ? 1.0f : rotation.value()[3];
+  float x = rotation ? rotation->x() : 0.0f;
+  float y = rotation ? rotation->y() : 0.0f;
+  float z = rotation ? rotation->z() : 0.0f;
+  float w = rotation ? rotation->w() : 1.0f;
   float x2 = x + x;
   float y2 = y + y;
   float z2 = z + z;
@@ -83,9 +83,9 @@
   out[9] = yz - wx;
   out[10] = 1 - (xx + yy);
   out[11] = 0;
-  out[12] = !translation ? 0.0f : translation.value()[0];
-  out[13] = !translation ? 0.0f : translation.value()[1];
-  out[14] = !translation ? 0.0f : translation.value()[2];
+  out[12] = translation ? translation->x : 0.0f;
+  out[13] = translation ? translation->y : 0.0f;
+  out[14] = translation ? translation->z : 0.0f;
   out[15] = 1;
 }
 
diff --git a/third_party/blink/renderer/modules/vr/vr_pose.cc b/third_party/blink/renderer/modules/vr/vr_pose.cc
index 78634dd..e2474dc 100644
--- a/third_party/blink/renderer/modules/vr/vr_pose.cc
+++ b/third_party/blink/renderer/modules/vr/vr_pose.cc
@@ -8,12 +8,47 @@
 
 namespace {
 
-DOMFloat32Array* MojoArrayToFloat32Array(
-    const base::Optional<WTF::Vector<float>>& vec) {
+DOMFloat32Array* QuaternionToFloat32Array(
+    const base::Optional<gfx::Quaternion>& quat) {
+  if (!quat)
+    return nullptr;
+
+  auto* dom_array = DOMFloat32Array::Create(4);
+  float* data = dom_array->Data();
+  data[0] = quat->x();
+  data[1] = quat->y();
+  data[2] = quat->z();
+  data[3] = quat->w();
+
+  return dom_array;
+}
+
+DOMFloat32Array* Vector3dFToFloat32Array(
+    const base::Optional<gfx::Vector3dF>& vec) {
   if (!vec)
     return nullptr;
 
-  return DOMFloat32Array::Create(&(vec.value().front()), vec.value().size());
+  auto* dom_array = DOMFloat32Array::Create(3);
+  float* data = dom_array->Data();
+  data[0] = vec->x();
+  data[1] = vec->y();
+  data[2] = vec->z();
+
+  return dom_array;
+}
+
+DOMFloat32Array* WebPoint3DToFloat32Array(
+    const base::Optional<WebFloatPoint3D>& p) {
+  if (!p)
+    return nullptr;
+
+  auto* dom_array = DOMFloat32Array::Create(3);
+  float* data = dom_array->Data();
+  data[0] = p->x;
+  data[1] = p->y;
+  data[2] = p->z;
+
+  return dom_array;
 }
 
 }  // namespace
@@ -24,12 +59,12 @@
   if (state.is_null())
     return;
 
-  orientation_ = MojoArrayToFloat32Array(state->orientation);
-  position_ = MojoArrayToFloat32Array(state->position);
-  angular_velocity_ = MojoArrayToFloat32Array(state->angularVelocity);
-  linear_velocity_ = MojoArrayToFloat32Array(state->linearVelocity);
-  angular_acceleration_ = MojoArrayToFloat32Array(state->angularAcceleration);
-  linear_acceleration_ = MojoArrayToFloat32Array(state->linearAcceleration);
+  orientation_ = QuaternionToFloat32Array(state->orientation);
+  position_ = WebPoint3DToFloat32Array(state->position);
+  angular_velocity_ = Vector3dFToFloat32Array(state->angularVelocity);
+  linear_velocity_ = Vector3dFToFloat32Array(state->linearVelocity);
+  angular_acceleration_ = Vector3dFToFloat32Array(state->angularAcceleration);
+  linear_acceleration_ = Vector3dFToFloat32Array(state->linearAcceleration);
 }
 
 void VRPose::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/vr/vr_stage_parameters.cc b/third_party/blink/renderer/modules/vr/vr_stage_parameters.cc
index 1a81f41..d04253b3 100644
--- a/third_party/blink/renderer/modules/vr/vr_stage_parameters.cc
+++ b/third_party/blink/renderer/modules/vr/vr_stage_parameters.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/modules/vr/vr_stage_parameters.h"
+#include "third_party/blink/renderer/modules/xr/xr_utils.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 
 namespace blink {
 
@@ -17,8 +19,8 @@
 
 void VRStageParameters::Update(
     const device::mojom::blink::VRStageParametersPtr& stage) {
-  standing_transform_ =
-      DOMFloat32Array::Create(&(stage->standingTransform.front()), 16);
+  standing_transform_ = transformationMatrixToDOMFloat32Array(
+      TransformationMatrix(stage->standingTransform.matrix()));
   size_x_ = stage->sizeX;
   size_z_ = stage->sizeZ;
 }
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
index 66b1c3e2..60407bad 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
@@ -26,7 +26,7 @@
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
 
 #include "build/build_config.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
diff --git a/third_party/blink/renderer/modules/webdatabase/BUILD.gn b/third_party/blink/renderer/modules/webdatabase/BUILD.gn
index cad740db..e590b5ec 100644
--- a/third_party/blink/renderer/modules/webdatabase/BUILD.gn
+++ b/third_party/blink/renderer/modules/webdatabase/BUILD.gn
@@ -71,6 +71,8 @@
     "sqlite/sqlite_transaction.cc",
     "sqlite/sqlite_transaction.h",
     "storage_log.h",
+    "web_database_host.cc",
+    "web_database_host.h",
     "web_database_impl.cc",
     "web_database_impl.h",
   ]
diff --git a/third_party/blink/renderer/modules/webdatabase/DEPS b/third_party/blink/renderer/modules/webdatabase/DEPS
index 6dcf663..4d65ced 100644
--- a/third_party/blink/renderer/modules/webdatabase/DEPS
+++ b/third_party/blink/renderer/modules/webdatabase/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
+    "+base/files/file.h",
+    "+base/task/task_traits.h",
     "+mojo/public/cpp/bindings/strong_binding.h",
     "-third_party/blink/renderer/modules",
     "+third_party/blink/renderer/modules/modules_export.h",
diff --git a/third_party/blink/renderer/modules/webdatabase/database.cc b/third_party/blink/renderer/modules/webdatabase/database.cc
index 7eb9c786..3dcb02b 100644
--- a/third_party/blink/renderer/modules/webdatabase/database.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database.cc
@@ -31,8 +31,6 @@
 #include "base/thread_annotations.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
-#include "third_party/blink/public/platform/web_database_observer.h"
-#include "third_party/blink/public/platform/web_security_origin.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/probe/core_probes.h"
@@ -51,6 +49,7 @@
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_statement.h"
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_transaction.h"
 #include "third_party/blink/renderer/modules/webdatabase/storage_log.h"
+#include "third_party/blink/renderer/modules/webdatabase/web_database_host.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
@@ -737,11 +736,8 @@
 }
 
 void Database::ReportSqliteError(int sqlite_error_code) {
-  if (Platform::Current()->DatabaseObserver()) {
-    Platform::Current()->DatabaseObserver()->ReportSqliteError(
-        WebSecurityOrigin(GetSecurityOrigin()), StringIdentifier(),
-        sqlite_error_code);
-  }
+  WebDatabaseHost::GetInstance().ReportSqliteError(
+      *GetSecurityOrigin(), StringIdentifier(), sqlite_error_code);
 }
 
 void Database::LogErrorMessage(const String& message) {
diff --git a/third_party/blink/renderer/modules/webdatabase/database_tracker.cc b/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
index 331f223..b19c0bd 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
@@ -35,7 +35,6 @@
 #include "base/location.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
-#include "third_party/blink/public/platform/web_database_observer.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -43,6 +42,7 @@
 #include "third_party/blink/renderer/modules/webdatabase/database_client.h"
 #include "third_party/blink/renderer/modules/webdatabase/database_context.h"
 #include "third_party/blink/renderer/modules/webdatabase/quota_tracker.h"
+#include "third_party/blink/renderer/modules/webdatabase/web_database_host.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
@@ -53,11 +53,8 @@
 namespace blink {
 
 static void DatabaseClosed(Database* database) {
-  if (Platform::Current()->DatabaseObserver()) {
-    Platform::Current()->DatabaseObserver()->DatabaseClosed(
-        WebSecurityOrigin(database->GetSecurityOrigin()),
-        database->StringIdentifier());
-  }
+  WebDatabaseHost::GetInstance().DatabaseClosed(*database->GetSecurityOrigin(),
+                                                database->StringIdentifier());
 }
 
 DatabaseTracker& DatabaseTracker::Tracker() {
@@ -142,23 +139,20 @@
 void DatabaseTracker::PrepareToOpenDatabase(Database* database) {
   DCHECK(
       database->GetDatabaseContext()->GetExecutionContext()->IsContextThread());
-  if (Platform::Current()->DatabaseObserver()) {
-    // This is an asynchronous call to the browser to open the database,
-    // however we can't actually use the database until we revieve an RPC back
-    // that advises is of the actual size of the database, so there is a race
-    // condition where the database is in an unusable state. To assist, we
-    // will record the size of the database straight away so we can use it
-    // immediately, and the real size will eventually be updated by the RPC from
-    // the browser.
-    Platform::Current()->DatabaseObserver()->DatabaseOpened(
-        WebSecurityOrigin(database->GetSecurityOrigin()),
-        database->StringIdentifier(), database->DisplayName(),
-        database->EstimatedSize());
-    // We write a temporary size of 0 to the QuotaTracker - we will be updated
-    // with the correct size via RPC asynchronously.
-    QuotaTracker::Instance().UpdateDatabaseSize(
-        database->GetSecurityOrigin(), database->StringIdentifier(), 0);
-  }
+
+  // This is an asynchronous call to the browser to open the database, however
+  // we can't actually use the database until we revieve an RPC back that
+  // advises is of the actual size of the database, so there is a race condition
+  // where the database is in an unusable state. To assist, we will record the
+  // size of the database straight away so we can use it immediately, and the
+  // real size will eventually be updated by the RPC from the browser.
+  WebDatabaseHost::GetInstance().DatabaseOpened(
+      *database->GetSecurityOrigin(), database->StringIdentifier(),
+      database->DisplayName(), database->EstimatedSize());
+  // We write a temporary size of 0 to the QuotaTracker - we will be updated
+  // with the correct size via RPC asynchronously.
+  QuotaTracker::Instance().UpdateDatabaseSize(database->GetSecurityOrigin(),
+                                              database->StringIdentifier(), 0);
 }
 
 void DatabaseTracker::FailedToOpenDatabase(Database* database) {
diff --git a/third_party/blink/renderer/modules/webdatabase/quota_tracker.cc b/third_party/blink/renderer/modules/webdatabase/quota_tracker.cc
index 1b6b484..3668e5e 100644
--- a/third_party/blink/renderer/modules/webdatabase/quota_tracker.cc
+++ b/third_party/blink/renderer/modules/webdatabase/quota_tracker.cc
@@ -32,6 +32,7 @@
 
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/renderer/modules/webdatabase/web_database_host.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/threading.h"
@@ -59,8 +60,8 @@
   }
 
   // The embedder hasn't pushed this value to us, so we pull it as needed.
-  *space_available = Platform::Current()->DatabaseGetSpaceAvailableForOrigin(
-      WebSecurityOrigin(origin));
+  *space_available =
+      WebDatabaseHost::GetInstance().GetSpaceAvailableForOrigin(*origin);
 }
 
 void QuotaTracker::UpdateDatabaseSize(const SecurityOrigin* origin,
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_statement.h b/third_party/blink/renderer/modules/webdatabase/sql_statement.h
index 7fd086c4..b5a460c 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_statement.h
+++ b/third_party/blink/renderer/modules/webdatabase/sql_statement.h
@@ -63,13 +63,13 @@
     }
 
     explicit OnSuccessV8Impl(V8SQLStatementCallback* callback)
-        : callback_(ToV8PersistentCallbackInterface(callback)) {}
+        : callback_(callback) {}
 
     void Trace(blink::Visitor*) override;
     bool OnSuccess(SQLTransaction*, SQLResultSet*) override;
 
    private:
-    Member<V8PersistentCallbackInterface<V8SQLStatementCallback>> callback_;
+    Member<V8SQLStatementCallback> callback_;
   };
 
   class OnErrorCallback : public GarbageCollectedFinalized<OnErrorCallback> {
@@ -89,14 +89,13 @@
     }
 
     explicit OnErrorV8Impl(V8SQLStatementErrorCallback* callback)
-        : callback_(ToV8PersistentCallbackInterface(callback)) {}
+        : callback_(callback) {}
 
     void Trace(blink::Visitor*) override;
     bool OnError(SQLTransaction*, SQLError*) override;
 
    private:
-    Member<V8PersistentCallbackInterface<V8SQLStatementErrorCallback>>
-        callback_;
+    Member<V8SQLStatementErrorCallback> callback_;
   };
 
   static SQLStatement* Create(Database*, OnSuccessCallback*, OnErrorCallback*);
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_transaction.h b/third_party/blink/renderer/modules/webdatabase/sql_transaction.h
index 7ba7863..1643cef7 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_transaction.h
+++ b/third_party/blink/renderer/modules/webdatabase/sql_transaction.h
@@ -74,13 +74,13 @@
     }
 
     explicit OnProcessV8Impl(V8SQLTransactionCallback* callback)
-        : callback_(ToV8PersistentCallbackInterface(callback)) {}
+        : callback_(callback) {}
 
     void Trace(blink::Visitor*) override;
     bool OnProcess(SQLTransaction*) override;
 
    private:
-    Member<V8PersistentCallbackInterface<V8SQLTransactionCallback>> callback_;
+    Member<V8SQLTransactionCallback> callback_;
   };
 
   class OnSuccessCallback
@@ -101,14 +101,13 @@
                       : nullptr;
     }
 
-    explicit OnSuccessV8Impl(V8VoidCallback* callback)
-        : callback_(ToV8PersistentCallbackInterface(callback)) {}
+    explicit OnSuccessV8Impl(V8VoidCallback* callback) : callback_(callback) {}
 
     void Trace(blink::Visitor*) override;
     void OnSuccess() override;
 
    private:
-    Member<V8PersistentCallbackInterface<V8VoidCallback>> callback_;
+    Member<V8VoidCallback> callback_;
   };
 
   class OnErrorCallback : public GarbageCollectedFinalized<OnErrorCallback> {
@@ -128,14 +127,13 @@
     }
 
     explicit OnErrorV8Impl(V8SQLTransactionErrorCallback* callback)
-        : callback_(ToV8PersistentCallbackInterface(callback)) {}
+        : callback_(callback) {}
 
     void Trace(blink::Visitor*) override;
     bool OnError(SQLError*) override;
 
    private:
-    Member<V8PersistentCallbackInterface<V8SQLTransactionErrorCallback>>
-        callback_;
+    Member<V8SQLTransactionErrorCallback> callback_;
   };
 
   static SQLTransaction* Create(Database*,
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_transaction_client.cc b/third_party/blink/renderer/modules/webdatabase/sql_transaction_client.cc
index 7a62050..1d9a7e1 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_transaction_client.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sql_transaction_client.cc
@@ -33,11 +33,10 @@
 #include "base/location.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
-#include "third_party/blink/public/platform/web_database_observer.h"
-#include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/webdatabase/database.h"
 #include "third_party/blink/renderer/modules/webdatabase/database_context.h"
+#include "third_party/blink/renderer/modules/webdatabase/web_database_host.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -45,11 +44,8 @@
 namespace blink {
 
 void SQLTransactionClient::DidCommitWriteTransaction(Database* database) {
-  if (Platform::Current()->DatabaseObserver()) {
-    Platform::Current()->DatabaseObserver()->DatabaseModified(
-        WebSecurityOrigin(database->GetSecurityOrigin()),
-        database->StringIdentifier());
-  }
+  WebDatabaseHost::GetInstance().DatabaseModified(
+      *database->GetSecurityOrigin(), database->StringIdentifier());
 }
 
 bool SQLTransactionClient::DidExceedQuota(Database* database) {
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.cc
index cad20de..31f8f40d 100644
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.cc
@@ -16,6 +16,7 @@
 #include "sql/initialization.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.h"
+#include "third_party/blink/renderer/modules/webdatabase/web_database_host.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/sqlite/sqlite3.h"
 
@@ -189,7 +190,8 @@
   //               is asynchronous, so we could open all the needed files (DB,
   //               journal, etc.) asynchronously, and store them in a hash table
   //               that would be used here.
-  base::File file = platform_->DatabaseOpenFile(file_name, requested_flags);
+  base::File file =
+      WebDatabaseHost::GetInstance().OpenFile(file_name, requested_flags);
 
   if (!file.IsValid()) {
     // TODO(pwnall): Figure out if we can remove the fallback to read-only.
@@ -214,14 +216,15 @@
 
 int SandboxedVfs::Delete(const char* full_path, int sync_dir) {
   DCHECK(full_path);
-  return platform_->DatabaseDeleteFile(StringFromFullPath(full_path), sync_dir);
+  return WebDatabaseHost::GetInstance().DeleteFile(
+      StringFromFullPath(full_path), sync_dir);
 }
 
 int SandboxedVfs::Access(const char* full_path, int flags, int* result) {
   DCHECK(full_path);
   DCHECK(result);
-  int32_t attributes =
-      platform_->DatabaseGetFileAttributes(StringFromFullPath(full_path));
+  int32_t attributes = WebDatabaseHost::GetInstance().GetFileAttributes(
+      StringFromFullPath(full_path));
 
   // TODO(pwnall): Make the mojo interface portable across OSes, instead of
   //               messing around with OS-dependent constants here.
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.cc
index 2fd7f1815..2762ed8 100644
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.cc
@@ -16,6 +16,7 @@
 #include "sql/initialization.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.h"
+#include "third_party/blink/renderer/modules/webdatabase/web_database_host.h"
 #include "third_party/sqlite/sqlite3.h"
 
 namespace blink {
@@ -219,7 +220,7 @@
   // TODO(pwnall): Figure out if we can allow ftruncate() in the renderer. It
   //               would be useful for low-level storage APIs, like the upcoming
   //               filesystem API.
-  if (!vfs_->GetPlatform()->DatabaseSetFileSize(file_name_, size))
+  if (!WebDatabaseHost::GetInstance().SetFileSize(file_name_, size))
     return SQLITE_IOERR_TRUNCATE;
 
   return SQLITE_OK;
diff --git a/third_party/blink/renderer/modules/webdatabase/web_database_host.cc b/third_party/blink/renderer/modules/webdatabase/web_database_host.cc
new file mode 100644
index 0000000..72ad465
--- /dev/null
+++ b/third_party/blink/renderer/modules/webdatabase/web_database_host.cc
@@ -0,0 +1,120 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webdatabase/web_database_host.h"
+
+#include <utility>
+
+#include "base/single_thread_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "third_party/blink/public/mojom/webdatabase/web_database.mojom-blink.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/sqlite/sqlite3.h"
+
+namespace blink {
+
+WebDatabaseHost* WebDatabaseHost::instance_ = nullptr;
+
+// static
+WebDatabaseHost& WebDatabaseHost::GetInstance() {
+  DEFINE_STATIC_LOCAL(WebDatabaseHost, instance, ());
+  return instance;
+}
+
+void WebDatabaseHost::Init() {
+  Platform::Current()->GetInterfaceProvider()->GetInterface(
+      pending_remote_.InitWithNewPipeAndPassReceiver());
+}
+
+WebDatabaseHost::WebDatabaseHost()
+    : main_thread_task_runner_(Thread::MainThread()->GetTaskRunner()) {}
+
+mojom::blink::WebDatabaseHost& WebDatabaseHost::GetWebDatabaseHost() {
+  if (!shared_remote_) {
+    DCHECK(pending_remote_);
+    shared_remote_ = mojo::SharedRemote<mojom::blink::WebDatabaseHost>(
+        std::move(pending_remote_), base::CreateSequencedTaskRunnerWithTraits(
+                                        {base::WithBaseSyncPrimitives()}));
+  }
+
+  return *shared_remote_;
+}
+
+base::File WebDatabaseHost::OpenFile(const String& vfs_file_name,
+                                     int desired_flags) {
+  base::File file;
+  GetWebDatabaseHost().OpenFile(vfs_file_name, desired_flags, &file);
+  return file;
+}
+
+int WebDatabaseHost::DeleteFile(const String& vfs_file_name, bool sync_dir) {
+  int rv = SQLITE_IOERR_DELETE;
+  GetWebDatabaseHost().DeleteFile(vfs_file_name, sync_dir, &rv);
+  return rv;
+}
+
+int32_t WebDatabaseHost::GetFileAttributes(const String& vfs_file_name) {
+  int32_t rv = -1;
+  GetWebDatabaseHost().GetFileAttributes(vfs_file_name, &rv);
+  return rv;
+}
+
+int64_t WebDatabaseHost::GetFileSize(const String& vfs_file_name) {
+  int64_t rv = 0LL;
+  GetWebDatabaseHost().GetFileSize(vfs_file_name, &rv);
+  return rv;
+}
+
+bool WebDatabaseHost::SetFileSize(const String& vfs_file_name, int64_t size) {
+  bool rv = false;
+  GetWebDatabaseHost().SetFileSize(vfs_file_name, size, &rv);
+  return rv;
+}
+
+int64_t WebDatabaseHost::GetSpaceAvailableForOrigin(
+    const SecurityOrigin& origin) {
+  int64_t rv = 0LL;
+  GetWebDatabaseHost().GetSpaceAvailable(&origin, &rv);
+  return rv;
+}
+
+void WebDatabaseHost::DatabaseOpened(const SecurityOrigin& origin,
+                                     const String& database_name,
+                                     const String& database_display_name,
+                                     uint32_t estimated_size) {
+  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  GetWebDatabaseHost().Opened(&origin, database_name, database_display_name,
+                              estimated_size);
+}
+
+void WebDatabaseHost::DatabaseModified(const SecurityOrigin& origin,
+                                       const String& database_name) {
+  DCHECK(!main_thread_task_runner_->RunsTasksInCurrentSequence());
+  GetWebDatabaseHost().Modified(&origin, database_name);
+}
+
+void WebDatabaseHost::DatabaseClosed(const SecurityOrigin& origin,
+                                     const String& database_name) {
+  DCHECK(!main_thread_task_runner_->RunsTasksInCurrentSequence());
+  GetWebDatabaseHost().Closed(&origin, database_name);
+}
+
+void WebDatabaseHost::ReportSqliteError(const SecurityOrigin& origin,
+                                        const String& database_name,
+                                        int error) {
+  DCHECK(!main_thread_task_runner_->RunsTasksInCurrentSequence());
+
+  // We filter out errors which the backend doesn't act on to avoid a
+  // unnecessary ipc traffic, this method can get called at a fairly high
+  // frequency (per-sqlstatement).
+  if (error != SQLITE_CORRUPT && error != SQLITE_NOTADB)
+    return;
+
+  GetWebDatabaseHost().HandleSqliteError(&origin, database_name, error);
+}
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webdatabase/web_database_host.h b/third_party/blink/renderer/modules/webdatabase/web_database_host.h
new file mode 100644
index 0000000..4e75b6b0
--- /dev/null
+++ b/third_party/blink/renderer/modules/webdatabase/web_database_host.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 THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_WEB_DATABASE_HOST_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_WEB_DATABASE_HOST_H_
+
+#include <stdint.h>
+
+#include "base/files/file.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/shared_remote.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace blink {
+
+namespace mojom {
+namespace blink {
+class WebDatabaseHost;
+}  // namespace blink
+}  // namespace mojom
+
+class SecurityOrigin;
+
+// Singleton owning a remote connection to the mojo::WebDatabaseHost implementor
+// in the browser process. The remote interface gets bound when initializing the
+// webdatabase module (from the main thread), but the actual remote object will
+// be created the first time a method gets invoked (from the Database thread).
+class WebDatabaseHost {
+  USING_FAST_MALLOC(WebDatabaseHost);
+
+ public:
+  static WebDatabaseHost& GetInstance();
+
+  // Should be called once before trying to use this class, so that we make sure
+  // the remote interface binding is done from the main thread before the first
+  // time GetInstance() is invoked (which will happen from the Database thread).
+  void Init();
+
+  // Init() must have been invoked first before using any of the methods below.
+  base::File OpenFile(const String& vfs_file_name, int desired_flags);
+  int DeleteFile(const String& vfs_file_name, bool sync_dir);
+  int32_t GetFileAttributes(const String& vfs_file_name);
+  int64_t GetFileSize(const String& vfs_file_name);
+  bool SetFileSize(const String& vfs_file_name, int64_t size);
+  int64_t GetSpaceAvailableForOrigin(const SecurityOrigin& origin);
+
+  void DatabaseOpened(const SecurityOrigin& origin,
+                      const String& database_name,
+                      const String& database_display_name,
+                      uint32_t estimated_size);
+  void DatabaseModified(const SecurityOrigin& origin,
+                        const String& database_name);
+  void DatabaseClosed(const SecurityOrigin& origin,
+                      const String& database_name);
+  void ReportSqliteError(const SecurityOrigin& origin,
+                         const String& database_name,
+                         int error);
+
+ private:
+  WebDatabaseHost();
+  ~WebDatabaseHost() = default;
+
+  // Returns an initialized mojom::blink::WebDatabaseHost remote. A connection
+  // will be established after the first call to this method.
+  mojom::blink::WebDatabaseHost& GetWebDatabaseHost();
+
+  // Needed to bind the pending remote from the constructor, in the main thread.
+  mojo::PendingRemote<mojom::blink::WebDatabaseHost> pending_remote_;
+
+  // Need a SharedRemote as method calls will happen from the Database thread.
+  mojo::SharedRemote<mojom::blink::WebDatabaseHost> shared_remote_;
+
+  // Used to ensure that the database gets opened from the main thread, but that
+  // other database-related event is reported from the database thread instead.
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+  static WebDatabaseHost* instance_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebDatabaseHost);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_WEB_DATABASE_HOST_H_
diff --git a/third_party/blink/renderer/modules/webmidi/midi_access.idl b/third_party/blink/renderer/modules/webmidi/midi_access.idl
index 905ceb4..6188bb9 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_access.idl
+++ b/third_party/blink/renderer/modules/webmidi/midi_access.idl
@@ -31,7 +31,8 @@
 // https://webaudio.github.io/web-midi-api/#MIDIAccess
 
 [
-    ActiveScriptWrappable
+    ActiveScriptWrappable,
+    SecureContext
 ] interface MIDIAccess : EventTarget {
     readonly attribute MIDIInputMap inputs;
     readonly attribute MIDIOutputMap outputs;
diff --git a/third_party/blink/renderer/modules/webmidi/midi_connection_event.idl b/third_party/blink/renderer/modules/webmidi/midi_connection_event.idl
index a63c28926..93532e7 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_connection_event.idl
+++ b/third_party/blink/renderer/modules/webmidi/midi_connection_event.idl
@@ -31,6 +31,7 @@
 // https://webaudio.github.io/web-midi-api/#MIDIConnectionEvent
 
 [
+    SecureContext,
     Constructor(DOMString type, optional MIDIConnectionEventInit eventInitDict)
 ] interface MIDIConnectionEvent : Event {
     readonly attribute MIDIPort port;
diff --git a/third_party/blink/renderer/modules/webmidi/midi_input.idl b/third_party/blink/renderer/modules/webmidi/midi_input.idl
index 1d11322..57accaf 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_input.idl
+++ b/third_party/blink/renderer/modules/webmidi/midi_input.idl
@@ -30,6 +30,8 @@
 
 // https://webaudio.github.io/web-midi-api/#MIDIInput
 
-interface MIDIInput : MIDIPort {
+[
+    SecureContext
+] interface MIDIInput : MIDIPort {
     attribute EventHandler onmidimessage;
 };
diff --git a/third_party/blink/renderer/modules/webmidi/midi_input_map.idl b/third_party/blink/renderer/modules/webmidi/midi_input_map.idl
index 6c0f263..fa5dca6a1 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_input_map.idl
+++ b/third_party/blink/renderer/modules/webmidi/midi_input_map.idl
@@ -4,6 +4,8 @@
 
 // https://webaudio.github.io/web-midi-api/#MIDIInput
 
-interface MIDIInputMap {
+[
+    SecureContext
+] interface MIDIInputMap {
     readonly maplike<DOMString, MIDIInput>;
 };
diff --git a/third_party/blink/renderer/modules/webmidi/midi_message_event.idl b/third_party/blink/renderer/modules/webmidi/midi_message_event.idl
index e2f3fb2c..48c9c68 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_message_event.idl
+++ b/third_party/blink/renderer/modules/webmidi/midi_message_event.idl
@@ -31,6 +31,7 @@
 // https://webaudio.github.io/web-midi-api/#MIDIMessageEvent
 
 [
+    SecureContext,
     Constructor(DOMString type, optional MIDIMessageEventInit eventInitDict)
 ] interface MIDIMessageEvent : Event {
     readonly attribute Uint8Array data;
diff --git a/third_party/blink/renderer/modules/webmidi/midi_output.idl b/third_party/blink/renderer/modules/webmidi/midi_output.idl
index b8a4e15..e81d810 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_output.idl
+++ b/third_party/blink/renderer/modules/webmidi/midi_output.idl
@@ -30,7 +30,9 @@
 
 // https://webaudio.github.io/web-midi-api/#MIDIOutput
 
-interface MIDIOutput : MIDIPort {
+[
+    SecureContext
+] interface MIDIOutput : MIDIPort {
     // TODO(toyoshim): implement void clear()
     [RaisesException] void send(Uint8Array data, optional DOMHighResTimeStamp timestamp);
     [RaisesException] void send(sequence<unsigned long> data, optional DOMHighResTimeStamp timestamp);
diff --git a/third_party/blink/renderer/modules/webmidi/midi_output_map.idl b/third_party/blink/renderer/modules/webmidi/midi_output_map.idl
index 63edbaf7..267c767 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_output_map.idl
+++ b/third_party/blink/renderer/modules/webmidi/midi_output_map.idl
@@ -4,6 +4,8 @@
 
 // https://webaudio.github.io/web-midi-api/#MIDIOutputMap
 
-interface MIDIOutputMap {
+[
+    SecureContext
+] interface MIDIOutputMap {
     readonly maplike<DOMString, MIDIOutput>;
 };
diff --git a/third_party/blink/renderer/modules/webmidi/midi_port.idl b/third_party/blink/renderer/modules/webmidi/midi_port.idl
index eddf0f0..19a2556f 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_port.idl
+++ b/third_party/blink/renderer/modules/webmidi/midi_port.idl
@@ -53,7 +53,8 @@
 // https://webaudio.github.io/web-midi-api/#MIDIPort
 
 [
-    ActiveScriptWrappable
+    ActiveScriptWrappable,
+    SecureContext
 ] interface MIDIPort : EventTarget {
     readonly attribute MIDIPortConnectionState connection;
     readonly attribute DOMString id;
diff --git a/third_party/blink/renderer/modules/webmidi/navigator_web_midi.idl b/third_party/blink/renderer/modules/webmidi/navigator_web_midi.idl
index fbc72c1..a569d1e 100644
--- a/third_party/blink/renderer/modules/webmidi/navigator_web_midi.idl
+++ b/third_party/blink/renderer/modules/webmidi/navigator_web_midi.idl
@@ -31,7 +31,8 @@
 // https://webaudio.github.io/web-midi-api/#requestMIDIAccess
 
 [
-  ImplementedAs=NavigatorWebMIDI
+  ImplementedAs=NavigatorWebMIDI,
+  SecureContext
 ] partial interface Navigator {
   [CallWith=ScriptState, MeasureAs=RequestMIDIAccess_ObscuredByFootprinting] Promise<MIDIAccess> requestMIDIAccess(optional MIDIOptions options);
 };
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket_test.cc b/third_party/blink/renderer/modules/websockets/dom_websocket_test.cc
index 7210df4..ebdb112 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket_test.cc
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket_test.cc
@@ -10,7 +10,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/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
diff --git a/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc b/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
index 75baf21..bcb0037ee 100644
--- a/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
+++ b/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
@@ -32,7 +32,7 @@
 
 #include <stddef.h>
 #include <memory>
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/web/web_array_buffer.h"
 #include "third_party/blink/public/web/web_document.h"
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel.h b/third_party/blink/renderer/modules/websockets/websocket_channel.h
index 546e4aa..6a8d6948 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel.h
@@ -33,7 +33,7 @@
 
 #include <memory>
 #include "base/macros.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
index 5524df8..1c6830d 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
@@ -41,7 +41,6 @@
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/websockets/websocket_channel.h"
 #include "third_party/blink/renderer/modules/websockets/websocket_handle.h"
-#include "third_party/blink/renderer/modules/websockets/websocket_handle_client.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -61,8 +60,7 @@
 // This is an implementation of WebSocketChannel. This is created on the main
 // thread for Document, or on the worker thread for WorkerGlobalScope. All
 // functions must be called on the execution context's thread.
-class MODULES_EXPORT WebSocketChannelImpl final : public WebSocketChannel,
-                                                  public WebSocketHandleClient {
+class MODULES_EXPORT WebSocketChannelImpl final : public WebSocketChannel {
  public:
   // You can specify the source file and the line number information
   // explicitly by passing the last parameter.
@@ -109,6 +107,52 @@
 
   ExecutionContext* GetExecutionContext();
 
+  // Called when the handle is opened.
+  void DidConnect(WebSocketHandle* handle,
+                  const String& selected_protocol,
+                  const String& extensions,
+                  uint64_t receive_quota_threshold);
+
+  // Called when the browser starts the opening handshake.
+  // This notification can be omitted when the inspector is not active.
+  void DidStartOpeningHandshake(
+      WebSocketHandle*,
+      network::mojom::blink::WebSocketHandshakeRequestPtr);
+
+  // Called when the browser finishes the opening handshake.
+  // This notification precedes didConnect.
+  // This notification can be omitted when the inspector is not active.
+  void DidFinishOpeningHandshake(
+      WebSocketHandle*,
+      network::mojom::blink::WebSocketHandshakeResponsePtr);
+
+  // Called when the browser is required to fail the connection.
+  // |message| can be displayed in the inspector, but should not be passed
+  // to scripts.
+  // This message also implies that channel is closed with
+  // (wasClean = false, code = 1006, reason = "") and
+  // |handle| becomes unavailable.
+  void DidFail(WebSocketHandle*, const String& message);
+
+  // Called when data are received.
+  void DidReceiveData(WebSocketHandle*,
+                      bool fin,
+                      WebSocketHandle::MessageType,
+                      const char* data,
+                      size_t);
+
+  // Called when the handle is closed.
+  // |handle| becomes unavailable once this notification arrives.
+  void DidClose(WebSocketHandle* handle,
+                bool was_clean,
+                uint16_t code,
+                const String& reason);
+  void AddSendFlowControlQuota(WebSocketHandle*, int64_t quota);
+
+  // Called when the browser receives a Close frame from the remote
+  // server. Not called when the renderer initiates the closing handshake.
+  void DidStartClosingHandshake(WebSocketHandle*);
+
   void Trace(blink::Visitor*) override;
 
  private:
@@ -154,30 +198,6 @@
   void AbortAsyncOperations();
   void HandleDidClose(bool was_clean, uint16_t code, const String& reason);
 
-  // WebSocketHandleClient functions.
-  void DidConnect(WebSocketHandle*,
-                  const String& selected_protocol,
-                  const String& extensions,
-                  uint64_t receive_quota_threshold) override;
-  void DidStartOpeningHandshake(
-      WebSocketHandle*,
-      network::mojom::blink::WebSocketHandshakeRequestPtr) override;
-  void DidFinishOpeningHandshake(
-      WebSocketHandle*,
-      network::mojom::blink::WebSocketHandshakeResponsePtr) override;
-  void DidFail(WebSocketHandle*, const String& message) override;
-  void DidReceiveData(WebSocketHandle*,
-                      bool fin,
-                      WebSocketHandle::MessageType,
-                      const char* data,
-                      size_t) override;
-  void DidClose(WebSocketHandle*,
-                bool was_clean,
-                uint16_t code,
-                const String& reason) override;
-  void AddSendFlowControlQuota(WebSocketHandle*, int64_t quota) override;
-  void DidStartClosingHandshake(WebSocketHandle*) override;
-
   // Completion callback. It is called with the results of throttling.
   void OnCompletion(const base::Optional<WebString>& error);
 
@@ -224,7 +244,8 @@
   base::Optional<uint64_t> receive_quota_threshold_;
 };
 
-std::ostream& operator<<(std::ostream&, const WebSocketChannelImpl*);
+MODULES_EXPORT std::ostream& operator<<(std::ostream&,
+                                        const WebSocketChannelImpl*);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
index af7ed16..fb2d73e 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
@@ -19,7 +19,6 @@
 #include "third_party/blink/renderer/modules/websockets/websocket_channel.h"
 #include "third_party/blink/renderer/modules/websockets/websocket_channel_client.h"
 #include "third_party/blink/renderer/modules/websockets/websocket_handle.h"
-#include "third_party/blink/renderer/modules/websockets/websocket_handle_client.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -88,16 +87,16 @@
                const Vector<String>& protocols,
                const KURL& site_for_cookies,
                const String& user_agent_override,
-               WebSocketHandleClient* client,
+               WebSocketChannelImpl* channel,
                base::SingleThreadTaskRunner*) override {
-    Connect(url, protocols, site_for_cookies, user_agent_override, client);
+    Connect(url, protocols, site_for_cookies, user_agent_override, channel);
   }
   MOCK_METHOD5(Connect,
                void(const KURL&,
                     const Vector<String>&,
                     const KURL&,
                     const String&,
-                    WebSocketHandleClient*));
+                    WebSocketChannelImpl*));
   MOCK_METHOD4(
       Send,
       void(bool, WebSocketHandle::MessageType, const char*, wtf_size_t));
@@ -150,10 +149,6 @@
     return static_cast<WebSocketChannel*>(channel_.Get());
   }
 
-  WebSocketHandleClient* HandleClient() {
-    return static_cast<WebSocketHandleClient*>(channel_.Get());
-  }
-
   WebSocketChannelImpl* ChannelImpl() { return channel_.Get(); }
 
   MockWebSocketHandle* Handle() { return handle_; }
@@ -166,13 +161,13 @@
     {
       InSequence s;
       EXPECT_CALL(*Handle(),
-                  Connect(KURL("ws://localhost/"), _, _, _, HandleClient()));
+                  Connect(KURL("ws://localhost/"), _, _, _, ChannelImpl()));
       EXPECT_CALL(*Handle(), AddReceiveFlowControlQuota(65536));
       EXPECT_CALL(*ChannelClient(), DidConnect(String("a"), String("b")));
     }
     EXPECT_TRUE(Channel()->Connect(KURL("ws://localhost/"), "x"));
-    HandleClient()->DidConnect(Handle(), String("a"), String("b"),
-                               kDefaultReceiveQuotaThreshold);
+    ChannelImpl()->DidConnect(Handle(), String("a"), String("b"),
+                              kDefaultReceiveQuotaThreshold);
     testing::Mock::VerifyAndClearExpectations(this);
   }
 
@@ -213,7 +208,7 @@
     InSequence s;
     EXPECT_CALL(*Handle(),
                 Connect(KURLEq("ws://localhost/"), _,
-                        KURLEq("http://example.com/"), _, HandleClient()))
+                        KURLEq("http://example.com/"), _, ChannelImpl()))
         .WillOnce(SaveArg<1>(&protocols));
     EXPECT_CALL(checkpoint, Call(1));
     EXPECT_CALL(*Handle(), AddReceiveFlowControlQuota(65536));
@@ -229,8 +224,8 @@
   EXPECT_EQ("x", protocols[0]);
 
   checkpoint.Call(1);
-  HandleClient()->DidConnect(Handle(), String("a"), String("b"),
-                             kDefaultReceiveQuotaThreshold);
+  ChannelImpl()->DidConnect(Handle(), String("a"), String("b"),
+                            kDefaultReceiveQuotaThreshold);
 }
 
 TEST_F(WebSocketChannelImplTest, sendText) {
@@ -245,7 +240,7 @@
                                 MemEq("baz", 3), 3));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Channel()->Send("foo");
@@ -278,18 +273,18 @@
                                 MemEq("MNOPQRSTUVWXYZ", 14), 14));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Channel()->Send("0123456789abcdefg");
   Channel()->Send("hijk");
   Channel()->Send("lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
   checkpoint.Call(1);
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   checkpoint.Call(2);
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   checkpoint.Call(3);
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
 
   EXPECT_EQ(62ul, sum_of_consumed_buffered_amount_);
 }
@@ -302,7 +297,7 @@
                                 MemEq("foo", 3), 3));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Vector<char> foo_vector;
@@ -326,7 +321,7 @@
                                 MemEq("\0\0\0", 3), 3));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   {
@@ -358,7 +353,7 @@
   EXPECT_CALL(*Handle(), Send(true, WebSocketHandle::kMessageTypeBinary,
                               MemEq("\xe7\x8b\x90", 3), 3));
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Vector<char> v;
@@ -373,7 +368,7 @@
   EXPECT_CALL(*Handle(), Send(true, WebSocketHandle::kMessageTypeBinary,
                               MemEq("\x80\xff\xe7", 3), 3));
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Vector<char> v;
@@ -398,7 +393,7 @@
                                 MemEq("\x8b\x90", 2), 2));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   Vector<char> v;
@@ -409,7 +404,7 @@
   Channel()->SendBinaryAsCharVector(std::make_unique<Vector<char>>(v));
   checkpoint.Call(1);
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
 
   EXPECT_EQ(18ul, sum_of_consumed_buffered_amount_);
 }
@@ -422,7 +417,7 @@
                                 MemEq("foo", 3), 3));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* foo_buffer = DOMArrayBuffer::Create("foo", 3);
@@ -445,7 +440,7 @@
                                 MemEq("a", 1), 1));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* foobar_buffer = DOMArrayBuffer::Create("foobar", 6);
@@ -472,7 +467,7 @@
                                 MemEq("\0\0\0", 3), 3));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   {
@@ -500,7 +495,7 @@
   EXPECT_CALL(*Handle(), Send(true, WebSocketHandle::kMessageTypeBinary,
                               MemEq("\xe7\x8b\x90", 3), 3));
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* b = DOMArrayBuffer::Create("\xe7\x8b\x90", 3);
@@ -514,7 +509,7 @@
   EXPECT_CALL(*Handle(), Send(true, WebSocketHandle::kMessageTypeBinary,
                               MemEq("\x80\xff\xe7", 3), 3));
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* b = DOMArrayBuffer::Create("\x80\xff\xe7", 3);
@@ -539,7 +534,7 @@
                                 MemEq("\x8b\x90", 2), 2));
   }
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
   EXPECT_CALL(*ChannelClient(), DidConsumeBufferedAmount(_)).Times(AnyNumber());
 
   DOMArrayBuffer* b = DOMArrayBuffer::Create(
@@ -549,7 +544,7 @@
   Channel()->Send(*b, 0, 18);
   checkpoint.Call(1);
 
-  HandleClient()->AddSendFlowControlQuota(Handle(), 16);
+  ChannelImpl()->AddSendFlowControlQuota(Handle(), 16);
 
   EXPECT_EQ(18ul, sum_of_consumed_buffered_amount_);
 }
@@ -564,21 +559,21 @@
     EXPECT_CALL(*ChannelClient(), DidReceiveTextMessage(String("BAR")));
   }
 
-  HandleClient()->DidReceiveData(Handle(), true,
-                                 WebSocketHandle::kMessageTypeText, "FOOX", 3);
-  HandleClient()->DidReceiveData(Handle(), true,
-                                 WebSocketHandle::kMessageTypeText, "BARX", 3);
+  ChannelImpl()->DidReceiveData(Handle(), true,
+                                WebSocketHandle::kMessageTypeText, "FOOX", 3);
+  ChannelImpl()->DidReceiveData(Handle(), true,
+                                WebSocketHandle::kMessageTypeText, "BARX", 3);
 }
 
 TEST_F(WebSocketChannelImplTest, receiveTextContinuation) {
   Connect();
   EXPECT_CALL(*ChannelClient(), DidReceiveTextMessage(String("BAZ")));
 
-  HandleClient()->DidReceiveData(Handle(), false,
-                                 WebSocketHandle::kMessageTypeText, "BX", 1);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(Handle(), false,
+                                WebSocketHandle::kMessageTypeText, "BX", 1);
+  ChannelImpl()->DidReceiveData(
       Handle(), false, WebSocketHandle::kMessageTypeContinuation, "AX", 1);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(
       Handle(), true, WebSocketHandle::kMessageTypeContinuation, "ZX", 1);
 }
 
@@ -588,9 +583,9 @@
   EXPECT_CALL(*ChannelClient(),
               DidReceiveTextMessage(String(non_latin1_string)));
 
-  HandleClient()->DidReceiveData(Handle(), true,
-                                 WebSocketHandle::kMessageTypeText,
-                                 "\xe7\x8b\x90\xe0\xa4\x94", 6);
+  ChannelImpl()->DidReceiveData(Handle(), true,
+                                WebSocketHandle::kMessageTypeText,
+                                "\xe7\x8b\x90\xe0\xa4\x94", 6);
 }
 
 TEST_F(WebSocketChannelImplTest, receiveTextNonLatin1Continuation) {
@@ -599,14 +594,14 @@
   EXPECT_CALL(*ChannelClient(),
               DidReceiveTextMessage(String(non_latin1_string)));
 
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(
       Handle(), false, WebSocketHandle::kMessageTypeText, "\xe7\x8b", 2);
-  HandleClient()->DidReceiveData(Handle(), false,
-                                 WebSocketHandle::kMessageTypeContinuation,
-                                 "\x90\xe0", 2);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(Handle(), false,
+                                WebSocketHandle::kMessageTypeContinuation,
+                                "\x90\xe0", 2);
+  ChannelImpl()->DidReceiveData(
       Handle(), false, WebSocketHandle::kMessageTypeContinuation, "\xa4", 1);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(
       Handle(), true, WebSocketHandle::kMessageTypeContinuation, "\x94", 1);
 }
 
@@ -616,8 +611,8 @@
   foo_vector.Append("FOO", 3);
   EXPECT_CALL(*ChannelClient(), DidReceiveBinaryMessageMock(foo_vector));
 
-  HandleClient()->DidReceiveData(
-      Handle(), true, WebSocketHandle::kMessageTypeBinary, "FOOx", 3);
+  ChannelImpl()->DidReceiveData(Handle(), true,
+                                WebSocketHandle::kMessageTypeBinary, "FOOx", 3);
 }
 
 TEST_F(WebSocketChannelImplTest, receiveBinaryContinuation) {
@@ -626,11 +621,11 @@
   baz_vector.Append("BAZ", 3);
   EXPECT_CALL(*ChannelClient(), DidReceiveBinaryMessageMock(baz_vector));
 
-  HandleClient()->DidReceiveData(Handle(), false,
-                                 WebSocketHandle::kMessageTypeBinary, "Bx", 1);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(Handle(), false,
+                                WebSocketHandle::kMessageTypeBinary, "Bx", 1);
+  ChannelImpl()->DidReceiveData(
       Handle(), false, WebSocketHandle::kMessageTypeContinuation, "Ax", 1);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(
       Handle(), true, WebSocketHandle::kMessageTypeContinuation, "Zx", 1);
 }
 
@@ -660,13 +655,13 @@
     }
   }
 
-  HandleClient()->DidReceiveData(
-      Handle(), true, WebSocketHandle::kMessageTypeBinary, "\0AR", 3);
-  HandleClient()->DidReceiveData(
-      Handle(), true, WebSocketHandle::kMessageTypeBinary, "B\0Z", 3);
-  HandleClient()->DidReceiveData(
-      Handle(), true, WebSocketHandle::kMessageTypeBinary, "QU\0", 3);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(Handle(), true,
+                                WebSocketHandle::kMessageTypeBinary, "\0AR", 3);
+  ChannelImpl()->DidReceiveData(Handle(), true,
+                                WebSocketHandle::kMessageTypeBinary, "B\0Z", 3);
+  ChannelImpl()->DidReceiveData(Handle(), true,
+                                WebSocketHandle::kMessageTypeBinary, "QU\0", 3);
+  ChannelImpl()->DidReceiveData(
       Handle(), true, WebSocketHandle::kMessageTypeBinary, "\0\0\0", 3);
 }
 
@@ -676,9 +671,9 @@
   v.Append("\xe7\x8b\x90\xe0\xa4\x94", 6);
   EXPECT_CALL(*ChannelClient(), DidReceiveBinaryMessageMock(v));
 
-  HandleClient()->DidReceiveData(Handle(), true,
-                                 WebSocketHandle::kMessageTypeBinary,
-                                 "\xe7\x8b\x90\xe0\xa4\x94", 6);
+  ChannelImpl()->DidReceiveData(Handle(), true,
+                                WebSocketHandle::kMessageTypeBinary,
+                                "\xe7\x8b\x90\xe0\xa4\x94", 6);
 }
 
 TEST_F(WebSocketChannelImplTest, receiveBinaryNonLatin1UTF8Continuation) {
@@ -687,14 +682,14 @@
   v.Append("\xe7\x8b\x90\xe0\xa4\x94", 6);
   EXPECT_CALL(*ChannelClient(), DidReceiveBinaryMessageMock(v));
 
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(
       Handle(), false, WebSocketHandle::kMessageTypeBinary, "\xe7\x8b", 2);
-  HandleClient()->DidReceiveData(Handle(), false,
-                                 WebSocketHandle::kMessageTypeContinuation,
-                                 "\x90\xe0", 2);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(Handle(), false,
+                                WebSocketHandle::kMessageTypeContinuation,
+                                "\x90\xe0", 2);
+  ChannelImpl()->DidReceiveData(
       Handle(), false, WebSocketHandle::kMessageTypeContinuation, "\xa4", 1);
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(
       Handle(), true, WebSocketHandle::kMessageTypeContinuation, "\x94", 1);
 }
 
@@ -704,7 +699,7 @@
   v.Append("\x80\xff", 2);
   EXPECT_CALL(*ChannelClient(), DidReceiveBinaryMessageMock(v));
 
-  HandleClient()->DidReceiveData(
+  ChannelImpl()->DidReceiveData(
       Handle(), true, WebSocketHandle::kMessageTypeBinary, "\x80\xff", 2);
 }
 
@@ -728,16 +723,16 @@
     EXPECT_CALL(checkpoint, Call(3));
   }
 
-  HandleClient()->DidStartClosingHandshake(Handle());
+  ChannelImpl()->DidStartClosingHandshake(Handle());
   checkpoint.Call(1);
 
   Channel()->Close(WebSocketChannel::kCloseEventCodeNormalClosure,
                    String("close reason"));
   checkpoint.Call(2);
 
-  HandleClient()->DidClose(Handle(), true,
-                           WebSocketChannel::kCloseEventCodeNormalClosure,
-                           String("close reason"));
+  ChannelImpl()->DidClose(Handle(), true,
+                          WebSocketChannel::kCloseEventCodeNormalClosure,
+                          String("close reason"));
   checkpoint.Call(3);
 
   Channel()->Disconnect();
@@ -764,9 +759,9 @@
                    String("close reason"));
   checkpoint.Call(1);
 
-  HandleClient()->DidClose(Handle(), true,
-                           WebSocketChannel::kCloseEventCodeNormalClosure,
-                           String("close reason"));
+  ChannelImpl()->DidClose(Handle(), true,
+                          WebSocketChannel::kCloseEventCodeNormalClosure,
+                          String("close reason"));
   checkpoint.Call(2);
 
   Channel()->Disconnect();
@@ -784,7 +779,7 @@
                  WebSocketChannel::kCloseEventCodeAbnormalClosure, String()));
   }
 
-  HandleClient()->DidFail(Handle(), "fail message");
+  ChannelImpl()->DidFail(Handle(), "fail message");
 }
 
 TEST_F(WebSocketChannelImplTest, failFromWebSocket) {
@@ -843,8 +838,8 @@
   checkpoint.Call(1);
   ChannelImpl()->OnCompletion(base::nullopt);
   checkpoint.Call(2);
-  HandleClient()->DidConnect(Handle(), String("a"), String("b"),
-                             kDefaultReceiveQuotaThreshold);
+  ChannelImpl()->DidConnect(Handle(), String("a"), String("b"),
+                            kDefaultReceiveQuotaThreshold);
 }
 
 TEST_F(WebSocketChannelImplHandshakeThrottleTest, HandshakeSucceedsFirst) {
@@ -860,8 +855,8 @@
   }
   Channel()->Connect(url(), "");
   checkpoint.Call(1);
-  HandleClient()->DidConnect(Handle(), String("a"), String("b"),
-                             kDefaultReceiveQuotaThreshold);
+  ChannelImpl()->DidConnect(Handle(), String("a"), String("b"),
+                            kDefaultReceiveQuotaThreshold);
   checkpoint.Call(2);
   ChannelImpl()->OnCompletion(base::nullopt);
 }
@@ -898,8 +893,8 @@
     EXPECT_CALL(checkpoint, Call(1));
   }
   Channel()->Connect(url(), "");
-  HandleClient()->DidConnect(Handle(), String("a"), String("b"),
-                             kDefaultReceiveQuotaThreshold);
+  ChannelImpl()->DidConnect(Handle(), String("a"), String("b"),
+                            kDefaultReceiveQuotaThreshold);
   Channel()->Fail("close during handshake",
                   mojom::ConsoleMessageLevel::kWarning,
                   std::make_unique<SourceLocation>(String(), 0, 0, nullptr));
@@ -932,8 +927,8 @@
     EXPECT_CALL(checkpoint, Call(1));
   }
   Channel()->Connect(url(), "");
-  HandleClient()->DidConnect(Handle(), String("a"), String("b"),
-                             kDefaultReceiveQuotaThreshold);
+  ChannelImpl()->DidConnect(Handle(), String("a"), String("b"),
+                            kDefaultReceiveQuotaThreshold);
   Channel()->Close(WebSocketChannelImpl::kCloseEventCodeGoingAway, "");
   checkpoint.Call(1);
 }
@@ -961,8 +956,8 @@
     EXPECT_CALL(checkpoint, Call(1));
   }
   Channel()->Connect(url(), "");
-  HandleClient()->DidConnect(Handle(), String("a"), String("b"),
-                             kDefaultReceiveQuotaThreshold);
+  ChannelImpl()->DidConnect(Handle(), String("a"), String("b"),
+                            kDefaultReceiveQuotaThreshold);
   Channel()->Disconnect();
   checkpoint.Call(1);
 }
@@ -990,8 +985,8 @@
     EXPECT_CALL(*ChannelClient(), DidClose(_, _, _));
   }
   Channel()->Connect(url(), "");
-  HandleClient()->DidConnect(Handle(), String("a"), String("b"),
-                             kDefaultReceiveQuotaThreshold);
+  ChannelImpl()->DidConnect(Handle(), String("a"), String("b"),
+                            kDefaultReceiveQuotaThreshold);
   ChannelImpl()->OnCompletion("Connection blocked by throttle");
 }
 
@@ -1004,7 +999,7 @@
     EXPECT_CALL(*ChannelClient(), DidClose(_, _, _));
   }
   Channel()->Connect(url(), "");
-  HandleClient()->DidFail(Handle(), "connect failed");
+  ChannelImpl()->DidFail(Handle(), "connect failed");
 }
 
 // TODO(ricea): Can this actually happen?
@@ -1016,9 +1011,9 @@
     EXPECT_CALL(*ChannelClient(), DidClose(_, _, _));
   }
   Channel()->Connect(url(), "");
-  HandleClient()->DidClose(Handle(), false,
-                           WebSocketChannel::kCloseEventCodeProtocolError,
-                           "connect error");
+  ChannelImpl()->DidClose(Handle(), false,
+                          WebSocketChannel::kCloseEventCodeProtocolError,
+                          "connect error");
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/websockets/websocket_handle.h b/third_party/blink/renderer/modules/websockets/websocket_handle.h
index 50ac7ba..bd574b8 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_handle.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_handle.h
@@ -41,14 +41,13 @@
 namespace blink {
 
 class KURL;
-class WebSocketHandleClient;
-
+class WebSocketChannelImpl;
 // WebSocketHandle is an interface class designed to be a handle of WebSocket
 // connection.  WebSocketHandle will be used together with
-// WebSocketHandleClient.
+// WebSocketChannelImpl.
 //
 // Once a WebSocketHandle is deleted there will be no notification to the
-// corresponding WebSocketHandleClient.  Once a WebSocketHandleClient receives
+// corresponding WebSocketChannelImpl.  Once a WebSocketChannelImpl receives
 // didClose, any method of the corresponding WebSocketHandle can't be called.
 
 class WebSocketHandle {
@@ -66,7 +65,7 @@
                        const Vector<String>& protocols,
                        const KURL& site_for_cookies,
                        const String& user_agent_override,
-                       WebSocketHandleClient*,
+                       WebSocketChannelImpl*,
                        base::SingleThreadTaskRunner*) = 0;
   virtual void Send(bool fin, MessageType, const char* data, wtf_size_t) = 0;
   virtual void AddReceiveFlowControlQuota(int64_t quota) = 0;
diff --git a/third_party/blink/renderer/modules/websockets/websocket_handle_client.h b/third_party/blink/renderer/modules/websockets/websocket_handle_client.h
deleted file mode 100644
index 86e230b..0000000
--- a/third_party/blink/renderer/modules/websockets/websocket_handle_client.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_HANDLE_CLIENT_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_HANDLE_CLIENT_H_
-
-#include "services/network/public/mojom/websocket.mojom-blink.h"
-#include "third_party/blink/renderer/modules/websockets/websocket_handle.h"
-#include "third_party/blink/renderer/platform/wtf/forward.h"
-
-namespace blink {
-
-class WebSocketHandleClient {
- public:
-  // Called when the handle is opened.
-  virtual void DidConnect(WebSocketHandle*,
-                          const String& selected_protocol,
-                          const String& extensions,
-                          uint64_t receive_quota_threshold) = 0;
-
-  // Called when the browser starts the opening handshake.
-  // This notification can be omitted when the inspector is not active.
-  virtual void DidStartOpeningHandshake(
-      WebSocketHandle*,
-      network::mojom::blink::WebSocketHandshakeRequestPtr) = 0;
-
-  // Called when the browser finishes the opening handshake.
-  // This notification precedes didConnect.
-  // This notification can be omitted when the inspector is not active.
-  virtual void DidFinishOpeningHandshake(
-      WebSocketHandle*,
-      network::mojom::blink::WebSocketHandshakeResponsePtr) = 0;
-
-  // Called when the browser is required to fail the connection.
-  // |message| can be displayed in the inspector, but should not be passed
-  // to scripts.
-  // This message also implies that channel is closed with
-  // (wasClean = false, code = 1006, reason = "") and
-  // |handle| becomes unavailable.
-  virtual void DidFail(WebSocketHandle* /* handle */,
-                       const String& message) = 0;
-
-  // Called when data are received.
-  virtual void DidReceiveData(WebSocketHandle*,
-                              bool fin,
-                              WebSocketHandle::MessageType,
-                              const char* data,
-                              size_t) = 0;
-
-  // Called when the handle is closed.
-  // |handle| becomes unavailable once this notification arrives.
-  virtual void DidClose(WebSocketHandle* /* handle */,
-                        bool was_clean,
-                        uint16_t code,
-                        const String& reason) = 0;
-
-  virtual void AddSendFlowControlQuota(WebSocketHandle*, int64_t quota) = 0;
-
-  // Called when the browser receives a Close frame from the remote
-  // server. Not called when the renderer initiates the closing handshake.
-  virtual void DidStartClosingHandshake(WebSocketHandle*) = 0;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_HANDLE_CLIENT_H_
diff --git a/third_party/blink/renderer/modules/websockets/websocket_handle_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_handle_impl.cc
index df3f26d..82fa18ed 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_handle_impl.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_handle_impl.cc
@@ -6,7 +6,7 @@
 
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/modules/websockets/websocket_handle_client.h"
+#include "third_party/blink/renderer/modules/websockets/websocket_channel_impl.h"
 #include "third_party/blink/renderer/platform/network/http_names.h"
 #include "third_party/blink/renderer/platform/network/network_log.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
@@ -22,7 +22,9 @@
 }  // namespace
 
 WebSocketHandleImpl::WebSocketHandleImpl()
-    : client_(nullptr), handshake_client_binding_(this), client_binding_(this) {
+    : channel_(nullptr),
+      handshake_client_binding_(this),
+      client_binding_(this) {
   NETWORK_DVLOG(1) << this << " created";
 }
 
@@ -38,7 +40,7 @@
                                   const Vector<String>& protocols,
                                   const KURL& site_for_cookies,
                                   const String& user_agent_override,
-                                  WebSocketHandleClient* client,
+                                  WebSocketChannelImpl* channel,
                                   base::SingleThreadTaskRunner* task_runner) {
   DCHECK(!websocket_);
   websocket_ = std::move(websocket);
@@ -48,9 +50,9 @@
 
   NETWORK_DVLOG(1) << this << " connect(" << url.GetString() << ")";
 
-  DCHECK(!client_);
-  DCHECK(client);
-  client_ = client;
+  DCHECK(!channel_);
+  DCHECK(channel);
+  channel_ = channel;
 
   network::mojom::blink::WebSocketHandshakeClientPtr handshake_client_proxy;
   Vector<network::mojom::blink::HttpHeaderPtr> additional_headers;
@@ -122,7 +124,7 @@
 
 void WebSocketHandleImpl::Disconnect() {
   websocket_.reset();
-  client_ = nullptr;
+  channel_ = nullptr;
 }
 
 void WebSocketHandleImpl::OnConnectionError(uint32_t custom_reason,
@@ -141,40 +143,40 @@
 void WebSocketHandleImpl::OnFailChannel(const String& message) {
   NETWORK_DVLOG(1) << this << " OnFailChannel(" << message << ")";
 
-  WebSocketHandleClient* client = client_;
+  WebSocketChannelImpl* channel = channel_;
   Disconnect();
-  if (!client)
+  if (!channel)
     return;
 
-  client->DidFail(this, message);
+  channel->DidFail(this, message);
   // |this| can be deleted here.
 }
 
-void WebSocketHandleImpl::OnStartOpeningHandshake(
+void WebSocketHandleImpl::OnOpeningHandshakeStarted(
     network::mojom::blink::WebSocketHandshakeRequestPtr request) {
-  NETWORK_DVLOG(1) << this << " OnStartOpeningHandshake("
+  NETWORK_DVLOG(1) << this << " OnOpeningHandshakeStarted("
                    << request->url.GetString() << ")";
-  client_->DidStartOpeningHandshake(this, std::move(request));
+  channel_->DidStartOpeningHandshake(this, std::move(request));
 }
 
-void WebSocketHandleImpl::OnFinishOpeningHandshake(
+void WebSocketHandleImpl::OnResponseReceived(
     network::mojom::blink::WebSocketHandshakeResponsePtr response) {
-  NETWORK_DVLOG(1) << this << " OnFinishOpeningHandshake("
+  NETWORK_DVLOG(1) << this << " OnResponseReceived("
                    << response->url.GetString() << ")";
-  client_->DidFinishOpeningHandshake(this, std::move(response));
+  channel_->DidFinishOpeningHandshake(this, std::move(response));
 }
 
-void WebSocketHandleImpl::OnAddChannelResponse(
+void WebSocketHandleImpl::OnConnectionEstablished(
     const String& protocol,
     const String& extensions,
     uint64_t receive_quota_threshold) {
-  NETWORK_DVLOG(1) << this << " OnAddChannelResponse(" << protocol << ", "
+  NETWORK_DVLOG(1) << this << " OnConnectionEstablished(" << protocol << ", "
                    << extensions << ", " << receive_quota_threshold << ")";
 
-  if (!client_)
+  if (!channel_)
     return;
 
-  client_->DidConnect(this, protocol, extensions, receive_quota_threshold);
+  channel_->DidConnect(this, protocol, extensions, receive_quota_threshold);
   // |this| can be deleted here.
 }
 
@@ -184,7 +186,7 @@
     base::span<const uint8_t> data) {
   NETWORK_DVLOG(1) << this << " OnDataFrame(" << fin << ", " << type << ", "
                    << "(data size = " << data.size() << "))";
-  if (!client_)
+  if (!channel_)
     return;
 
   WebSocketHandle::MessageType type_to_pass =
@@ -202,16 +204,16 @@
   }
   const char* data_to_pass =
       reinterpret_cast<const char*>(data.empty() ? nullptr : data.data());
-  client_->DidReceiveData(this, fin, type_to_pass, data_to_pass, data.size());
+  channel_->DidReceiveData(this, fin, type_to_pass, data_to_pass, data.size());
   // |this| can be deleted here.
 }
 
 void WebSocketHandleImpl::AddSendFlowControlQuota(int64_t quota) {
   NETWORK_DVLOG(1) << this << " AddSendFlowControlQuota(" << quota << ")";
-  if (!client_)
+  if (!channel_)
     return;
 
-  client_->AddSendFlowControlQuota(this, quota);
+  channel_->AddSendFlowControlQuota(this, quota);
   // |this| can be deleted here.
 }
 
@@ -221,21 +223,21 @@
   NETWORK_DVLOG(1) << this << " OnDropChannel(" << was_clean << ", " << code
                    << ", " << reason << ")";
 
-  WebSocketHandleClient* client = client_;
+  WebSocketChannelImpl* channel = channel_;
   Disconnect();
-  if (!client)
+  if (!channel)
     return;
 
-  client->DidClose(this, was_clean, code, reason);
+  channel->DidClose(this, was_clean, code, reason);
   // |this| can be deleted here.
 }
 
 void WebSocketHandleImpl::OnClosingHandshake() {
   NETWORK_DVLOG(1) << this << " OnClosingHandshake()";
-  if (!client_)
+  if (!channel_)
     return;
 
-  client_->DidStartClosingHandshake(this);
+  channel_->DidStartClosingHandshake(this);
   // |this| can be deleted here.
 }
 
diff --git a/third_party/blink/renderer/modules/websockets/websocket_handle_impl.h b/third_party/blink/renderer/modules/websockets/websocket_handle_impl.h
index d6183bf7..995f57b 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_handle_impl.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_handle_impl.h
@@ -34,6 +34,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/network/public/mojom/websocket.mojom-blink.h"
 #include "third_party/blink/renderer/modules/websockets/websocket_handle.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
 
 namespace blink {
@@ -51,7 +52,7 @@
                const Vector<String>& protocols,
                const KURL& site_for_cookies,
                const String& user_agent_override,
-               WebSocketHandleClient*,
+               WebSocketChannelImpl*,
                base::SingleThreadTaskRunner*) override;
   void Send(bool fin, MessageType, const char* data, wtf_size_t) override;
   void AddReceiveFlowControlQuota(int64_t quota) override;
@@ -63,13 +64,13 @@
                          const std::string& description);
 
   // network::mojom::blink::WebSocketHandshakeClient methods:
-  void OnStartOpeningHandshake(
+  void OnOpeningHandshakeStarted(
       network::mojom::blink::WebSocketHandshakeRequestPtr) override;
-  void OnFinishOpeningHandshake(
+  void OnResponseReceived(
       network::mojom::blink::WebSocketHandshakeResponsePtr) override;
-  void OnAddChannelResponse(const String& selected_protocol,
-                            const String& extensions,
-                            uint64_t receive_quota_threshold) override;
+  void OnConnectionEstablished(const String& selected_protocol,
+                               const String& extensions,
+                               uint64_t receive_quota_threshold) override;
   // network::mojom::blink::WebSocketClient methods:
   void OnDataFrame(bool fin,
                    network::mojom::blink::WebSocketMessageType,
@@ -81,7 +82,7 @@
   void OnClosingHandshake() override;
   void OnFailChannel(const String& reason) override;
 
-  WebSocketHandleClient* client_;
+  WeakPersistent<WebSocketChannelImpl> channel_;
 
   network::mojom::blink::WebSocketPtr websocket_;
   mojo::Binding<network::mojom::blink::WebSocketHandshakeClient>
diff --git a/third_party/blink/renderer/modules/xr/BUILD.gn b/third_party/blink/renderer/modules/xr/BUILD.gn
index c104733..13e379e1 100644
--- a/third_party/blink/renderer/modules/xr/BUILD.gn
+++ b/third_party/blink/renderer/modules/xr/BUILD.gn
@@ -42,6 +42,8 @@
     "xr_plane_detection_state.h",
     "xr_plane_set.cc",
     "xr_plane_set.h",
+    "xr_plane_space.cc",
+    "xr_plane_space.h",
     "xr_pose.cc",
     "xr_pose.h",
     "xr_ray.cc",
diff --git a/third_party/blink/renderer/modules/xr/type_converters.cc b/third_party/blink/renderer/modules/xr/type_converters.cc
index 90f6523b..aaf5e39 100644
--- a/third_party/blink/renderer/modules/xr/type_converters.cc
+++ b/third_party/blink/renderer/modules/xr/type_converters.cc
@@ -35,18 +35,19 @@
 
   if (pose->orientation) {
     // TODO(https://crbug.com/929841): Remove negation once the bug is fixed.
-    decomp.quaternion_x = -pose->orientation.value()[0];
-    decomp.quaternion_y = -pose->orientation.value()[1];
-    decomp.quaternion_z = -pose->orientation.value()[2];
-    decomp.quaternion_w = pose->orientation.value()[3];
+    gfx::Quaternion quat = pose->orientation->inverse();
+    decomp.quaternion_x = quat.x();
+    decomp.quaternion_y = quat.y();
+    decomp.quaternion_z = quat.z();
+    decomp.quaternion_w = quat.w();
   } else {
     decomp.quaternion_w = 1.0;
   }
 
   if (pose->position) {
-    decomp.translate_x = pose->position.value()[0];
-    decomp.translate_y = pose->position.value()[1];
-    decomp.translate_z = pose->position.value()[2];
+    decomp.translate_x = pose->position->x;
+    decomp.translate_y = pose->position->y;
+    decomp.translate_z = pose->position->z;
   }
 
   result.Recompose(decomp);
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index cb1e5d2..c8ab1a6 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -551,9 +551,9 @@
   if (environment_integration)
     blend_mode = XRSession::kBlendModeAlphaBlend;
 
-  XRSession* session = CreateSession(query->mode(), blend_mode,
-                                     std::move(session_ptr->client_request),
-                                     std::move(session_ptr->display_info));
+  XRSession* session = CreateSession(
+      query->mode(), blend_mode, std::move(session_ptr->client_request),
+      std::move(session_ptr->display_info), session_ptr->uses_input_eventing);
 
   if (query->mode() == XRSession::kModeImmersiveVR ||
       query->mode() == XRSession::kModeImmersiveAR) {
@@ -568,6 +568,12 @@
       environment_provider_.set_connection_error_handler(WTF::Bind(
           &XR::OnEnvironmentProviderDisconnect, WrapWeakPersistent(this)));
     }
+
+    if (query->mode() == XRSession::kModeImmersiveVR &&
+        session->UsesInputEventing()) {
+      frameProvider()->GetDataProvider()->SetInputSourceButtonListener(
+          session->GetInputClickListener());
+    }
   } else {
     magic_window_provider_.Bind(std::move(session_ptr->data_provider));
     magic_window_provider_.set_connection_error_handler(WTF::Bind(
@@ -625,10 +631,11 @@
     XRSession::EnvironmentBlendMode blend_mode,
     device::mojom::blink::XRSessionClientRequest client_request,
     device::mojom::blink::VRDisplayInfoPtr display_info,
+    bool uses_input_eventing,
     bool sensorless_session) {
   XRSession* session = MakeGarbageCollected<XRSession>(
       this, client_request ? std::move(client_request) : nullptr, mode,
-      blend_mode, sensorless_session);
+      blend_mode, uses_input_eventing, sensorless_session);
   if (display_info)
     session->SetXRDisplayInfo(std::move(display_info));
   sessions_.insert(session);
@@ -640,6 +647,7 @@
   XRSession::EnvironmentBlendMode blend_mode = XRSession::kBlendModeOpaque;
   return CreateSession(XRSession::kModeInline, blend_mode,
                        nullptr /* client request */, nullptr /* display_info */,
+                       false /* uses_input_eventing */,
                        true /* sensorless_session */);
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr.h b/third_party/blink/renderer/modules/xr/xr.h
index 0f16c363..03a200a 100644
--- a/third_party/blink/renderer/modules/xr/xr.h
+++ b/third_party/blink/renderer/modules/xr/xr.h
@@ -173,6 +173,7 @@
       XRSession::EnvironmentBlendMode blend_mode,
       device::mojom::blink::XRSessionClientRequest client_request,
       device::mojom::blink::VRDisplayInfoPtr display_info,
+      bool uses_input_eventing,
       bool sensorless_session = false);
   XRSession* CreateSensorlessInlineSession();
 
diff --git a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
index ac00f79..af3a800 100644
--- a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.cc
@@ -41,8 +41,7 @@
   if (display_info && display_info->stageParameters) {
     // Use the transform given by xrDisplayInfo's stageParameters if available.
     floor_level_transform_ = std::make_unique<TransformationMatrix>(
-        WTFFloatVectorToTransformationMatrix(
-            display_info->stageParameters->standingTransform));
+        display_info->stageParameters->standingTransform.matrix());
 
     // In order to ensure that the bounds continue to line up with the user's
     // physical environment we need to transform by the inverse of the
@@ -54,7 +53,7 @@
 
       for (const auto& bound : *(display_info->stageParameters->bounds)) {
         FloatPoint3D p =
-            bounds_transform.MapPoint(FloatPoint3D(bound->x, 0.0, bound->z));
+            bounds_transform.MapPoint(FloatPoint3D(bound.x, 0.0, bound.z));
         bounds_geometry_.push_back(
             DOMPointReadOnly::Create(p.X(), p.Y(), p.Z(), 1.0));
       }
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index 52d8810..ba5aea1 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
+#include "third_party/blink/renderer/modules/xr/type_converters.h"
 #include "third_party/blink/renderer/modules/xr/xr.h"
 #include "third_party/blink/renderer/modules/xr/xr_plane_detection_state.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
@@ -53,35 +54,9 @@
   if (!pose)
     return nullptr;
 
-  std::unique_ptr<TransformationMatrix> pose_matrix =
-      std::make_unique<TransformationMatrix>();
-
-  TransformationMatrix::DecomposedType decomp;
-
-  memset(&decomp, 0, sizeof(decomp));
-  decomp.perspective_w = 1;
-  decomp.scale_x = 1;
-  decomp.scale_y = 1;
-  decomp.scale_z = 1;
-
-  if (pose->orientation) {
-    decomp.quaternion_x = -pose->orientation.value()[0];
-    decomp.quaternion_y = -pose->orientation.value()[1];
-    decomp.quaternion_z = -pose->orientation.value()[2];
-    decomp.quaternion_w = pose->orientation.value()[3];
-  } else {
-    decomp.quaternion_w = 1.0;
-  }
-
-  if (pose->position) {
-    decomp.translate_x = pose->position.value()[0];
-    decomp.translate_y = pose->position.value()[1];
-    decomp.translate_z = pose->position.value()[2];
-  }
-
-  pose_matrix->Recompose(decomp);
-
-  return pose_matrix;
+  return std::make_unique<TransformationMatrix>(
+      mojo::TypeConverter<TransformationMatrix,
+                          device::mojom::blink::VRPosePtr>::Convert(pose));
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/modules/xr/xr_input_source.cc b/third_party/blink/renderer/modules/xr/xr_input_source.cc
index abdccb6..f9292226 100644
--- a/third_party/blink/renderer/modules/xr/xr_input_source.cc
+++ b/third_party/blink/renderer/modules/xr/xr_input_source.cc
@@ -16,10 +16,9 @@
 
 namespace {
 std::unique_ptr<TransformationMatrix> TryGetTransformationMatrix(
-    const gfx::mojom::blink::TransformPtr& transform) {
-  if (transform && transform->matrix.has_value()) {
-    return std::make_unique<TransformationMatrix>(
-        WTFFloatVectorToTransformationMatrix(transform->matrix.value()));
+    const base::Optional<gfx::Transform>& transform) {
+  if (transform) {
+    return std::make_unique<TransformationMatrix>(transform->matrix());
   }
 
   return nullptr;
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.cc b/third_party/blink/renderer/modules/xr/xr_plane.cc
index 06ad1bb..a587a5ce 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.cc
+++ b/third_party/blink/renderer/modules/xr/xr_plane.cc
@@ -3,9 +3,11 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/modules/xr/xr_plane.h"
+
 #include "third_party/blink/renderer/modules/xr/type_converters.h"
-#include "third_party/blink/renderer/modules/xr/xr_pose.h"
+#include "third_party/blink/renderer/modules/xr/xr_plane_space.h"
 #include "third_party/blink/renderer/modules/xr/xr_reference_space.h"
+#include "third_party/blink/renderer/modules/xr/xr_rigid_transform.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 
 namespace blink {
@@ -34,11 +36,16 @@
   DVLOG(3) << __func__;
 }
 
-XRPose* XRPlane::getPose(XRReferenceSpace* reference_space) const {
-  std::unique_ptr<TransformationMatrix> viewer_pose =
-      reference_space->GetViewerPoseMatrix(pose_matrix_.get());
-  return MakeGarbageCollected<XRPose>(*viewer_pose,
-                                      session_->EmulatedPosition());
+XRSpace* XRPlane::planeSpace() const {
+  if (!plane_space_) {
+    plane_space_ = MakeGarbageCollected<XRPlaneSpace>(session_, this);
+  }
+
+  return plane_space_;
+}
+
+TransformationMatrix XRPlane::poseMatrix() const {
+  return *pose_matrix_;
 }
 
 String XRPlane::orientation() const {
@@ -82,6 +89,7 @@
 void XRPlane::Trace(blink::Visitor* visitor) {
   visitor->Trace(polygon_);
   visitor->Trace(session_);
+  visitor->Trace(plane_space_);
   ScriptWrappable::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.h b/third_party/blink/renderer/modules/xr/xr_plane.h
index 69e98588..913ceb4 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.h
+++ b/third_party/blink/renderer/modules/xr/xr_plane.h
@@ -15,9 +15,8 @@
 
 namespace blink {
 
-class XRPose;
 class XRSession;
-class XRReferenceSpace;
+class XRSpace;
 
 class XRPlane : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
@@ -34,8 +33,9 @@
           const HeapVector<Member<DOMPointReadOnly>>& polygon,
           double timestamp);
 
-  // Returns a pose expressed in passed in reference space.
-  XRPose* getPose(XRReferenceSpace* reference_space) const;
+  XRSpace* planeSpace() const;
+
+  TransformationMatrix poseMatrix() const;
 
   String orientation() const;
   HeapVector<Member<DOMPointReadOnly>> polygon() const;
@@ -59,6 +59,9 @@
   Member<XRSession> session_;
 
   double last_changed_time_;
+
+  // Cached plane space - it will be created by `planeSpace()` if it's not set.
+  mutable Member<XRSpace> plane_space_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.idl b/third_party/blink/renderer/modules/xr/xr_plane.idl
index 50c0586..3db6a890b 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.idl
+++ b/third_party/blink/renderer/modules/xr/xr_plane.idl
@@ -15,7 +15,7 @@
     RuntimeEnabled=WebXRPlaneDetection
 ]
 interface XRPlane {
-    XRPose getPose(XRReferenceSpace referenceSpace);
+    readonly attribute XRSpace planeSpace;
     readonly attribute FrozenArray<DOMPointReadOnly> polygon;
     readonly attribute XRPlaneOrientation? orientation;
     readonly attribute DOMHighResTimeStamp lastChangedTime;
diff --git a/third_party/blink/renderer/modules/xr/xr_plane_space.cc b/third_party/blink/renderer/modules/xr/xr_plane_space.cc
new file mode 100644
index 0000000..d58edc31
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_plane_space.cc
@@ -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.
+
+#include "third_party/blink/renderer/modules/xr/xr_plane_space.h"
+
+#include "third_party/blink/renderer/modules/xr/xr_plane.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+namespace blink {
+
+XRPlaneSpace::XRPlaneSpace(XRSession* session, const XRPlane* plane)
+    : XRSpace(session), plane_(plane) {}
+
+std::unique_ptr<TransformationMatrix> XRPlaneSpace::GetTransformToMojoSpace() {
+  auto mojo_to_plane = plane_->poseMatrix();
+
+  if (!mojo_to_plane.IsInvertible()) {
+    return nullptr;
+  }
+
+  return std::make_unique<TransformationMatrix>(mojo_to_plane.Inverse());
+}
+
+void XRPlaneSpace::Trace(blink::Visitor* visitor) {
+  visitor->Trace(plane_);
+  XRSpace::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_plane_space.h b/third_party/blink/renderer/modules/xr/xr_plane_space.h
new file mode 100644
index 0000000..6028316
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_plane_space.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PLANE_SPACE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PLANE_SPACE_H_
+
+#include "third_party/blink/renderer/modules/xr/xr_space.h"
+
+namespace blink {
+
+class TransformationMatrix;
+class XRPlane;
+class XRSession;
+
+class XRPlaneSpace : public XRSpace {
+ public:
+  explicit XRPlaneSpace(XRSession* session, const XRPlane* plane);
+
+  std::unique_ptr<TransformationMatrix> GetTransformToMojoSpace() override;
+
+  void Trace(blink::Visitor* visitor) override;
+
+ private:
+  Member<const XRPlane> plane_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PLANE_SPACE_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
index 10fba2c..f509fb63 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
@@ -69,8 +69,7 @@
   if (display_info && display_info->stageParameters) {
     // Use the transform given by xrDisplayInfo's stageParameters if available.
     floor_level_transform_ = std::make_unique<TransformationMatrix>(
-        WTFFloatVectorToTransformationMatrix(
-            display_info->stageParameters->standingTransform));
+        display_info->stageParameters->standingTransform.matrix());
   } else {
     // Otherwise, create a transform based on the default emulated height.
     floor_level_transform_ = std::make_unique<TransformationMatrix>();
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index 2051c835..558c244 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -82,7 +82,7 @@
       fov->leftDegrees * kDegToRad, fov->rightDegrees * kDegToRad, depth_near,
       depth_far);
 
-  view.UpdateOffset(eye->offset[0], eye->offset[1], eye->offset[2]);
+  view.UpdateOffset(eye->offset.x(), eye->offset.y(), eye->offset.z());
 }
 
 }  // namespace
@@ -116,6 +116,7 @@
     device::mojom::blink::XRSessionClientRequest client_request,
     XRSession::SessionMode mode,
     EnvironmentBlendMode environment_blend_mode,
+    bool uses_input_eventing,
     bool sensorless_session)
     : xr_(xr),
       mode_(mode),
@@ -124,9 +125,11 @@
       world_information_(MakeGarbageCollected<XRWorldInformation>(this)),
       input_sources_(MakeGarbageCollected<XRInputSourceArray>()),
       client_binding_(this, std::move(client_request)),
+      input_binding_(this),
       callback_collection_(
           MakeGarbageCollected<XRFrameRequestCallbackCollection>(
               xr_->GetExecutionContext())),
+      uses_input_eventing_(uses_input_eventing),
       sensorless_session_(sensorless_session) {
   render_state_ = MakeGarbageCollected<XRRenderState>(immersive());
   blurred_ = !HasAppropriateFocus();
@@ -159,6 +162,15 @@
   return event_target_names::kXRSession;
 }
 
+device::mojom::blink::XRInputSourceButtonListenerAssociatedPtrInfo
+XRSession::GetInputClickListener() {
+  DCHECK(!input_binding_);
+  device::mojom::blink::XRInputSourceButtonListenerAssociatedPtrInfo
+      input_listener;
+  input_binding_.Bind(MakeRequest(&input_listener));
+  return input_listener;
+}
+
 void XRSession::updateRenderState(XRRenderStateInit* init,
                                   ExceptionState& exception_state) {
   if (ended_) {
@@ -372,15 +384,11 @@
 
   device::mojom::blink::XRRayPtr ray_mojo = device::mojom::blink::XRRay::New();
 
-  ray_mojo->origin = gfx::mojom::blink::Point3F::New();
-  ray_mojo->origin->x = ray->origin()->x();
-  ray_mojo->origin->y = ray->origin()->y();
-  ray_mojo->origin->z = ray->origin()->z();
+  ray_mojo->origin = WebFloatPoint3D(ray->origin()->x(), ray->origin()->y(),
+                                     ray->origin()->z());
 
-  ray_mojo->direction = gfx::mojom::blink::Vector3dF::New();
-  ray_mojo->direction->x = ray->direction()->x();
-  ray_mojo->direction->y = ray->direction()->y();
-  ray_mojo->direction->z = ray->direction()->z();
+  ray_mojo->direction = {ray->direction()->x(), ray->direction()->y(),
+                         ray->direction()->z()};
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
@@ -408,16 +416,8 @@
 
   HeapVector<Member<XRHitResult>> hit_results;
   for (const auto& mojom_result : results.value()) {
-    XRHitResult* hit_result =
-        MakeGarbageCollected<XRHitResult>(TransformationMatrix(
-            mojom_result->hit_matrix[0], mojom_result->hit_matrix[1],
-            mojom_result->hit_matrix[2], mojom_result->hit_matrix[3],
-            mojom_result->hit_matrix[4], mojom_result->hit_matrix[5],
-            mojom_result->hit_matrix[6], mojom_result->hit_matrix[7],
-            mojom_result->hit_matrix[8], mojom_result->hit_matrix[9],
-            mojom_result->hit_matrix[10], mojom_result->hit_matrix[11],
-            mojom_result->hit_matrix[12], mojom_result->hit_matrix[13],
-            mojom_result->hit_matrix[14], mojom_result->hit_matrix[15]));
+    XRHitResult* hit_result = MakeGarbageCollected<XRHitResult>(
+        TransformationMatrix(mojom_result->hit_matrix.matrix()));
     hit_results.push_back(hit_result);
   }
   resolver->Resolve(hit_results);
@@ -736,12 +736,27 @@
   }
 }
 
+void XRSession::OnButtonEvent(
+    device::mojom::blink::XRInputSourceStatePtr input_state) {
+  DCHECK(uses_input_eventing_);
+  OnInputStateChangeInternal(last_frame_id_, base::make_span(&input_state, 1),
+                             true /* from_eventing */);
+}
+
 void XRSession::OnInputStateChange(
     int16_t frame_id,
-    const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>&
+    base::span<const device::mojom::blink::XRInputSourceStatePtr>
         input_states) {
+  OnInputStateChangeInternal(frame_id, input_states, false /* from_eventing */);
+}
+
+void XRSession::OnInputStateChangeInternal(
+    int16_t frame_id,
+    base::span<const device::mojom::blink::XRInputSourceStatePtr> input_states,
+    bool from_eventing) {
   HeapVector<Member<XRInputSource>> added;
   HeapVector<Member<XRInputSource>> removed;
+  last_frame_id_ = frame_id;
 
   // Build up our added array, and update the frame id of any active input
   // sources so we can flag the ones that are no longer active.
@@ -802,6 +817,12 @@
     if (ended_)
       break;
 
+    // If this data is not from eventing and we support it, ignore it's click
+    // states.
+    // If this data is from eventing, but we don't support it, then ignore it
+    if (from_eventing != uses_input_eventing_)
+      continue;
+
     XRInputSource* input_source =
         input_sources_->GetWithSourceId(input_state->source_id);
     DCHECK(input_source);
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index 1e84876..7a183bd1 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -5,7 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SESSION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SESSION_H_
 
+#include "base/containers/span.h"
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
@@ -42,9 +44,11 @@
 class XRWorldTrackingState;
 class XRWorldTrackingStateInit;
 
-class XRSession final : public EventTargetWithInlineData,
-                        public device::mojom::blink::XRSessionClient,
-                        public ActiveScriptWrappable<XRSession> {
+class XRSession final
+    : public EventTargetWithInlineData,
+      public device::mojom::blink::XRSessionClient,
+      public device::mojom::blink::XRInputSourceButtonListener,
+      public ActiveScriptWrappable<XRSession> {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(XRSession);
 
@@ -61,6 +65,7 @@
             device::mojom::blink::XRSessionClientRequest client_request,
             SessionMode mode,
             EnvironmentBlendMode environment_blend_mode,
+            bool uses_input_eventing,
             bool sensorless_session);
   ~XRSession() override = default;
 
@@ -132,7 +137,11 @@
 
   void OnInputStateChange(
       int16_t frame_id,
-      const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>&);
+      base::span<const device::mojom::blink::XRInputSourceStatePtr>);
+
+  // XRInputSourceButtonListener
+  void OnButtonEvent(
+      device::mojom::blink::XRInputSourceStatePtr input_source) override;
 
   WTF::Vector<XRViewData>& views();
 
@@ -149,6 +158,9 @@
     return display_info_;
   }
 
+  device::mojom::blink::XRInputSourceButtonListenerAssociatedPtrInfo
+  GetInputClickListener();
+
   // TODO(crbug.com/969131): Update the mojom to deliver this per-frame.
   bool EmulatedPosition() const {
     if (display_info_) {
@@ -178,6 +190,8 @@
 
   void SetXRDisplayInfo(device::mojom::blink::VRDisplayInfoPtr display_info);
 
+  bool UsesInputEventing() { return uses_input_eventing_; }
+
   void Trace(blink::Visitor*) override;
 
   // ScriptWrappable
@@ -196,6 +210,11 @@
   XRInputSourceEvent* CreateInputSourceEvent(const AtomicString&,
                                              XRInputSource*);
 
+  void OnInputStateChangeInternal(
+      int16_t frame_id,
+      base::span<const device::mojom::blink::XRInputSourceStatePtr>,
+      bool from_eventing);
+
   // XRSessionClient
   void OnChanged(device::mojom::blink::VRDisplayInfoPtr) override;
   void OnExitPresent() override;
@@ -237,6 +256,8 @@
   device::mojom::blink::VRDisplayInfoPtr display_info_;
 
   mojo::Binding<device::mojom::blink::XRSessionClient> client_binding_;
+  mojo::AssociatedBinding<device::mojom::blink::XRInputSourceButtonListener>
+      input_binding_;
 
   Member<XRFrameRequestCallbackCollection> callback_collection_;
   std::unique_ptr<TransformationMatrix> base_pose_matrix_;
@@ -257,9 +278,13 @@
   int output_width_ = 1;
   int output_height_ = 1;
 
+  bool uses_input_eventing_ = false;
+
   // Indicates that this is a sensorless session which should only support the
   // identity reference space.
   bool sensorless_session_ = false;
+
+  int16_t last_frame_id_ = -1;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/animation/timing_function.cc b/third_party/blink/renderer/platform/animation/timing_function.cc
index 37fceb5..2ea4d36 100644
--- a/third_party/blink/renderer/platform/animation/timing_function.cc
+++ b/third_party/blink/renderer/platform/animation/timing_function.cc
@@ -12,7 +12,7 @@
   return "linear";
 }
 
-double LinearTimingFunction::Evaluate(double fraction, double) const {
+double LinearTimingFunction::Evaluate(double fraction) const {
   return fraction;
 }
 
@@ -73,9 +73,8 @@
   }
 }
 
-double CubicBezierTimingFunction::Evaluate(double fraction,
-                                           double accuracy) const {
-  return bezier_->bezier().SolveWithEpsilon(fraction, accuracy);
+double CubicBezierTimingFunction::Evaluate(double fraction) const {
+  return bezier_->bezier().Solve(fraction);
 }
 
 void CubicBezierTimingFunction::Range(double* min_value,
@@ -145,13 +144,12 @@
 }
 
 double StepsTimingFunction::Evaluate(double fraction,
-                                     LimitDirection limit_direction,
-                                     double) const {
+                                     LimitDirection limit_direction) const {
   return steps_->GetPreciseValue(fraction, limit_direction);
 }
 
-double StepsTimingFunction::Evaluate(double fraction, double) const {
-  NOTREACHED() << "Use Evaluate(fraction, limit_direction, ...) instead.";
+double StepsTimingFunction::Evaluate(double fraction) const {
+  NOTREACHED() << "Use Evaluate(fraction, limit_direction) instead.";
   return steps_->GetPreciseValue(fraction, LimitDirection::RIGHT);
 }
 
diff --git a/third_party/blink/renderer/platform/animation/timing_function.h b/third_party/blink/renderer/platform/animation/timing_function.h
index ca77aaf..f7a9eae 100644
--- a/third_party/blink/renderer/platform/animation/timing_function.h
+++ b/third_party/blink/renderer/platform/animation/timing_function.h
@@ -50,18 +50,14 @@
 
   // Evaluates the timing function at the given fraction. The limit direction
   // applies when evaluating a function at a discontinuous boundary and
-  // indicates if the left or right limit should be applied. The accuracy
-  // parameter provides a hint as to the required accuracy and is not
-  // guaranteed.
+  // indicates if the left or right limit should be applied.
   virtual double Evaluate(double fraction,
-                          LimitDirection limit_direction,
-                          double accuracy) const {
-    return Evaluate(fraction, accuracy);
+                          LimitDirection limit_direction) const {
+    return Evaluate(fraction);
   }
 
-  // Evaluates the timing function at the given fraction. The accuracy parameter
-  // provides a hint as to the required accuracy and is not guaranteed.
-  virtual double Evaluate(double fraction, double accuracy) const = 0;
+  // Evaluates the timing function at the given fraction.
+  virtual double Evaluate(double fraction) const = 0;
 
   // This function returns the minimum and maximum values obtainable when
   // calling evaluate();
@@ -89,7 +85,7 @@
 
   // TimingFunction implementation.
   String ToString() const override;
-  double Evaluate(double fraction, double) const override;
+  double Evaluate(double fraction) const override;
   void Range(double* min_value, double* max_value) const override;
   std::unique_ptr<cc::TimingFunction> CloneToCC() const override;
 
@@ -114,7 +110,7 @@
 
   // TimingFunction implementation.
   String ToString() const override;
-  double Evaluate(double fraction, double accuracy) const override;
+  double Evaluate(double fraction) const override;
   void Range(double* min_value, double* max_value) const override;
   std::unique_ptr<cc::TimingFunction> CloneToCC() const override;
 
@@ -191,9 +187,8 @@
   // TimingFunction implementation.
   String ToString() const override;
   double Evaluate(double fraction,
-                  LimitDirection limit_direction,
-                  double) const override;
-  double Evaluate(double fraction, double) const override;
+                  LimitDirection limit_direction) const override;
+  double Evaluate(double fraction) const override;
 
   void Range(double* min_value, double* max_value) const override;
   std::unique_ptr<cc::TimingFunction> CloneToCC() const override;
diff --git a/third_party/blink/renderer/platform/animation/timing_function_test.cc b/third_party/blink/renderer/platform/animation/timing_function_test.cc
index 253f415..405ceda 100644
--- a/third_party/blink/renderer/platform/animation/timing_function_test.cc
+++ b/third_party/blink/renderer/platform/animation/timing_function_test.cc
@@ -246,10 +246,10 @@
 
 TEST_F(TimingFunctionTest, LinearEvaluate) {
   scoped_refptr<TimingFunction> linear_timing = LinearTimingFunction::Shared();
-  EXPECT_EQ(0.2, linear_timing->Evaluate(0.2, 0));
-  EXPECT_EQ(0.6, linear_timing->Evaluate(0.6, 0));
-  EXPECT_EQ(-0.2, linear_timing->Evaluate(-0.2, 0));
-  EXPECT_EQ(1.6, linear_timing->Evaluate(1.6, 0));
+  EXPECT_EQ(0.2, linear_timing->Evaluate(0.2));
+  EXPECT_EQ(0.6, linear_timing->Evaluate(0.6));
+  EXPECT_EQ(-0.2, linear_timing->Evaluate(-0.2));
+  EXPECT_EQ(1.6, linear_timing->Evaluate(1.6));
 }
 
 TEST_F(TimingFunctionTest, LinearRange) {
@@ -362,47 +362,36 @@
   scoped_refptr<TimingFunction> cubic_ease_timing =
       CubicBezierTimingFunction::Preset(
           CubicBezierTimingFunction::EaseType::EASE);
-  EXPECT_NEAR(0.409, cubic_ease_timing->Evaluate(0.25, tolerance), tolerance);
-  EXPECT_NEAR(0.802, cubic_ease_timing->Evaluate(0.50, tolerance), tolerance);
-  EXPECT_NEAR(0.960, cubic_ease_timing->Evaluate(0.75, tolerance), tolerance);
+  EXPECT_NEAR(0.409, cubic_ease_timing->Evaluate(0.25), tolerance);
+  EXPECT_NEAR(0.802, cubic_ease_timing->Evaluate(0.50), tolerance);
+  EXPECT_NEAR(0.960, cubic_ease_timing->Evaluate(0.75), tolerance);
 
   scoped_refptr<TimingFunction> cubic_ease_in_timing =
       CubicBezierTimingFunction::Preset(
           CubicBezierTimingFunction::EaseType::EASE_IN);
-  EXPECT_NEAR(0.093, cubic_ease_in_timing->Evaluate(0.25, tolerance),
-              tolerance);
-  EXPECT_NEAR(0.315, cubic_ease_in_timing->Evaluate(0.50, tolerance),
-              tolerance);
-  EXPECT_NEAR(0.622, cubic_ease_in_timing->Evaluate(0.75, tolerance),
-              tolerance);
+  EXPECT_NEAR(0.093, cubic_ease_in_timing->Evaluate(0.25), tolerance);
+  EXPECT_NEAR(0.315, cubic_ease_in_timing->Evaluate(0.50), tolerance);
+  EXPECT_NEAR(0.622, cubic_ease_in_timing->Evaluate(0.75), tolerance);
 
   scoped_refptr<TimingFunction> cubic_ease_out_timing =
       CubicBezierTimingFunction::Preset(
           CubicBezierTimingFunction::EaseType::EASE_OUT);
-  EXPECT_NEAR(0.378, cubic_ease_out_timing->Evaluate(0.25, tolerance),
-              tolerance);
-  EXPECT_NEAR(0.685, cubic_ease_out_timing->Evaluate(0.50, tolerance),
-              tolerance);
-  EXPECT_NEAR(0.907, cubic_ease_out_timing->Evaluate(0.75, tolerance),
-              tolerance);
+  EXPECT_NEAR(0.378, cubic_ease_out_timing->Evaluate(0.25), tolerance);
+  EXPECT_NEAR(0.685, cubic_ease_out_timing->Evaluate(0.50), tolerance);
+  EXPECT_NEAR(0.907, cubic_ease_out_timing->Evaluate(0.75), tolerance);
 
   scoped_refptr<TimingFunction> cubic_ease_in_out_timing =
       CubicBezierTimingFunction::Preset(
           CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
-  EXPECT_NEAR(0.129, cubic_ease_in_out_timing->Evaluate(0.25, tolerance),
-              tolerance);
-  EXPECT_NEAR(0.500, cubic_ease_in_out_timing->Evaluate(0.50, tolerance),
-              tolerance);
-  EXPECT_NEAR(0.871, cubic_ease_in_out_timing->Evaluate(0.75, tolerance),
-              tolerance);
+  EXPECT_NEAR(0.129, cubic_ease_in_out_timing->Evaluate(0.25), tolerance);
+  EXPECT_NEAR(0.500, cubic_ease_in_out_timing->Evaluate(0.50), tolerance);
+  EXPECT_NEAR(0.871, cubic_ease_in_out_timing->Evaluate(0.75), tolerance);
 
   scoped_refptr<TimingFunction> cubic_custom_timing =
       CubicBezierTimingFunction::Create(0.17, 0.67, 1, -1.73);
-  EXPECT_NEAR(0.034, cubic_custom_timing->Evaluate(0.25, tolerance), tolerance);
-  EXPECT_NEAR(-0.217, cubic_custom_timing->Evaluate(0.50, tolerance),
-              tolerance);
-  EXPECT_NEAR(-0.335, cubic_custom_timing->Evaluate(0.75, tolerance),
-              tolerance);
+  EXPECT_NEAR(0.034, cubic_custom_timing->Evaluate(0.25), tolerance);
+  EXPECT_NEAR(-0.217, cubic_custom_timing->Evaluate(0.50), tolerance);
+  EXPECT_NEAR(-0.335, cubic_custom_timing->Evaluate(0.75), tolerance);
 }
 
 TEST_F(TimingFunctionTest, StepsEvaluate) {
@@ -411,67 +400,67 @@
 
   scoped_refptr<TimingFunction> steps_timing_start =
       StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::START);
-  EXPECT_EQ(-1.00, steps_timing_start->Evaluate(-1.10, right, 0));
-  EXPECT_EQ(0.00, steps_timing_start->Evaluate(-0.10, right, 0));
-  EXPECT_EQ(0.00, steps_timing_start->Evaluate(0.00, left, 0));
-  EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.00, right, 0));
-  EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.20, right, 0));
-  EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.60, right, 0));
-  EXPECT_EQ(1.00, steps_timing_start->Evaluate(1.00, left, 0));
-  EXPECT_EQ(1.00, steps_timing_start->Evaluate(1.00, right, 0));
-  EXPECT_EQ(2.00, steps_timing_start->Evaluate(2.00, left, 0));
-  EXPECT_EQ(3.00, steps_timing_start->Evaluate(2.00, right, 0));
+  EXPECT_EQ(-1.00, steps_timing_start->Evaluate(-1.10, right));
+  EXPECT_EQ(0.00, steps_timing_start->Evaluate(-0.10, right));
+  EXPECT_EQ(0.00, steps_timing_start->Evaluate(0.00, left));
+  EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.00, right));
+  EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.20, right));
+  EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.60, right));
+  EXPECT_EQ(1.00, steps_timing_start->Evaluate(1.00, left));
+  EXPECT_EQ(1.00, steps_timing_start->Evaluate(1.00, right));
+  EXPECT_EQ(2.00, steps_timing_start->Evaluate(2.00, left));
+  EXPECT_EQ(3.00, steps_timing_start->Evaluate(2.00, right));
 
   scoped_refptr<TimingFunction> steps_timing_end =
       StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::END);
-  EXPECT_EQ(-2.00, steps_timing_end->Evaluate(-2.00, right, 0));
-  EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.00, left, 0));
-  EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.00, right, 0));
-  EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.20, right, 0));
-  EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.60, right, 0));
-  EXPECT_EQ(0.00, steps_timing_end->Evaluate(1.00, left, 0));
-  EXPECT_EQ(1.00, steps_timing_end->Evaluate(1.00, right, 0));
-  EXPECT_EQ(2.00, steps_timing_end->Evaluate(2.00, right, 0));
+  EXPECT_EQ(-2.00, steps_timing_end->Evaluate(-2.00, right));
+  EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.00, left));
+  EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.00, right));
+  EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.20, right));
+  EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.60, right));
+  EXPECT_EQ(0.00, steps_timing_end->Evaluate(1.00, left));
+  EXPECT_EQ(1.00, steps_timing_end->Evaluate(1.00, right));
+  EXPECT_EQ(2.00, steps_timing_end->Evaluate(2.00, right));
 
   scoped_refptr<TimingFunction> steps_timing_custom_start =
       StepsTimingFunction::Create(4, StepsTimingFunction::StepPosition::START);
-  EXPECT_EQ(-0.50, steps_timing_custom_start->Evaluate(-0.50, left, 0));
-  EXPECT_EQ(-0.25, steps_timing_custom_start->Evaluate(-0.50, right, 0));
-  EXPECT_EQ(0.00, steps_timing_custom_start->Evaluate(0.00, left, 0));
-  EXPECT_EQ(0.25, steps_timing_custom_start->Evaluate(0.00, right, 0));
-  EXPECT_EQ(0.25, steps_timing_custom_start->Evaluate(0.24, right, 0));
-  EXPECT_EQ(0.25, steps_timing_custom_start->Evaluate(0.25, left, 0));
-  EXPECT_EQ(0.50, steps_timing_custom_start->Evaluate(0.25, right, 0));
-  EXPECT_EQ(0.50, steps_timing_custom_start->Evaluate(0.49, right, 0));
-  EXPECT_EQ(0.50, steps_timing_custom_start->Evaluate(0.50, left, 0));
-  EXPECT_EQ(0.75, steps_timing_custom_start->Evaluate(0.50, right, 0));
-  EXPECT_EQ(0.75, steps_timing_custom_start->Evaluate(0.74, right, 0));
-  EXPECT_EQ(0.75, steps_timing_custom_start->Evaluate(0.75, left, 0));
-  EXPECT_EQ(1.00, steps_timing_custom_start->Evaluate(0.75, right, 0));
-  EXPECT_EQ(1.00, steps_timing_custom_start->Evaluate(1.00, left, 0));
-  EXPECT_EQ(1.00, steps_timing_custom_start->Evaluate(1.00, right, 0));
-  EXPECT_EQ(1.75, steps_timing_custom_start->Evaluate(1.50, right, 0));
+  EXPECT_EQ(-0.50, steps_timing_custom_start->Evaluate(-0.50, left));
+  EXPECT_EQ(-0.25, steps_timing_custom_start->Evaluate(-0.50, right));
+  EXPECT_EQ(0.00, steps_timing_custom_start->Evaluate(0.00, left));
+  EXPECT_EQ(0.25, steps_timing_custom_start->Evaluate(0.00, right));
+  EXPECT_EQ(0.25, steps_timing_custom_start->Evaluate(0.24, right));
+  EXPECT_EQ(0.25, steps_timing_custom_start->Evaluate(0.25, left));
+  EXPECT_EQ(0.50, steps_timing_custom_start->Evaluate(0.25, right));
+  EXPECT_EQ(0.50, steps_timing_custom_start->Evaluate(0.49, right));
+  EXPECT_EQ(0.50, steps_timing_custom_start->Evaluate(0.50, left));
+  EXPECT_EQ(0.75, steps_timing_custom_start->Evaluate(0.50, right));
+  EXPECT_EQ(0.75, steps_timing_custom_start->Evaluate(0.74, right));
+  EXPECT_EQ(0.75, steps_timing_custom_start->Evaluate(0.75, left));
+  EXPECT_EQ(1.00, steps_timing_custom_start->Evaluate(0.75, right));
+  EXPECT_EQ(1.00, steps_timing_custom_start->Evaluate(1.00, left));
+  EXPECT_EQ(1.00, steps_timing_custom_start->Evaluate(1.00, right));
+  EXPECT_EQ(1.75, steps_timing_custom_start->Evaluate(1.50, right));
 
   scoped_refptr<TimingFunction> steps_timing_custom_end =
       StepsTimingFunction::Create(4, StepsTimingFunction::StepPosition::END);
-  EXPECT_EQ(-2.25, steps_timing_custom_end->Evaluate(-2.00, left, 0));
-  EXPECT_EQ(-2.00, steps_timing_custom_end->Evaluate(-2.00, right, 0));
-  EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.00, left, 0));
-  EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.00, right, 0));
-  EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.24, right, 0));
-  EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.25, left, 0));
-  EXPECT_EQ(0.25, steps_timing_custom_end->Evaluate(0.25, right, 0));
-  EXPECT_EQ(0.25, steps_timing_custom_end->Evaluate(0.49, right, 0));
-  EXPECT_EQ(0.25, steps_timing_custom_end->Evaluate(0.50, left, 0));
-  EXPECT_EQ(0.50, steps_timing_custom_end->Evaluate(0.50, right, 0));
-  EXPECT_EQ(0.50, steps_timing_custom_end->Evaluate(0.74, right, 0));
-  EXPECT_EQ(0.50, steps_timing_custom_end->Evaluate(0.75, left, 0));
-  EXPECT_EQ(0.75, steps_timing_custom_end->Evaluate(0.75, right, 0));
-  EXPECT_EQ(0.75, steps_timing_custom_end->Evaluate(0.99, right, 0));
-  EXPECT_EQ(0.75, steps_timing_custom_end->Evaluate(1.00, left, 0));
-  EXPECT_EQ(1.00, steps_timing_custom_end->Evaluate(1.00, right, 0));
-  EXPECT_EQ(1.75, steps_timing_custom_end->Evaluate(2.00, left, 0));
-  EXPECT_EQ(2.00, steps_timing_custom_end->Evaluate(2.00, right, 0));
+  EXPECT_EQ(-2.25, steps_timing_custom_end->Evaluate(-2.00, left));
+  EXPECT_EQ(-2.00, steps_timing_custom_end->Evaluate(-2.00, right));
+  EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.00, left));
+  EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.00, right));
+  EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.24, right));
+  EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.25, left));
+  EXPECT_EQ(0.25, steps_timing_custom_end->Evaluate(0.25, right));
+  EXPECT_EQ(0.25, steps_timing_custom_end->Evaluate(0.49, right));
+  EXPECT_EQ(0.25, steps_timing_custom_end->Evaluate(0.50, left));
+  EXPECT_EQ(0.50, steps_timing_custom_end->Evaluate(0.50, right));
+  EXPECT_EQ(0.50, steps_timing_custom_end->Evaluate(0.74, right));
+  EXPECT_EQ(0.50, steps_timing_custom_end->Evaluate(0.75, left));
+  EXPECT_EQ(0.75, steps_timing_custom_end->Evaluate(0.75, right));
+  EXPECT_EQ(0.75, steps_timing_custom_end->Evaluate(0.99, right));
+  EXPECT_EQ(0.75, steps_timing_custom_end->Evaluate(1.00, left));
+  EXPECT_EQ(1.00, steps_timing_custom_end->Evaluate(1.00, right));
+  EXPECT_EQ(1.75, steps_timing_custom_end->Evaluate(2.00, left));
+  EXPECT_EQ(2.00, steps_timing_custom_end->Evaluate(2.00, right));
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
index 07f5bd50..f51fdf80 100644
--- a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
+++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
@@ -72,8 +72,4 @@
   return nullptr;
 }
 
-void V8PersistentCallbackInterfaceBase::Trace(blink::Visitor* visitor) {
-  visitor->Trace(callback_interface_);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.h b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
index ed76654..896a9fa 100644
--- a/third_party/blink/renderer/platform/bindings/callback_interface_base.h
+++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
@@ -12,8 +12,6 @@
 
 namespace blink {
 
-class V8PersistentCallbackInterfaceBase;
-
 // CallbackInterfaceBase is the common base class of all the callback interface
 // classes. Most importantly this class provides a way of type dispatching (e.g.
 // overload resolutions, SFINAE technique, etc.) so that it's possible to
@@ -105,71 +103,8 @@
   // converted to an IDL value.
   // https://heycam.github.io/webidl/#dfn-callback-context
   Member<ScriptState> incumbent_script_state_;
-
-  friend class V8PersistentCallbackInterfaceBase;
 };
 
-// CAUTION: THIS CLASS IS OBSOLETE AFTER THE UNIFIED HEAP AND WILL BE REMOVED.
-//
-// V8PersistentCallbackInterfaceBase retains the underlying v8::Object of a
-// CallbackInterfaceBase without wrapper-tracing. This class is necessary and
-// useful where wrapper-tracing is not suitable. Remember that, as a nature of
-// v8::Persistent, abuse of V8PersistentCallbackInterfaceBase would result in
-// memory leak, so the use of V8PersistentCallbackInterfaceBase should be
-// limited to those which are guaranteed to release the persistents in a finite
-// time period.
-class PLATFORM_EXPORT V8PersistentCallbackInterfaceBase
-    : public GarbageCollectedFinalized<V8PersistentCallbackInterfaceBase> {
- public:
-  virtual ~V8PersistentCallbackInterfaceBase() = default;
-
-  virtual void Trace(blink::Visitor*);
-
-  v8::Isolate* GetIsolate() { return callback_interface_->GetIsolate(); }
-
- protected:
-  explicit V8PersistentCallbackInterfaceBase(
-      CallbackInterfaceBase* callback_interface)
-      : callback_interface_(callback_interface) {}
-
-  template <typename V8CallbackInterface>
-  V8CallbackInterface* As() {
-    static_assert(
-        std::is_base_of<CallbackInterfaceBase, V8CallbackInterface>::value,
-        "V8CallbackInterface must be a subclass of CallbackInterfaceBase.");
-    return static_cast<V8CallbackInterface*>(callback_interface_.Get());
-  }
-
- private:
-  Member<CallbackInterfaceBase> callback_interface_;
-};
-
-// V8PersistentCallbackInterface<V8CallbackInterface> is a counter-part of
-// V8CallbackInterface. While V8CallbackInterface uses wrapper-tracing,
-// V8PersistentCallbackInterface<V8CallbackInterface> uses v8::Persistent to
-// make the underlying v8::Object alive.
-//
-// Since the signatures of the operations vary depending on the IDL definition,
-// the class definition is specialized and generated by the bindings code
-// generator.
-template <typename V8CallbackInterface>
-class V8PersistentCallbackInterface;
-
-// Converts the wrapper-tracing version of a callback interface to the
-// v8::Persistent version of it.
-template <typename V8CallbackInterface>
-inline V8PersistentCallbackInterface<V8CallbackInterface>*
-ToV8PersistentCallbackInterface(V8CallbackInterface* callback_interface) {
-  static_assert(
-      std::is_base_of<CallbackInterfaceBase, V8CallbackInterface>::value,
-      "V8CallbackInterface must be a subclass of CallbackInterfaceBase.");
-  return callback_interface
-             ? MakeGarbageCollected<
-                   V8PersistentCallbackInterface<V8CallbackInterface>>(
-                   callback_interface)
-             : nullptr;
-}
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_CALLBACK_INTERFACE_BASE_H_
diff --git a/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc b/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc
index ce7d6b7e..7a7378cb 100644
--- a/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc
@@ -44,10 +44,9 @@
     int64_t total_encoded_data_length,
     int64_t total_encoded_body_length,
     int64_t total_decoded_body_length) {
-  original_client->DidFinishLoading(
-      finish_time, total_encoded_data_length, total_encoded_body_length,
-      total_decoded_body_length, false,
-      std::vector<network::cors::PreflightTimingInfo>());
+  original_client->DidFinishLoading(finish_time, total_encoded_data_length,
+                                    total_encoded_body_length,
+                                    total_decoded_body_length, false, {});
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc
index 6053b3d3..aceed95 100644
--- a/third_party/blink/renderer/platform/exported/web_url_request.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -34,7 +34,7 @@
 
 #include "base/time/time.h"
 #include "net/base/load_flags.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_http_body.h"
 #include "third_party/blink/public/platform/web_http_header_visitor.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
diff --git a/third_party/blink/renderer/platform/exported/web_url_response.cc b/third_party/blink/renderer/platform/exported/web_url_response.cc
index d640461..1324bc8 100644
--- a/third_party/blink/renderer/platform/exported/web_url_response.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_response.cc
@@ -244,8 +244,7 @@
 }
 
 void WebURLResponse::SetSecurityStyle(WebSecurityStyle security_style) {
-  resource_response_->SetSecurityStyle(
-      static_cast<ResourceResponse::SecurityStyle>(security_style));
+  resource_response_->SetSecurityStyle(security_style);
 }
 
 void WebURLResponse::SetSecurityDetails(
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
index 80b40f8..465e525f 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
@@ -123,13 +123,13 @@
     num_locked_images_--;
   }
 
-  const std::vector<cc::DrawImage>& decoded_images() const {
+  const Vector<cc::DrawImage>& decoded_images() const {
     return decoded_images_;
   }
   int num_locked_images() const { return num_locked_images_; }
 
  private:
-  std::vector<cc::DrawImage> decoded_images_;
+  Vector<cc::DrawImage> decoded_images_;
   int num_locked_images_ = 0;
   bool budget_exceeded_ = false;
   bool disallow_cache_use_ = false;
@@ -1236,7 +1236,7 @@
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  color_params);
   gfx::ColorSpace expected_color_space = gfx::ColorSpace::CreateSRGB();
-  std::vector<cc::DrawImage> images = {
+  Vector<cc::DrawImage> images = {
       cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
                     SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
                     SkMatrix::I(), 0u, expected_color_space),
@@ -1259,7 +1259,7 @@
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  color_params);
-  std::vector<cc::DrawImage> images = {
+  Vector<cc::DrawImage> images = {
       cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
                     SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
                     SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()),
@@ -1285,7 +1285,7 @@
                  color_params);
   bridge->DisableDeferral(DisableDeferralReason::kDisableDeferralReasonUnknown);
 
-  std::vector<cc::DrawImage> images = {
+  Vector<cc::DrawImage> images = {
       cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
                     SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
                     SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()),
@@ -1342,7 +1342,7 @@
   bridge->DisableDeferral(DisableDeferralReason::kDisableDeferralReasonUnknown);
 
   PaintFlags flags;
-  std::vector<cc::DrawImage> images = {
+  Vector<cc::DrawImage> images = {
       cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
                     SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
                     SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()),
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 213632e..3f423b6a 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -736,82 +736,79 @@
   kSharedImage,
 };
 
-const std::vector<CanvasResourceType>& GetResourceTypeFallbackList(
+const Vector<CanvasResourceType>& GetResourceTypeFallbackList(
     CanvasResourceProvider::ResourceUsage usage) {
-  static const std::vector<CanvasResourceType> kSoftwareFallbackList({
+  static const Vector<CanvasResourceType> kSoftwareFallbackList({
       CanvasResourceType::kBitmap,
   });
 
-  static const std::vector<CanvasResourceType> kAcceleratedFallbackList({
+  static const Vector<CanvasResourceType> kAcceleratedFallbackList({
       CanvasResourceType::kSharedImage,
       CanvasResourceType::kTexture,
       // Fallback to software
       CanvasResourceType::kBitmap,
   });
 
-  static const std::vector<CanvasResourceType> kSoftwareCompositedFallbackList({
+  static const Vector<CanvasResourceType> kSoftwareCompositedFallbackList({
       CanvasResourceType::kBitmapGpuMemoryBuffer,
       CanvasResourceType::kSharedBitmap,
       // Fallback to no direct compositing support
       CanvasResourceType::kBitmap,
   });
 
-  static const std::vector<CanvasResourceType>
-      kAcceleratedCompositedFallbackList({
-          CanvasResourceType::kSharedImage,
-          CanvasResourceType::kTextureGpuMemoryBuffer,
-          CanvasResourceType::kTexture,
-          // Fallback to software composited
-          // (|kSoftwareCompositedFallbackList|).
-          CanvasResourceType::kBitmapGpuMemoryBuffer,
-          CanvasResourceType::kSharedBitmap,
-          // Fallback to no direct compositing support
-          CanvasResourceType::kBitmap,
-      });
+  static const Vector<CanvasResourceType> kAcceleratedCompositedFallbackList({
+      CanvasResourceType::kSharedImage,
+      CanvasResourceType::kTextureGpuMemoryBuffer,
+      CanvasResourceType::kTexture,
+      // Fallback to software composited
+      // (|kSoftwareCompositedFallbackList|).
+      CanvasResourceType::kBitmapGpuMemoryBuffer,
+      CanvasResourceType::kSharedBitmap,
+      // Fallback to no direct compositing support
+      CanvasResourceType::kBitmap,
+  });
   DCHECK(std::equal(kAcceleratedCompositedFallbackList.begin() + 3,
                     kAcceleratedCompositedFallbackList.end(),
                     kSoftwareCompositedFallbackList.begin(),
                     kSoftwareCompositedFallbackList.end()));
 
-  static const std::vector<CanvasResourceType> kAcceleratedDirect2DFallbackList(
-      {
-          // TODO(khushalsagar): This is used for low-latency canvas. We'll need
-          // support for single buffering to use shared images here.
-          CanvasResourceType::kDirect2DGpuMemoryBuffer,
-          // The rest is equal to |kAcceleratedCompositedFallbackList|.
-          CanvasResourceType::kSharedImage,
-          CanvasResourceType::kTextureGpuMemoryBuffer,
-          CanvasResourceType::kTexture,
-          // Fallback to software composited
-          CanvasResourceType::kBitmapGpuMemoryBuffer,
-          CanvasResourceType::kSharedBitmap,
-          // Fallback to no direct compositing support
-          CanvasResourceType::kBitmap,
-      });
+  static const Vector<CanvasResourceType> kAcceleratedDirect2DFallbackList({
+      // TODO(khushalsagar): This is used for low-latency canvas. We'll need
+      // support for single buffering to use shared images here.
+      CanvasResourceType::kDirect2DGpuMemoryBuffer,
+      // The rest is equal to |kAcceleratedCompositedFallbackList|.
+      CanvasResourceType::kSharedImage,
+      CanvasResourceType::kTextureGpuMemoryBuffer,
+      CanvasResourceType::kTexture,
+      // Fallback to software composited
+      CanvasResourceType::kBitmapGpuMemoryBuffer,
+      CanvasResourceType::kSharedBitmap,
+      // Fallback to no direct compositing support
+      CanvasResourceType::kBitmap,
+  });
   DCHECK(std::equal(kAcceleratedDirect2DFallbackList.begin() + 1,
                     kAcceleratedDirect2DFallbackList.end(),
                     kAcceleratedCompositedFallbackList.begin(),
                     kAcceleratedCompositedFallbackList.end()));
 
-  static const std::vector<CanvasResourceType> kAcceleratedDirect3DFallbackList(
-      {
-          // This is used with single-buffered WebGL where the resource comes
-          // from an external source. The external site should take care of
-          // using SharedImages since the resource will be used by the display
-          // compositor.
-          CanvasResourceType::kDirect3DSwapChain,
-          CanvasResourceType::kDirect3DGpuMemoryBuffer,
-          CanvasResourceType::kDirect2DGpuMemoryBuffer,
-          // The rest is equal to |kAcceleratedCompositedFallbackList|.
-          CanvasResourceType::kSharedImage,
-          CanvasResourceType::kTextureGpuMemoryBuffer,
-          CanvasResourceType::kTexture,
-          // Fallback to software composited
-          CanvasResourceType::kBitmapGpuMemoryBuffer,
-          CanvasResourceType::kSharedBitmap,
-          // Fallback to no direct compositing support
-          CanvasResourceType::kBitmap,
-      });
+  static const Vector<CanvasResourceType> kAcceleratedDirect3DFallbackList({
+      // This is used with single-buffered WebGL where the resource comes
+      // from an external source. The external site should take care of
+      // using SharedImages since the resource will be used by the display
+      // compositor.
+      CanvasResourceType::kDirect3DSwapChain,
+      CanvasResourceType::kDirect3DGpuMemoryBuffer,
+      CanvasResourceType::kDirect2DGpuMemoryBuffer,
+      // The rest is equal to |kAcceleratedCompositedFallbackList|.
+      CanvasResourceType::kSharedImage,
+      CanvasResourceType::kTextureGpuMemoryBuffer,
+      CanvasResourceType::kTexture,
+      // Fallback to software composited
+      CanvasResourceType::kBitmapGpuMemoryBuffer,
+      CanvasResourceType::kSharedBitmap,
+      // Fallback to no direct compositing support
+      CanvasResourceType::kBitmap,
+  });
   DCHECK(std::equal(kAcceleratedDirect3DFallbackList.begin() + 2,
                     kAcceleratedDirect3DFallbackList.end(),
                     kAcceleratedDirect2DFallbackList.begin(),
@@ -845,7 +842,7 @@
     base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
     bool is_origin_top_left) {
   std::unique_ptr<CanvasResourceProvider> provider;
-  const std::vector<CanvasResourceType>& fallback_list =
+  const Vector<CanvasResourceType>& fallback_list =
       GetResourceTypeFallbackList(usage);
 
   const bool is_gpu_memory_buffer_image_allowed =
@@ -1021,7 +1018,7 @@
 
   bool is_hardware_decode_cache_;
   bool cleanup_task_pending_ = false;
-  std::vector<ScopedResult> locked_images_;
+  Vector<ScopedResult> locked_images_;
   cc::PlaybackImageProvider playback_image_provider_n32_;
   base::Optional<cc::PlaybackImageProvider> playback_image_provider_f16_;
 
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 15d9e66..e6c5094e 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
@@ -731,15 +731,16 @@
     bool needs_layer,
     CompositorElementId& mask_isolation_id,
     CompositorElementId& mask_effect_id) {
-  auto entry =
+  auto* entry =
       std::find_if(synthesized_clip_cache_.begin(),
                    synthesized_clip_cache_.end(), [&node](const auto& entry) {
                      return entry.key == &node && !entry.in_use;
                    });
   if (entry == synthesized_clip_cache_.end()) {
     auto clip = std::make_unique<SynthesizedClip>();
-    entry = synthesized_clip_cache_.insert(
-        entry, SynthesizedClipEntry{&node, std::move(clip), false});
+    synthesized_clip_cache_.push_back(
+        SynthesizedClipEntry{&node, std::move(clip), false});
+    entry = synthesized_clip_cache_.end() - 1;
   }
 
   entry->in_use = true;
@@ -988,11 +989,12 @@
   content_layer_clients_.swap(new_content_layer_clients);
   scroll_hit_test_layers_.swap(new_scroll_hit_test_layers);
 
-  synthesized_clip_cache_.erase(
-      std::remove_if(synthesized_clip_cache_.begin(),
-                     synthesized_clip_cache_.end(),
-                     [](const auto& entry) { return !entry.in_use; }),
-      synthesized_clip_cache_.end());
+  auto pos = std::remove_if(synthesized_clip_cache_.begin(),
+                            synthesized_clip_cache_.end(),
+                            [](const auto& entry) { return !entry.in_use; }) -
+             synthesized_clip_cache_.begin();
+  synthesized_clip_cache_.EraseAt(pos, synthesized_clip_cache_.size() - pos);
+
   if (extra_data_for_testing_enabled_) {
     for (const auto& entry : synthesized_clip_cache_) {
       extra_data_for_testing_->synthesized_clip_layers.push_back(
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
index 3e13c67..b88b3154 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -318,7 +318,7 @@
     std::unique_ptr<SynthesizedClip> synthesized_clip;
     bool in_use;
   };
-  std::vector<SynthesizedClipEntry> synthesized_clip_cache_;
+  Vector<SynthesizedClipEntry> synthesized_clip_cache_;
 
   Vector<scoped_refptr<cc::Layer>> scroll_hit_test_layers_;
 
diff --git a/third_party/blink/renderer/platform/graphics/compositor_element_id.h b/third_party/blink/renderer/platform/graphics/compositor_element_id.h
index 43858166..304d8e9 100644
--- a/third_party/blink/renderer/platform/graphics/compositor_element_id.h
+++ b/third_party/blink/renderer/platform/graphics/compositor_element_id.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ELEMENT_ID_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ELEMENT_ID_H_
 
-#include <unordered_set>
-
 #include "cc/trees/element_id.h"
 #include "third_party/blink/renderer/platform/graphics/dom_node_id.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -57,9 +55,6 @@
 CompositorElementIdNamespace PLATFORM_EXPORT
     NamespaceFromCompositorElementId(CompositorElementId);
 
-using CompositorElementIdSet =
-    std::unordered_set<cc::ElementId, cc::ElementIdHash>;
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ELEMENT_ID_H_
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier.cc b/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier.cc
index b807335d..611b6f2 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier.cc
@@ -7,6 +7,9 @@
 #include "base/rand_util.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/graphics/darkmode/darkmode_classifier.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
 #include "third_party/skia/include/utils/SkNullCanvas.h"
 
 namespace {
@@ -46,8 +49,8 @@
       src_rect.Height() < kMinImageSizeForClassification1D)
     return DarkModeClassification::kApplyDarkModeFilter;
 
-  std::vector<float> features;
-  std::vector<SkColor> sampled_pixels;
+  Vector<float> features;
+  Vector<SkColor> sampled_pixels;
   if (!ComputeImageFeatures(image, src_rect, &features, &sampled_pixels)) {
     // TODO(https://crbug.com/945434): Do not cache the classification when
     // the correct resource is not loaded
@@ -64,8 +67,8 @@
 bool DarkModeBitmapImageClassifier::ComputeImageFeatures(
     Image& image,
     const FloatRect& src_rect,
-    std::vector<float>* features,
-    std::vector<SkColor>* sampled_pixels) {
+    Vector<float>* features,
+    Vector<SkColor>* sampled_pixels) {
   SkBitmap bitmap;
   if (!GetBitmap(image, src_rect, &bitmap))
     return false;
@@ -111,19 +114,18 @@
 // Extracts sample pixels from the image. The image is separated into uniformly
 // distributed blocks through its width and height, each block is sampled, and
 // checked to see if it seems to be background or foreground.
-void DarkModeBitmapImageClassifier::GetSamples(
-    const SkBitmap& bitmap,
-    std::vector<SkColor>* sampled_pixels,
-    float* transparency_ratio,
-    float* background_ratio) {
+void DarkModeBitmapImageClassifier::GetSamples(const SkBitmap& bitmap,
+                                               Vector<SkColor>* sampled_pixels,
+                                               float* transparency_ratio,
+                                               float* background_ratio) {
   int pixels_per_block = pixels_to_sample_ / (kBlocksCount1D * kBlocksCount1D);
 
   int transparent_pixels = 0;
   int opaque_pixels = 0;
   int blocks_count = 0;
 
-  std::vector<int> horizontal_grid(kBlocksCount1D + 1);
-  std::vector<int> vertical_grid(kBlocksCount1D + 1);
+  Vector<int> horizontal_grid(kBlocksCount1D + 1);
+  Vector<int> vertical_grid(kBlocksCount1D + 1);
   for (int block = 0; block <= kBlocksCount1D; block++) {
     horizontal_grid[block] = static_cast<int>(
         round(block * bitmap.width() / static_cast<float>(kBlocksCount1D)));
@@ -132,7 +134,7 @@
   }
 
   sampled_pixels->clear();
-  std::vector<IntRect> foreground_blocks;
+  Vector<IntRect> foreground_blocks;
 
   for (int y = 0; y < kBlocksCount1D; y++) {
     for (int x = 0; x < kBlocksCount1D; x++) {
@@ -140,14 +142,13 @@
                     horizontal_grid[x + 1] - horizontal_grid[x],
                     vertical_grid[y + 1] - vertical_grid[y]);
 
-      std::vector<SkColor> block_samples;
+      Vector<SkColor> block_samples;
       int block_transparent_pixels;
       GetBlockSamples(bitmap, block, pixels_per_block, &block_samples,
                       &block_transparent_pixels);
       opaque_pixels += static_cast<int>(block_samples.size());
       transparent_pixels += block_transparent_pixels;
-      sampled_pixels->insert(sampled_pixels->end(), block_samples.begin(),
-                             block_samples.end());
+      sampled_pixels->AppendRange(block_samples.begin(), block_samples.end());
       if (opaque_pixels >
           kMinOpaquePixelPercentageForForeground * pixels_per_block) {
         foreground_blocks.push_back(block);
@@ -169,7 +170,7 @@
     const SkBitmap& bitmap,
     const IntRect& block,
     const int required_samples_count,
-    std::vector<SkColor>* sampled_pixels,
+    Vector<SkColor>* sampled_pixels,
     int* transparent_pixels_count) {
   *transparent_pixels_count = 0;
 
@@ -208,10 +209,10 @@
 // 2: Ratio of transparent area to the whole image.
 // 3: Ratio of the background area to the whole image.
 void DarkModeBitmapImageClassifier::GetFeatures(
-    const std::vector<SkColor>& sampled_pixels,
+    const Vector<SkColor>& sampled_pixels,
     const float transparency_ratio,
     const float background_ratio,
-    std::vector<float>* features) {
+    Vector<float>* features) {
   int samples_count = static_cast<int>(sampled_pixels.size());
 
   // Is image grayscale.
@@ -240,9 +241,12 @@
 }
 
 float DarkModeBitmapImageClassifier::ComputeColorBucketsRatio(
-    const std::vector<SkColor>& sampled_pixels,
+    const Vector<SkColor>& sampled_pixels,
     const ColorMode color_mode) {
-  std::set<unsigned> buckets;
+  HashSet<unsigned, WTF::AlreadyHashed,
+          WTF::UnsignedWithZeroKeyHashTraits<unsigned>>
+      buckets;
+
   // If image is in color, use 4 bits per color channel, otherwise 4 bits for
   // illumination.
   if (color_mode == ColorMode::kColor) {
@@ -271,7 +275,7 @@
 
 DarkModeClassification
 DarkModeBitmapImageClassifier::ClassifyImageUsingDecisionTree(
-    const std::vector<float>& features) {
+    const Vector<float>& features) {
   DCHECK_EQ(features.size(), 4u);
 
   int is_color = features[0] > 0;
@@ -292,7 +296,7 @@
 }
 
 DarkModeClassification DarkModeBitmapImageClassifier::ClassifyImage(
-    const std::vector<float>& features) {
+    const Vector<float>& features) {
   DCHECK_EQ(features.size(), 4u);
 
   DarkModeClassification result = ClassifyImageUsingDecisionTree(features);
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier.h b/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier.h
index 8cddf8aa..9267904 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier.h
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_BITMAP_IMAGE_CLASSIFIER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_BITMAP_IMAGE_CLASSIFIER_H_
 
-#include <vector>
-
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
@@ -25,9 +23,8 @@
 
   DarkModeClassification Classify(Image& image, const FloatRect& src_rect);
 
-  bool ComputeImageFeaturesForTesting(Image& image,
-                                      std::vector<float>* features) {
-    std::vector<SkColor> sampled_pixels;
+  bool ComputeImageFeaturesForTesting(Image& image, Vector<float>* features) {
+    Vector<SkColor> sampled_pixels;
     return ComputeImageFeatures(
         image,
         FloatRect(0, 0, static_cast<float>(image.width()),
@@ -36,7 +33,7 @@
   }
 
   DarkModeClassification ClassifyImageUsingDecisionTreeForTesting(
-      const std::vector<float>& features) {
+      const Vector<float>& features) {
     return ClassifyImageUsingDecisionTree(features);
   }
 
@@ -46,8 +43,8 @@
   // Computes the features vector for a given image.
   bool ComputeImageFeatures(Image&,
                             const FloatRect&,
-                            std::vector<float>*,
-                            std::vector<SkColor>*);
+                            Vector<float>*,
+                            Vector<SkColor>*);
 
   // Converts image to SkBitmap and returns true if successful.
   bool GetBitmap(Image&, const FloatRect&, SkBitmap*);
@@ -55,39 +52,38 @@
   // Given a SkBitmap, extracts a sample set of pixels (|sampled_pixels|),
   // |transparency_ratio|, and |background_ratio|.
   void GetSamples(const SkBitmap&,
-                  std::vector<SkColor>* sampled_pixels,
+                  Vector<SkColor>* sampled_pixels,
                   float* transparency_ratio,
                   float* background_ratio);
 
   // Given |sampled_pixels|, |transparency_ratio|, and |background_ratio| for an
   // image, computes the required |features| for classification.
-  void GetFeatures(const std::vector<SkColor>& sampled_pixels,
+  void GetFeatures(const Vector<SkColor>& sampled_pixels,
                    const float transparency_ratio,
                    const float background_ratio,
-                   std::vector<float>* features);
+                   Vector<float>* features);
 
   // Makes a decision about the image given its features.
-  DarkModeClassification ClassifyImage(const std::vector<float>&);
+  DarkModeClassification ClassifyImage(const Vector<float>&);
 
   // Receives sampled pixels and color mode, and returns the ratio of color
   // buckets count to all possible color buckets. If image is in color, a color
   // bucket is a 4 bit per channel representation of each RGB color, and if it
   // is grayscale, each bucket is a 4 bit representation of luminance.
-  float ComputeColorBucketsRatio(const std::vector<SkColor>&, const ColorMode);
+  float ComputeColorBucketsRatio(const Vector<SkColor>&, const ColorMode);
 
   // Gets the |required_samples_count| for a specific |block| of the given
   // SkBitmap, and returns |sampled_pixels| and |transparent_pixels_count|.
   void GetBlockSamples(const SkBitmap&,
                        const IntRect& block,
                        const int required_samples_count,
-                       std::vector<SkColor>* sampled_pixels,
+                       Vector<SkColor>* sampled_pixels,
                        int* transparent_pixels_count);
 
   // Decides if the filter should be applied to the image or not, only using the
   // decision tree. Returns 'kNotClassified' if decision tree cannot give a
   // trustable answer.
-  DarkModeClassification ClassifyImageUsingDecisionTree(
-      const std::vector<float>&);
+  DarkModeClassification ClassifyImageUsingDecisionTree(const Vector<float>&);
 
   int pixels_to_sample_;
 };
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier_test.cc b/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier_test.cc
index ee4b011..3420c38 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier_test.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_bitmap_image_classifier_test.cc
@@ -54,8 +54,8 @@
  public:
   // Loads the image from |file_name|, computes features vector into |features|,
   // and returns the classification result.
-  bool GetFeaturesAndClassification(const std::string& file_name,
-                                    std::vector<float>* features) {
+  bool GetFeaturesAndClassification(const String& file_name,
+                                    Vector<float>* features) {
     SCOPED_TRACE(file_name);
     scoped_refptr<BitmapImage> image = LoadImage(file_name);
     classifier_.ComputeImageFeaturesForTesting(*image.get(), features);
@@ -64,8 +64,8 @@
     return result == DarkModeClassification::kApplyDarkModeFilter;
   }
 
-  void AssertFeaturesEqual(const std::vector<float>& features,
-                           const std::vector<float>& expected_features) {
+  void AssertFeaturesEqual(const Vector<float>& features,
+                           const Vector<float>& expected_features) {
     EXPECT_EQ(features.size(), expected_features.size());
     for (unsigned i = 0; i < features.size(); i++) {
       EXPECT_NEAR(features[i], expected_features[i], kEpsilon)
@@ -76,8 +76,8 @@
   DarkModeBitmapImageClassifier* classifier() { return &classifier_; }
 
  protected:
-  scoped_refptr<BitmapImage> LoadImage(const std::string& file_name) {
-    String file_path = test::BlinkWebTestsDir() + file_name.c_str();
+  scoped_refptr<BitmapImage> LoadImage(const String& file_name) {
+    String file_path = test::BlinkWebTestsDir() + file_name;
     scoped_refptr<SharedBuffer> image_data = test::ReadFromFile(file_path);
     EXPECT_TRUE(image_data.get() && image_data.get()->size());
 
@@ -92,7 +92,7 @@
 };
 
 TEST_F(DarkModeBitmapImageClassifierTest, FeaturesAndClassification) {
-  std::vector<float> features;
+  Vector<float> features;
 
   // Test Case 1:
   // Grayscale
diff --git a/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc b/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
index 30aace89..ccfdf138 100644
--- a/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
+++ b/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
@@ -64,7 +64,8 @@
   if (!frame)
     return nullptr;
 
-  std::vector<FrameMetadata> frames = {FrameMetadata()};
+  WebVector<FrameMetadata> frames;
+  frames.emplace_back(FrameMetadata());
   sk_sp<DecodingImageGenerator> generator = DecodingImageGenerator::Create(
       std::move(frame), info, std::move(segment_reader), std::move(frames),
       PaintImage::GetNextContentId(), true /* all_data_received */,
@@ -80,7 +81,7 @@
     scoped_refptr<ImageFrameGenerator> frame_generator,
     const SkImageInfo& info,
     scoped_refptr<SegmentReader> data,
-    std::vector<FrameMetadata> frames,
+    WebVector<FrameMetadata> frames,
     PaintImage::ContentId content_id,
     bool all_data_received,
     bool is_eligible_for_accelerated_decoding,
@@ -95,12 +96,12 @@
     scoped_refptr<ImageFrameGenerator> frame_generator,
     const SkImageInfo& info,
     scoped_refptr<SegmentReader> data,
-    std::vector<FrameMetadata> frames,
+    WebVector<FrameMetadata> frames,
     PaintImage::ContentId complete_frame_content_id,
     bool all_data_received,
     bool is_eligible_for_accelerated_decoding,
     bool can_yuv_decode)
-    : PaintImageGenerator(info, std::move(frames)),
+    : PaintImageGenerator(info, frames.ReleaseVector()),
       frame_generator_(std::move(frame_generator)),
       data_(std::move(data)),
       all_data_received_(all_data_received),
diff --git a/third_party/blink/renderer/platform/graphics/decoding_image_generator.h b/third_party/blink/renderer/platform/graphics/decoding_image_generator.h
index 7363490..b2250d4 100644
--- a/third_party/blink/renderer/platform/graphics/decoding_image_generator.h
+++ b/third_party/blink/renderer/platform/graphics/decoding_image_generator.h
@@ -28,6 +28,7 @@
 
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
 #include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -59,7 +60,7 @@
       scoped_refptr<ImageFrameGenerator>,
       const SkImageInfo&,
       scoped_refptr<SegmentReader>,
-      std::vector<FrameMetadata>,
+      WebVector<FrameMetadata>,
       PaintImage::ContentId,
       bool all_data_received,
       bool is_eligible_for_accelerated_decoding,
@@ -91,7 +92,7 @@
   DecodingImageGenerator(scoped_refptr<ImageFrameGenerator>,
                          const SkImageInfo&,
                          scoped_refptr<SegmentReader>,
-                         std::vector<FrameMetadata>,
+                         WebVector<FrameMetadata>,
                          PaintImage::ContentId,
                          bool all_data_received,
                          bool is_eligible_for_accelerated_decoding,
diff --git a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc
index 99066f1..5c3e3fcf 100644
--- a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc
+++ b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc
@@ -207,7 +207,7 @@
   if (image_is_high_bit_depth_)
     info = info.makeColorType(kRGBA_F16_SkColorType);
 
-  std::vector<FrameMetadata> frames(frame_data_.size());
+  WebVector<FrameMetadata> frames(frame_data_.size());
   for (size_t i = 0; i < frame_data_.size(); ++i) {
     frames[i].complete = frame_data_[i].is_received_;
     frames[i].duration = FrameDurationAtIndex(i);
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 1da5879d..2a32c58 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -1406,7 +1406,7 @@
       use_half_float_storage_) {
     row_bytes *= 2;
   }
-  std::vector<uint8_t> scanline(row_bytes);
+  Vector<uint8_t> scanline(row_bytes);
   unsigned count = height / 2;
   for (unsigned i = 0; i < count; i++) {
     uint8_t* row_a = framebuffer + i * row_bytes;
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item.cc b/third_party/blink/renderer/platform/graphics/paint/display_item.cc
index 8a46ce1..d1de073 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item.cc
@@ -40,8 +40,10 @@
     case 7:
       return "PaintPhaseDescendantOutlinesOnly";
     case 8:
-      return "PaintPhaseSelection";
+      return "PaintPhaseOverlayScrollbars";
     case 9:
+      return "PaintPhaseSelection";
+    case 10:
       return "PaintPhaseTextClip";
     case DisplayItem::kPaintPhaseMax:
       return "PaintPhaseMask";
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item.h b/third_party/blink/renderer/platform/graphics/paint/display_item.h
index 4434f62..c248132 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item.h
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item.h
@@ -28,7 +28,7 @@
  public:
   enum {
     // Must be kept in sync with core/paint/PaintPhase.h.
-    kPaintPhaseMax = 10,
+    kPaintPhaseMax = 11,
   };
 
   // A display item type uniquely identifies a display item of a client.
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
index 77a674aed..38028ad0 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
@@ -16,6 +16,7 @@
 #include "components/viz/common/quads/yuv_video_draw_quad.h"
 #include "media/base/video_frame.h"
 #include "media/renderers/video_resource_updater.h"
+#include "third_party/blink/public/platform/web_vector.h"
 
 namespace blink {
 
@@ -126,15 +127,19 @@
 }
 
 void VideoFrameResourceProvider::PrepareSendToParent(
-    const std::vector<viz::ResourceId>& resource_ids,
-    std::vector<viz::TransferableResource>* transferable_resources) {
-  resource_provider_->PrepareSendToParent(resource_ids, transferable_resources,
-                                          context_provider_);
+    const WebVector<viz::ResourceId>& resource_ids,
+    WebVector<viz::TransferableResource>* transferable_resources) {
+  std::vector<viz::TransferableResource> resources_list;
+  resource_provider_->PrepareSendToParent(
+      const_cast<WebVector<viz::ResourceId>&>(resource_ids).ReleaseVector(),
+      &resources_list, context_provider_);
+  *transferable_resources = std::move(resources_list);
 }
 
 void VideoFrameResourceProvider::ReceiveReturnsFromParent(
-    const std::vector<viz::ReturnedResource>& transferable_resources) {
-  resource_provider_->ReceiveReturnsFromParent(transferable_resources);
+    const Vector<viz::ReturnedResource>& transferable_resources) {
+  resource_provider_->ReceiveReturnsFromParent(
+      WebVector<viz::ReturnedResource>(transferable_resources).ReleaseVector());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
index e4e5d2a3..9c15df16 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
@@ -9,8 +9,10 @@
 #include "cc/trees/layer_tree_settings.h"
 #include "components/viz/client/client_resource_provider.h"
 #include "components/viz/client/shared_bitmap_reporter.h"
+#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/platform/web_video_frame_submitter.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace media {
 class VideoFrame;
@@ -53,10 +55,10 @@
   bool IsInitialized() { return resource_updater_.get(); }
 
   virtual void PrepareSendToParent(
-      const std::vector<viz::ResourceId>& resource_ids,
-      std::vector<viz::TransferableResource>* transferable_resources);
+      const WebVector<viz::ResourceId>& resource_ids,
+      WebVector<viz::TransferableResource>* transferable_resources);
   virtual void ReceiveReturnsFromParent(
-      const std::vector<viz::ReturnedResource>& transferable_resources);
+      const Vector<viz::ReturnedResource>& transferable_resources);
 
  private:
   const cc::LayerTreeSettings settings_;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index 1ea24ca4..53fd58c 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -4,8 +4,6 @@
 
 #include "third_party/blink/renderer/platform/graphics/video_frame_submitter.h"
 
-#include <vector>
-
 #include "base/bind.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
@@ -230,8 +228,7 @@
 void VideoFrameSubmitter::ReclaimResources(
     const WTF::Vector<viz::ReturnedResource>& resources) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  resource_provider_->ReceiveReturnsFromParent(
-      WebVector<viz::ReturnedResource>(resources).ReleaseVector());
+  resource_provider_->ReceiveReturnsFromParent(resources);
 }
 
 void VideoFrameSubmitter::DidAllocateSharedBitmap(
@@ -410,15 +407,16 @@
   auto compositor_frame =
       CreateCompositorFrame(begin_frame_ack, std::move(video_frame));
 
-  std::vector<viz::ResourceId> resources;
+  WebVector<viz::ResourceId> resources;
   const auto& quad_list = compositor_frame.render_pass_list.back()->quad_list;
   if (!quad_list.empty()) {
     DCHECK_EQ(quad_list.size(), 1u);
-    resources.assign(quad_list.front()->resources.begin(),
-                     quad_list.front()->resources.end());
+    resources.Assign(quad_list.front()->resources);
   }
-  resource_provider_->PrepareSendToParent(resources,
-                                          &compositor_frame.resource_list);
+
+  WebVector<viz::TransferableResource> resource_list;
+  resource_provider_->PrepareSendToParent(resources, &resource_list);
+  compositor_frame.resource_list = resource_list.ReleaseVector();
 
   // We can pass nullptr for the HitTestData as the CompositorFram will not
   // contain any SurfaceDrawQuads.
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
index 737afe9..fbe8d91 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
@@ -138,11 +138,11 @@
                     bool));
   MOCK_METHOD0(ReleaseFrameResources, void());
   MOCK_METHOD2(PrepareSendToParent,
-               void(const std::vector<viz::ResourceId>&,
-                    std::vector<viz::TransferableResource>*));
+               void(const WebVector<viz::ResourceId>&,
+                    WebVector<viz::TransferableResource>*));
   MOCK_METHOD1(
       ReceiveReturnsFromParent,
-      void(const std::vector<viz::ReturnedResource>& transferable_resources));
+      void(const Vector<viz::ReturnedResource>& transferable_resources));
   MOCK_METHOD0(ObtainContextProvider, void());
 
  private:
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index fb48aba2..b8238fa 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -372,10 +372,6 @@
 bool HeapCompact::ShouldCompact(BlinkGC::StackState stack_state,
                                 BlinkGC::MarkingType marking_type,
                                 BlinkGC::GCReason reason) {
-  if (!RuntimeEnabledFeatures::HeapCompactionEnabled()) {
-    return false;
-  }
-
   DCHECK_NE(BlinkGC::MarkingType::kTakeSnapshot, marking_type);
   if (marking_type == BlinkGC::MarkingType::kAtomicMarking &&
       stack_state == BlinkGC::StackState::kHeapPointersOnStack) {
@@ -391,6 +387,10 @@
     return true;
   }
 
+  if (!RuntimeEnabledFeatures::HeapCompactionEnabled()) {
+    return false;
+  }
+
   // Only enable compaction when in a memory reduction garbage collection as it
   // may significantly increase the final garbage collection pause.
   if (reason == BlinkGC::GCReason::kUnifiedHeapForMemoryReductionGC) {
@@ -401,7 +401,7 @@
 }
 
 void HeapCompact::Initialize(ThreadState* state) {
-  CHECK(RuntimeEnabledFeatures::HeapCompactionEnabled());
+  CHECK(force_for_next_gc_ || RuntimeEnabledFeatures::HeapCompactionEnabled());
   CHECK(!do_compact_);
   CHECK(!fixups_);
   LOG_HEAP_COMPACTION() << "Compacting: free=" << free_list_size_;
diff --git a/third_party/blink/renderer/platform/instrumentation/use_counter.h b/third_party/blink/renderer/platform/instrumentation/use_counter.h
index cc9e6ec..3581df6 100644
--- a/third_party/blink/renderer/platform/instrumentation/use_counter.h
+++ b/third_party/blink/renderer/platform/instrumentation/use_counter.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_USE_COUNTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_USE_COUNTER_H_
 
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/loader/cors/cors.h b/third_party/blink/renderer/platform/loader/cors/cors.h
index db3b05c..6544bf1 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors.h
+++ b/third_party/blink/renderer/platform/loader/cors/cors.h
@@ -7,9 +7,9 @@
 
 #include "base/optional.h"
 #include "services/network/public/cpp/cors/cors_error_status.h"
-#include "services/network/public/mojom/cors.mojom-shared.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "services/network/public/mojom/cors.mojom-blink.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_http_header_set.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/platform/loader/cors/cors_error_string.h b/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
index f9e8664f1..fa80650 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
+++ b/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "services/network/public/cpp/cors/cors_error_status.h"
-#include "services/network/public/mojom/cors.mojom-shared.h"
+#include "services/network/public/mojom/cors.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
index c292c1dc..8320f8b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
+++ b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
@@ -6,8 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_HANDLER_H_
 
 #include <stdint.h>
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
-#include "third_party/blink/public/mojom/loader/code_cache.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.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/weborigin/security_origin.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/console_logger.h b/third_party/blink/renderer/platform/loader/fetch/console_logger.h
index 34de420..37d935fa 100644
--- a/third_party/blink/renderer/platform/loader/fetch/console_logger.h
+++ b/third_party/blink/renderer/platform/loader/fetch/console_logger.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CONSOLE_LOGGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CONSOLE_LOGGER_H_
 
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.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/wtf/forward.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h b/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
index 2a5ab53..1ebddbf0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CLIENT_SETTINGS_OBJECT_H_
 
 #include "base/optional.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h b/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h
index 8c9075c2..82fedc9 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CLIENT_SETTINGS_OBJECT_SNAPSHOT_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CLIENT_SETTINGS_OBJECT_SNAPSHOT_H_
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
index 8302973..f298e32 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -36,8 +36,8 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
-#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc b/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc
index 6a00438..e9b7611 100644
--- a/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h"
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/allowed_by_nosniff.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/loader/fetch/https_state.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
index 1f1d3377..4acc782 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -26,7 +26,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
 
 #include <memory>
-#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index 3c1f7c2..41619f4 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -208,10 +208,12 @@
 
   if (SubresourceIntegrity::CheckSubresourceIntegrity(IntegrityMetadata(), data,
                                                       data_length, Url(), *this,
-                                                      integrity_report_info_))
+                                                      integrity_report_info_)) {
     integrity_disposition_ = ResourceIntegrityDisposition::kPassed;
-  else
+  } else {
     integrity_disposition_ = ResourceIntegrityDisposition::kFailed;
+  }
+
   DCHECK_NE(IntegrityDisposition(), ResourceIntegrityDisposition::kNotChecked);
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index c0bd313e..953701c 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -29,7 +29,7 @@
 #include "base/callback.h"
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
-#include "third_party/blink/public/mojom/loader/code_cache.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h"
 #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
 #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index d83017c..1663ade 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -1722,7 +1722,7 @@
     LoaderFinishType type,
     uint32_t inflight_keepalive_bytes,
     bool should_report_corb_blocking,
-    const std::vector<network::cors::PreflightTimingInfo>&
+    const WebVector<network::cors::PreflightTimingInfo>&
         cors_preflight_timing_info) {
   DCHECK(resource);
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index 8379c23..0702bdc 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -207,7 +207,7 @@
                           LoaderFinishType,
                           uint32_t inflight_keepalive_bytes,
                           bool should_report_corb_blocking,
-                          const std::vector<network::cors::PreflightTimingInfo>&
+                          const WebVector<network::cors::PreflightTimingInfo>&
                               cors_preflight_timing_info);
   void HandleLoaderError(Resource*,
                          const ResourceError&,
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index 80d17614..59f9ef0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -34,8 +34,8 @@
 #include "base/optional.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
-#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
index b2a9c67d..15e8957f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
@@ -43,10 +43,6 @@
 // Maximum request count that request count metrics assume.
 constexpr base::HistogramBase::Sample kMaximumReportSize10K = 10000;
 
-// Maximum traffic bytes that traffic metrics assume.
-constexpr base::HistogramBase::Sample kMaximumReportSize1G =
-    1 * 1000 * 1000 * 1000;
-
 // Bucket count for metrics.
 constexpr int32_t kReportBucketCount = 25;
 
@@ -148,12 +144,7 @@
       scheduler::SchedulingLifecycleState::kStopped;
 
   uint32_t total_throttled_request_count_ = 0;
-  size_t total_throttled_traffic_bytes_ = 0;
-  size_t total_throttled_decoded_bytes_ = 0;
   uint32_t total_not_throttled_request_count_ = 0;
-  size_t total_not_throttled_traffic_bytes_ = 0;
-  size_t total_not_throttled_decoded_bytes_ = 0;
-  uint32_t throttling_state_change_count_ = 0;
   bool report_all_is_called_ = false;
 
   scheduler::AggregatedMetricReporter<scheduler::FrameStatus, int64_t>
@@ -179,7 +170,6 @@
 void ResourceLoadScheduler::TrafficMonitor::OnLifecycleStateChanged(
     scheduler::SchedulingLifecycleState state) {
   current_state_ = state;
-  throttling_state_change_count_++;
 }
 
 void ResourceLoadScheduler::TrafficMonitor::Report(
@@ -205,8 +195,6 @@
             ToSample(ReportCircumstance::kSubframeThrottled));
       }
       total_throttled_request_count_++;
-      total_throttled_traffic_bytes_ += hints.encoded_data_length();
-      total_throttled_decoded_bytes_ += hints.decoded_body_length();
       break;
     case scheduler::SchedulingLifecycleState::kNotThrottled:
       if (resource_fetcher_properties_->IsMainFrame()) {
@@ -217,8 +205,6 @@
             ToSample(ReportCircumstance::kSubframeNotThrottled));
       }
       total_not_throttled_request_count_++;
-      total_not_throttled_traffic_bytes_ += hints.encoded_data_length();
-      total_not_throttled_decoded_bytes_ += hints.decoded_body_length();
       break;
     case scheduler::SchedulingLifecycleState::kStopped:
       break;
@@ -270,81 +256,17 @@
       ("Blink.ResourceLoadScheduler.TotalRequestCount.SubframeNotThrottled", 0,
        kMaximumReportSize10K, kReportBucketCount));
 
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, main_frame_total_throttled_traffic_bytes,
-      ("Blink.ResourceLoadScheduler.TotalTrafficBytes.MainframeThrottled", 0,
-       kMaximumReportSize1G, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, main_frame_total_not_throttled_traffic_bytes,
-      ("Blink.ResourceLoadScheduler.TotalTrafficBytes.MainframeNotThrottled", 0,
-       kMaximumReportSize1G, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, sub_frame_total_throttled_traffic_bytes,
-      ("Blink.ResourceLoadScheduler.TotalTrafficBytes.SubframeThrottled", 0,
-       kMaximumReportSize1G, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, sub_frame_total_not_throttled_traffic_bytes,
-      ("Blink.ResourceLoadScheduler.TotalTrafficBytes.SubframeNotThrottled", 0,
-       kMaximumReportSize1G, kReportBucketCount));
-
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, main_frame_total_throttled_decoded_bytes,
-      ("Blink.ResourceLoadScheduler.TotalDecodedBytes.MainframeThrottled", 0,
-       kMaximumReportSize1G, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, main_frame_total_not_throttled_decoded_bytes,
-      ("Blink.ResourceLoadScheduler.TotalDecodedBytes.MainframeNotThrottled", 0,
-       kMaximumReportSize1G, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, sub_frame_total_throttled_decoded_bytes,
-      ("Blink.ResourceLoadScheduler.TotalDecodedBytes.SubframeThrottled", 0,
-       kMaximumReportSize1G, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, sub_frame_total_not_throttled_decoded_bytes,
-      ("Blink.ResourceLoadScheduler.TotalDecodedBytes.SubframeNotThrottled", 0,
-       kMaximumReportSize1G, kReportBucketCount));
-
-  DEFINE_STATIC_LOCAL(CustomCountHistogram, throttling_state_change_count,
-                      ("Blink.ResourceLoadScheduler.ThrottlingStateChangeCount",
-                       0, 100, kReportBucketCount));
-
   if (resource_fetcher_properties_->IsMainFrame()) {
     main_frame_total_throttled_request_count.Count(
         total_throttled_request_count_);
     main_frame_total_not_throttled_request_count.Count(
         total_not_throttled_request_count_);
-    main_frame_total_throttled_traffic_bytes.Count(
-        base::saturated_cast<base::Histogram::Sample>(
-            total_throttled_traffic_bytes_));
-    main_frame_total_not_throttled_traffic_bytes.Count(
-        base::saturated_cast<base::Histogram::Sample>(
-            total_not_throttled_traffic_bytes_));
-    main_frame_total_throttled_decoded_bytes.Count(
-        base::saturated_cast<base::Histogram::Sample>(
-            total_throttled_decoded_bytes_));
-    main_frame_total_not_throttled_decoded_bytes.Count(
-        base::saturated_cast<base::Histogram::Sample>(
-            total_not_throttled_decoded_bytes_));
   } else {
     sub_frame_total_throttled_request_count.Count(
         total_throttled_request_count_);
     sub_frame_total_not_throttled_request_count.Count(
         total_not_throttled_request_count_);
-    sub_frame_total_throttled_traffic_bytes.Count(
-        base::saturated_cast<base::Histogram::Sample>(
-            total_throttled_traffic_bytes_));
-    sub_frame_total_not_throttled_traffic_bytes.Count(
-        base::saturated_cast<base::Histogram::Sample>(
-            total_not_throttled_traffic_bytes_));
-    sub_frame_total_throttled_decoded_bytes.Count(
-        base::saturated_cast<base::Histogram::Sample>(
-            total_throttled_decoded_bytes_));
-    sub_frame_total_not_throttled_decoded_bytes.Count(
-        base::saturated_cast<base::Histogram::Sample>(
-            total_not_throttled_decoded_bytes_));
   }
-
-  throttling_state_change_count.Count(throttling_state_change_count_);
 }
 
 constexpr ResourceLoadScheduler::ClientId
@@ -508,46 +430,6 @@
 
   // Flush out all traffic reports here for safety.
   traffic_monitor_->ReportAll();
-
-  if (maximum_running_requests_seen_ == 0)
-    return;
-
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, main_frame_throttled,
-      ("Blink.ResourceLoadScheduler.PeakRequests.MainframeThrottled", 0,
-       kMaximumReportSize10K, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, main_frame_not_throttled,
-      ("Blink.ResourceLoadScheduler.PeakRequests.MainframeNotThrottled", 0,
-       kMaximumReportSize10K, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, sub_frame_throttled,
-      ("Blink.ResourceLoadScheduler.PeakRequests.SubframeThrottled", 0,
-       kMaximumReportSize10K, kReportBucketCount));
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, sub_frame_not_throttled,
-      ("Blink.ResourceLoadScheduler.PeakRequests.SubframeNotThrottled", 0,
-       kMaximumReportSize10K, kReportBucketCount));
-
-  switch (throttling_history_) {
-    case ThrottlingHistory::kInitial:
-    case ThrottlingHistory::kNotThrottled:
-      if (resource_fetcher_properties_->IsMainFrame())
-        main_frame_not_throttled.Count(maximum_running_requests_seen_);
-      else
-        sub_frame_not_throttled.Count(maximum_running_requests_seen_);
-      break;
-    case ThrottlingHistory::kThrottled:
-      if (resource_fetcher_properties_->IsMainFrame())
-        main_frame_throttled.Count(maximum_running_requests_seen_);
-      else
-        sub_frame_throttled.Count(maximum_running_requests_seen_);
-      break;
-    case ThrottlingHistory::kPartiallyThrottled:
-      break;
-    case ThrottlingHistory::kStopped:
-      break;
-  }
 }
 
 bool ResourceLoadScheduler::IsClientDelayable(const ClientIdWithPriority& info,
@@ -585,25 +467,9 @@
 
   frame_scheduler_lifecycle_state_ = state;
 
-  switch (state) {
-    case scheduler::SchedulingLifecycleState::kHidden:
-    case scheduler::SchedulingLifecycleState::kThrottled:
-      if (throttling_history_ == ThrottlingHistory::kInitial)
-        throttling_history_ = ThrottlingHistory::kThrottled;
-      else if (throttling_history_ == ThrottlingHistory::kNotThrottled)
-        throttling_history_ = ThrottlingHistory::kPartiallyThrottled;
-      break;
-    case scheduler::SchedulingLifecycleState::kNotThrottled:
-      if (throttling_history_ == ThrottlingHistory::kInitial)
-        throttling_history_ = ThrottlingHistory::kNotThrottled;
-      else if (throttling_history_ == ThrottlingHistory::kThrottled)
-        throttling_history_ = ThrottlingHistory::kPartiallyThrottled;
-      ShowConsoleMessageIfNeeded();
-      break;
-    case scheduler::SchedulingLifecycleState::kStopped:
-      throttling_history_ = ThrottlingHistory::kStopped;
-      break;
-  }
+  if (state == scheduler::SchedulingLifecycleState::kNotThrottled)
+    ShowConsoleMessageIfNeeded();
+
   MaybeRun();
 }
 
@@ -700,9 +566,6 @@
   running_requests_.insert(id);
   if (throttleable)
     running_throttleable_requests_.insert(id);
-  if (running_requests_.size() > maximum_running_requests_seen_) {
-    maximum_running_requests_seen_ = running_requests_.size();
-  }
   client->Run();
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
index 6b87a470..f56a3156 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
@@ -310,21 +310,9 @@
 
   HashSet<ClientId> running_throttleable_requests_;
 
-  // Largest number of running requests seen so far.
-  unsigned maximum_running_requests_seen_ = 0;
-
   // Holds a flag to omit repeating console messages.
   bool is_console_info_shown_ = false;
 
-  enum class ThrottlingHistory {
-    kInitial,
-    kThrottled,
-    kNotThrottled,
-    kPartiallyThrottled,
-    kStopped,
-  };
-  ThrottlingHistory throttling_history_ = ThrottlingHistory::kInitial;
-
   scheduler::SchedulingLifecycleState frame_scheduler_lifecycle_state_ =
       scheduler::SchedulingLifecycleState::kNotThrottled;
 
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 a046523..9d4426ed 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -39,7 +39,7 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/public/platform/code_cache_loader.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_data.h"
@@ -504,7 +504,7 @@
   const ResourceResponse& response = resource_->GetResponse();
   if (deferred_finish_loading_info_) {
     // Create a copy to pass a reference.
-    const std::vector<network::cors::PreflightTimingInfo>
+    const WebVector<network::cors::PreflightTimingInfo>
         cors_preflight_timing_info =
             deferred_finish_loading_info_->cors_preflight_timing_info;
     DidFinishLoading(deferred_finish_loading_info_->response_end,
@@ -1111,10 +1111,9 @@
                           TRACE_ID_LOCAL(resource_->InspectorId())),
       "endData", EndResourceLoadData(RequestOutcome::kSuccess));
 
-  fetcher_->HandleLoaderFinish(
-      resource_.Get(), base::TimeTicks(),
-      ResourceFetcher::kDidFinishFirstPartInMultipart, 0, false,
-      std::vector<network::cors::PreflightTimingInfo>());
+  fetcher_->HandleLoaderFinish(resource_.Get(), base::TimeTicks(),
+                               ResourceFetcher::kDidFinishFirstPartInMultipart,
+                               0, false, {});
 }
 
 void ResourceLoader::DidFinishLoading(
@@ -1123,7 +1122,7 @@
     int64_t encoded_body_length,
     int64_t decoded_body_length,
     bool should_report_corb_blocking,
-    const std::vector<network::cors::PreflightTimingInfo>&
+    const WebVector<network::cors::PreflightTimingInfo>&
         cors_preflight_timing_info) {
   resource_->SetEncodedDataLength(encoded_data_length);
   resource_->SetEncodedBodyLength(encoded_body_length);
@@ -1291,8 +1290,7 @@
     FinishedCreatingBlob(blob);
   }
   DidFinishLoading(CurrentTimeTicks(), encoded_data_length, encoded_body_length,
-                   decoded_body_length, false,
-                   std::vector<network::cors::PreflightTimingInfo>());
+                   decoded_body_length, false, {});
 }
 
 void ResourceLoader::RequestAsynchronously(const ResourceRequest& request) {
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 c76e448c..df86e10e 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
@@ -30,12 +30,11 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_H_
 
 #include <memory>
-#include <vector>
 #include "base/containers/span.h"
 #include "base/gtest_prod_util.h"
 #include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_loader.h"
 #include "third_party/blink/public/platform/web_url_loader_client.h"
@@ -136,7 +135,7 @@
       int64_t encoded_body_length,
       int64_t decoded_body_length,
       bool should_report_corb_blocking,
-      const std::vector<network::cors::PreflightTimingInfo>&) override;
+      const WebVector<network::cors::PreflightTimingInfo>&) override;
   void DidFail(const WebURLError&,
                int64_t encoded_data_length,
                int64_t encoded_body_length,
@@ -240,7 +239,7 @@
   struct DeferredFinishLoadingInfo {
     base::TimeTicks response_end;
     bool should_report_corb_blocking;
-    std::vector<network::cors::PreflightTimingInfo> cors_preflight_timing_info;
+    WebVector<network::cors::PreflightTimingInfo> cors_preflight_timing_info;
   };
   base::Optional<DeferredFinishLoadingInfo> deferred_finish_loading_info_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_body_loader_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
index 67c41b4..d4a63c1f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "mojo/public/c/system/data_pipe.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_url_loader.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
index 060b722..0cc04b6 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -37,7 +37,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/mojom/cors.mojom-blink.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
 #include "third_party/blink/public/platform/web_url_request.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
index f4272c6..0859399 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
@@ -223,8 +223,8 @@
     time_t valid_to,
     const Vector<AtomicString>& certificate,
     const SignedCertificateTimestampList& sct_list) {
-  DCHECK_NE(security_style_, kSecurityStyleUnknown);
-  DCHECK_NE(security_style_, kSecurityStyleUnauthenticated);
+  DCHECK_NE(security_style_, kWebSecurityStyleUnknown);
+  DCHECK_NE(security_style_, kWebSecurityStyleNeutral);
   security_details_ = SecurityDetails(
       protocol, key_exchange, key_exchange_group, cipher, mac, subject_name,
       san_list, issuer, valid_from, valid_to, certificate, sct_list);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
index c7d4e8a2..e7b39c1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -67,12 +67,6 @@
     kHTTPVersion_1_1,
     kHTTPVersion_2_0
   };
-  enum SecurityStyle : uint8_t {
-    kSecurityStyleUnknown,
-    kSecurityStyleUnauthenticated,
-    kSecurityStyleAuthenticationBroken,
-    kSecurityStyleAuthenticated
-  };
 
   enum CTPolicyCompliance {
     kCTPolicyComplianceDetailsNotAvailable,
@@ -287,8 +281,8 @@
   bool IsLegacyTLSVersion() const { return is_legacy_tls_version_; }
   void SetIsLegacyTLSVersion(bool value) { is_legacy_tls_version_ = value; }
 
-  SecurityStyle GetSecurityStyle() const { return security_style_; }
-  void SetSecurityStyle(SecurityStyle security_style) {
+  WebSecurityStyle GetSecurityStyle() const { return security_style_; }
+  void SetSecurityStyle(WebSecurityStyle security_style) {
     security_style_ = security_style;
   }
 
@@ -532,7 +526,7 @@
   // The security style of the resource.
   // This only contains a valid value when the DevTools Network domain is
   // enabled. (Otherwise, it contains a default value of Unknown.)
-  SecurityStyle security_style_ = kSecurityStyleUnknown;
+  WebSecurityStyle security_style_ = kWebSecurityStyleUnknown;
 
   // Security details of this request's connection.
   base::Optional<SecurityDetails> security_details_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h b/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
index 17ab66d..1dbb457 100644
--- a/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
+++ b/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SCRIPT_FETCH_OPTIONS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SCRIPT_FETCH_OPTIONS_H_
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
diff --git a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc
index 251c345..f0682f4 100644
--- a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc
+++ b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/allowed_by_nosniff.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/loader/fetch/https_state.h"
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h b/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h
index d346771..0a2ae5a9 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h
@@ -87,11 +87,17 @@
   MediaStreamComponent* AudioComponent(unsigned index) const {
     return audio_components_[index].Get();
   }
+  const HeapVector<Member<MediaStreamComponent>>& AudioComponents() const {
+    return audio_components_;
+  }
 
   unsigned NumberOfVideoComponents() const { return video_components_.size(); }
   MediaStreamComponent* VideoComponent(unsigned index) const {
     return video_components_[index].Get();
   }
+  const HeapVector<Member<MediaStreamComponent>>& VideoComponents() const {
+    return video_components_;
+  }
 
   void AddComponent(MediaStreamComponent*);
   void RemoveComponent(MediaStreamComponent*);
diff --git a/third_party/blink/renderer/platform/mhtml/mhtml_archive.h b/third_party/blink/renderer/platform/mhtml/mhtml_archive.h
index a2a011e9..a7690a2a 100644
--- a/third_party/blink/renderer/platform/mhtml/mhtml_archive.h
+++ b/third_party/blink/renderer/platform/mhtml/mhtml_archive.h
@@ -31,7 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_MHTML_ARCHIVE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_MHTML_ARCHIVE_H_
 
-#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-shared.h"
+#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
diff --git a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
index 21cb9e5..39a49ae0 100644
--- a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
+++ b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 typemaps = [
+  "//ui//gfx//mojo//transform.typemap",
   "//third_party/blink/renderer/core/messaging/blink_cloneable_message.typemap",
   "//third_party/blink/renderer/core/messaging/blink_transferable_message.typemap",
   "//third_party/blink/renderer/modules/indexeddb/indexed_db_blink.typemap",
diff --git a/third_party/blink/renderer/platform/mojo/geometry.typemap b/third_party/blink/renderer/platform/mojo/geometry.typemap
index 5675e71e..525042f 100644
--- a/third_party/blink/renderer/platform/mojo/geometry.typemap
+++ b/third_party/blink/renderer/platform/mojo/geometry.typemap
@@ -4,8 +4,11 @@
 
 mojom = "//ui/gfx/geometry/mojo/geometry.mojom"
 public_headers = [
+  "//ui/gfx/geometry/quaternion.h",
+  "//ui/gfx/geometry/vector3d_f.h",
   "//third_party/blink/public/platform/web_float_rect.h",
   "//third_party/blink/public/platform/web_float_point.h",
+  "//third_party/blink/public/platform/web_float_point_3d.h",
   "//third_party/blink/public/platform/web_point.h",
   "//third_party/blink/public/platform/web_rect.h",
   "//third_party/blink/public/platform/web_size.h",
@@ -29,7 +32,10 @@
 type_mappings = [
   "gfx.mojom.Point=::blink::WebPoint",
   "gfx.mojom.PointF=::blink::WebFloatPoint",
+  "gfx.mojom.Point3F=::blink::WebFloatPoint3D",
+  "gfx.mojom.Quaternion=gfx::Quaternion",
   "gfx.mojom.RectF=::blink::WebFloatRect",
   "gfx.mojom.Rect=::blink::WebRect",
   "gfx.mojom.Size=::blink::WebSize",
+  "gfx.mojom.Vector3dF=gfx::Vector3dF",
 ]
diff --git a/third_party/blink/renderer/platform/mojo/geometry_struct_traits.cc b/third_party/blink/renderer/platform/mojo/geometry_struct_traits.cc
index b721a83..663dcfd3 100644
--- a/third_party/blink/renderer/platform/mojo/geometry_struct_traits.cc
+++ b/third_party/blink/renderer/platform/mojo/geometry_struct_traits.cc
@@ -50,6 +50,15 @@
   return true;
 }
 
+bool StructTraits<gfx::mojom::Point3FDataView, ::blink::WebFloatPoint3D>::Read(
+    gfx::mojom::Point3FDataView data,
+    ::blink::WebFloatPoint3D* out) {
+  out->x = data.x();
+  out->y = data.y();
+  out->z = data.z();
+  return true;
+}
+
 // static
 bool StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize>::Read(
     gfx::mojom::SizeDataView data,
diff --git a/third_party/blink/renderer/platform/mojo/geometry_struct_traits.h b/third_party/blink/renderer/platform/mojo/geometry_struct_traits.h
index 9dda7b8..fcd2e21 100644
--- a/third_party/blink/renderer/platform/mojo/geometry_struct_traits.h
+++ b/third_party/blink/renderer/platform/mojo/geometry_struct_traits.h
@@ -6,11 +6,12 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_GEOMETRY_STRUCT_TRAITS_H_
 
 #include "third_party/blink/public/platform/web_float_point.h"
+#include "third_party/blink/public/platform/web_float_point_3d.h"
 #include "third_party/blink/public/platform/web_float_rect.h"
 #include "third_party/blink/public/platform/web_point.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_size.h"
-#include "ui/gfx/geometry/mojo/geometry.mojom-shared.h"
+#include "ui/gfx/geometry/mojo/geometry.mojom-blink.h"
 
 namespace mojo {
 
@@ -29,6 +30,15 @@
 };
 
 template <>
+struct StructTraits<gfx::mojom::Point3FDataView, ::blink::WebFloatPoint3D> {
+  static float x(const gfx::Point3F& p) { return p.x(); }
+  static float y(const gfx::Point3F& p) { return p.y(); }
+  static float z(const gfx::Point3F& p) { return p.z(); }
+  static bool Read(gfx::mojom::Point3FDataView data,
+                   ::blink::WebFloatPoint3D* out);
+};
+
+template <>
 struct StructTraits<gfx::mojom::RectFDataView, ::blink::WebFloatRect> {
   static float x(const ::blink::WebFloatRect& rect) { return rect.x; }
   static float y(const ::blink::WebFloatRect& rect) { return rect.y; }
diff --git a/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc b/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc
index 3333a12..26c74b1a 100644
--- a/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc
+++ b/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc
@@ -38,10 +38,9 @@
     std::move(callback).Run(p);
   }
 
-  void EchoPoint3F(::gfx::mojom::blink::Point3FPtr p,
+  void EchoPoint3F(const WebFloatPoint3D& p,
                    EchoPoint3FCallback callback) override {
-    // The type map is not specified.
-    NOTREACHED();
+    std::move(callback).Run(p);
   }
 
   void EchoSize(const WebSize& s, EchoSizeCallback callback) override {
@@ -84,16 +83,14 @@
     NOTREACHED();
   }
 
-  void EchoVector3dF(gfx::mojom::blink::Vector3dFPtr,
-                     EchoVector3dFCallback) override {
-    // The type map is not specified.
-    NOTREACHED();
+  void EchoVector3dF(const gfx::Vector3dF& v,
+                     EchoVector3dFCallback callback) override {
+    std::move(callback).Run(v);
   }
 
-  void EchoQuaternion(gfx::mojom::blink::QuaternionPtr,
-                      EchoQuaternionCallback) override {
-    // The type map is not specified.
-    NOTREACHED();
+  void EchoQuaternion(const gfx::Quaternion& q,
+                      EchoQuaternionCallback callback) override {
+    std::move(callback).Run(q);
   }
 
   mojo::BindingSet<gfx::mojom::blink::GeometryTraitsTestService>
@@ -136,6 +133,17 @@
   EXPECT_EQ(input, output);
 }
 
+TEST_F(GeometryStructTraitsTest, Point3D) {
+  const float kX = 1.234;
+  const float kY = 5.678;
+  const float kZ = 9.098;
+  WebFloatPoint3D input(kX, kY, kZ);
+  gfx::mojom::blink::GeometryTraitsTestServicePtr proxy = GetTraitsTestProxy();
+  WebFloatPoint3D output;
+  proxy->EchoPoint3F(input, &output);
+  EXPECT_EQ(input, output);
+}
+
 TEST_F(GeometryStructTraitsTest, Rect) {
   const float kX = 1;
   const float kY = 2;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a0cb82e..139d8a30 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -345,6 +345,11 @@
       name: "CSSColorScheme",
     },
     {
+      // CSS min(), max() and clamp()
+      // https://drafts.csswg.org/css-values-4/#comp-func
+      name: "CSSComparisonFunctions",
+    },
+    {
       name: "CSSFocusVisible",
       status: "experimental",
     },
@@ -425,6 +430,11 @@
       name: "CSSVariables2",
       status: "experimental",
     },
+    // Support for @property rules.
+    {
+      name: "CSSVariables2AtProperty",
+      depends_on: ["CSSVariables2"],
+    },
     // Support for registered custom properties with <image> syntax.
     {
       name: "CSSVariables2ImageValues",
@@ -685,7 +695,6 @@
     },
     {
       name: "HeapCompaction",
-      status: "stable",
     },
     {
       name: "HeapConcurrentMarking",
@@ -699,7 +708,6 @@
     },
     {
       name: "HeapUnifiedGCScheduling",
-      status: "stable",
     },
     {
       name: "HrefTranslate",
@@ -916,10 +924,22 @@
       status: "experimental",
     },
     {
+      name: "MediaSourceInWorkers",
+      status: "experimental",
+    },
+    {
       name: "MediaSourceNewAbortAndDuration",
       status: "experimental",
     },
     {
+      // This is used in cases of mixed specification of stable and
+      // experimental MediaSource features, such as in the IDL for an interface
+      // constructor where exposure of the constructor in Window vs other
+      // contexts can vary in stable vs experimental.
+      name: "MediaSourceStable",
+      status: "stable",
+    },
+    {
       name: "MergeBlockingNonBlockingPools",
     },
     // Support for META tag for setting color-scheme used for opting into dark
diff --git a/third_party/blink/renderer/platform/scheduler/common/thread_cpu_throttler.cc b/third_party/blink/renderer/platform/scheduler/common/thread_cpu_throttler.cc
index 6c2458e..c184e22 100644
--- a/third_party/blink/renderer/platform/scheduler/common/thread_cpu_throttler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/thread_cpu_throttler.cc
@@ -7,7 +7,7 @@
 #include "base/atomicops.h"
 #include "base/macros.h"
 #include "base/memory/singleton.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/threading/platform_thread.h"
 #include "build/build_config.h"
 
@@ -57,7 +57,7 @@
 
   base::PlatformThreadHandle throttled_thread_handle_;
   base::PlatformThreadHandle throttling_thread_handle_;
-  base::CancellationFlag cancellation_flag_;
+  base::AtomicFlag cancellation_flag_;
 
   DISALLOW_COPY_AND_ASSIGN(ThrottlingThread);
 };
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc
index 5443e75..f47ccc61 100644
--- a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc
@@ -6,6 +6,8 @@
 
 #include <cstdint>
 
+#include "base/debug/stack_trace.h"
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -94,27 +96,21 @@
       task_queue->SetTimeDomain(thread_scheduler_->GetActiveTimeDomain());
       task_queue->RemoveFence();
     }
-    if (map_entry.second.throttling_ref_count != 0)
-      task_queue->SetObserver(nullptr);
   }
 
   thread_scheduler_->UnregisterTimeDomain(time_domain_.get());
 }
 
 void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) {
-  std::pair<TaskQueueMap::iterator, bool> insert_result =
-      queue_details_.insert(std::make_pair(task_queue, Metadata()));
-  insert_result.first->second.throttling_ref_count++;
-
-  // If ref_count is 1, the task queue is newly throttled.
-  if (insert_result.first->second.throttling_ref_count != 1)
+  std::pair<TaskQueueMap::iterator, bool> insert_result = queue_details_.insert(
+      std::make_pair(task_queue, Metadata(task_queue, this)));
+  if (!insert_result.first->second.IncrementRefCount())
     return;
 
+  // Task queue is newly throttled.
   TRACE_EVENT1("renderer.scheduler", "TaskQueueThrottler_TaskQueueThrottled",
                "task_queue", task_queue);
 
-  task_queue->SetObserver(this);
-
   if (!allow_throttling_)
     return;
 
@@ -138,16 +134,12 @@
 
   if (iter == queue_details_.end())
     return;
-  if (iter->second.throttling_ref_count == 0)
-    return;
-  if (--iter->second.throttling_ref_count != 0)
+  if (!iter->second.DecrementRefCount())
     return;
 
   TRACE_EVENT1("renderer.scheduler", "TaskQueueThrottler_TaskQueueUnthrottled",
                "task_queue", task_queue);
 
-  task_queue->SetObserver(nullptr);
-
   MaybeDeleteQueueMetadata(iter);
 
   if (!allow_throttling_)
@@ -164,7 +156,7 @@
   auto find_it = queue_details_.find(task_queue);
   if (find_it == queue_details_.end())
     return false;
-  return find_it->second.throttling_ref_count > 0;
+  return find_it->second.throttling_ref_count() > 0;
 }
 
 void TaskQueueThrottler::ShutdownTaskQueue(TaskQueue* task_queue) {
@@ -177,7 +169,8 @@
   task_queue->SetTimeDomain(thread_scheduler_->GetActiveTimeDomain());
   task_queue->RemoveFence();
 
-  std::unordered_set<BudgetPool*> budget_pools = find_it->second.budget_pools;
+  // Copy intended.
+  std::unordered_set<BudgetPool*> budget_pools = find_it->second.budget_pools();
   for (BudgetPool* budget_pool : budget_pools) {
     budget_pool->UnregisterQueue(task_queue);
   }
@@ -214,7 +207,7 @@
   if (find_it == queue_details_.end())
     return;
 
-  for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+  for (BudgetPool* budget_pool : find_it->second.budget_pools()) {
     budget_pool->OnQueueNextWakeUpChanged(queue, now, next_wake_up);
   }
 
@@ -308,7 +301,7 @@
   if (find_it == queue_details_.end())
     return;
 
-  for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+  for (BudgetPool* budget_pool : find_it->second.budget_pools()) {
     budget_pool->RecordTaskRunTime(task_queue, start_time, end_time);
   }
 }
@@ -410,7 +403,7 @@
 
   bool has_new_tasks_only_block = false;
 
-  for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+  for (BudgetPool* budget_pool : find_it->second.budget_pools()) {
     if (!budget_pool->CanRunTasksAt(now, false)) {
       if (budget_pool->GetBlockType() == QueueBlockType::kAllTasks)
         return QueueBlockType::kAllTasks;
@@ -444,8 +437,9 @@
   state->BeginDictionary("queue_details");
   for (const auto& map_entry : queue_details_) {
     state->BeginDictionaryWithCopiedName(PointerToString(map_entry.first));
-    state->SetInteger("throttling_ref_count",
-                      static_cast<int>(map_entry.second.throttling_ref_count));
+    state->SetInteger(
+        "throttling_ref_count",
+        static_cast<int>(map_entry.second.throttling_ref_count()));
     state->EndDictionary();
   }
   state->EndDictionary();
@@ -454,24 +448,24 @@
 void TaskQueueThrottler::AddQueueToBudgetPool(TaskQueue* queue,
                                               BudgetPool* budget_pool) {
   std::pair<TaskQueueMap::iterator, bool> insert_result =
-      queue_details_.insert(std::make_pair(queue, Metadata()));
+      queue_details_.insert(std::make_pair(queue, Metadata(queue, this)));
 
   Metadata& metadata = insert_result.first->second;
 
-  DCHECK(metadata.budget_pools.find(budget_pool) ==
-         metadata.budget_pools.end());
+  DCHECK(metadata.budget_pools().find(budget_pool) ==
+         metadata.budget_pools().end());
 
-  metadata.budget_pools.insert(budget_pool);
+  metadata.budget_pools().insert(budget_pool);
 }
 
 void TaskQueueThrottler::RemoveQueueFromBudgetPool(TaskQueue* queue,
                                                    BudgetPool* budget_pool) {
   auto find_it = queue_details_.find(queue);
   DCHECK(find_it != queue_details_.end() &&
-         find_it->second.budget_pools.find(budget_pool) !=
-             find_it->second.budget_pools.end());
+         find_it->second.budget_pools().find(budget_pool) !=
+             find_it->second.budget_pools().end());
 
-  find_it->second.budget_pools.erase(budget_pool);
+  find_it->second.budget_pools().erase(budget_pool);
 
   MaybeDeleteQueueMetadata(find_it);
 }
@@ -489,7 +483,7 @@
   if (find_it == queue_details_.end())
     return next_run_time;
 
-  for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+  for (BudgetPool* budget_pool : find_it->second.budget_pools()) {
     next_run_time = std::max(
         next_run_time, budget_pool->GetNextAllowedRunTime(desired_run_time));
   }
@@ -504,7 +498,7 @@
   if (find_it == queue_details_.end())
     return true;
 
-  for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+  for (BudgetPool* budget_pool : find_it->second.budget_pools()) {
     if (!budget_pool->CanRunTasksAt(moment, is_wake_up))
       return false;
   }
@@ -521,7 +515,7 @@
   if (find_it == queue_details_.end())
     return result;
 
-  for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+  for (BudgetPool* budget_pool : find_it->second.budget_pools()) {
     result = Min(result, budget_pool->GetTimeTasksCanRunUntil(now, is_wake_up));
   }
 
@@ -529,8 +523,10 @@
 }
 
 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) {
-  if (it->second.throttling_ref_count == 0 && it->second.budget_pools.empty())
+  if (it->second.throttling_ref_count() == 0 &&
+      it->second.budget_pools().empty()) {
     queue_details_.erase(it);
+  }
 }
 
 void TaskQueueThrottler::DisableThrottling() {
@@ -540,7 +536,7 @@
   allow_throttling_ = false;
 
   for (const auto& map_entry : queue_details_) {
-    if (map_entry.second.throttling_ref_count == 0)
+    if (map_entry.second.throttling_ref_count() == 0)
       continue;
 
     TaskQueue* queue = map_entry.first;
@@ -564,7 +560,7 @@
   LazyNow lazy_now(tick_clock_);
 
   for (const auto& map_entry : queue_details_) {
-    if (map_entry.second.throttling_ref_count == 0)
+    if (map_entry.second.throttling_ref_count() == 0)
       continue;
 
     TaskQueue* queue = map_entry.first;
@@ -579,5 +575,41 @@
   TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler_EnableThrottling");
 }
 
+TaskQueueThrottler::Metadata::Metadata(base::sequence_manager::TaskQueue* queue,
+                                       TaskQueueThrottler* throttler)
+    : queue_(queue), throttler_(throttler) {}
+
+TaskQueueThrottler::Metadata::~Metadata() {
+  if (throttling_ref_count_ > 0)
+    queue_->SetObserver(nullptr);
+}
+
+bool TaskQueueThrottler::Metadata::IncrementRefCount() {
+  if (throttling_ref_count_++ == 0) {
+    queue_->SetObserver(this);
+    return true;
+  }
+  return false;
+}
+
+// Returns true if |throttling_ref_count_| is now zero.
+bool TaskQueueThrottler::Metadata::DecrementRefCount() {
+  if (throttling_ref_count_ == 0)
+    return false;
+  if (--throttling_ref_count_ == 0) {
+    queue_->SetObserver(nullptr);
+    return true;
+  }
+  return false;
+}
+
+void TaskQueueThrottler::Metadata::OnPostTask(base::Location from_here,
+                                              base::TimeDelta delay) {}
+
+void TaskQueueThrottler::Metadata::OnQueueNextWakeUpChanged(
+    base::TimeTicks wake_up) {
+  throttler_->OnQueueNextWakeUpChanged(queue_, wake_up);
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h
index b29f409b..a3ab005 100644
--- a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h
+++ b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h
@@ -94,9 +94,7 @@
 // See IncreaseThrottleRefCount & DecreaseThrottleRefCount.
 //
 // This class is main-thread only.
-class PLATFORM_EXPORT TaskQueueThrottler
-    : public base::sequence_manager::TaskQueue::Observer,
-      public BudgetPoolController {
+class PLATFORM_EXPORT TaskQueueThrottler : public BudgetPoolController {
  public:
   // We use tracing controller from ThreadSchedulerImpl because an instance
   // of this class is always its member, so has the same lifetime.
@@ -105,9 +103,8 @@
 
   ~TaskQueueThrottler() override;
 
-  // TaskQueue::Observer implementation:
   void OnQueueNextWakeUpChanged(base::sequence_manager::TaskQueue* queue,
-                                base::TimeTicks wake_up) override;
+                                base::TimeTicks wake_up);
 
   // BudgetPoolController implementation:
   void AddQueueToBudgetPool(base::sequence_manager::TaskQueue* queue,
@@ -159,12 +156,36 @@
                    base::TimeTicks now) const;
 
  private:
-  struct Metadata {
-    Metadata() : throttling_ref_count(0) {}
+  class Metadata : public base::sequence_manager::TaskQueue::Observer {
+   public:
+    Metadata(base::sequence_manager::TaskQueue* queue,
+             TaskQueueThrottler* throttler);
 
-    size_t throttling_ref_count;
+    ~Metadata() override;
 
-    std::unordered_set<BudgetPool*> budget_pools;
+    // Returns true if |throttling_ref_count_| was zero.
+    bool IncrementRefCount();
+
+    // Returns true if |throttling_ref_count_| is now zero.
+    bool DecrementRefCount();
+
+    // TaskQueue::Observer implementation:
+    void OnPostTask(base::Location from_here, base::TimeDelta delay) override;
+    void OnQueueNextWakeUpChanged(base::TimeTicks wake_up) override;
+
+    size_t throttling_ref_count() const { return throttling_ref_count_; }
+
+    const std::unordered_set<BudgetPool*>& budget_pools() const {
+      return budget_pools_;
+    }
+
+    std::unordered_set<BudgetPool*>& budget_pools() { return budget_pools_; }
+
+   private:
+    base::sequence_manager::TaskQueue* const queue_;
+    TaskQueueThrottler* const throttler_;
+    size_t throttling_ref_count_ = 0;
+    std::unordered_set<BudgetPool*> budget_pools_;
   };
   using TaskQueueMap =
       std::unordered_map<base::sequence_manager::TaskQueue*, Metadata>;
diff --git a/third_party/blink/renderer/platform/testing/empty_web_media_player.h b/third_party/blink/renderer/platform/testing/empty_web_media_player.h
index 13598d2..cb234f8 100644
--- a/third_party/blink/renderer/platform/testing/empty_web_media_player.h
+++ b/third_party/blink/renderer/platform/testing/empty_web_media_player.h
@@ -61,6 +61,7 @@
              cc::PaintFlags&,
              int already_uploaded_id,
              VideoFrameUploadMetadata*) override {}
+  bool HasAvailableVideoFrame() const override { return false; }
   base::WeakPtr<WebMediaPlayer> AsWeakPtr() override { return nullptr; }
 };
 
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
index 7d1234b..768c64b2 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
@@ -80,7 +80,7 @@
                      webrtc::DegradationPreference,
                      WebRTCVoidRequest) override {}
   void GetStats(WebRTCStatsReportCallback,
-                const std::vector<webrtc::NonStandardGroupId>&) override {}
+                const WebVector<webrtc::NonStandardGroupId>&) override {}
   void SetStreams(
       const blink::WebVector<blink::WebString>& stream_ids) override {}
 
@@ -136,7 +136,7 @@
     return WebVector<std::unique_ptr<WebRTCRtpSource>>();
   }
   void GetStats(WebRTCStatsReportCallback,
-                const std::vector<webrtc::NonStandardGroupId>&) override {}
+                const WebVector<webrtc::NonStandardGroupId>&) override {}
   std::unique_ptr<webrtc::RtpParameters> GetParameters() const override {
     return nullptr;
   }
@@ -247,14 +247,14 @@
   return true;
 }
 
-std::vector<std::unique_ptr<WebRTCRtpTransceiver>>
+WebVector<std::unique_ptr<WebRTCRtpTransceiver>>
 MockWebRTCPeerConnectionHandler::CreateOffer(
     const WebRTCSessionDescriptionRequest&,
     const WebMediaConstraints&) {
   return {};
 }
 
-std::vector<std::unique_ptr<WebRTCRtpTransceiver>>
+WebVector<std::unique_ptr<WebRTCRtpTransceiver>>
 MockWebRTCPeerConnectionHandler::CreateOffer(
     const WebRTCSessionDescriptionRequest&,
     const WebRTCOfferOptions&) {
@@ -320,7 +320,7 @@
 
 void MockWebRTCPeerConnectionHandler::GetStats(
     blink::WebRTCStatsReportCallback,
-    const std::vector<webrtc::NonStandardGroupId>&) {}
+    const WebVector<webrtc::NonStandardGroupId>&) {}
 
 webrtc::RTCErrorOr<std::unique_ptr<WebRTCRtpTransceiver>>
 MockWebRTCPeerConnectionHandler::AddTransceiverWithTrack(
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
index 17af9f6..577edefb 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
@@ -28,10 +27,10 @@
   bool Initialize(const webrtc::PeerConnectionInterface::RTCConfiguration&,
                   const WebMediaConstraints&) override;
 
-  std::vector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
+  WebVector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
       const WebRTCSessionDescriptionRequest&,
       const WebMediaConstraints&) override;
-  std::vector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
+  WebVector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
       const WebRTCSessionDescriptionRequest&,
       const WebRTCOfferOptions&) override;
   void CreateAnswer(const WebRTCSessionDescriptionRequest&,
@@ -54,7 +53,7 @@
       const webrtc::PeerConnectionInterface::RTCConfiguration&) override;
   void GetStats(const WebRTCStatsRequest&) override;
   void GetStats(WebRTCStatsReportCallback,
-                const std::vector<webrtc::NonStandardGroupId>&) override;
+                const WebVector<webrtc::NonStandardGroupId>&) override;
   webrtc::RTCErrorOr<std::unique_ptr<WebRTCRtpTransceiver>>
   AddTransceiverWithTrack(const WebMediaStreamTrack&,
                           const webrtc::RtpTransceiverInit&) override;
diff --git a/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc b/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
index 5a040c61..c9d777e7 100644
--- a/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
+++ b/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
@@ -30,7 +30,7 @@
 
 #include "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
 
-#include "services/network/public/mojom/cors.mojom-shared.h"
+#include "services/network/public/mojom/cors.mojom-blink.h"
 #include "third_party/blink/renderer/platform/weborigin/known_ports.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
diff --git a/third_party/blink/renderer/platform/weborigin/referrer.h b/third_party/blink/renderer/platform/weborigin/referrer.h
index 98fe744..2a647708 100644
--- a/third_party/blink/renderer/platform/weborigin/referrer.h
+++ b/third_party/blink/renderer/platform/weborigin/referrer.h
@@ -31,7 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REFERRER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REFERRER_H_
 
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index 215c7b71a..1acaaf1 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -33,7 +33,7 @@
 #include <stdint.h>
 
 #include "base/stl_util.h"
-#include "services/network/public/mojom/cors.mojom-shared.h"
+#include "services/network/public/mojom/cors.mojom-blink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/blob/blob_url.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.h b/third_party/blink/renderer/platform/weborigin/security_policy.h
index a6c2e0a0..d041d1d9 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.h
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.h
@@ -29,8 +29,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_POLICY_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_POLICY_H_
 
-#include "services/network/public/mojom/cors_origin_pattern.mojom-shared.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/cors_origin_pattern.mojom-blink.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/referrer.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
index baea980..32414fa 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
@@ -30,7 +30,7 @@
 
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 
-#include "services/network/public/mojom/cors.mojom-shared.h"
+#include "services/network/public/mojom/cors.mojom-blink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 62ff8a6..22a5ba5 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -110,6 +110,9 @@
             # //base/allocator/partition_allocator/oom_callback.h.
             'base::SetPartitionAllocOomCallback',
 
+            # //base/containers/adapters.h
+            'base::Reversed',
+
             # //base/metrics/histogram_functions.h
             'base::UmaHistogram.+',
 
@@ -220,6 +223,7 @@
             # Chromium geometry types.
             'gfx::Point',
             'gfx::Point3F',
+            'gfx::Quaternion',
             'gfx::Rect',
             'gfx::RectF',
             'gfx::RRectF',
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder.py b/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder.py
index 66c72fab..b9643fd 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder.py
@@ -111,8 +111,16 @@
             metadata_file_contents = "disabled: build_wpt_metadata.py"
         else:
             # For individual tests, we create one file per test, with the name
-            # of the test in the file as well.
-            test_filename = test_name_parts[-1]
+            # of the test in the file as well. This name can contain variants.
+            test_name = test_name_parts[-1]
+
+            # If the test name uses variants, we want to omit them from the
+            # filename.
+            if "?" in test_name:
+                # Update test_name_parts so the created metadata file doesn't
+                # include any variants
+                test_name_parts[-1] = test_name.split("?")[0]
+
             # Append `.ini` to the test filename to indicate it's the metadata
             # file.
             test_name_parts[-1] += ".ini"
@@ -123,5 +131,5 @@
             # The contents of the metadata file is two lines:
             # 1. the test name inside square brackets
             # 2. an indented line with the test status and reason
-            metadata_file_contents = ("[%s]\n  disabled: build_wpt_metadata.py" % test_filename)
+            metadata_file_contents = ("[%s]\n  disabled: build_wpt_metadata.py" % test_name)
         return metadata_filename, metadata_file_contents
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder_unittest.py
index 3076914..a26323b 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_metadata_builder_unittest.py
@@ -45,6 +45,17 @@
         self.assertEqual("test.html.ini", filename)
         self.assertEqual("[test.html]\n  disabled: build_wpt_metadata.py", contents)
 
+    def test_skipped_test_with_variants(self):
+        """A skipped WPT tests with variants should get a test-specific metadata file."""
+        test_name = "external/wpt/test.html?foo=bar"
+        expectations = _make_expectation(self.port, test_name, "SKIP")
+        metadata_builder = WPTMetadataBuilder(expectations)
+        filename, contents = metadata_builder.get_metadata_filename_and_contents(test_name)
+        # The metadata file name should not include variants
+        self.assertEqual("test.html.ini", filename)
+        # ..but the contents of the file should include variants in the test name
+        self.assertEqual("[test.html?foo=bar]\n  disabled: build_wpt_metadata.py", contents)
+
     def test_skipped_directory(self):
         """A skipped WPT directory should get a dir-wide metadata file."""
         test_name = "external/wpt/test_dir/"
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
index 7eb3050..49ab56e 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -62,38 +62,25 @@
 Bug(none) compositing/layer-creation/scroll-partial-update.html [ Failure ]
 Bug(none) compositing/masks/mask-with-removed-filters.html [ Failure ]
 Bug(none) compositing/nested-multicol-mask-composited-filter-crash.html [ Crash ]
-Bug(none) compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective.html [ Failure ]
-Bug(none) compositing/overflow/accelerated-scrolling-with-clip-path.html [ Failure ]
 Bug(none) compositing/overflow/avoid-ancestor-clip-for-scroll-children.html [ Failure ]
 Bug(none) compositing/overflow/clip-descendents.html [ Failure ]
-Bug(none) compositing/overflow/content-loses-scrollbars.html [ Failure ]
-Bug(none) compositing/overflow/content-gains-scrollbars.html [ Failure ]
 Bug(none) compositing/overflow/opt-in-if-composited.html [ Failure ]
-Bug(none) compositing/overflow/overflow-scroll-background-fractional-offset.html [ Failure ]
+crbug.com/979367 compositing/overflow/overflow-scroll-background-fractional-offset.html [ Failure ]
 Bug(none) compositing/overflow/overflow-scroll-background-opaque-to-transparent.html [ Failure ]
 Bug(none) compositing/overflow/overflow-scroll-background-transparent-to-opaque.html [ Failure ]
 Bug(none) compositing/overflow/overflow-scroll-content-fractional-offset.html [ Failure ]
 Bug(none) compositing/overflow/overflow-scroll-with-opaque-background-will-change.html [ Failure ]
 Bug(none) compositing/overflow/overflow-scroll-with-opaque-background.html [ Failure ]
 Bug(none) compositing/overflow/overflow-scroll-with-transparent-background.html [ Failure ]
-Bug(none) compositing/overflow/overflow-scrollbar-layers.html [ Failure ]
-Bug(none) compositing/overflow/reparented-scrollbars-non-sc-anc.html [ Failure ]
 Bug(none) compositing/overflow/resize-painting.html [ Failure ]
 Bug(none) compositing/overflow/scaled-overflow.html [ Failure ]
-Bug(none) compositing/overflow/scroll-parent-with-non-stacking-context-composited-ancestor.html [ Failure ]
-Bug(none) compositing/overflow/scrollbar-layer-placement.html [ Failure ]
 Bug(none) compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html [ Failure ]
 crbug.com/667946 compositing/overflow/scrolls-with-respect-to-nested.html [ Failure ]
 crbug.com/667946 compositing/overflow/scrolls-with-respect-to-transform.html [ Failure ]
 crbug.com/667946 compositing/overflow/scrolls-with-respect-to.html [ Failure ]
-Bug(none) compositing/overflow/universal-accelerated-overflow-scroll.html [ Failure ]
 Bug(none) compositing/reflections/deeply-nested-reflections.html [ Failure ]
 Bug(none) compositing/reflections/nested-reflection-mask-change.html [ Failure ]
-Bug(none) compositing/rtl/rtl-absolute-overflow-scrolled.html [ Failure ]
-Bug(none) compositing/rtl/rtl-fixed-overflow-scrolled.html [ Failure ]
-Bug(none) compositing/rtl/rtl-iframe-absolute-overflow-scrolled.html [ Failure ]
 Bug(none) compositing/rtl/rtl-iframe-absolute.html [ Failure ]
-Bug(none) compositing/rtl/rtl-iframe-fixed-overflow-scrolled.html [ Failure ]
 Bug(none) compositing/rtl/rtl-iframe-relative.html [ Failure ]
 Bug(none) compositing/scrollbars/nested-overlay-scrollbars.html [ Failure ]
 Bug(none) compositing/squashing/add-remove-squashed-layers.html [ Failure ]
@@ -123,7 +110,6 @@
 Bug(none) fast/css/outline-offset-large.html [ Failure ]
 Bug(none) fast/forms/validation-bubble-device-emulation-change.html [ Failure ]
 Bug(none) fast/webgl/pixelated.html [ Failure ]
-Bug(none) ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling.htm [ Failure ]
 Bug(none) images/color-profile-svg-foreign-object.html [ Failure ]
 Bug(none) fast/multicol/insane-column-count-and-padding-nested-crash.html [ Crash ]
 Bug(none) fast/sub-pixel/should-not-repaint-subpixel-composited-layer.html [ Failure ]
@@ -158,7 +144,6 @@
 Bug(none) paint/invalidation/overflow/overflow-changed-on-child-of-composited-layer.html [ Failure ]
 Bug(none) paint/invalidation/scroll/overflow-hidden-yet-scrolled-with-custom-scrollbar.html [ Failure ]
 Bug(none) paint/invalidation/scroll/overflow-hidden-yet-scrolled.html [ Failure ]
-Bug(none) paint/invalidation/scroll/scrollbar-ancestor-clip-change.html [ Failure ]
 Bug(none) paint/invalidation/svg/resize-svg-invalidate-children-2.html [ Failure ]
 
 # Under-invalidation
@@ -171,14 +156,8 @@
 crbug.com/842356 paint/invalidation/compositing/containing-block-removed-individual.html [ Failure ]
 crbug.com/842356 paint/invalidation/compositing/containing-block-removed.html [ Failure ]
 
-# Corrupted painting or rasterization.
-Bug(none) paint/invalidation/scroll/scroll-descendant-with-cached-cliprects.html [ Failure ]
-
 # Extra raster invalidations.
-Bug(none) compositing/overflow/do-not-repaint-if-scrolling-composited-layers.html [ Failure Crash ]
 Bug(none) paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants.html [ Failure ]
-Bug(none) paint/invalidation/compositing/fixed-scroll-in-empty-root-layer.html [ Failure ]
-Bug(none) paint/invalidation/compositing/scrolling-neg-z-index-descendants.html [ Failure ]
 Bug(none) paint/invalidation/compositing/should-not-repaint-composited-filter.html [ Failure ]
 Bug(none) paint/invalidation/compositing/should-not-repaint-composited-opacity.html [ Failure ]
 Bug(none) paint/invalidation/compositing/should-not-repaint-move-backface-hidden.html [ Failure ]
@@ -190,12 +169,9 @@
 # Extra layer for fixed-position.
 Bug(none) paint/invalidation/scroll/fixed-and-absolute-position-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/scroll/fixed-img-src-change-after-scroll.html [ Failure ]
-Bug(none) paint/invalidation/scroll/fixed-move-after-scroll.html [ Failure ]
 Bug(none) paint/invalidation/scroll/fixed-scroll-simple.html [ Failure ]
 Bug(none) paint/invalidation/table/fixed-table-overflow.html [ Failure ]
-Bug(none) paint/invalidation/scroll/fixed-under-composited-absolute-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/scroll/fixed-under-composited-fixed-scrolled.html [ Failure ]
-Bug(none) paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/position/fixed.html [ Failure ]
 Bug(none) paint/invalidation/scroll/scroll-in-fixed-layer.html [ Failure ]
 
@@ -209,10 +185,7 @@
 Bug(none) paint/invalidation/svg/text-xy-updates-SVGList.xhtml [ Failure ]
 
 # Other extra layers.
-Bug(none) compositing/composite-scrollable-fixed-position-when-descendants-composite.html [ Failure ]
 Bug(none) paint/invalidation/image/canvas-composite-repaint-by-all-imagesource.html [ Failure ]
-Bug(none) paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll.html [ Failure ]
-Bug(none) paint/invalidation/compositing/should-not-repaint-scrolling-contents-outline-change.html [ Failure ]
 Bug(none) paint/invalidation/filters/filter-on-html-element-with-fixed-position-child.html [ Failure ]
 Bug(none) paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container.html [ Failure ]
 Bug(none) paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container.html [ Failure ]
@@ -239,6 +212,7 @@
 crbug.com/707444 svg/custom/clip-mask-negative-scale.svg [ Failure ]
 
 # Subpixel or 1-pixel differences. May be real subpixel handling bugs.
+Bug(none) fast/forms/text/input-appearance-autocomplete-very-long-value.html [ Failure ]
 Bug(none) fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Failure ]
 Bug(none) virtual/threaded/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Skip ]
 Bug(none) virtual/scroll_customization/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Skip ]
@@ -276,7 +250,6 @@
 crbug.com/738613 paint/invalidation/table/table-overflow-auto-in-overflow-auto-scrolled.html [ Failure ]
 crbug.com/738613 paint/invalidation/table/table-overflow-scroll-in-overflow-scroll-scrolled.html [ Failure ]
 crbug.com/738613 paint/invalidation/scroll/resize-scrollable-iframe.html [ Failure ]
-crbug.com/738613 paint/invalidation/scroll/scrollbar-damage-and-full-viewport-repaint.html [ Failure ]
 crbug.com/738613 paint/invalidation/scroll/caret-invalidation-in-overflow-scroll.html [ Failure ]
 crbug.com/738613 paint/invalidation/forms/textarea-caret.html [ Failure ]
 crbug.com/738613 paint/invalidation/scroll/overflow-auto-in-overflow-auto-scrolled.html [ Failure ]
@@ -303,26 +276,23 @@
 crbug.com/738613 paint/invalidation/scroll/invalidate-after-composited-scroll.html [ Failure ]
 crbug.com/738613 paint/invalidation/scroll/overflow-move-after-scroll.html [ Failure ]
 crbug.com/738613 paint/invalidation/scroll/overflow-scroll-after-move.html [ Failure ]
-crbug.com/738613 paint/invalidation/scroll/scroll-stacking-context-backface-visiblity-leaves-traces.html [ Failure ]
 crbug.com/738613 compositing/overflow/overflow-scroll-with-pointer-events-toggle.html [ Failure ]
-crbug.com/738613 paint/invalidation/compositing/pointer-events-composited-scrolling.html [ Failure ]
 
 # Needs fully composited scrollbar support.
-crbug.com/589279 paint/invalidation/compositing/scroll-fixed-layer-no-content.html [ Failure ]
-crbug.com/589279 paint/invalidation/compositing/scroll-fixed-layer-out-of-view.html [ Failure ]
-crbug.com/589279 paint/invalidation/compositing/scroll-fixed-squahed-layer.html [ Failure ]
-crbug.com/589279 paint/invalidation/compositing/should-not-clip-composited-viewport-scrolling-layer.html [ Failure ]
-crbug.com/589279 paint/invalidation/scroll/document-flipped-blocks-writing-mode-scroll.html [ Failure ]
-crbug.com/589279 paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer.html [ Failure ]
-crbug.com/589279 paint/invalidation/scroll/composited-iframe-scroll-repaint.html [ Failure ]
-crbug.com/589279 paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited.html [ Failure ]
-
-# Scrollbar layers are too big. Horizontal/vertical scrollbars squashed together or with
-# content layers.
+crbug.com/931486 compositing/composite-scrollable-fixed-position-when-descendants-composite.html [ Failure ]
 crbug.com/931486 compositing/force-compositing-mode/overflow-iframe-enter-compositing.html [ Failure ]
 crbug.com/931486 compositing/force-compositing-mode/overflow-iframe-layer.html [ Failure ]
 crbug.com/931486 compositing/iframes/overlapped-iframe-iframe.html [ Failure ]
+crbug.com/931486 compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective.html [ Failure ]
+crbug.com/931486 compositing/overflow/content-gains-scrollbars.html [ Failure ]
+crbug.com/931486 compositing/overflow/content-loses-scrollbars.html [ Failure ]
+crbug.com/931486 compositing/overflow/overflow-scrollbar-layers.html [ Failure ]
+crbug.com/931486 compositing/overflow/overlap-testing-ancestor-scroller-high-dpi.html [ Failure ]
+crbug.com/931486 compositing/overflow/reparented-scrollbars-non-sc-anc.html [ Failure ]
 crbug.com/931486 compositing/overflow/scroll-parent-absolute-with-backdrop-filter.html [ Failure ]
+crbug.com/931486 compositing/overflow/scroll-parent-with-non-stacking-context-composited-ancestor.html [ Failure ]
+crbug.com/931486 compositing/overflow/scrollbar-layer-placement-negative-z-index-child-positioned.html [ Failure ]
+crbug.com/931486 compositing/overflow/scrollbar-layer-placement.html [ Failure ]
 crbug.com/931486 compositing/layer-creation/fixed-position-nonscrollable-body-mismatch-containers.html [ Failure ]
 crbug.com/931486 compositing/layer-creation/fixed-position-out-of-view-positioning.html [ Failure ]
 crbug.com/931486 compositing/layer-creation/overflow-scroll-overlap.html [ Failure ]
@@ -330,14 +300,27 @@
 crbug.com/931486 compositing/overflow/scroll-parent-absolute.html [ Failure ]
 crbug.com/931486 compositing/overflow/scrolling-content-clip-to-viewport.html [ Failure ]
 crbug.com/931486 compositing/overflow/textarea-scroll-touch.html [ Failure ]
+crbug.com/931486 compositing/overflow/universal-accelerated-overflow-scroll.html [ Failure ]
 crbug.com/931486 compositing/rtl/rtl-absolute-overflow.html [ Failure ]
 crbug.com/931486 compositing/rtl/rtl-fixed-overflow.html [ Failure ]
 crbug.com/931486 compositing/rtl/rtl-iframe-absolute-overflow.html [ Failure ]
+crbug.com/931486 compositing/squashing/frame-clip-squashed-scrolled.html [ Failure ]
 crbug.com/931486 paint/invalidation/scroll/fixed-child-move-after-scroll.html [ Failure ]
 crbug.com/931486 paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll.html [ Failure ]
 crbug.com/931486 paint/invalidation/position/fixed-tranformed.html [ Failure ]
 crbug.com/931486 paint/invalidation/scroll/inline-style-change-in-scrolled-view.html [ Failure ]
 crbug.com/931486 paint/invalidation/background/background-image-paint-invalidation.html [ Failure ]
+crbug.com/931486 crbug.com/931491 paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer.html [ Failure ]
+crbug.com/931486 crbug.com/931491 paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll.html [ Failure ]
+crbug.com/931486 crbug.com/931491 paint/invalidation/compositing/fixed-scroll-in-empty-root-layer.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/pointer-events-composited-scrolling.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/scroll-fixed-layer-out-of-view.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/scroll-fixed-layer-no-content.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/scroll-fixed-squahed-layer.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/scrolling-neg-z-index-descendants.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/should-not-clip-composited-viewport-scrolling-layer.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/should-not-repaint-scrolling-contents-outline-change.html [ Failure ]
 crbug.com/931486 paint/invalidation/compositing/text-color-change.html [ Failure ]
 crbug.com/931486 paint/invalidation/compositing/text-match-highlight.html [ Failure ]
 crbug.com/931486 paint/invalidation/line-flow-with-floats-1.html [ Failure ]
@@ -351,9 +334,18 @@
 crbug.com/931486 paint/invalidation/line-flow-with-floats-8.html [ Failure ]
 crbug.com/931486 paint/invalidation/line-flow-with-floats-9.html [ Failure ]
 crbug.com/931486 paint/invalidation/flexbox/scrollbars-changed.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/composited-iframe-scroll-repaint.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/document-flipped-blocks-writing-mode-scroll.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/fixed-move-after-scroll.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/fixed-under-composited-absolute-scrolled.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled.html [ Failure ]
 crbug.com/931486 paint/invalidation/scroll/overflow-scroll-composited-non-stacking-child.html [ Failure ]
-crbug.com/931486 paint/invalidation/text-match-document-change.html [ Failure ]
 crbug.com/931486 paint/invalidation/scroll/overflow-scroll-delete.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/scroll-descendant-with-cached-cliprects.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/scroll-stacking-context-backface-visiblity-leaves-traces.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/scrollbar-ancestor-clip-change.html [ Failure ]
+crbug.com/931486 paint/invalidation/scroll/scrollbar-damage-and-full-viewport-repaint.html [ Failure ]
+crbug.com/931486 paint/invalidation/text-match-document-change.html [ Failure ]
 crbug.com/931486 paint/invalidation/invalidation-on-foreground-graphics-layer.html [ Failure ]
 
 # See comment regarding this test in NeverFixTests. It also fails for other
@@ -361,27 +353,17 @@
 # different results.
 Bug(none) paint/invalidation/compositing/subpixel-offset-scaled-transform-composited.html [ Failure ]
 
-Bug(none) paint/clipath/change-mask-clip-path-multicol-crash.html [ Crash ]
 Bug(none) paint/pagination/composited-paginated-outlined-box.html [ Failure ]
 
 crbug.com/882075 compositing/overflow/composited-sticky-element-with-non-integer-relative-position-to-container.html [ Failure ]
 
-Bug(none) compositing/overflow/ancestor-with-clip-path.html [ Failure ]
-Bug(none) compositing/overflow/descendant-with-clip-path.html [ Failure ]
 Bug(none) paint/invalidation/media-audio-no-spurious-repaints.html [ Failure ]
 Bug(none) paint/invalidation/subpixel-shadow-included-in-invalidation.html [ Failure ]
-Bug(none) paint/invalidation/clip/clip-path-constant-repaint.html [ Failure ]
-Bug(none) paint/invalidation/clip/clip-path-in-mask-layer.html [ Failure ]
 Bug(none) paint/invalidation/reflection/scroll-fixed-layer-with-reflection.html [ Failure ]
 Bug(none) paint/invalidation/reflection/scroll-fixed-reflected-layer.html [ Failure ]
 Bug(none) svg/transforms/text-with-mask-with-svg-transform.svg [ Failure ]
-
-Bug(none) compositing/squashing/frame-clip-squashed-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/selection/japanese-rl-selection-repaint.html [ Failure ]
-Bug(none) compositing/overflow/scrollbar-layer-placement-negative-z-index-child-positioned.html [ Failure ]
-
 Bug(none) paint/float/float-under-inline-self-painting-change.html [ Failure ]
-Bug(none) compositing/overflow/overlap-testing-ancestor-scroller-high-dpi.html [ Failure ]
 
 # Extra raster invalidation on start/end of animation. Caused by animation
 # element id namespaces
@@ -399,6 +381,7 @@
 crbug.com/923429 css3/filters/backdrop-filter-browser-zoom.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-border-radius.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-clip-rect-zoom.html [ Failure ]
+crbug.com/923429 css3/filters/backdrop-filter-edge-clipping-2.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-rendering.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-rendering-no-background.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-transform.html [ Failure ]
@@ -414,6 +397,13 @@
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-update.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-clipped.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-plus-filter.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filters-brightness.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filters-contrast.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filters-grayscale.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filters-hue-rotate.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filters-invert.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filters-saturate.html [ Failure ]
+crbug.com/923429 external/wpt/css/filter-effects/backdrop-filters-sepia.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-basic-blur.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-border-radius.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-boundary.html [ Failure ]
@@ -433,6 +423,14 @@
 crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-isolation.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-update.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-plus-filter.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filters-brightness.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filters-contrast.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filters-grayscale.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filters-hue-rotate.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filters-invert.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filters-saturate.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filters-sepia.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-edge-clipping-2.html [ Failure ]
 
 crbug.com/940033 virtual/fractional_scrolling_threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ]
 crbug.com/940033 virtual/threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ]
@@ -455,6 +453,11 @@
 crbug.com/931491 compositing/squashing/squash-above-fixed-3.html [ Failure ]
 crbug.com/931491 compositing/squashing/squash-paint-invalidation-fixed-position.html [ Failure ]
 
+crbug.com/918155 scrollbars/overlay-scrollbar-over-child-layer.html [ Failure ]
+crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer.html [ Failure ]
+
+crbug.com/979389 ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling.htm [ Failure ]
+
 # Crash during PictureLayer::GetPicture() when DisplayItemList is finished twice.
 Bug(none) http/tests/devtools/layers/layer-canvas-log.js [ Crash ]
 Bug(none) http/tests/devtools/layers/layer-replay-scale.js [ Crash ]
@@ -463,47 +466,24 @@
 # Missing WheelEventHandler
 Bug(none) http/tests/devtools/layers/layer-scroll-rects-get.js [ Failure ]
 
-# Other:
-crbug.com/935728 fast/frames/transparent-scrollbar.html [ Failure ]
+# Scrollbar snapping issues
+crbug.com/979380 compositing/rtl/rtl-absolute-overflow-scrolled.html [ Failure ]
+crbug.com/979380 compositing/rtl/rtl-fixed-overflow-scrolled.html [ Failure ]
+crbug.com/979380 compositing/rtl/rtl-iframe-absolute-overflow-scrolled.html [ Failure ]
+crbug.com/979380 compositing/rtl/rtl-iframe-fixed-overflow-scrolled.html [ Failure ]
+crbug.com/979380 fast/frames/transparent-scrollbar.html [ Failure ]
 
 # Wrong contentsOpaque for will-change: opacity layers
 Bug(none) compositing/will-change/composited-layers.html [ Failure ]
 # Crash on non-contiguous effect on multiple columns
 Bug(none) fast/multicol/composited-layer-will-change.html [ Crash ]
 
-Bug(none) external/wpt/css/css-masking/clip-path-svg-content/clip-path-recursion-001.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-001.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-002.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-004.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-007.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-008.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-010.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-panning-001.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/mask-svg-content/mask-type-002.svg [ Failure ]
-Bug(none) external/wpt/css/css-masking/mask-svg-content/mask-type-003.svg [ Failure ]
-Bug(none) svg/W3C-SVG-1.1/masking-intro-01-f.svg [ Failure ]
-Bug(none) svg/clip-path/clip-in-mask.svg [ Failure ]
-Bug(none) svg/clip-path/deep-nested-clip-in-mask-different-unitTypes.svg [ Failure ]
-Bug(none) svg/clip-path/deep-nested-clip-in-mask-panning.svg [ Failure ]
-Bug(none) svg/clip-path/deep-nested-clip-in-mask.svg [ Failure ]
-Bug(none) svg/clip-path/nested-clip-in-mask-image-based-clipping.svg [ Failure ]
-Bug(none) svg/clip-path/nested-clip-in-mask-path-and-image-based-clipping.svg [ Failure ]
-Bug(none) svg/clip-path/nested-clip-in-mask-path-based-clipping.svg [ Failure ]
-Bug(none) svg/custom/clamped-masking-clipping.svg [ Failure ]
-Bug(none) svg/custom/circular-clip-path-references-crash.svg [ Failure ]
-Bug(none) svg/custom/grayscale-gradient-mask.svg [ Failure ]
-Bug(none) svg/custom/mask-changes.svg [ Failure ]
-Bug(none) svg/custom/mask-colorspace.svg [ Failure ]
-Bug(none) svg/custom/mask-with-default-value.svg [ Failure ]
-Bug(none) svg/custom/masking-clipping-hidpi.svg [ Failure ]
-Bug(none) svg/filters/filter-clip.svg [ Failure ]
-Bug(none) svg/masking/mask-type-luminance.svg [ Failure ]
-Bug(none) svg/masking/mask-type-not-set.svg [ Failure ]
-
+# Crash in CompositeorAnimations::CheckCanStartElementOnCompositor() etc.
 crbug.com/962191 animations/stability/base-render-style-crash.html [ Crash ]
 crbug.com/962191 animations/stability/option-element-crash.html [ Crash ]
 crbug.com/962191 animations/stability/option-opacity-inherit-crash.html [ Crash ]
 crbug.com/962191 animations/web-animations/animation-state-changes-positive-playback-rate.html [ Crash ]
+crbug.com/962191 compositing/overflow/do-not-repaint-if-scrolling-composited-layers.html [ Crash ]
 crbug.com/962191 external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative.html [ Crash ]
 crbug.com/962191 external/wpt/css/css-animations/KeyframeEffect-target.tentative.html [ Crash ]
 crbug.com/962191 external/wpt/css/css-easing/cubic-bezier-timing-functions-output.html [ Crash ]
@@ -563,10 +543,46 @@
 crbug.com/962191 virtual/threaded/external/wpt/css/css-animations/KeyframeEffect-target.tentative.html [ Crash ]
 crbug.com/962191 virtual/threaded/transitions/svg-transitions.html [ Crash ]
 
+# Clip-path and mask.
+crbug.com/979369 compositing/images/direct-image-clip-path.html [ Failure ]
+crbug.com/979369 compositing/images/direct-image-dynamic-clip-path.html [ Failure ]
+crbug.com/979369 compositing/overflow/accelerated-scrolling-with-clip-path.html [ Failure ]
+crbug.com/979369 compositing/overflow/ancestor-with-clip-path.html [ Failure ]
+crbug.com/979369 compositing/overflow/descendant-with-clip-path.html [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/clip-path-svg-content/clip-path-recursion-001.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-001.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-002.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-004.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-007.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-008.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-010.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/clip-path-svg-content/mask-nested-clip-path-panning-001.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/mask-svg-content/mask-type-002.svg [ Failure ]
+crbug.com/979369 external/wpt/css/css-masking/mask-svg-content/mask-type-003.svg [ Failure ]
+crbug.com/979369 paint/clipath/change-mask-clip-path-multicol-crash.html [ Crash ]
+crbug.com/979369 paint/clipath/clip-path-with-background-and-box-behind.html [ Failure ]
+crbug.com/979369 paint/invalidation/clip/clip-path-constant-repaint.html [ Failure ]
+crbug.com/979369 paint/invalidation/clip/clip-path-in-mask-layer.html [ Failure ]
+crbug.com/979369 svg/W3C-SVG-1.1/masking-intro-01-f.svg [ Failure ]
+crbug.com/979369 svg/clip-path/clip-in-mask.svg [ Failure ]
+crbug.com/979369 svg/clip-path/deep-nested-clip-in-mask-different-unitTypes.svg [ Failure ]
+crbug.com/979369 svg/clip-path/deep-nested-clip-in-mask-panning.svg [ Failure ]
+crbug.com/979369 svg/clip-path/deep-nested-clip-in-mask.svg [ Failure ]
+crbug.com/979369 svg/clip-path/nested-clip-in-mask-image-based-clipping.svg [ Failure ]
+crbug.com/979369 svg/clip-path/nested-clip-in-mask-path-and-image-based-clipping.svg [ Failure ]
+crbug.com/979369 svg/clip-path/nested-clip-in-mask-path-based-clipping.svg [ Failure ]
+crbug.com/979369 svg/custom/circular-clip-path-references-crash.svg [ Failure ]
+crbug.com/979369 svg/custom/clamped-masking-clipping.svg [ Failure ]
+crbug.com/979369 svg/custom/grayscale-gradient-mask.svg [ Failure ]
+crbug.com/979369 svg/custom/mask-changes.svg [ Failure ]
+crbug.com/979369 svg/custom/mask-colorspace.svg [ Failure ]
+crbug.com/979369 svg/custom/mask-with-default-value.svg [ Failure ]
+crbug.com/979369 svg/custom/masking-clipping-hidpi.svg [ Failure ]
+crbug.com/979369 svg/filters/filter-clip.svg [ Failure ]
+crbug.com/979369 svg/masking/mask-type-luminance.svg [ Failure ]
+crbug.com/979369 svg/masking/mask-type-not-set.svg [ Failure ]
+
 # These failures need to be triaged and are being added to unblock the CQ (see: https://crbug.com/966981).
-crbug.com/966981 compositing/images/direct-image-clip-path.html [ Failure ]
-crbug.com/966981 compositing/images/direct-image-dynamic-clip-path.html [ Failure ]
-crbug.com/966981 paint/clipath/clip-path-with-background-and-box-behind.html [ Failure ]
 crbug.com/966981 virtual/fractional_scrolling_threaded/fast/scrolling/fractional-scroll-offset-document.html [ Failure ]
 crbug.com/966981 virtual/fractional_scrolling_threaded/fast/scrolling/hover-during-scroll.html [ Failure ]
 crbug.com/966981 virtual/fractional_scrolling_threaded/fast/scrolling/no-hover-during-scroll.html [ Failure ]
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index fe66d14..e404e64 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -252,3 +252,53 @@
 # Sheriff 2019-06-05
 crbug.com/856601 [ Linux ] external/wpt/orientation-event/idlharness.https.window.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] virtual/scalefactor200/external/wpt/css/filter-effects/interfaces.any.html [ Pass Timeout ]
+
+# Sheriff 2019-06-27
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/idlharness.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large-multiple.any.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large-multiple.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large-multiple.any.sharedworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large-multiple.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large.any.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large.any.sharedworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-small.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/animation-worklet/idlharness.any.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/audio-output/idlharness.https.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/background-fetch/idlharness.https.any.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/background-fetch/idlharness.https.any.sharedworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/css/cssom/interfaces.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/css/filter-effects/interfaces.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/feature-policy/idlharness.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/fetch/api/idl.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/idle-detection/idlharness.https.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/mediacapture-record/idlharness.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/netinfo/idlharness.any.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/sms/idlharness.https.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/storage/idlharness.https.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/web-locks/idlharness.tentative.https.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/webmidi/idlharness.https.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/webrtc/idlharness.https.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/webvtt/api/idlharness.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/xhr/idlharness.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] http/tests/devtools/a11y-axe-core/console-a11y-test.js [ Pass Timeout ]
+crbug.com/856601 [ Linux ] http/tests/devtools/a11y-axe-core/application-panel/clear-storage-a11y-test.js [ Pass Timeout ]
+crbug.com/856601 [ Linux ] http/tests/devtools/a11y-axe-core/network/network-condition-a11y-test.js [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/idl.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/fetch/cors-rfc1918/idlharness.tentative.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/fetch/cors-rfc1918/idlharness.tentative.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/omt-worker-fetch/external/wpt/fetch/api/idl.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/omt-worker-fetch/external/wpt/fetch/api/idl.any.worker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/scalefactor200/external/wpt/css/filter-effects/interfaces.any.worker.html [ Pass Timeout ]
+
+# Sheriff 2019-06-28
+crbug.com/856601 [ Linux ] external/wpt/battery-status/battery-interface-idlharness.https.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/hr-time/idlharness.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/mediacapture-fromelement/idlharness.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/permissions/interfaces.any.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/requestidlecallback/idlharness.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/webdriver/tests/interface.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] http/tests/devtools/a11y-axe-core/security/security-origin-a11y-test.js [ Pass Timeout ]
+crbug.com/856601 [ Linux ] http/tests/devtools/indexeddb/live-update-indexeddb-content.js [ Pass Timeout ]
+crbug.com/856601 [ Linux ] http/tests/devtools/indexeddb/live-update-indexeddb-list.js [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index c478f550..ea4b31a 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -2147,6 +2147,11 @@
 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/fetch-event-is-reload-navigation-manual.https.html [ WontFix ]
 virtual/omt-worker-fetch/external/wpt/xhr/send-authentication-existing-session-manual.htm [ WontFix ]
 virtual/omt-worker-fetch/external/wpt/xhr/send-authentication-prompt-2-manual.htm [ WontFix ]
+virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/fetch-event-is-history-backward-navigation-manual.https.html [ WontFix ]
+virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/fetch-event-is-history-forward-navigation-manual.https.html [ WontFix ]
+virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/fetch-event-is-reload-navigation-manual.https.html [ WontFix ]
+virtual/not-omt-sw-fetch/external/wpt/xhr/send-authentication-existing-session-manual.htm [ WontFix ]
+virtual/not-omt-sw-fetch/external/wpt/xhr/send-authentication-prompt-2-manual.htm [ WontFix ]
 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-abort-manual.https.html [ WontFix ]
 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-onerror-manual.https.html [ WontFix ]
 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-onresult-manual.https.html [ WontFix ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 8443a3c..e8ac2a68 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -190,8 +190,6 @@
 crbug.com/974720 [ Mac10.13 ] fast/text/firstline/capitalize-transform-2.html [ Pass Crash ]
 crbug.com/974720 [ Mac10.13 ] fast/text/firstline/capitalize-transform.html [ Pass Crash ]
 
-crbug.com/978436 external/wpt/svg/coordinate-systems/viewBox-scaling-text-001.html [ Failure ]
-
 # Flakily timing out or failing.
 # TODO(ccameron): Investigate this.
 crbug.com/730267 virtual/gpu-rasterization/images/color-profile-group.html [ Pass Timeout Failure ]
@@ -261,8 +259,6 @@
 Bug(none) virtual/display-lock/inspector-protocol/css/css-get-platform-fonts-display-locked.js [ Pass ]
 
 # Display locking failures
-crbug.com/926276 virtual/display-lock/wpt_internal/display-lock/lock-after-append/nested-update.html [ Timeout ]
-crbug.com/926276 virtual/display-lock/wpt_internal/display-lock/lock-after-append/nested-update-and-commit.html [ Timeout ]
 crbug.com/955533 virtual/display-lock/wpt_internal/display-lock/sizing/overflow-auto-with-overflow.html [ Failure ]
 
 # Sheriff 2018/05/25
@@ -351,7 +347,6 @@
 # Subpixel rounding differences that are incorrect.
 crbug.com/836886 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scaled-overflow.html [ Failure ]
 crbug.com/836886 compositing/overflow/scaled-overflow.html [ Failure ]
-crbug.com/836886 compositing/scrollbars/nested-overlay-scrollbars.html [ Failure ]
 # Flaky subpixel AA difference (not necessarily incorrect, but flaky)
 crbug.com/921105 virtual/threaded/animations/skew-notsequential-compositor.html [ Failure Pass ]
 crbug.com/921105 virtual/disable-blink-gen-property-trees/animations/skew-notsequential-compositor.html [ Skip ]
@@ -362,6 +357,10 @@
 crbug.com/954591 external/wpt/css/css-transforms/composited-under-rotateY-180deg.html [ Failure ]
 crbug.com/954591 external/wpt/css/css-transforms/composited-under-rotateY-180deg-clip.html [ Failure ]
 
+# Fixed in CompositeAfterPaint.
+crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer-nested-2.html [ Failure ]
+crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer-nested.html [ Failure ]
+
 # ====== Paint team owned tests to here ======
 
 crbug.com/922249 virtual/android/fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure Pass ]
@@ -862,10 +861,8 @@
 crbug.com/591099 [ Mac ] fast/replaced/input-radio-height-inside-auto-container.html [ Failure ]
 crbug.com/591099 [ Mac ] images/feature-policy-oversized-images-resize.html [ Failure ]
 crbug.com/974717 [ Linux ] images/feature-policy-oversized-images-resize.html [ Failure Pass ]
-crbug.com/591099 [ Mac10.11 ] fast/events/before-unload-return-value-from-listener.html [ Crash Timeout ]
-crbug.com/591099 [ Mac10.11 ] media/track/track-cue-rendering-position-auto-rtl.html [ Failure ]
+# crbug.com/591099 [ Mac10.11 ] media/track/track-cue-rendering-position-auto-rtl.html [ Failure ]
 crbug.com/591099 [ Mac10.11 ] virtual/scalefactor200/fast/hidpi/static/popup-menu-appearance.html [ Failure ]
-crbug.com/976355 [ Mac10.12 ] fast/events/before-unload-return-value-from-listener.html [ Crash Timeout ]
 crbug.com/591099 [ Mac10.13 ] compositing/geometry/root-layer-update.html [ Failure ]
 crbug.com/591099 [ Mac10.13 ] compositing/overlap-blending/reflection-opacity-huge.html [ Failure ]
 crbug.com/591099 [ Mac10.13 ] compositing/reflections/reflection-positioning2.html [ Failure ]
@@ -1044,7 +1041,7 @@
 crbug.com/591099 [ Mac10.13 ] fast/dynamic/insert-before-table-part-in-continuation.html [ Failure ]
 crbug.com/591099 [ Mac10.13 ] fast/dynamic/noninlinebadness.html [ Failure ]
 crbug.com/591099 [ Mac10.13 ] fast/dynamic/outerHTML-doc.html [ Failure ]
-crbug.com/591099 [ Mac10.13 ] fast/events/before-unload-return-value-from-listener.html [ Crash Timeout ]
+crbug.com/591099 [ Mac ] fast/events/before-unload-return-value-from-listener.html [ Crash Timeout ]
 crbug.com/591099 [ Mac10.13 ] fast/forms/basic-inputs.html [ Failure ]
 crbug.com/591099 [ Mac10.13 ] fast/forms/button/button-inner-block-reuse.html [ Failure ]
 crbug.com/591099 [ Mac10.13 ] fast/forms/formmove2.html [ Failure ]
@@ -3056,7 +3053,7 @@
 crbug.com/832071 external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
 crbug.com/832071 virtual/blink-cors/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
 crbug.com/832071 virtual/navigation-mojo-response/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
-crbug.com/832071 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Timeout ]
+crbug.com/832071 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
 
 # failures in external/wpt/css/css-animations/ and web-animations/ from Mozilla tests
 crbug.com/849859 external/wpt/css/css-animations/CSSAnimation-pausing.tentative.html [ Failure ]
@@ -3195,6 +3192,7 @@
 crbug.com/968164 external/wpt/css/css-ui/webkit-appearance-menulist-button-001.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/font-family-name-025.html [ Failure ]
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/script-tag/keep-origin-redirect/generic.http.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/script-tag/swap-origin-redirect/insecure-protocol.http.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/fetch/api/basic/header-value-combining.any.sharedworker.html [ Timeout ]
@@ -3287,6 +3285,7 @@
 crbug.com/626703 external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
 crbug.com/626703 virtual/blink-cors/external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
@@ -3356,6 +3355,7 @@
 crbug.com/626703 external/wpt/xhr/abort-after-stop.any.worker.html [ Timeout ]
 crbug.com/626703 virtual/blink-cors/external/wpt/xhr/abort-after-stop.any.worker.html [ Timeout ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/abort-after-stop.any.worker.html [ Timeout ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/xhr/abort-after-stop.any.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html [ Failure ]
@@ -3423,6 +3423,7 @@
 crbug.com/626703 external/wpt/xhr/event-readystatechange-loaded.any.worker.html [ Timeout Failure ]
 crbug.com/626703 virtual/blink-cors/external/wpt/xhr/event-readystatechange-loaded.any.worker.html [ Timeout Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/event-readystatechange-loaded.any.worker.html [ Timeout Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/xhr/event-readystatechange-loaded.any.worker.html [ Timeout Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/line-breaking/line-breaking-017.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-scroll-snap/scroll-target-padding-003.html [ Failure ]
@@ -3445,6 +3446,7 @@
 crbug.com/626703 external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
 crbug.com/626703 virtual/blink-cors/external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-margin-002.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-padding-002.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-snap-003.html [ Failure ]
@@ -3587,6 +3589,7 @@
 crbug.com/626703 external/wpt/fetch/content-type/response.window.html [ Timeout ]
 crbug.com/626703 virtual/blink-cors/external/wpt/fetch/content-type/response.window.html [ Timeout ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/fetch/content-type/response.window.html [ Timeout ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/fetch/content-type/response.window.html [ Timeout ]
 crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-module.html [ Timeout ]
 crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-classic.html [ Timeout ]
 crbug.com/626703 external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-classic.html [ Timeout ]
@@ -3669,103 +3672,11 @@
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html [ Timeout Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/svg/presentation-attribute.html [ Timeout Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/svg/processing-instruction.html [ Timeout Failure ]
-
-crbug.com/906959 external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/referrer-policy/css-integration/svg/external-stylesheet.html [ Timeout Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/referrer-policy/css-integration/svg/inline-style.html [ Timeout Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html [ Timeout Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/referrer-policy/css-integration/svg/presentation-attribute.html [ Timeout Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/referrer-policy/css-integration/svg/processing-instruction.html [ Timeout Failure ]
 
 crbug.com/367760 external/wpt/svg/pservers/reftests/meshgradient-complex-001.svg [ Failure ]
 crbug.com/367760 external/wpt/svg/pservers/reftests/meshgradient-bicubic-001.svg [ Failure ]
@@ -3892,6 +3803,7 @@
 crbug.com/626703 external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
 crbug.com/626703 virtual/blink-cors/external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-024.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-048.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-026.html [ Failure ]
@@ -3966,6 +3878,7 @@
 crbug.com/626703 external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 virtual/blink-cors/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-fonts/font-feature-settings-descriptor-01.html [ Failure ]
 crbug.com/626703 [ Win10 ] external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Timeout ]
 crbug.com/626703 [ Win10 ] external/wpt/fetch/api/redirect/redirect-count.any.html [ Timeout ]
@@ -4207,6 +4120,7 @@
 crbug.com/648295 external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
 crbug.com/648295 virtual/blink-cors/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
 crbug.com/648295 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
+crbug.com/648295 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
 crbug.com/626703 external/wpt/svg/linking/reftests/href-filter-element.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_cues_overlapping_completely_move_up.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_cues_overlapping_partially_move_down.html [ Failure ]
@@ -4422,9 +4336,11 @@
 crbug.com/626703 external/wpt/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
 crbug.com/626703 virtual/blink-cors/external/wpt/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
 crbug.com/626703 external/wpt/xhr/setrequestheader-header-allowed.htm [ Failure ]
 crbug.com/626703 virtual/blink-cors/external/wpt/xhr/setrequestheader-header-allowed.htm [ Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/setrequestheader-header-allowed.htm [ Failure ]
+crbug.com/626703 virtual/not-omt-sw-fetch/external/wpt/xhr/setrequestheader-header-allowed.htm [ Failure ]
 crbug.com/626703 [ Win10 ] external/wpt/preload/delaying-onload-link-preload-after-discovery.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vlr-005.xht [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vrl-004.xht [ Failure ]
@@ -4433,17 +4349,6 @@
 crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/background-image-tiled.https.html [ Crash ]
 crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/geometry-background-image-tiled-001.https.html [ Crash ]
 crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/invalid-image-pending-script.https.html [ Crash ]
-crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/paint2d-image.https.html [ Failure Timeout ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-004.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/registered-property-interpolation-010.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/registered-property-stylemap.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-003.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-007.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-009.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/registered-property-value-018.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/style-background-image.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/style-before-pseudo.https.html [ Failure ]
-crbug.com/957459 virtual/threaded/external/wpt/css/css-paint-api/style-first-letter-pseudo.https.html [ Failure ]
 
 # Shared memory growth temporarily disabled.
 crbug.com/951795 external/wpt/wasm/jsapi/memory/grow.any.html [ Pass Failure ]
@@ -4483,6 +4388,7 @@
 
 crbug.com/917554 external/wpt/upgrade-insecure-requests/worker-subresource-fetch-upgrade.https.html [ Crash ]
 crbug.com/917554 virtual/omt-worker-fetch/external/wpt/upgrade-insecure-requests/worker-subresource-fetch-upgrade.https.html [ Crash ]
+crbug.com/917554 virtual/not-omt-sw-fetch/external/wpt/upgrade-insecure-requests/worker-subresource-fetch-upgrade.https.html [ Crash ]
 
 # Different results on try bots and CQ, skipped to unblock wpt import.
 crbug.com/888443 external/wpt/css/cssom-view/scroll-behavior-default-css.html [ Skip ]
@@ -4501,6 +4407,8 @@
 crbug.com/888470 virtual/blink-cors/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html [ Failure ]
 crbug.com/888470 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html [ Failure ]
 crbug.com/888470 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html [ Failure ]
+crbug.com/888470 virtual/not-omt-sw-fetch/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html [ Failure ]
+crbug.com/888470 virtual/not-omt-sw-fetch/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html [ Failure ]
 
 # This behavior (popups during unload) is being reverted on trunk; this test is
 # expected to fail when this change is merged back to earlier branches.
@@ -4533,11 +4441,14 @@
 crbug.com/881180 external/wpt/xhr/open-url-multi-window-4.htm [ Timeout ]
 crbug.com/881180 virtual/blink-cors/external/wpt/xhr/open-url-multi-window-4.htm [ Timeout ]
 crbug.com/881180 virtual/omt-worker-fetch/external/wpt/xhr/open-url-multi-window-4.htm [ Timeout ]
+crbug.com/881180 virtual/not-omt-sw-fetch/external/wpt/xhr/open-url-multi-window-4.htm [ Timeout ]
 
 crbug.com/655458 external/wpt/workers/Worker_terminate_event_queue.htm [ Pass Timeout ]
 crbug.com/655458 external/wpt/workers/semantics/multiple-workers/007.html [ Timeout ]
 crbug.com/655458 virtual/omt-worker-fetch/external/wpt/workers/Worker_terminate_event_queue.htm [ Pass Timeout ]
 crbug.com/655458 virtual/omt-worker-fetch/external/wpt/workers/semantics/multiple-workers/007.html [ Timeout ]
+crbug.com/655458 virtual/not-omt-sw-fetch/external/wpt/workers/Worker_terminate_event_queue.htm [ Pass Timeout ]
+crbug.com/655458 virtual/not-omt-sw-fetch/external/wpt/workers/semantics/multiple-workers/007.html [ Timeout ]
 
 crbug.com/910709 navigator_language/worker_navigator_language.html [ Timeout ]
 
@@ -4545,89 +4456,19 @@
 # This fails because running a worker on an opaque origin is blocked.
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/workers/opaque-origin.html [ Timeout ]
 # Needs investigation. These are timing out.
-crbug.com/835717 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/clients-get-client-types.https.html [ Skip ]
-crbug.com/835717 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/fetch-request-xhr-sync-on-worker.https.html [ Skip ]
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/sandboxed-iframe-fetch-event.https.html [ Skip ]
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/worker-in-sandboxed-iframe-by-csp-fetch-event.https.html [ Skip ]
-crbug.com/835717 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/worker-interception.https.html [ Skip ]
 # This is crashing because of DCHECK.
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/dedicated-worker-service-worker-interception.https.html [ Skip ]
 # These are timing out.
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/dedicated-script.html [ Skip ]
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/dedicated-inheritance.html [ Skip ]
 
-# PlzDedicatedWorker
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/module-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/worker-request/no-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/module-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/worker-request/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/module-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/worker-request/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/module-worker/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/worker-request/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/module-worker/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/same-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/worker-request/no-redirect/same-insecure.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/module-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/worker-request/no-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/module-worker/no-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/worker-request/no-redirect/generic.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/module-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/module-worker/no-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/worker-request/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
-crbug.com/971173 virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/worker-request/no-redirect/insecure-protocol.http.html [ Failure ]
-
-crbug.com/971581 virtual/omt-worker-fetch/external/wpt/fetch/api/request/destination/fetch-destination-no-load-event.https.html [ Timeout ]
-
 # This fails because AllowedByNoSniff::MimeTypeAsScript() blocks the nested worker's
 # worker script, because the script url has a .html file extension.
 crbug.com/655458 external/wpt/workers/semantics/multiple-workers/003.html [ Timeout ]
 crbug.com/655458 virtual/omt-worker-fetch/external/wpt/workers/semantics/multiple-workers/003.html [ Timeout ]
+crbug.com/655458 virtual/not-omt-sw-fetch/external/wpt/workers/semantics/multiple-workers/003.html [ Timeout ]
 
 crbug.com/435547 http/tests/cachestorage/serviceworker/ignore-search-with-credentials.html [ Skip ]
 
@@ -4684,11 +4525,13 @@
 crbug.com/691944 external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 crbug.com/691944 virtual/blink-cors/external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 crbug.com/691944 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
+crbug.com/691944 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 
 # These tests (erroneously) see a platform-specific User-Agent header
 crbug.com/595993 external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
 crbug.com/595993 virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
 crbug.com/595993 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
+crbug.com/595993 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
 
 crbug.com/619427 [ Mac ] fast/overflow/overflow-height-float-not-removed-crash3.html [ Pass Failure ]
 
@@ -5457,6 +5300,7 @@
 
 # User Activation
 crbug.com/736415 external/wpt/html/user-activation/activation-api-iframe.tenative.html [ Failure ]
+crbug.com/978620 external/wpt/html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html [ Timeout ]
 
 # Sheriff 2018-07-05
 crbug.com/860731 fast/scroll-snap/animate-fling-to-snap-points.html [ Failure Pass ]
@@ -5491,6 +5335,8 @@
 
 crbug.com/873873 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Timeout Pass ]
 crbug.com/873873 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Timeout Pass ]
+crbug.com/873873 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Timeout Pass ]
+crbug.com/873873 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Timeout Pass ]
 
 crbug.com/875884 [ Linux ] lifecycle/background-change-lifecycle-count.html [ Pass Failure ]
 crbug.com/875884 [ Win ] lifecycle/background-change-lifecycle-count.html [ Pass Failure ]
@@ -5839,6 +5685,7 @@
 crbug.com/933880 external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 crbug.com/933880 virtual/blink-cors/external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 crbug.com/933880 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
+crbug.com/933880 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 crbug.com/933880 http/tests/inspector-protocol/network/interception-take-stream.js [ Failure ]
 crbug.com/933880 http/tests/inspector-protocol/network/xhr-interception-auth-fail.js [ Failure ]
 # This passes in content_shell but not in chrome with network service disabled,
@@ -6077,6 +5924,9 @@
 crbug.com/865432 [ Linux ] virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import-blob-url.any.worker.html [ Timeout Pass ]
 crbug.com/867532 [ Linux ] virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import-data-url.any.worker.html [ Timeout Pass ]
 crbug.com/867532 [ Linux ] virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import.any.worker.html [ Timeout Pass ]
+crbug.com/865432 [ Linux ] virtual/not-omt-sw-fetch/external/wpt/workers/modules/dedicated-worker-import-blob-url.any.worker.html [ Timeout Pass ]
+crbug.com/867532 [ Linux ] virtual/not-omt-sw-fetch/external/wpt/workers/modules/dedicated-worker-import-data-url.any.worker.html [ Timeout Pass ]
+crbug.com/867532 [ Linux ] virtual/not-omt-sw-fetch/external/wpt/workers/modules/dedicated-worker-import.any.worker.html [ Timeout Pass ]
 crbug.com/937170 [ Linux Win7 ] external/wpt/IndexedDB/interleaved-cursors-large.html [ Pass Timeout ]
 crbug.com/937991 [ Win7 Release ] http/tests/devtools/cache-storage/cache-data.js [ Pass Timeout ]
 
@@ -6098,6 +5948,7 @@
 
 # Allow failure until its appearance gets stable.
 crbug.com/972476 std-switch/switch-appearance.html [ Failure ]
+crbug.com/972476 std-switch/switch-appearance-on-focus.html [ Failure ]
 
 # Sheriff 2019-05-20
 crbug.com/963739 [ Fuchsia ] synthetic_gestures/smooth-scroll-tiny-delta.html [ Pass Timeout ]
@@ -6139,6 +5990,7 @@
 crbug.com/943636 virtual/mouseevent_fractional/fast/events/set-attribute-listener-window-onerror-crash.html [ Pass Failure ]
 crbug.com/943636 virtual/mouseevent_fractional/fast/events/window-onerror-05.html [ Pass Failure ]
 crbug.com/943636 virtual/omt-worker-fetch/fast/workers/worker-onerror-01.html [ Pass Failure ]
+crbug.com/943636 virtual/not-omt-sw-fetch/fast/workers/worker-onerror-01.html [ Pass Failure ]
 
 # Sheriff 2019-05-27
 crbug.com/942411 [ Win ] http/tests/devtools/network/network-search.js [ Pass Timeout ]
@@ -6189,9 +6041,9 @@
 # Sheriff 2019-06-20
 crbug.com/977153 [ Mac ] fast/forms/validation-bubble-appearance-wrap.html [ Pass Failure ]
 crbug.com/976045 fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Pass Failure ]
+crbug.com/976045 virtual/fractional_scrolling/fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Pass Failure ]
 crbug.com/976045 virtual/scroll_customization/fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Pass Failure ]
 crbug.com/976045 virtual/threaded/fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Pass Failure ]
-crbug.com/977379 [ Mac10.10 ] fast/events/before-unload-return-value-from-listener.html [ Pass Timeout Failure ]
 
 crbug.com/974710 [ Win7 ] http/tests/security/isolatedWorld/bypass-main-world-csp-iframes.html [ Pass Failure ]
 crbug.com/974710 [ Win7 ] virtual/blink-cors/http/tests/security/isolatedWorld/bypass-main-world-csp-iframes.html [ Pass Failure ]
@@ -6214,7 +6066,23 @@
 crbug.com/535738 external/wpt/media-source/mediasource-changetype-play-negative.html [ Failure ]
 crbug.com/535738 external/wpt/media-source/mediasource-changetype-play-without-codecs-parameter.html [ Failure ]
 
-crbug.com/978587 external/wpt/largest-contentful-paint/observe-image.html [ Pass Failure ]
-
 crbug.com/978611 http/tests/devtools/layers/layers-3d-view-hit-testing.js [ Pass Failure ]
 crbug.com/974675 http/tests/devtools/sources/debugger-frameworks/frameworks-jquery.js [ Pass Crash ]
+
+# Sheriff 2019-06-26
+crbug.com/978966 [ Mac ] paint/markers/ellipsis-mixed-text-in-ltr-flow-with-markers.html [ Pass Failure ]
+
+# Sheriff 2019-06-27
+crbug.com/979193 [ Mac ] paint/invalidation/svg/relative-sized-use-on-symbol.xhtml [ Pass Failure ]
+crbug.com/979193 [ Mac ] paint/invalidation/svg/remove-background-property-on-root.html [ Pass Failure ]
+crbug.com/979243 [ Mac ] editing/selection/inline-closest-leaf-child.html [ Pass Failure ]
+crbug.com/979298 [ Mac ] media/controls/captions-menu-always-visible.html [ Pass Failure ]
+crbug.com/979323 [ Mac ] media/track/track-cue-rendering-position-auto-rtl.html [ Pass Failure ]
+crbug.com/979336 [ Mac ] fast/dynamic/anonymous-block-orphaned-lines.html [ Pass Failure ]
+
+# Sheriff 2019-06-28
+crbug.com/979565 [ Mac ] virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-fullscreen-video.html [ Pass Failure ]
+crbug.com/979593 [ Linux Win ] external/wpt/service-workers/service-worker/registration-schedule-job.https.html [ Pass Failure ]
+crbug.com/979593 [ Linux Win ] virtual/blink-cors/external/wpt/service-workers/service-worker/registration-schedule-job.https.html [ Pass Failure ]
+crbug.com/979593 [ Linux Win ] virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/registration-schedule-job.https.html [ Pass Failure ]
+crbug.com/979593 [ Linux Win ] virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/registration-schedule-job.https.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index a4f8e0f..fada6727 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -642,6 +642,96 @@
     "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/content-security-policy/inside-worker",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/content-security-policy/worker-src",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/fetch/",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/html/browsers/offline/appcache/workers/",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/mixed-content/classic-data-worker-fetch",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/mixed-content/module-data-worker-import",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/mixed-content/module-worker-top-level",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/mixed-content/worker-request",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/referrer-policy",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/resource-timing",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/service-workers",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/upgrade-insecure-requests",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/workers",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "external/wpt/xhr",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "fast/workers",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "http/tests/origin_trials",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "http/tests/security/cors-rfc1918",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch", "--enable-blink-features=CorsRFC1918"]
+  },
+  {
+    "prefix": "not-omt-sw-fetch",
+    "base": "http/tests/workers",
+    "args": ["--disable-features=OffMainThreadServiceWorkerScriptFetch"]
+  },
+  {
     "prefix": "webrtc-wpt-plan-b",
     "base": "external/wpt/webrtc",
     "args": ["--disable-features=RTCUnifiedPlanByDefault"]
@@ -1031,5 +1121,15 @@
     "prefix": "force-defer-script",
     "base": "defer-script",
     "args": ["--enable-blink-features=ForceDeferScriptIntervention"]
+  },
+  {
+    "prefix": "at-property",
+    "base": "fast/css/css-properties-values-api",
+    "args": ["--enable-blink-features=CSSVariables2AtProperty"]
+  },
+  {
+    "prefix": "at-property",
+    "base": "external/wpt/css/css-properties-values-api",
+    "args": ["--enable-blink-features=CSSVariables2AtProperty"]
   }
 ]
diff --git a/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html b/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html
index 45beb68..54f3f8b 100644
--- a/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html
+++ b/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html
@@ -43,9 +43,9 @@
   }
 </style>
 <div class="outer">
-<div class="scroller">
-<div class="content"></div>
-</div>
+  <div class="scroller">
+    <div class="content"></div>
+  </div>
 </div>
 <script>
 if (window.testRunner) {
diff --git a/third_party/blink/web_tests/compositing/scrollbars/nested-overlay-scrollbars-expected.png b/third_party/blink/web_tests/compositing/scrollbars/nested-overlay-scrollbars-expected.png
index 219172d..1fa1711d 100644
--- a/third_party/blink/web_tests/compositing/scrollbars/nested-overlay-scrollbars-expected.png
+++ b/third_party/blink/web_tests/compositing/scrollbars/nested-overlay-scrollbars-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/compositing/scrollbars/nested-overlay-scrollbars-expected.txt b/third_party/blink/web_tests/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
index abd8db5..32b2959 100644
--- a/third_party/blink/web_tests/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
+++ b/third_party/blink/web_tests/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
@@ -18,7 +18,7 @@
       "backgroundColor": "#FFFFFF"
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV id='outer'",
+      "name": "LayoutNGBlockFlow (positioned) DIV id='outer'",
       "position": [8, 8],
       "bounds": [404, 404]
     },
@@ -35,7 +35,7 @@
       "transform": 1
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV id='inner'",
+      "name": "LayoutNGBlockFlow (positioned) DIV id='inner'",
       "position": [10, 510],
       "bounds": [204, 204],
       "transform": 1
@@ -60,7 +60,7 @@
       "transform": 1
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV id='grey'",
+      "name": "LayoutNGBlockFlow (positioned) DIV id='grey'",
       "position": [12, 512],
       "bounds": [100, 800],
       "contentsOpaque": true,
@@ -68,7 +68,7 @@
       "transform": 1
     },
     {
-      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='spacer')",
+      "name": "Squashing Layer (first squashed layer: LayoutNGBlockFlow (positioned) DIV id='spacer')",
       "position": [12, 2512],
       "bounds": [5000, 1000],
       "transform": 1
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2-expected.png
new file mode 100644
index 0000000..7a94de0
--- /dev/null
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2.html
new file mode 100644
index 0000000..dea3aeb
--- /dev/null
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>backdrop-filter: Filter input is at element bounds</title>
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
+<link rel="match"  href="backdrop-filter-edge-clipping-2-ref.html">
+
+<!-- TODO(978481): Convert this back to WPT test. See crbug.com/978481 for
+  an implementation in WPT that is waiting for the WPT Fuzzy Matching feature.
+-->
+
+<!-- Expected: A white box with a blue border, surrounded by green.
+     A red blurred region should be seen at the bottom of the white box.
+     No green should be observed within the white box.
+     No dark/black should be observed within the white box either. -->
+
+<div class="greenbox top"></div>
+<div class="greenbox right"></div>
+<div class="greenbox bottom"></div>
+<div class="redline"></div>
+<div class="filterbox"></div>
+<style>
+.filterbox {
+  position: absolute;
+  width: 100px;
+  height: 100px;
+  top: 150px;
+  left: 0px;
+  backdrop-filter: blur(10px);
+}
+.greenbox {
+  position:absolute;
+  width: 160px;
+  height: 50px;
+  background: green;
+}
+.redline {
+  position:absolute;
+  width: 90px;
+  height: 5px;
+  top:245px;
+  left:5px;
+  background: red;
+}
+.top {
+  top:100px;
+  left: 0;
+}
+.right {
+  top:130px;
+  left: 102px;
+  width: 58px;
+  height: 150px;
+}
+.bottom {
+  top:250px;
+  left: 0;
+  height: 60px;
+}
+</style>
+
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index c9d848e..ce351822 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -5869,6 +5869,12 @@
      {}
     ]
    ],
+   "html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html": [
+    [
+     "html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html",
+     {}
+    ]
+   ],
    "html/user-activation/activation-thru-contextmenu-event-manual.html": [
     [
      "html/user-activation/activation-thru-contextmenu-event-manual.html",
@@ -46241,6 +46247,18 @@
      {}
     ]
    ],
+   "css/css-fonts/font-family-name-025.html": [
+    [
+     "css/css-fonts/font-family-name-025.html",
+     [
+      [
+       "/css/css-fonts/font-family-name-025-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-fonts/font-feature-resolution-001.html": [
     [
      "css/css-fonts/font-feature-resolution-001.html",
@@ -129458,6 +129476,9 @@
    "css/css-backgrounds/justfortest.html": [
     []
    ],
+   "css/css-backgrounds/parsing/background-image-computed.sub-expected.txt": [
+    []
+   ],
    "css/css-backgrounds/parsing/background-position-computed-expected.txt": [
     []
    ],
@@ -131519,6 +131540,9 @@
    "css/css-fonts/font-family-name-024-ref.xht": [
     []
    ],
+   "css/css-fonts/font-family-name-025-ref.html": [
+    []
+   ],
    "css/css-fonts/font-family-name-mixcase-ref.xht": [
     []
    ],
@@ -137267,6 +137291,15 @@
    "css/css-lists/nested-marker-ref.html": [
     []
    ],
+   "css/css-lists/parsing/list-style-image-computed.sub-expected.txt": [
+    []
+   ],
+   "css/css-lists/parsing/list-style-type-computed-expected.txt": [
+    []
+   ],
+   "css/css-lists/parsing/list-style-type-valid-expected.txt": [
+    []
+   ],
    "css/css-lists/resources/white.gif": [
     []
    ],
@@ -142820,6 +142853,9 @@
    "css/css-ui/appearance-cssom-001-expected.txt": [
     []
    ],
+   "css/css-ui/appearance-initial-value-001-expected.txt": [
+    []
+   ],
    "css/css-ui/appearance-parsing-expected.txt": [
     []
    ],
@@ -150461,13 +150497,13 @@
    "feature-policy/reporting/microphone-reporting.https.html.headers": [
     []
    ],
-   "feature-policy/reporting/midi-report-only-expected.txt": [
+   "feature-policy/reporting/midi-report-only.https-expected.txt": [
     []
    ],
-   "feature-policy/reporting/midi-report-only.html.headers": [
+   "feature-policy/reporting/midi-report-only.https.html.headers": [
     []
    ],
-   "feature-policy/reporting/midi-reporting.html.headers": [
+   "feature-policy/reporting/midi-reporting.https.html.headers": [
     []
    ],
    "feature-policy/reporting/payment-report-only.https-expected.txt": [
@@ -151160,9 +151196,15 @@
    "fetch/nosniff/resources/x-content-type-options.json": [
     []
    ],
+   "fetch/origin/assorted.window-expected.txt": [
+    []
+   ],
    "fetch/origin/resources/redirect-and-stash.py": [
     []
    ],
+   "fetch/origin/resources/referrer-policy.py": [
+    []
+   ],
    "fetch/range/general.window-expected.txt": [
     []
    ],
@@ -153011,9 +153053,6 @@
    "html/cross-origin-opener/new_window_null.tentative-expected.txt": [
     []
    ],
-   "html/cross-origin-opener/new_window_null.tentative.html.ini": [
-    []
-   ],
    "html/cross-origin-opener/new_window_same_origin.tentative-expected.txt": [
     []
    ],
@@ -153410,9 +153449,6 @@
    "html/dom/reflection-misc-expected.txt": [
     []
    ],
-   "html/dom/reflection-obsolete-expected.txt": [
-    []
-   ],
    "html/dom/reflection-original.html": [
     []
    ],
@@ -158717,6 +158753,12 @@
    "html/tools/update_html5lib_tests.py": [
     []
    ],
+   "html/user-activation/resources/activation-hierarchy-child.sub.html": [
+    []
+   ],
+   "html/user-activation/resources/activation-hierarchy-grandchild.html": [
+    []
+   ],
    "html/user-activation/resources/child-four.html": [
     []
    ],
@@ -159308,6 +159350,9 @@
    "import-maps/static-import.js": [
     []
    ],
+   "inert/frame/button.html": [
+    []
+   ],
    "inert/inert-node-is-uneditable.tentative-expected.txt": [
     []
    ],
@@ -196774,6 +196819,12 @@
      {}
     ]
    ],
+   "css/CSS2/floats/hit-test-floats-001.html": [
+    [
+     "css/CSS2/floats/hit-test-floats-001.html",
+     {}
+    ]
+   ],
    "css/CSS2/floats/list-item-taller-than-opportunity-001.html": [
     [
      "css/CSS2/floats/list-item-taller-than-opportunity-001.html",
@@ -199428,6 +199479,12 @@
      {}
     ]
    ],
+   "css/css-fonts/crash-font-face-invalid-descriptor.html": [
+    [
+     "css/css-fonts/crash-font-face-invalid-descriptor.html",
+     {}
+    ]
+   ],
    "css/css-fonts/font-display/font-display-failure-fallback.html": [
     [
      "css/css-fonts/font-display/font-display-failure-fallback.html",
@@ -201724,6 +201781,12 @@
      {}
     ]
    ],
+   "css/css-layout-api/layout-child-inlines-dynamic.https.html": [
+    [
+     "css/css-layout-api/layout-child-inlines-dynamic.https.html",
+     {}
+    ]
+   ],
    "css/css-layout-api/supports.https.html": [
     [
      "css/css-layout-api/supports.https.html",
@@ -201754,12 +201817,102 @@
      {}
     ]
    ],
+   "css/css-lists/list-and-margin-collapse-002.html": [
+    [
+     "css/css-lists/list-and-margin-collapse-002.html",
+     {}
+    ]
+   ],
+   "css/css-lists/list-and-margin-collapse-003.html": [
+    [
+     "css/css-lists/list-and-margin-collapse-003.html",
+     {}
+    ]
+   ],
+   "css/css-lists/list-and-margin-collapse-004.html": [
+    [
+     "css/css-lists/list-and-margin-collapse-004.html",
+     {}
+    ]
+   ],
    "css/css-lists/list-and-writing-mode-001.html": [
     [
      "css/css-lists/list-and-writing-mode-001.html",
      {}
     ]
    ],
+   "css/css-lists/parsing/list-style-computed.html": [
+    [
+     "css/css-lists/parsing/list-style-computed.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-image-computed.sub.html": [
+    [
+     "css/css-lists/parsing/list-style-image-computed.sub.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-image-invalid.html": [
+    [
+     "css/css-lists/parsing/list-style-image-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-image-valid.html": [
+    [
+     "css/css-lists/parsing/list-style-image-valid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-invalid.html": [
+    [
+     "css/css-lists/parsing/list-style-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-position-computed.html": [
+    [
+     "css/css-lists/parsing/list-style-position-computed.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-position-invalid.html": [
+    [
+     "css/css-lists/parsing/list-style-position-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-position-valid.html": [
+    [
+     "css/css-lists/parsing/list-style-position-valid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-type-computed.html": [
+    [
+     "css/css-lists/parsing/list-style-type-computed.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-type-invalid.html": [
+    [
+     "css/css-lists/parsing/list-style-type-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-type-valid.html": [
+    [
+     "css/css-lists/parsing/list-style-type-valid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/list-style-valid.html": [
+    [
+     "css/css-lists/parsing/list-style-valid.html",
+     {}
+    ]
+   ],
    "css/css-logical/animation-001.html": [
     [
      "css/css-logical/animation-001.html",
@@ -202918,6 +203071,18 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-crash-chrome-010.html": [
+    [
+     "css/css-position/position-absolute-crash-chrome-010.html",
+     {}
+    ]
+   ],
+   "css/css-position/position-absolute-crash-chrome-011.html": [
+    [
+     "css/css-position/position-absolute-crash-chrome-011.html",
+     {}
+    ]
+   ],
    "css/css-position/position-absolute-dynamic-containing-block.html": [
     [
      "css/css-position/position-absolute-dynamic-containing-block.html",
@@ -209640,6 +209805,12 @@
      {}
     ]
    ],
+   "css/css-ui/appearance-initial-value-001.html": [
+    [
+     "css/css-ui/appearance-initial-value-001.html",
+     {}
+    ]
+   ],
    "css/css-ui/appearance-parsing.html": [
     [
      "css/css-ui/appearance-parsing.html",
@@ -227169,15 +227340,15 @@
      {}
     ]
    ],
-   "feature-policy/reporting/midi-report-only.html": [
+   "feature-policy/reporting/midi-report-only.https.html": [
     [
-     "feature-policy/reporting/midi-report-only.html",
+     "feature-policy/reporting/midi-report-only.https.html",
      {}
     ]
    ],
-   "feature-policy/reporting/midi-reporting.html": [
+   "feature-policy/reporting/midi-reporting.https.html": [
     [
-     "feature-policy/reporting/midi-reporting.html",
+     "feature-policy/reporting/midi-reporting.https.html",
      {}
     ]
    ],
@@ -230243,24 +230414,9 @@
      {}
     ]
    ],
-   "fetch/origin/no-cors.any.js": [
+   "fetch/origin/assorted.window.js": [
     [
-     "fetch/origin/no-cors.any.html",
-     {
-      "script_metadata": [
-       [
-        "script",
-        "/common/utils.js"
-       ],
-       [
-        "script",
-        "/common/get-host-info.sub.js"
-       ]
-      ]
-     }
-    ],
-    [
-     "fetch/origin/no-cors.any.worker.html",
+     "fetch/origin/assorted.window.html",
      {
       "script_metadata": [
        [
@@ -245343,6 +245499,22 @@
      {}
     ]
    ],
+   "inert/inert-retargeting-iframe.tentative.html": [
+    [
+     "inert/inert-retargeting-iframe.tentative.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "inert/inert-retargeting.tentative.html": [
+    [
+     "inert/inert-retargeting.tentative.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "infrastructure/assumptions/allowed-to-play.html": [
     [
      "infrastructure/assumptions/allowed-to-play.html",
@@ -246895,6 +247067,30 @@
      {}
     ]
    ],
+   "media-source/mediasource-changetype-play-implicit.html": [
+    [
+     "media-source/mediasource-changetype-play-implicit.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "media-source/mediasource-changetype-play-negative.html": [
+    [
+     "media-source/mediasource-changetype-play-negative.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
+   "media-source/mediasource-changetype-play-without-codecs-parameter.html": [
+    [
+     "media-source/mediasource-changetype-play-without-codecs-parameter.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
    "media-source/mediasource-changetype-play.html": [
     [
      "media-source/mediasource-changetype-play.html",
@@ -262794,6 +262990,12 @@
      {}
     ]
    ],
+   "preload/subresource-integrity.html": [
+    [
+     "preload/subresource-integrity.html",
+     {}
+    ]
+   ],
    "presentation-api/controlling-ua/PresentationRequest_error.https.html": [
     [
      "presentation-api/controlling-ua/PresentationRequest_error.https.html",
@@ -276395,12 +276597,6 @@
      {}
     ]
    ],
-   "shape-detection/detection-HTMLVideoElement-invalid-state.html": [
-    [
-     "shape-detection/detection-HTMLVideoElement-invalid-state.html",
-     {}
-    ]
-   ],
    "shape-detection/detection-HTMLVideoElement.html": [
     [
      "shape-detection/detection-HTMLVideoElement.html",
@@ -310891,7 +311087,7 @@
    "testharness"
   ],
   "2dcontext/imagebitmap/canvas-createImageBitmap-video-resize.html": [
-   "366d1fd36e0cbeae4b2aca60d1c86c98844e69cc",
+   "94fc0d1c57ac6cab4e5f637584d5e1d9db7e1a5c",
    "testharness"
   ],
   "2dcontext/imagebitmap/common.sub.js": [
@@ -326770,6 +326966,10 @@
    "679f5b073f98d0fb9dd3efe29c5655897d2ceafd",
    "reftest"
   ],
+  "css/CSS2/floats/hit-test-floats-001.html": [
+   "e554918e7538b0afedb60c80c74647967a114d47",
+   "testharness"
+  ],
   "css/CSS2/floats/intrinsic-size-float-and-line.html": [
    "060312454915307da67988ac52dc41e558479dd1",
    "reftest"
@@ -341966,12 +342166,16 @@
    "cb7d10998e0f46c2553fe161cb215179c4bab4c7",
    "testharness"
   ],
+  "css/css-backgrounds/parsing/background-image-computed.sub-expected.txt": [
+   "d57259dd1817f5f107827c886aa6ba287b32da41",
+   "support"
+  ],
   "css/css-backgrounds/parsing/background-image-computed.sub.html": [
-   "4dc0aad0fd5101f7dc7bc64c1bd0b77d2f872b4f",
+   "a67823cc125a89b454d6c25a055ec5f2ae56ae21",
    "testharness"
   ],
   "css/css-backgrounds/parsing/background-image-invalid.html": [
-   "eaba64a715ce71a9f828569a24df51f1476664dd",
+   "7f1bc98ab63907eb8ab71abc1dd1cc214737bac3",
    "testharness"
   ],
   "css/css-backgrounds/parsing/background-image-valid.html": [
@@ -349310,6 +349514,10 @@
    "1a7c59abd5ddebfc6cd1f972ee6a186bdb7b97df",
    "testharness"
   ],
+  "css/css-fonts/crash-font-face-invalid-descriptor.html": [
+   "73cea13f091a767c66ff4eba956fa130cb3ea66e",
+   "testharness"
+  ],
   "css/css-fonts/first-available-font-001-ref.html": [
    "0acbd338e0ce9f558d2eaa2e48ad4be0524fb0ae",
    "support"
@@ -349590,6 +349798,14 @@
    "92fc90ea4401bdeac1f773dc7fccadfca016f47c",
    "manual"
   ],
+  "css/css-fonts/font-family-name-025-ref.html": [
+   "035ed5f29d0fb9e7f08f130a20c0e82fe89f7225",
+   "support"
+  ],
+  "css/css-fonts/font-family-name-025.html": [
+   "d6ee10db24820cc98102b0fd21e2b4c6af4f4a65",
+   "reftest"
+  ],
   "css/css-fonts/font-family-name-mixcase-ref.xht": [
    "6721b652a21d84b92f2ae9511c46bd63e64538f9",
    "support"
@@ -350527,7 +350743,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html": [
-   "084e181b63d96d5df38ccbc9a639a067f556ec39",
+   "6fe35c2719e4e460e0c88f261ba3f5185a5f062d",
    "testharness"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-005.tentative-ref.html": [
@@ -360674,6 +360890,10 @@
    "11f2b88aa1fe930f36907e3e4c3b5e1b13d28b06",
    "reftest"
   ],
+  "css/css-layout-api/layout-child-inlines-dynamic.https.html": [
+   "50052087f44e42740e639d098ce4861dd008d81a",
+   "testharness"
+  ],
   "css/css-layout-api/layout-child-inlines.https.html": [
    "811af3f40b299b874bcc71d38e8de6b86c67ab34",
    "reftest"
@@ -360974,6 +361194,18 @@
    "e267b839083b4dfb343c92118f93d73a7e8d019f",
    "testharness"
   ],
+  "css/css-lists/list-and-margin-collapse-002.html": [
+   "ef110ca17ce04b458100ca9c7b61f71cbc7b3b67",
+   "testharness"
+  ],
+  "css/css-lists/list-and-margin-collapse-003.html": [
+   "78dcbc6cd4503c2dac6122809f498fbd772c3bc1",
+   "testharness"
+  ],
+  "css/css-lists/list-and-margin-collapse-004.html": [
+   "4a92f8becf204f03e865218b9d82060f83a8cf7e",
+   "testharness"
+  ],
   "css/css-lists/list-and-writing-mode-001.html": [
    "df54e8fb0df8146f896c2abd136d63d930d92d1c",
    "testharness"
@@ -361030,6 +361262,66 @@
    "9627ce936ae570325b430a1ac673cd66ae7d4252",
    "reftest"
   ],
+  "css/css-lists/parsing/list-style-computed.html": [
+   "84409e8335b097cf4f30aa15e10427a242df91d1",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-image-computed.sub-expected.txt": [
+   "14246c8de72437e96558e66e9fc1e5b6b89b5f92",
+   "support"
+  ],
+  "css/css-lists/parsing/list-style-image-computed.sub.html": [
+   "114f08ecd75f77cf7889e8af375476702a0fb13f",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-image-invalid.html": [
+   "ea8bb8ca9632bde992ef4f171368409331a2b58a",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-image-valid.html": [
+   "7b7d43e4bf63e754b302cd7694e895f1cc6dc974",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-invalid.html": [
+   "0dda1630ad8f451908cc5aeddecb7549f12a40c2",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-position-computed.html": [
+   "e36181b1dd4c703d5b6c5b6d8a1ea9d7f3ef81b9",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-position-invalid.html": [
+   "3cf596135a78e2210e5131df4390d47c2d21c5f3",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-position-valid.html": [
+   "ff0c8366bb988fafe72746837a330801ae2fe34b",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-type-computed-expected.txt": [
+   "eabf33a193d5bbb194bcaa708f13b2f2e474d216",
+   "support"
+  ],
+  "css/css-lists/parsing/list-style-type-computed.html": [
+   "0a8cd48b6af9e214841064ad6fcf63a6174c9a73",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-type-invalid.html": [
+   "c49f61ca705f83863ecc1219d24b962b8221426c",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-type-valid-expected.txt": [
+   "b19f911c7831381295946c94ee797db03cca79c2",
+   "support"
+  ],
+  "css/css-lists/parsing/list-style-type-valid.html": [
+   "66067ca43232975409aab83684da9258f10abcea",
+   "testharness"
+  ],
+  "css/css-lists/parsing/list-style-valid.html": [
+   "6f6c344f305d84d8334fc38f07484ebc95266643",
+   "testharness"
+  ],
   "css/css-lists/resources/white.gif": [
    "3aa2098dc8817f5360d13440f3d6245a030dc31d",
    "support"
@@ -365462,6 +365754,14 @@
    "23df49cfaae5cdba6225c981d652b491f9df6784",
    "testharness"
   ],
+  "css/css-position/position-absolute-crash-chrome-010.html": [
+   "91b439a3422db62df86d2ae0f4b8e52cce7fa550",
+   "testharness"
+  ],
+  "css/css-position/position-absolute-crash-chrome-011.html": [
+   "fd3d63e0cf749d315d06e4fe5067f0840d8c64dd",
+   "testharness"
+  ],
   "css/css-position/position-absolute-dynamic-containing-block.html": [
    "3968f685849663574ca213fcb90dc5fb3eaffaa3",
    "testharness"
@@ -385130,6 +385430,14 @@
    "f7dd4d1d761ed57831ae7842280f47164c6c080e",
    "testharness"
   ],
+  "css/css-ui/appearance-initial-value-001-expected.txt": [
+   "7b676a1cf2630657c56d5a40d3582828a03ff0d5",
+   "support"
+  ],
+  "css/css-ui/appearance-initial-value-001.html": [
+   "6e7d3002676d7c9a7bde572e903e5a148c5f34d5",
+   "testharness"
+  ],
   "css/css-ui/appearance-listbox-001.html": [
    "b391754b7493242010cda8789bcb10beb0be5ea1",
    "reftest"
@@ -388023,7 +388331,7 @@
    "testharness"
   ],
   "css/css-values/calc-parenthesis-stack.html": [
-   "1d9033d7eecd14066ee9e4f9c52bf1a39e6ddd1b",
+   "d8dbafb3f720253df4225f5279a1a7a9af2caa35",
    "reftest"
   ],
   "css/css-values/calc-positive-fraction-001.html": [
@@ -396419,7 +396727,7 @@
    "support"
   ],
   "css/cssom/shorthand-values.html": [
-   "d8d7f5349a4561cb4d764f366236e88eb3775c8f",
+   "5a50bc44b789378e54590b83ce4818696ae83bcd",
    "testharness"
   ],
   "css/cssom/style-attr-update-across-documents.html": [
@@ -403851,11 +404159,11 @@
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056-ref.html": [
-   "f4f21058a66693efb729653ba2d0599d40f19138",
+   "519d33864d811ca71c15ab4d1b7572371f48e2a9",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056.html": [
-   "57323a42444ee4229d4eb3a1c6e58c5c7b63ce5d",
+   "d51cb50447ee5681459569fbd4fd573fbd2f2ccf",
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-content-box-001-ref.html": [
@@ -404043,11 +404351,11 @@
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052-ref.html": [
-   "f4f21058a66693efb729653ba2d0599d40f19138",
+   "519d33864d811ca71c15ab4d1b7572371f48e2a9",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052.html": [
-   "ee95f59dd5cdd6d16237928a272c7540a80ec6eb",
+   "33f7a8fe34fdcd671c96e307d7ee753003f1a2b5",
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-016-ref.html": [
@@ -404307,11 +404615,11 @@
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032-ref.html": [
-   "f1fcdf40d719beac17a1cc85619daca2c0abb0ff",
+   "b3f7028eec4112f1b818e95d474b725193e79b84",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032.html": [
-   "7649a98cdc6b7855babc5da87abf55e0d4825ac3",
+   "6e17492e4496fbd33ec42486f737ba204c343fcf",
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/support/Ahem.ttf": [
@@ -411846,23 +412154,23 @@
    "a86e0a077851a84f704e2aa4df1d526ecd9a55b2",
    "support"
   ],
-  "feature-policy/reporting/midi-report-only-expected.txt": [
+  "feature-policy/reporting/midi-report-only.https-expected.txt": [
    "bba8e2aa4e6bcfcd26d918c967e6b0f4d14078ae",
    "support"
   ],
-  "feature-policy/reporting/midi-report-only.html": [
+  "feature-policy/reporting/midi-report-only.https.html": [
    "bf0234d3f6511f4ecfeb43857494e4dee0666b79",
    "testharness"
   ],
-  "feature-policy/reporting/midi-report-only.html.headers": [
+  "feature-policy/reporting/midi-report-only.https.html.headers": [
    "d307ccc9b09ca486e2ffd85efec14cfdbd285ad4",
    "support"
   ],
-  "feature-policy/reporting/midi-reporting.html": [
+  "feature-policy/reporting/midi-reporting.https.html": [
    "94674a37a8106ccc44ae4c8bb646e472ae98aab9",
    "testharness"
   ],
-  "feature-policy/reporting/midi-reporting.html.headers": [
+  "feature-policy/reporting/midi-reporting.https.html.headers": [
    "0e145978a014f08fb5faff42750e9338da0f9ede",
    "support"
   ],
@@ -413614,12 +413922,20 @@
    "c8c1076df5cffe60845f67ad275c6e36f024d989",
    "testharness"
   ],
-  "fetch/origin/no-cors.any.js": [
-   "c9d1d3b3af3750ec43549a3d96a0912e2cf6b399",
+  "fetch/origin/assorted.window-expected.txt": [
+   "f7ee4a70fd1c6a5763156923b1c1fb4081b91d15",
+   "support"
+  ],
+  "fetch/origin/assorted.window.js": [
+   "9e832724a70ddf406511555e791c9eac5902397c",
    "testharness"
   ],
   "fetch/origin/resources/redirect-and-stash.py": [
-   "1b1b46ba177ad3af56ef83e90b0974dd81cd3eae",
+   "aa9eb35db2bddb59acb1c7ebcbb971ab38d344b9",
+   "support"
+  ],
+  "fetch/origin/resources/referrer-policy.py": [
+   "22b71e33b54160c451f1bbfc8a157600eeb7059f",
    "support"
   ],
   "fetch/range/general.any.js": [
@@ -418150,10 +418466,6 @@
    "35a42fd2f0963f886324ac7ca01d5b45cdc1588e",
    "testharness"
   ],
-  "html/cross-origin-opener/new_window_null.tentative.html.ini": [
-   "f5b55a40f2ec1d2e7292f8289e32f256fa163154",
-   "support"
-  ],
   "html/cross-origin-opener/new_window_same_origin.tentative-expected.txt": [
    "89b66e08c1e2a6f5b6930eae9e2b6033b75a0419",
    "support"
@@ -418539,7 +418851,7 @@
    "support"
   ],
   "html/dom/elements-obsolete.js": [
-   "7f673cb7f0ee9bbeae746448163bbc11e31a1220",
+   "3ef9e9f99711f7321a86f7f7ab7e6dab691b58ea",
    "support"
   ],
   "html/dom/elements-sections.js": [
@@ -419442,10 +419754,6 @@
    "915f6fad47ffb6d92066f8854bf42f8a75586f28",
    "testharness"
   ],
-  "html/dom/reflection-obsolete-expected.txt": [
-   "27bd13b69096cdfdd3781133f436bd1ff51c3676",
-   "support"
-  ],
   "html/dom/reflection-obsolete.html": [
    "0aa439813ed9b436c26476fb367a2b666892909e",
    "testharness"
@@ -433638,6 +433946,10 @@
    "a8eba38c60eeef7396c2725d26ea3c6e6609be7a",
    "testharness"
   ],
+  "html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html": [
+   "7dd8018573db2808b7166cf7e94650a2a5a330bc",
+   "manual"
+  ],
   "html/user-activation/activation-thru-contextmenu-event-manual.html": [
    "998bac3694c6f81e099c1c9eab41c704adcf10f9",
    "manual"
@@ -433670,6 +433982,14 @@
    "4f03195f434742d80574c481092b307d4ad9108f",
    "manual"
   ],
+  "html/user-activation/resources/activation-hierarchy-child.sub.html": [
+   "ebccc8c0dbf9da8a95049712f0fdd641b66b629b",
+   "support"
+  ],
+  "html/user-activation/resources/activation-hierarchy-grandchild.html": [
+   "b9fe19a746ffbbe63740df67a77da8a894b573ae",
+   "support"
+  ],
   "html/user-activation/resources/child-four.html": [
    "65d17f273e1868312f59c89f3f2d05bda759ae1a",
    "support"
@@ -435622,6 +435942,10 @@
    "1686fc123a798bddbf626f4d112516317739da8f",
    "support"
   ],
+  "inert/frame/button.html": [
+   "5867c758142c6652362a23f19533a8d86fe29648",
+   "support"
+  ],
   "inert/inert-does-not-match-disabled-selector.tentative.html": [
    "74b8ac3f7dd5c3447bf47fd732fade9220497c93",
    "testharness"
@@ -435654,6 +435978,14 @@
    "7d5e90821bb0bd0d83711685853c78ff9fee6c4f",
    "testharness"
   ],
+  "inert/inert-retargeting-iframe.tentative.html": [
+   "78d7f6b36a7af8181916c125a789e24dd2535ac1",
+   "testharness"
+  ],
+  "inert/inert-retargeting.tentative.html": [
+   "d239a7056f2ffa971eda8e185bb1ab594e29226d",
+   "testharness"
+  ],
   "infrastructure/META.yml": [
    "c1d3b0c9ec4c3f6458597d8596cc3213866598df",
    "support"
@@ -436531,7 +436863,7 @@
    "support"
   ],
   "interfaces/performance-timeline.idl": [
-   "8ded59d8a269f83037a8845417c1275dd91ec538",
+   "56c0ec4bc2bd7dbfd5bca24f9c548d8c37595259",
    "support"
   ],
   "interfaces/permissions.idl": [
@@ -436615,7 +436947,7 @@
    "support"
   ],
   "interfaces/service-workers.idl": [
-   "89229e377f7eca23e2f9ce625f40fe1e7e87ea78",
+   "8e37060a180328d937d26623aaa030ad434eacc6",
    "support"
   ],
   "interfaces/shape-detection-api.idl": [
@@ -436655,7 +436987,7 @@
    "support"
   ],
   "interfaces/wai-aria.idl": [
-   "cd039adfc5bc39bae26b1024bb7cbee22b0e9612",
+   "c4fbf11409cf142d8295038d256f5a686d860a55",
    "support"
   ],
   "interfaces/wake-lock.idl": [
@@ -436707,7 +437039,7 @@
    "support"
   ],
   "interfaces/webmidi.idl": [
-   "aa2ac7f297b9b6e5d13a34d44ef772a471059f9d",
+   "58d6357f7365765c7e9142024caddba51c373927",
    "support"
   ],
   "interfaces/webrtc-dscp.idl": [
@@ -436715,7 +437047,7 @@
    "support"
   ],
   "interfaces/webrtc-stats.idl": [
-   "981317f6be42d79f8113a1c032f325f673007d27",
+   "f29e00d195e898cb6c2dfce9a82bc81fd168787a",
    "support"
   ],
   "interfaces/webrtc.idl": [
@@ -436731,7 +437063,7 @@
    "support"
   ],
   "interfaces/webxr.idl": [
-   "4af1c96a1847445c7559dabba20cb8a3a1de5c60",
+   "c78683ef6bf6822b0320fe4c16bc9630a63884de",
    "support"
   ],
   "interfaces/worklets.idl": [
@@ -437139,7 +437471,7 @@
    "testharness"
   ],
   "largest-contentful-paint/observe-image.html": [
-   "f98912085aaf2677f4dbd85fba37c4b7f49ac5c2",
+   "43cdfab074d62827fca5e55b8b334b9fafb6507b",
    "testharness"
   ],
   "largest-contentful-paint/observe-text.html": [
@@ -437215,7 +437547,7 @@
    "testharness"
   ],
   "lint.whitelist": [
-   "9966d7c307b5520ab251d69f231526df762dc4ba",
+   "c344c3a834444db4be24db1d0132c85a4b14395c",
    "support"
   ],
   "loading/preloader-css-import-no-quote.tentative.html": [
@@ -438314,12 +438646,24 @@
    "38cd1717eb23be2d6ef9dde3959aa79b48621193",
    "testharness"
   ],
+  "media-source/mediasource-changetype-play-implicit.html": [
+   "c186361e791b303e958c93171ba58a260e20d5fa",
+   "testharness"
+  ],
+  "media-source/mediasource-changetype-play-negative.html": [
+   "f74e12945ac1a4a5865f0f10cc66a522301400f7",
+   "testharness"
+  ],
+  "media-source/mediasource-changetype-play-without-codecs-parameter.html": [
+   "f802b155f724357905d1a92f6ed1e1155b45f1b8",
+   "testharness"
+  ],
   "media-source/mediasource-changetype-play.html": [
-   "210f66e1a28dfda173ab3baa5836afe59ecd54ca",
+   "26a67c32704de1ab4f9a60aebbadf7d8f25c2601",
    "testharness"
   ],
   "media-source/mediasource-changetype-util.js": [
-   "3dd1c4738766155d2ab079fe5b6a7970ebdb71db",
+   "28e92893657b956d84bff1cf0c87c000f272063d",
    "support"
   ],
   "media-source/mediasource-changetype.html": [
@@ -439259,7 +439603,7 @@
    "testharness"
   ],
   "mediacapture-streams/idlharness.https.window.js": [
-   "976d31b6609350a0ccc18a4be7764e7e3797b614",
+   "5b255fca6b0b770ab6f849afe5d657c66052215a",
    "testharness"
   ],
   "mediacapture-streams/idlharness.window-expected.txt": [
@@ -451070,6 +451414,10 @@
    "16d893ca7e54adde5fec3744b95a14f7e2cf3f34",
    "testharness"
   ],
+  "preload/subresource-integrity.html": [
+   "08c7854a9ecff0c6510d7ce7752de8e601e1530a",
+   "testharness"
+  ],
   "presentation-api/META.yml": [
    "beba89062154273b1eabca34f392ccbc1794932c",
    "support"
@@ -461171,7 +461519,7 @@
    "support"
   ],
   "resources/chromium/webxr-test.js": [
-   "5406b6745bdf021b59aa21f669e0cdbc6e858045",
+   "5ed96b8fc93fa21dd5631e27ccec0544828d1c3b",
    "support"
   ],
   "resources/chromium/webxr-test.js.headers": [
@@ -461191,7 +461539,7 @@
    "support"
   ],
   "resources/sriharness.js": [
-   "9d7fa76a7d65f63ce8a3844920388ffb39b83bc3",
+   "fe8ad2b4270c2f048a6a9eada153b1a1b2e90f1b",
    "support"
   ],
   "resources/testdriver-actions.js": [
@@ -463395,11 +463743,11 @@
    "testharness"
   ],
   "service-workers/service-worker/interfaces-sw.https-expected.txt": [
-   "5e51cf6272eebfd26a8af8489e681bfdb35d4766",
+   "c368ef5311b68e6fd474a9871a999618db913a96",
    "support"
   ],
   "service-workers/service-worker/interfaces-sw.https.html": [
-   "50c76d306dc2f43eac0f4d827e9b72464b63220c",
+   "73ffb28b36ea38b7230a7042feb444fde0bcf2e9",
    "testharness"
   ],
   "service-workers/service-worker/interfaces-window.https.html": [
@@ -465894,10 +466242,6 @@
    "979efabdcb07cfdc0190383026a24e144dea1747",
    "testharness"
   ],
-  "shape-detection/detection-HTMLVideoElement-invalid-state.html": [
-   "e777c96e4a1737968d60f390bf97d5f4c971e95d",
-   "testharness"
-  ],
   "shape-detection/detection-HTMLVideoElement.html": [
    "7b3736d02e9b6b0769a18354b054e5c3ce268773",
    "testharness"
@@ -465951,7 +466295,7 @@
    "support"
   ],
   "shape-detection/shapedetection-cross-origin.sub.html": [
-   "f45369266973ee771c648d01b6e4a48ce1282959",
+   "c9d86430356de470bca7a8dfef8596e9159164ad",
    "testharness"
   ],
   "shape-detection/shapedetection-empty-input.html": [
@@ -466579,7 +466923,7 @@
    "support"
   ],
   "std-toast/attributes.html": [
-   "9bfc65c395cd37c07f7499a03e5cdfb6fe16c85e",
+   "2aef41aea618e0ec838ba7885494c69dd106ff17",
    "testharness"
   ],
   "std-toast/events-open.html": [
@@ -467383,7 +467727,7 @@
    "support"
   ],
   "subresource-integrity/sri-test-helpers.sub.js": [
-   "22c9e9c5dd9af81d988f9c1570645925a558d070",
+   "53102d45442ead0576f34b8c33f659f6f7a10999",
    "support"
   ],
   "subresource-integrity/style.css": [
@@ -467455,11 +467799,11 @@
    "support"
   ],
   "svg/coordinate-systems/support/viewBox-scaling-text-001-ref.html": [
-   "09321909954d4c9a73d47671e64f4f6555b59087",
+   "effaba5fc4aeceb2249b8a843873ef53860e4ba7",
    "support"
   ],
   "svg/coordinate-systems/viewBox-scaling-text-001.html": [
-   "4250e4a2f5f980f1db119dfe6e1c068ce6c11c8f",
+   "3089043078746231cda5331140f56a6b907c70fd",
    "reftest"
   ],
   "svg/embedded/image-embedding-svg-viewref-with-viewbox.svg": [
@@ -482003,7 +482347,7 @@
    "testharness"
   ],
   "webusb/usbDevice.https.any.js": [
-   "baff28367b28ac373f0b01af99881c64fefb33c2",
+   "03dbe8f441369f824e17a850dffdfa3b616c63b0",
    "testharness"
   ],
   "webusb/usbEndpoint.https.any.js": [
@@ -485143,7 +485487,7 @@
    "support"
   ],
   "webxr/idlharness.https.window-expected.txt": [
-   "4bff46592bbe393232eda88c69f9fd4d1d4714cf",
+   "bf29a9f700848cf6d28b8f466afda0c2a29a2387",
    "support"
   ],
   "webxr/idlharness.https.window.js": [
diff --git a/third_party/blink/web_tests/external/wpt/2dcontext/imagebitmap/canvas-createImageBitmap-video-resize.html b/third_party/blink/web_tests/external/wpt/2dcontext/imagebitmap/canvas-createImageBitmap-video-resize.html
index 366d1fd..94fc0d1 100644
--- a/third_party/blink/web_tests/external/wpt/2dcontext/imagebitmap/canvas-createImageBitmap-video-resize.html
+++ b/third_party/blink/web_tests/external/wpt/2dcontext/imagebitmap/canvas-createImageBitmap-video-resize.html
@@ -51,6 +51,7 @@
 
 // HTMLVideoElement
 var video = document.createElement("video");
+video.preload = "auto";
 video.oncanplaythrough = t.step_func(function() {
     return generateTest();
 });
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/floats/hit-test-floats-001.html b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/hit-test-floats-001.html
new file mode 100644
index 0000000..e554918e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/hit-test-floats-001.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/977856">
+<link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#floats" />
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div style="width: 270px">
+  <img width="260" height="68">
+  <div style="float: left">
+    <a id="target" href="#">Link</a>
+  </div>
+  <br style="clear: both">
+</div>
+<script>
+test(() => {
+  let target = document.getElementById('target');
+  let bounds = target.getBoundingClientRect();
+  let element = document.elementFromPoint(bounds.x + 1, bounds.y + 1);
+  assert_equals(element, target);
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-computed.sub-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-computed.sub-expected.txt
new file mode 100644
index 0000000..d57259d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-computed.sub-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Property background-image value 'none' computes to 'none'
+PASS Property background-image value 'url("http://web-platform.test/")' computes to 'url("http://web-platform.test/")'
+PASS Property background-image value 'none, url("http://web-platform.test/")' computes to 'none, url("http://web-platform.test/")'
+FAIL Property background-image value 'linear-gradient(to left bottom, red, blue)' computes to 'linear-gradient(to left bottom, rgb(255, 0, 0), rgb(0, 0, 255))' assert_equals: expected "linear-gradient(to left bottom, rgb(255, 0, 0), rgb(0, 0, 255))" but got "linear-gradient(to left bottom, red, blue)"
+PASS Property background-image value 'radial-gradient(10px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))' computes to 'radial-gradient(10px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))'
+FAIL Property background-image value 'radial-gradient(circle calc(-0.5em + 10px) at calc(-1em + 10px) calc(-2em + 10px), red, blue)' computes to 'radial-gradient(0px at -30px -70px, rgb(255, 0, 0), rgb(0, 0, 255))' assert_equals: expected "radial-gradient(0px at -30px -70px, rgb(255, 0, 0), rgb(0, 0, 255))" but got "radial-gradient(calc(-0.5em + 10px) at calc(-1em + 10px) calc(-2em + 10px), red, blue)"
+FAIL Property background-image value 'radial-gradient(ellipse calc(-0.5em + 10px) calc(0.5em + 10px) at 20px 30px, red, blue)' computes to 'radial-gradient(0px 30px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))' assert_equals: expected "radial-gradient(0px 30px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))" but got "radial-gradient(calc(-0.5em + 10px) calc(0.5em + 10px) at 20px 30px, red, blue)"
+FAIL Property background-image value 'radial-gradient(ellipse calc(0.5em + 10px) calc(-0.5em + 10px) at 20px 30px, red, blue)' computes to 'radial-gradient(30px 0px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))' assert_equals: expected "radial-gradient(30px 0px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))" but got "radial-gradient(calc(0.5em + 10px) calc(-0.5em + 10px) at 20px 30px, red, blue)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-computed.sub.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-computed.sub.html
index 4dc0aad..a67823c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-computed.sub.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-computed.sub.html
@@ -5,17 +5,31 @@
 <title>CSS Backgrounds and Borders: getComputedValue().backgroundImage</title>
 <link rel="help" href="https://drafts.csswg.org/css-backgrounds/#background-image">
 <meta name="assert" content="background-image computed value is as specified.">
+<meta name="assert" content="Colors and lengths are computed, with radii clamped.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
 </head>
 <body>
+<!-- target is used by test_computed_value -->
 <div id="target"></div>
 <script>
 test_computed_value("background-image", "none");
 
 test_computed_value("background-image", 'url("http://{{host}}/")');
 test_computed_value("background-image", 'none, url("http://{{host}}/")');
+
+test_computed_value('background-image', 'linear-gradient(to left bottom, red, blue)', 'linear-gradient(to left bottom, rgb(255, 0, 0), rgb(0, 0, 255))');
+
+test_computed_value('background-image', 'radial-gradient(10px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))');
+test_computed_value('background-image', 'radial-gradient(circle calc(-0.5em + 10px) at calc(-1em + 10px) calc(-2em + 10px), red, blue)', 'radial-gradient(0px at -30px -70px, rgb(255, 0, 0), rgb(0, 0, 255))');
+test_computed_value('background-image', 'radial-gradient(ellipse calc(-0.5em + 10px) calc(0.5em + 10px) at 20px 30px, red, blue)', 'radial-gradient(0px 30px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))');
+test_computed_value('background-image', 'radial-gradient(ellipse calc(0.5em + 10px) calc(-0.5em + 10px) at 20px 30px, red, blue)', 'radial-gradient(30px 0px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))');
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-invalid.html
index eaba64a7..7f1bc98 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-invalid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/background-image-invalid.html
@@ -5,7 +5,9 @@
 <title>CSS Backgrounds and Borders Module Level 3: parsing background-image with invalid values</title>
 <link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-backgrounds/#background-image">
+<link rel="help" href="https://drafts.csswg.org/css-images/#radial-gradients">
 <meta name="assert" content="background-image supports only the grammar '<bg-image>#'.">
+<meta name="assert" content="Negative radial-gradient radii are invalid.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
@@ -13,6 +15,14 @@
 <body>
 <script>
 test_invalid_value("background-image", "none, auto");
+
+// Negative radii are invalid.
+test_invalid_value("background-image", "radial-gradient(circle -10px at center, red, blue)");
+test_invalid_value("background-image", "repeating-radial-gradient(-10px at center, red, blue)");
+test_invalid_value("background-image", "radial-gradient(ellipse -20px 30px at center, red, blue)");
+test_invalid_value("background-image", "repeating-radial-gradient(-20% 30% at center, red, blue)");
+test_invalid_value("background-image", "radial-gradient(20px -30px at center, red, blue)");
+test_invalid_value("background-image", "repeating-radial-gradient(20px -30px ellipse at center, red, blue)");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/crash-font-face-invalid-descriptor.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/crash-font-face-invalid-descriptor.html
new file mode 100644
index 0000000..73cea13
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/crash-font-face-invalid-descriptor.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/977953" />
+<script type="text/javascript" src="/resources/testharness.js"></script>
+<script type="text/javascript" src="/resources/testharnessreport.js"></script>
+<style>
+    @font-face {}
+</style>
+<script>
+    test(() => {
+        let rule = document.styleSheets[0].cssRules[0];
+        rule.style.backgroundPosition = 'bottom 10px right 20px';
+    }, 'Do not crash when setting an invalid @font-face descriptor via CSSOM');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/font-family-name-025-ref.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-family-name-025-ref.html
new file mode 100644
index 0000000..035ed5f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-family-name-025-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>CSS Test: PASS rendering</title>
+<link rel="help" href="http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family" />
+<link rel="help" href="http://www.w3.org/TR/CSS21/fonts.html#font-family-prop" />
+<meta name="assert" content="The 'font-family' property set to and installed font renders the appropriate font." />
+<style type="text/css">
+body { font-size: 36px; }
+span#verify { font-family: CSSTest Verify; }
+p {
+  font-family: ahem, monospace;
+}
+</style>
+<div><a href="http://www.w3.org/Style/CSS/Test/Fonts/">Test fonts</a> must be installed for this test: <span id="verify">FAIL</span></div>
+<p>These two lines should use the same font.</p>
+<p>These two lines should use the same font.</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/font-family-name-025.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-family-name-025.html
new file mode 100644
index 0000000..d6ee10d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-family-name-025.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>CSS Test: font family name should not match postscript font name</title>
+<link rel="help" href="http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family" />
+<link rel="help" href="http://www.w3.org/TR/CSS21/fonts.html#font-family-prop" />
+<link rel="help" href="http://www.w3.org/TR/css-fonts-3/#font-family-prop" />
+<link rel="match" href="font-family-name-025-ref.html" />
+<meta name="assert" content="The 'font-family' property set to and installed font renders the appropriate font. Postscript name should not match." />
+<style>
+body { font-size: 36px; }
+span#verify { font-family: CSSTest Verify; }
+p.test {
+  /* Verdana-Bold is a standard supplied font on Windows, Mac, and Linux
+     allowing this test to actually work on most systems without the CSS
+     test fonts. */
+  font-family: CSSTestBasic-Bold, Verdana-Bold, ahem, monospace;
+}
+p {
+  font-family: ahem, monospace;
+}
+</style>
+<div><a href="http://www.w3.org/Style/CSS/Test/Fonts/">Test fonts</a> must be installed for this test: <span id="verify">FAIL</span></div>
+<p class="test">These two lines should use the same font.</p>
+<p>These two lines should use the same font.</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html
index 084e181..6fe35c2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html
@@ -45,7 +45,7 @@
       .level5 { math-script-level: 5; }
     </style>
     <script>
-      const big = 2000;
+      const big = 3000;
       const small = 150;
       setup({ explicit_done: true });
       window.addEventListener("load", function() {
@@ -94,11 +94,12 @@
           }, "scriptPercentScaleDown=80, scriptScriptPercentScaleDown=40");
 
           test(function() {
+              var scriptPercentScaleDown = .71;
               CheckFontSizes("scale0-40-scaledown", {
                   "-3": big,
                   "-1": big * .71 * .71,
                   "0": big * .71 * .71 * .71,
-                  "1": big * .71 * .71 * .71 * .71,
+                  "1": big * .71 * .71 * .71 * scriptPercentScaleDown,
                   "2": big * .71 * .71 * .71 * .4,
                   "3": big * .71 * .71 * .71 * .4 * .71,
                   "5": big * .71 * .71 * .71 * .4 * .71 * .71 * .71
@@ -107,7 +108,7 @@
                   "5": small,
                   "3": small / (.71 * .71),
                   "2": small / (.71 * .71 * .71),
-                  "1": small / (.71 * .71 * .71 * (.4 / .71)),
+                  "1": small / (.71 * .71 * .71 * (.4 / scriptPercentScaleDown)),
                   "0": small  / (.71 * .71 * .71 * .4),
                   "-1": small / (.71 * .71 * .71 * .4 * .71),
                   "-3": small / (.71 * .71 * .71 * .4 * .71 * .71 * .71)
@@ -115,23 +116,24 @@
           }, "scriptPercentScaleDown=0, scriptScriptPercentScaleDown=40");
 
           test(function() {
+              var scriptScriptPercentScaleDown = 0.5041;
               CheckFontSizes("scale80-0-scaledown", {
                   "-3": big,
                   "-1": big * .71 * .71,
                   "0": big * .71 * .71 * .71,
                   "1": big * .71 * .71 * .71 * .8,
-                  "2": big * .71 * .71 * .71 * .71 * .71,
-                  "3": big * .71 * .71 * .71 * .71 * .71 * .71,
-                  "5": big * .71 * .71 * .71 * .71 * .71 * .71 * .71 * .71
+                  "2": big * .71 * .71 * .71 * scriptScriptPercentScaleDown,
+                  "3": big * .71 * .71 * .71 * scriptScriptPercentScaleDown * .71,
+                  "5": big * .71 * .71 * .71 * scriptScriptPercentScaleDown * .71 * .71 * .71
               });
               CheckFontSizes("scale80-0-scaleup", {
                   "5": small,
                   "3": small / (.71 * .71),
                   "2": small / (.71 * .71 * .71),
-                  "1": small / (.71 * .71 * .71 * (.71 / .8)),
-                  "0": small  / (.71 * .71 * .71 * .71),
-                  "-1": small / (.71 * .71 * .71 * .71 * .71),
-                  "-3": small / (.71 * .71 * .71 * .71 * .71 * .71 * .71)
+                  "1": small / (.71 * .71 * .71 * (scriptScriptPercentScaleDown / .8)),
+                  "0": small  / (.71 * .71 * .71 * scriptScriptPercentScaleDown),
+                  "-1": small / (.71 * .71 * .71 * scriptScriptPercentScaleDown * .71),
+                  "-3": small / (.71 * .71 * .71 * scriptScriptPercentScaleDown * .71 * .71 * .71)
               });
           }, "scriptPercentScaleDown=80, scriptScriptPercentScaleDown=0");
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-layout-api/layout-child-inlines-dynamic.https.html b/third_party/blink/web_tests/external/wpt/css/css-layout-api/layout-child-inlines-dynamic.https.html
new file mode 100644
index 0000000..50052087
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-layout-api/layout-child-inlines-dynamic.https.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Layout API: Dynamic blockification of inline children</title>
+<link rel="author" href="mailto:obrufau@igalia.com" title="Oriol Brufau">
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layout-children" title="4.1. Layout Children">
+<meta name="assert" content="This test checks that inline children are correctly blockified or unblockified when the display of the parent changes dynamically." />
+
+<style>
+#wrapper {
+  display: layout(foo);
+}
+#test {
+  display: inline;
+}
+</style>
+
+<div id="wrapper">
+  <div id="test">Lorem ipsum</div>
+</div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+<script>
+promise_test(async function() {
+  await importWorklet(CSS.layoutWorklet, {url: 'support/layout-child-worklet.js'});
+
+  const wrapper = document.getElementById("wrapper");
+  const test = document.getElementById("test");
+
+  assert_equals(getComputedStyle(test).display, "block", "The child should have been blockified by the custom layout");
+
+  wrapper.style.display = "block";
+  assert_equals(getComputedStyle(test).display, "inline", "The child should no longer be blockified in block layout");
+
+  wrapper.style.display = "";
+  assert_equals(getComputedStyle(test).display, "block", "The child should have been blockified again");
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-002.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-002.html
new file mode 100644
index 0000000..ef110ca1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-002.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Lists: test the margin collapse of marker</title>
+<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists">
+<link rel="help" href="http://crbug.com/969741">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<div id="target_with_marker" style="overflow:hidden; width:100px;">
+  <div style="margin-bottom:100px;"></div>
+  <div style="display:list-item; margin-left:100px; height:0px;"></div>
+  <div style="margin-top:100px;"></div>
+</div>
+
+<div id="target_without_marker" style="overflow:hidden; width:100px;">
+  <div style="margin-bottom:100px;"></div>
+  <div style="display:list-item; margin-left:100px; list-style:none;"></div>
+  <div style="margin-top:100px;"></div>
+</div>
+
+<script>
+test(function() {
+  var height_with_marker = document.getElementById("target_with_marker").clientHeight;
+  assert_equals(height_with_marker, 200, "Should not allow margin-collapsing through if list is with marker.");
+  var height_without_marker = document.getElementById("target_without_marker").clientHeight;
+  assert_equals(height_without_marker, 100, "Should allow margin-collapsing through if list is without marker.");
+}, "list and margin collapse");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-003.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-003.html
new file mode 100644
index 0000000..78dcbc6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-003.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Lists: test the margin collapse of marker</title>
+<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists">
+<link rel="help" href="http://crbug.com/969741">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style type="text/css">
+.checkbox{
+  -moz-appearance: checkbox;
+  -webkit-appearance: checkbox;
+  height: 0px;
+}
+</style>
+
+<div id="log"></div>
+
+<div id="target_with_marker" style="overflow:hidden; width:100px;">
+  <div style="margin-bottom:100px;"></div>
+  <div style="display:list-item; margin-left:100px; height:0px;"><div class="checkbox"></div></div>
+  <div style="margin-top:100px;"></div>
+</div>
+
+<div id="target_without_marker" style="overflow:hidden; width:100px;">
+  <div style="margin-bottom:100px;"></div>
+  <div style="display:list-item; margin-left:100px; list-style:none;"><div class="checkbox"></div></div>
+  <div style="margin-top:100px;"></div>
+</div>
+
+<script>
+test(function() {
+  var height_with_marker = document.getElementById("target_with_marker").clientHeight;
+  assert_equals(height_with_marker, 200, "Should not allow margin-collapsing through if list is with marker.");
+  var height_without_marker = document.getElementById("target_without_marker").clientHeight;
+  assert_equals(height_without_marker, 100, "Should allow margin-collapsing through if list is without marker.");
+}, "list and margin collapse");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-004.html b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-004.html
new file mode 100644
index 0000000..4a92f8b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/list-and-margin-collapse-004.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Test if it doesn't crash when resolving BFC block-offset abort</title>
+<link rel=help href="https://www.w3.org/TR/CSS22/generate.html#lists">
+<link rel="help" href="http://crbug.com/969741">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type="text/css">
+#checkbox{
+  -moz-appearance: checkbox;
+  -webkit-appearance: checkbox;
+  height: 0px;
+}
+</style>
+<body>
+  <div style="float: left; width: 100px; height: 100px;"></div>
+  <div style="display: list-item;">
+    <div id="checkbox"></div>
+  </div>
+<script>
+test(() => {}, "Layout should not crash");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-image-computed.sub-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-image-computed.sub-expected.txt
new file mode 100644
index 0000000..14246c8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-image-computed.sub-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+PASS Property list-style-image value 'none' computes to 'none'
+PASS Property list-style-image value 'url("https://web-platform.test/")' computes to 'url("https://web-platform.test/")'
+FAIL Property list-style-image value 'linear-gradient(to left bottom, red , blue )' computes to 'linear-gradient(to left bottom, rgb(255, 0, 0), rgb(0, 0, 255))' assert_equals: expected "linear-gradient(to left bottom, rgb(255, 0, 0), rgb(0, 0, 255))" but got "linear-gradient(to left bottom, red, blue)"
+PASS Property list-style-image value 'radial-gradient(10px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))' computes to 'radial-gradient(10px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))'
+FAIL Property list-style-image value 'radial-gradient(circle calc(-0.5em + 10px) at calc(-1em + 10px) calc(-2em + 10px), rgb(255, 0, 0), rgb(0, 0, 255))' computes to 'radial-gradient(0px at -30px -70px, rgb(255, 0, 0), rgb(0, 0, 255))' assert_equals: expected "radial-gradient(0px at -30px -70px, rgb(255, 0, 0), rgb(0, 0, 255))" but got "radial-gradient(calc(-0.5em + 10px) at calc(-1em + 10px) calc(-2em + 10px), rgb(255, 0, 0), rgb(0, 0, 255))"
+FAIL Property list-style-image value 'radial-gradient(ellipse calc(-0.5em + 10px) calc(0.5em + 10px) at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))' computes to 'radial-gradient(0px 30px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))' assert_equals: expected "radial-gradient(0px 30px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))" but got "radial-gradient(calc(-0.5em + 10px) calc(0.5em + 10px) at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))"
+FAIL Property list-style-image value 'radial-gradient(ellipse calc(0.5em + 10px) calc(-0.5em + 10px) at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))' computes to 'radial-gradient(30px 0px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))' assert_equals: expected "radial-gradient(30px 0px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))" but got "radial-gradient(calc(0.5em + 10px) calc(-0.5em + 10px) at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-image-computed.sub.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-image-computed.sub.html
new file mode 100644
index 0000000..114f08e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-image-computed.sub.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Lists: getComputedValue().listStyleImage</title>
+<link rel="help" href="https://drafts.csswg.org/css-lists-3/#propdef-list-style-image">
+<meta name="assert" content="list-style-image computed value is as specified.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value('list-style-image', 'none');
+
+test_computed_value('list-style-image', 'url("https://{{host}}/")');
+
+test_computed_value('list-style-image', 'linear-gradient(to left bottom, red , blue )', 'linear-gradient(to left bottom, rgb(255, 0, 0), rgb(0, 0, 255))');
+
+test_computed_value('list-style-image', 'radial-gradient(10px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))');
+test_computed_value('list-style-image', 'radial-gradient(circle calc(-0.5em + 10px) at calc(-1em + 10px) calc(-2em + 10px), rgb(255, 0, 0), rgb(0, 0, 255))', 'radial-gradient(0px at -30px -70px, rgb(255, 0, 0), rgb(0, 0, 255))');
+test_computed_value('list-style-image', 'radial-gradient(ellipse calc(-0.5em + 10px) calc(0.5em + 10px) at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))', 'radial-gradient(0px 30px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))');
+test_computed_value('list-style-image', 'radial-gradient(ellipse calc(0.5em + 10px) calc(-0.5em + 10px) at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))', 'radial-gradient(30px 0px at 20px 30px, rgb(255, 0, 0), rgb(0, 0, 255))');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-011.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-011.html
new file mode 100644
index 0000000..fd3d63e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-011.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://crbug.com/977930">
+<style>
+body {
+  margin: 20px;
+}
+.container {
+  position: relative;
+}
+#inline-container-absolute {
+  position: relative;
+  background: rgba(0,255,0,0.3);
+}
+#inline-container-fixed {
+  filter:  blur(2px);
+  background: rgba(0,255,0,0.3);
+}
+.outofflow {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  background: green;
+  top:0;
+  left:0;
+}
+.splitter {
+  width: 100px;
+  height: 20px;
+  background: gray;
+}
+</style>
+<div class="container">
+ <div>
+    absolute
+    <span id="inline-container-absolute" >
+      container
+        <div class="outofflow" style="position:absolute">A</div>
+      container
+      <div class="splitter">splitter</div>
+      container
+    </span>
+  </div>
+</div>
+<div class="container">
+ <div>
+    fixed
+    <span id="inline-container-fixed" >
+      container
+        <div class="outofflow" style="position:fixed">F</div>
+      container
+      <div class="splitter">splitter</div>
+      container
+    </span>
+  </div>
+</div>
+<script>
+test(_ => {
+  document.body.offsetTop;
+  document.querySelector("#inline-container-absolute").style.position = 'static';
+}, 'test passes if changing abspos inline container to static does not crash');
+test(_ => {
+  document.body.offsetTop;
+  document.querySelector("#inline-container-fixed").style.filter = 'none';
+}, 'test passes if changing fixed inline container to static does not crash');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001-expected.txt
new file mode 100644
index 0000000..7b676a1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL support for appearance assert_equals: expected "button" but got ""
+FAIL initial value for appearance assert_equals: expected "none" but got ""
+PASS support for -webkit-appearance
+PASS initial value for -webkit-appearance
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001.html b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001.html
new file mode 100644
index 0000000..6e7d3002
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test: appearance and -webkit-appearance</title>
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
+<meta name="assert" content="The appearance and -webkit-appearance properties are supported.">
+<meta name="assert" content="Initial value is none.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #appearance { appearance: button; }
+ #-webkit-appearance { -webkit-appearance: button; }
+</style>
+<div id="appearance"></div>
+<div id="-webkit-appearance"></div>
+<div id="initial"></div>
+<script>
+for (const prop of ['appearance', '-webkit-appearance']) {
+	test(() => {
+		const actual = getComputedStyle(document.getElementById(prop)).getPropertyValue(prop);
+		assert_equals(actual, 'button');
+	}, `support for ${prop}`);
+
+	test(() => {
+		const actual = getComputedStyle(document.getElementById('initial')).getPropertyValue(prop);
+		assert_equals(actual, 'none');
+	}, `initial value for ${prop}`);
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-parenthesis-stack.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-parenthesis-stack.html
index 1d9033d..d8dbafb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-values/calc-parenthesis-stack.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-parenthesis-stack.html
@@ -4,12 +4,18 @@
 	<meta charset="utf-8">
 	<title>
 		CSS Values and Units Test:
-		Calc() inside calc()
+		32 nested pairs of parentheses inside calc()
 	</title>
 	<meta name="assert" content="
-		The calc() function notation is allowed inside a calc() notation.
+		This test checks the support for the minimum required number of 32 nested pairs of parentheses inside a calc() function.
 	" />
 
+  <!--
+  More info:
+  [css-values] Limit nested pairs of parentheses in calc to 32
+  https://github.com/w3c/csswg-drafts/issues/3462
+  -->
+
 	<link
 		rel="author"
 		title="François REMY"
@@ -29,7 +35,7 @@
 
 			html { background: red; overflow: hidden; }
 			#outer { position: absolute; top: 0px; left: 0px; background: green; width: 100%; }
-			#outer { height: calc((((((((((((((((((((((((100%)))))))))))))))))))))))); }
+			#outer { height: calc((((((((((((((((((((((((((((((((100%)))))))))))))))))))))))))))))))); }
 
 	</style>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056-ref.html b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056-ref.html
index f4f2105..519d3386 100644
--- a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056-ref.html
@@ -3,7 +3,6 @@
     <head>
         <title>CSS Reference File</title>
         <link rel="author" title="Rebecca Hauck" href="mailto:rhauck@adobe.com">
-    </head>
     <style>
        body {
            margin: 0;
@@ -28,6 +27,7 @@
            background-color: green;
        }
     </style>
+    </head>
     <body>
         <p>The test passes if there is a green square to the right of the blue line. There should be no red.</p>
         <div id="container">
diff --git a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056.html b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056.html
index 57323a42..d51cb50 100644
--- a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056.html
+++ b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-056.html
@@ -15,7 +15,6 @@
                                      top left with a shape-margin. Additionally, the
                                      shape-outside: circle element is offset from
                                      its containing block.">
-    </head>
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
     <style>
     @font-face {
@@ -66,6 +65,7 @@
         z-index: -1;
     }
     </style>
+    </head>
     <body>
     <p>The test passes if there is a green square to the right of the blue line. There should be no red.</p>
     <div id="container">
diff --git a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052-ref.html b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052-ref.html
index f4f2105..519d3386 100644
--- a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052-ref.html
@@ -3,7 +3,6 @@
     <head>
         <title>CSS Reference File</title>
         <link rel="author" title="Rebecca Hauck" href="mailto:rhauck@adobe.com">
-    </head>
     <style>
        body {
            margin: 0;
@@ -28,6 +27,7 @@
            background-color: green;
        }
     </style>
+    </head>
     <body>
         <p>The test passes if there is a green square to the right of the blue line. There should be no red.</p>
         <div id="container">
diff --git a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052.html b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052.html
index ee95f59..33f7a8f 100644
--- a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052.html
+++ b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-052.html
@@ -13,7 +13,6 @@
                                      a ellipse with a shape-margin in pixel units.
                                      Additionally, the shape-outside: ellipse element
                                      is offset from its containing block.">
-    </head>
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
     <style>
     @font-face {
@@ -64,6 +63,7 @@
         z-index: -1;
     }
     </style>
+    </head>
     <body>
     <p>The test passes if there is a green square to the right of the blue line. There should be no red.</p>
     <div id="container">
diff --git a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032-ref.html b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032-ref.html
index f1fcdf4..b3f7028 100644
--- a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032-ref.html
@@ -3,7 +3,6 @@
     <head>
         <title>CSS Reference File</title>
         <link rel="author" title="Rebecca Hauck" href="mailto:rhauck@adobe.com">
-    </head>
     <style>
        body {
             margin: 0;
@@ -17,8 +16,9 @@
           background-color: green;
       }
     </style>
+    </head>
     <body>
         <p>The test passes if there is green square and no red.</p>
         <div id="green-square"></div>
     </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032.html b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032.html
index 7649a98c..6e17492 100644
--- a/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032.html
+++ b/third_party/blink/web_tests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-032.html
@@ -14,7 +14,6 @@
                                      an polygon from the margin box with a shape margin.
                                      Additionally, the shape-outside: polygon element is
                                      offset from its containing block.">
-    </head>
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
     <style>
         @font-face {
@@ -77,6 +76,7 @@
             height: 60px;
         }
     </style>
+    </head>
     <body>
     <p>The test passes if there is green square and no red.</p>
     <div id="red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only-expected.txt b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only-expected.txt
rename to third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.html
rename to third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.https.html
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.html.headers b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.https.html.headers
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.html.headers
rename to third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-report-only.https.html.headers
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-reporting.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-reporting.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-reporting.html
rename to third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-reporting.https.html
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-reporting.html.headers b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-reporting.https.html.headers
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-reporting.html.headers
rename to third_party/blink/web_tests/external/wpt/feature-policy/reporting/midi-reporting.https.html.headers
diff --git a/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window-expected.txt
new file mode 100644
index 0000000..f7ee4a7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window-expected.txt
@@ -0,0 +1,36 @@
+This is a testharness.js-based test.
+PASS Origin header and 308 redirect
+PASS Origin header and GET navigation
+FAIL Origin header and POST navigation assert_equals: expected "http://web-platform.test:8001" but got "null"
+PASS Origin header and POST same-origin navigation with Referrer-Policy no-referrer
+FAIL Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and POST cross-origin navigation with Referrer-Policy no-referrer
+FAIL Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and POST same-origin navigation with Referrer-Policy same-origin
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy same-origin
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and POST cross-origin navigation with Referrer-Policy same-origin
+FAIL Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy same-origin assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and POST same-origin navigation with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin navigation with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin navigation with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin navigation with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin navigation with Referrer-Policy unsafe-url
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin navigation with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy unsafe-url
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window.js b/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window.js
new file mode 100644
index 0000000..9e83272
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window.js
@@ -0,0 +1,202 @@
+// META: script=/common/utils.js
+// META: script=/common/get-host-info.sub.js
+
+const origins = get_host_info();
+
+promise_test(async function () {
+  const stash = token(),
+        redirectPath = "/fetch/origin/resources/redirect-and-stash.py";
+
+  // Cross-origin -> same-origin will result in setting the tainted origin flag for the second
+  // request.
+  let url = origins.HTTP_ORIGIN + redirectPath + "?stash=" + stash;
+  url = origins.HTTP_REMOTE_ORIGIN + redirectPath + "?stash=" + stash + "&location=" + encodeURIComponent(url);
+
+  await fetch(url, { mode: "no-cors", method: "POST" });
+
+  const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();
+
+  assert_equals(json[0], origins.HTTP_ORIGIN);
+  assert_equals(json[1], "null");
+}, "Origin header and 308 redirect");
+
+promise_test(async function () {
+  const stash = token(),
+        redirectPath = "/fetch/origin/resources/redirect-and-stash.py";
+
+  let url = origins.HTTP_ORIGIN + redirectPath + "?stash=" + stash;
+  url = origins.HTTP_REMOTE_ORIGIN + redirectPath + "?stash=" + stash + "&location=" + encodeURIComponent(url);
+
+  await new Promise(resolve => {
+    const frame = document.createElement("iframe");
+    frame.src = url;
+    frame.onload = () => {
+      resolve();
+      frame.remove();
+    }
+    document.body.appendChild(frame);
+  });
+
+  const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();
+
+  assert_equals(json[0], "no Origin header");
+  assert_equals(json[1], "no Origin header");
+}, "Origin header and GET navigation");
+
+promise_test(async function () {
+  const stash = token(),
+        redirectPath = "/fetch/origin/resources/redirect-and-stash.py";
+
+  let url = origins.HTTP_ORIGIN + redirectPath + "?stash=" + stash;
+  url = origins.HTTP_REMOTE_ORIGIN + redirectPath + "?stash=" + stash + "&location=" + encodeURIComponent(url);
+
+  await new Promise(resolve => {
+    const frame = document.createElement("iframe");
+    self.addEventListener("message", e => {
+      if (e.data === "loaded") {
+        resolve();
+        frame.remove();
+      }
+    }, { once: true });
+    frame.onload = () => {
+      const doc = frame.contentDocument,
+            form = doc.body.appendChild(doc.createElement("form")),
+            submit = form.appendChild(doc.createElement("input"));
+      form.action = url;
+      form.method = "POST";
+      submit.type = "submit";
+      submit.click();
+    }
+    document.body.appendChild(frame);
+  });
+
+  const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();
+
+  assert_equals(json[0], origins.HTTP_ORIGIN);
+  assert_equals(json[1], "null");
+}, "Origin header and POST navigation");
+
+function navigationReferrerPolicy(referrerPolicy, destination, expectedOrigin) {
+  return async function () {
+    const stash = token();
+    const referrerPolicyPath = "/fetch/origin/resources/referrer-policy.py";
+    const redirectPath = "/fetch/origin/resources/redirect-and-stash.py";
+
+    let postUrl =
+            (destination === "same-origin" ? origins.HTTP_ORIGIN
+                                           : origins.HTTP_REMOTE_ORIGIN) +
+            redirectPath + "?stash=" + stash;
+
+    await new Promise(resolve => {
+      const frame = document.createElement("iframe");
+      document.body.appendChild(frame);
+      frame.src = origins.HTTP_ORIGIN + referrerPolicyPath +
+                  "?referrerPolicy=" + referrerPolicy;
+      self.addEventListener("message", function listener(e) {
+        if (e.data === "loaded") {
+          resolve();
+          frame.remove();
+          self.removeEventListener("message", listener);
+        } else if (e.data === "action") {
+          const doc = frame.contentDocument,
+                form = doc.body.appendChild(doc.createElement("form")),
+                submit = form.appendChild(doc.createElement("input"));
+          form.action = postUrl;
+          form.method = "POST";
+          submit.type = "submit";
+          submit.click();
+        }
+      });
+    });
+
+    const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();
+
+    assert_equals(json[0], expectedOrigin);
+  };
+}
+
+function fetchReferrerPolicy(referrerPolicy, destination, fetchMode, expectedOrigin) {
+  return async function () {
+    const stash = token();
+    const referrerPolicyPath = "/fetch/origin/resources/referrer-policy.py";
+    const redirectPath = "/fetch/origin/resources/redirect-and-stash.py";
+
+    let fetchUrl =
+        (destination === "same-origin" ? origins.HTTP_ORIGIN
+                                       : origins.HTTP_REMOTE_ORIGIN) +
+        redirectPath + "?stash=" + stash;
+
+    await fetch(fetchUrl, { mode: fetchMode, method: "POST" , "referrerPolicy": referrerPolicy});
+
+    const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();
+
+    assert_equals(json[0], expectedOrigin);
+  };
+}
+
+function referrerPolicyTestString(referrerPolicy, destination) {
+  return "Origin header and POST " + destination + " with Referrer-Policy " +
+         referrerPolicy;
+}
+
+[
+  {
+    "policy": "no-referrer",
+    "expectedOriginForSameOrigin": "null",
+    "expectedOriginForCrossOrigin": "null"
+  },
+  {
+    "policy": "same-origin",
+    "expectedOriginForSameOrigin": origins.HTTP_ORIGIN,
+    "expectedOriginForCrossOrigin": "null"
+  },
+  {
+    "policy": "origin-when-cross-origin",
+    "expectedOriginForSameOrigin": origins.HTTP_ORIGIN,
+    "expectedOriginForCrossOrigin": origins.HTTP_ORIGIN
+  },
+  {
+    "policy": "no-referrer-when-downgrade",
+    "expectedOriginForSameOrigin": origins.HTTP_ORIGIN,
+    "expectedOriginForCrossOrigin": origins.HTTP_ORIGIN
+  },
+  {
+    "policy": "unsafe-url",
+    "expectedOriginForSameOrigin": origins.HTTP_ORIGIN,
+    "expectedOriginForCrossOrigin": origins.HTTP_ORIGIN
+  },
+].forEach(testObj => {
+  [
+    {
+      "name": "same-origin",
+      "expectedOrigin": testObj.expectedOriginForSameOrigin
+    },
+    {
+      "name": "cross-origin",
+      "expectedOrigin": testObj.expectedOriginForCrossOrigin
+    }
+  ].forEach(destination => {
+    // Test form POST navigation
+    promise_test(navigationReferrerPolicy(testObj.policy,
+                                          destination.name,
+                                          destination.expectedOrigin),
+                 referrerPolicyTestString(testObj.policy,
+                                          destination.name + " navigation"));
+    // Test fetch
+    promise_test(fetchReferrerPolicy(testObj.policy,
+                                     destination.name,
+                                     "no-cors",
+                                     destination.expectedOrigin),
+                 referrerPolicyTestString(testObj.policy,
+                                          destination.name + " fetch no-cors mode"));
+
+    // When we're dealing with CORS (mode is "cors"), we shouldn't take the
+    // Referrer-Policy into account
+    promise_test(fetchReferrerPolicy(testObj.policy,
+                                     destination.name,
+                                     "cors",
+                                     origins.HTTP_ORIGIN),
+                 referrerPolicyTestString(testObj.policy,
+                                          destination.name + " fetch cors mode"));
+  });
+});
diff --git a/third_party/blink/web_tests/external/wpt/fetch/origin/no-cors.any.js b/third_party/blink/web_tests/external/wpt/fetch/origin/no-cors.any.js
deleted file mode 100644
index c9d1d3b..0000000
--- a/third_party/blink/web_tests/external/wpt/fetch/origin/no-cors.any.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// META: script=/common/utils.js
-// META: script=/common/get-host-info.sub.js
-
-promise_test(async function() {
-  const stash = token(),
-        origins = get_host_info(),
-        redirectPath = "/fetch/origin/resources/redirect-and-stash.py";
-
-  // Cross-origin -> same-origin will result in setting the tainted origin flag for the second
-  // request.
-  let url = origins.HTTP_ORIGIN + redirectPath + "?stash=" + stash;
-  url = origins.HTTP_REMOTE_ORIGIN + redirectPath + "?stash=" + stash + "&location=" + encodeURIComponent(url);
-
-  await fetch(url, { mode: "no-cors", method: "POST" });
-
-  const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();
-
-  assert_equals(json[0], origins.HTTP_ORIGIN, "first origin should equal this origin");
-  assert_equals(json[1], "null", "second origin should be opaque and therefore null");
-}, "Origin header and 308 redirect");
diff --git a/third_party/blink/web_tests/external/wpt/fetch/origin/resources/redirect-and-stash.py b/third_party/blink/web_tests/external/wpt/fetch/origin/resources/redirect-and-stash.py
index 1b1b46ba..aa9eb35 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/origin/resources/redirect-and-stash.py
+++ b/third_party/blink/web_tests/external/wpt/fetch/origin/resources/redirect-and-stash.py
@@ -9,7 +9,7 @@
     origin_list = request.server.stash.take(key)
 
     if "dump" in request.GET:
-        response.headers.set("content-Type", "application/json")
+        response.headers.set("Content-Type", "application/json")
         response.content = json.dumps(origin_list)
         return
 
@@ -25,5 +25,6 @@
         response.headers.set("Location", request.GET.first("location"))
         return
 
-    response.headers.set("content-Type", "text/plain")
-    response.content = "Fix https://github.com/whatwg/fetch/issues/737..."
+    response.headers.set("Content-Type", "text/html")
+    response.headers.set("Access-Control-Allow-Origin", "*")
+    response.content = "<meta charset=utf-8>\n<body><script>parent.postMessage('loaded','*')</script></body>"
diff --git a/third_party/blink/web_tests/external/wpt/fetch/origin/resources/referrer-policy.py b/third_party/blink/web_tests/external/wpt/fetch/origin/resources/referrer-policy.py
new file mode 100644
index 0000000..22b71e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/origin/resources/referrer-policy.py
@@ -0,0 +1,7 @@
+def main(request, response):
+    if "referrerPolicy" in request.GET:
+        response.headers.set("Referrer-Policy",
+                             request.GET.first("referrerPolicy"))
+    response.status = 200
+    response.headers.set("Content-Type", "text/html")
+    response.content = "<meta charset=utf-8>\n<body><script>parent.postMessage('action','*')</script></body>"
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener/new_window_null.tentative.html.ini b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener/new_window_null.tentative.html.ini
deleted file mode 100644
index f5b55a40..0000000
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener/new_window_null.tentative.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[new_window_null.tentative.html]
-  disabled:
-    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1559494
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/elements-obsolete.js b/third_party/blink/web_tests/external/wpt/html/dom/elements-obsolete.js
index 7f673cb7..3ef9e9f 100644
--- a/third_party/blink/web_tests/external/wpt/html/dom/elements-obsolete.js
+++ b/third_party/blink/web_tests/external/wpt/html/dom/elements-obsolete.js
@@ -1,17 +1,4 @@
 var obsoleteElements = {
-  applet: {
-    align: "string",
-    alt: "string",
-    archive: "string",
-    code: "string",
-    codeBase: "url",
-    height: "string",
-    hspace: "unsigned long",
-    name: "string",
-    object: "url",
-    vspace: "unsigned long",
-    width: "string",
-  },
   marquee: {
     behavior: {
       type: {
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/reflection-obsolete-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/reflection-obsolete-expected.txt
deleted file mode 100644
index 27bd13b..0000000
--- a/third_party/blink/web_tests/external/wpt/html/dom/reflection-obsolete-expected.txt
+++ /dev/null
@@ -1,492 +0,0 @@
-This is a testharness.js-based test.
-Found 2745 tests; 2321 PASS, 424 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS applet.title: 32 tests
-PASS applet.lang: 32 tests
-PASS applet.dir: 62 tests
-PASS applet.className (<applet class>): 32 tests
-PASS applet.hidden: 33 tests
-PASS applet.accessKey: 32 tests
-PASS applet.tabIndex: 24 tests
-FAIL applet.align: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.align: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.align: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL applet.align: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL applet.align: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL applet.align: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL applet.align: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL applet.align: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL applet.align: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL applet.align: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL applet.align: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL applet.align: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL applet.align: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL applet.align: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL applet.align: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL applet.align: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL applet.align: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL applet.align: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.align: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL applet.align: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.align: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.align: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.align: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.align: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.align: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.align: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.align: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.align: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.align: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.align: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.align: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.align: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
-FAIL applet.alt: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.alt: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL applet.alt: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL applet.alt: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL applet.alt: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.alt: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL applet.alt: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.alt: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.alt: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.alt: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.alt: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.alt: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.alt: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.alt: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.alt: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.alt: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.alt: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.alt: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.alt: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
-FAIL applet.archive: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.archive: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL applet.archive: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL applet.archive: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL applet.archive: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.archive: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL applet.archive: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.archive: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.archive: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.archive: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.archive: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.archive: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.archive: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.archive: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.archive: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.archive: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.archive: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.archive: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.archive: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
-FAIL applet.code: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.code: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.code: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL applet.code: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL applet.code: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL applet.code: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL applet.code: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL applet.code: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL applet.code: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL applet.code: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL applet.code: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL applet.code: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL applet.code: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL applet.code: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL applet.code: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL applet.code: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL applet.code: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL applet.code: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.code: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL applet.code: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.code: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.code: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.code: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.code: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.code: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.code: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.code: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.code: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.code: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.code: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.code: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.code: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
-FAIL applet.codeBase: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.codeBase: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to "" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/reflection-obsolete.html" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to " foo " assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/foo" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to "http://site.example/" assert_equals: IDL get expected (string) "http://site.example/" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to "//site.example/path???@#l" assert_equals: IDL get expected (string) "http://site.example/path???@#l" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to "\0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f " assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/reflection-obsolete.html" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to undefined assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/undefined" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to 7 assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/7" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to 1.5 assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/1.5" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to true assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/true" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to false assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/false" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/[object%20Object]" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to NaN assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/NaN" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to Infinity assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/Infinity" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to -Infinity assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/-Infinity" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to "\0" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/reflection-obsolete.html" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to null assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/null" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/test-toString" but got (undefined) undefined
-FAIL applet.codeBase: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/test-valueOf" but got (undefined) undefined
-FAIL applet.codeBase: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to " foo " assert_equals: getAttribute() expected " foo " but got "test-valueOf"
-FAIL applet.codeBase: IDL set to "http://site.example/" assert_equals: getAttribute() expected "http://site.example/" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to "//site.example/path???@#l" assert_equals: getAttribute() expected "//site.example/path???@#l" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to "\0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f " assert_equals: getAttribute() expected "\0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f " but got "test-valueOf"
-FAIL applet.codeBase: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.codeBase: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/test-valueOf" but got (object) object "test-valueOf"
-FAIL applet.height: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.height: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.height: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL applet.height: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL applet.height: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL applet.height: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL applet.height: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL applet.height: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL applet.height: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL applet.height: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL applet.height: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL applet.height: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL applet.height: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL applet.height: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL applet.height: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL applet.height: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL applet.height: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL applet.height: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.height: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL applet.height: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.height: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.height: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.height: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.height: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.height: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.height: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.height: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.height: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.height: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.height: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.height: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.height: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
-FAIL applet.hspace: typeof IDL attribute assert_equals: expected "number" but got "undefined"
-FAIL applet.hspace: IDL get with DOM attribute unset assert_equals: expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to -2147483649 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to -2147483648 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to -36 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to -1 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to 0 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to 1 assert_equals: IDL get expected (number) 1 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to 257 assert_equals: IDL get expected (number) 257 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to 2147483647 assert_equals: IDL get expected (number) 2147483647 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to 2147483648 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to 4294967295 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to 4294967296 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "-1" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "-0" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "0" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "1" assert_equals: IDL get expected (number) 1 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "\t7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "\v7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "\f7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "\n7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "\r7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "
7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "
7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "᠎7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to undefined assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to 1.5 assert_equals: IDL get expected (number) 1 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to true assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to false assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to object "[object Object]" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to NaN assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to Infinity assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to -Infinity assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to "\0" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to object "2" assert_equals: IDL get expected (number) 2 but got (undefined) undefined
-FAIL applet.hspace: setAttribute() to object "3" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.hspace: IDL set to 0 assert_equals: getAttribute() expected "0" but got "[object Object]"
-FAIL applet.hspace: IDL set to 1 assert_equals: getAttribute() expected "1" but got "[object Object]"
-FAIL applet.hspace: IDL set to 257 assert_equals: getAttribute() expected "257" but got "[object Object]"
-FAIL applet.hspace: IDL set to 2147483647 assert_equals: getAttribute() expected "2147483647" but got "[object Object]"
-FAIL applet.hspace: IDL set to "-0" assert_equals: getAttribute() expected "0" but got "[object Object]"
-FAIL applet.hspace: IDL set to 2147483648 assert_equals: getAttribute() expected "0" but got "[object Object]"
-FAIL applet.hspace: IDL set to 4294967295 assert_equals: getAttribute() expected "0" but got "[object Object]"
-FAIL applet.name: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.name: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.name: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL applet.name: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL applet.name: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL applet.name: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL applet.name: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL applet.name: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL applet.name: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL applet.name: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL applet.name: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL applet.name: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL applet.name: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL applet.name: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL applet.name: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL applet.name: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL applet.name: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL applet.name: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.name: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL applet.name: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.name: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.name: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.name: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.name: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.name: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.name: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.name: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.name: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.name: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.name: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.name: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.name: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
-FAIL applet.object: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.object: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.object: setAttribute() to "" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/reflection-obsolete.html" but got (undefined) undefined
-FAIL applet.object: setAttribute() to " foo " assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/foo" but got (undefined) undefined
-FAIL applet.object: setAttribute() to "http://site.example/" assert_equals: IDL get expected (string) "http://site.example/" but got (undefined) undefined
-FAIL applet.object: setAttribute() to "//site.example/path???@#l" assert_equals: IDL get expected (string) "http://site.example/path???@#l" but got (undefined) undefined
-FAIL applet.object: setAttribute() to "\0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f " assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/reflection-obsolete.html" but got (undefined) undefined
-FAIL applet.object: setAttribute() to undefined assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/undefined" but got (undefined) undefined
-FAIL applet.object: setAttribute() to 7 assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/7" but got (undefined) undefined
-FAIL applet.object: setAttribute() to 1.5 assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/1.5" but got (undefined) undefined
-FAIL applet.object: setAttribute() to true assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/true" but got (undefined) undefined
-FAIL applet.object: setAttribute() to false assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/false" but got (undefined) undefined
-FAIL applet.object: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/[object%20Object]" but got (undefined) undefined
-FAIL applet.object: setAttribute() to NaN assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/NaN" but got (undefined) undefined
-FAIL applet.object: setAttribute() to Infinity assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/Infinity" but got (undefined) undefined
-FAIL applet.object: setAttribute() to -Infinity assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/-Infinity" but got (undefined) undefined
-FAIL applet.object: setAttribute() to "\0" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/reflection-obsolete.html" but got (undefined) undefined
-FAIL applet.object: setAttribute() to null assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/null" but got (undefined) undefined
-FAIL applet.object: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/test-toString" but got (undefined) undefined
-FAIL applet.object: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/test-valueOf" but got (undefined) undefined
-FAIL applet.object: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.object: IDL set to " foo " assert_equals: getAttribute() expected " foo " but got "test-valueOf"
-FAIL applet.object: IDL set to "http://site.example/" assert_equals: getAttribute() expected "http://site.example/" but got "test-valueOf"
-FAIL applet.object: IDL set to "//site.example/path???@#l" assert_equals: getAttribute() expected "//site.example/path???@#l" but got "test-valueOf"
-FAIL applet.object: IDL set to "\0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f " assert_equals: getAttribute() expected "\0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f " but got "test-valueOf"
-FAIL applet.object: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.object: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.object: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.object: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.object: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.object: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.object: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.object: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.object: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.object: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.object: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.object: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.object: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "http://web-platform.test:8001/html/dom/test-valueOf" but got (object) object "test-valueOf"
-FAIL applet.vspace: typeof IDL attribute assert_equals: expected "number" but got "undefined"
-FAIL applet.vspace: IDL get with DOM attribute unset assert_equals: expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to -2147483649 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to -2147483648 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to -36 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to -1 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to 0 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to 1 assert_equals: IDL get expected (number) 1 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to 257 assert_equals: IDL get expected (number) 257 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to 2147483647 assert_equals: IDL get expected (number) 2147483647 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to 2147483648 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to 4294967295 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to 4294967296 assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "-1" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "-0" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "0" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "1" assert_equals: IDL get expected (number) 1 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "\t7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "\v7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "\f7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "\n7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "\r7" assert_equals: IDL get expected (number) 7 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "
7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "
7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "᠎7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " 7" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to undefined assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to 1.5 assert_equals: IDL get expected (number) 1 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to true assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to false assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to object "[object Object]" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to NaN assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to Infinity assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to -Infinity assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to "\0" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to object "2" assert_equals: IDL get expected (number) 2 but got (undefined) undefined
-FAIL applet.vspace: setAttribute() to object "3" assert_equals: IDL get expected (number) 0 but got (undefined) undefined
-FAIL applet.vspace: IDL set to 0 assert_equals: getAttribute() expected "0" but got "[object Object]"
-FAIL applet.vspace: IDL set to 1 assert_equals: getAttribute() expected "1" but got "[object Object]"
-FAIL applet.vspace: IDL set to 257 assert_equals: getAttribute() expected "257" but got "[object Object]"
-FAIL applet.vspace: IDL set to 2147483647 assert_equals: getAttribute() expected "2147483647" but got "[object Object]"
-FAIL applet.vspace: IDL set to "-0" assert_equals: getAttribute() expected "0" but got "[object Object]"
-FAIL applet.vspace: IDL set to 2147483648 assert_equals: getAttribute() expected "0" but got "[object Object]"
-FAIL applet.vspace: IDL set to 4294967295 assert_equals: getAttribute() expected "0" but got "[object Object]"
-FAIL applet.width: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL applet.width: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL applet.width: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL applet.width: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL applet.width: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL applet.width: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL applet.width: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL applet.width: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL applet.width: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL applet.width: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL applet.width: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL applet.width: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL applet.width: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL applet.width: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL applet.width: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL applet.width: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL applet.width: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL applet.width: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL applet.width: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL applet.width: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL applet.width: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL applet.width: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL applet.width: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL applet.width: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL applet.width: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL applet.width: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL applet.width: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL applet.width: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL applet.width: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL applet.width: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL applet.width: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL applet.width: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
-PASS marquee.title: 32 tests
-PASS marquee.lang: 32 tests
-PASS marquee.dir: 62 tests
-PASS marquee.className (<marquee class>): 32 tests
-PASS marquee.hidden: 33 tests
-PASS marquee.accessKey: 32 tests
-PASS marquee.tabIndex: 24 tests
-PASS marquee.bgColor: 32 tests
-PASS marquee.height: 32 tests
-PASS marquee.hspace: 62 tests
-PASS marquee.scrollAmount: 62 tests
-PASS marquee.scrollDelay: 62 tests
-PASS marquee.trueSpeed: 33 tests
-PASS marquee.vspace: 62 tests
-PASS marquee.width: 32 tests
-PASS frameset.title: 32 tests
-PASS frameset.lang: 32 tests
-PASS frameset.dir: 62 tests
-PASS frameset.className (<frameset class>): 32 tests
-PASS frameset.hidden: 33 tests
-PASS frameset.accessKey: 32 tests
-PASS frameset.tabIndex: 24 tests
-PASS frameset.cols: 32 tests
-PASS frameset.rows: 32 tests
-PASS frame.title: 32 tests
-PASS frame.lang: 32 tests
-PASS frame.dir: 62 tests
-PASS frame.className (<frame class>): 32 tests
-PASS frame.hidden: 33 tests
-PASS frame.accessKey: 32 tests
-PASS frame.tabIndex: 24 tests
-PASS frame.name: 32 tests
-PASS frame.scrolling: 32 tests
-PASS frame.src: 38 tests
-PASS frame.frameBorder: 32 tests
-PASS frame.longDesc: 38 tests
-PASS frame.noResize: 33 tests
-PASS frame.marginHeight: 32 tests
-PASS frame.marginWidth: 32 tests
-PASS dir.title: 32 tests
-PASS dir.lang: 32 tests
-PASS dir.dir: 62 tests
-PASS dir.className (<dir class>): 32 tests
-PASS dir.hidden: 33 tests
-PASS dir.accessKey: 32 tests
-PASS dir.tabIndex: 24 tests
-PASS dir.compact: 33 tests
-PASS font.title: 32 tests
-PASS font.lang: 32 tests
-PASS font.dir: 62 tests
-PASS font.className (<font class>): 32 tests
-PASS font.hidden: 33 tests
-PASS font.accessKey: 32 tests
-PASS font.tabIndex: 24 tests
-PASS font.color: 32 tests
-PASS font.face: 32 tests
-PASS font.size: 32 tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html
new file mode 100644
index 0000000..7dd8018
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/activation-hierarchy-parent-manual.sub.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+   https://github.com/whatwg/html/issues/1903
+-->
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+</head>
+<body>
+  <h1>Activation state is visible in parent and not in child</h1>
+  <ol id="instructions">
+    <li>Click anywhere on the green area (child frame).
+  </ol>
+  <iframe id="child" width="400" height="400"></iframe>
+  <script>
+    async_test(function(t) {
+        assert_false(navigator.userActivation.isActive);
+        assert_false(navigator.userActivation.hasBeenActive);
+
+        let child = document.getElementById("child");
+
+        window.addEventListener("message", t.step_func(event => {
+            if (event.source === frames[0].frames[0] && event.data === 'checked') {
+                // Parent should be active after child is clicked.
+                assert_true(navigator.userActivation.isActive);
+                assert_true(navigator.userActivation.hasBeenActive);
+                t.done();
+            }
+        }));
+        child.src = "http://{{domains[www1]}}:{{ports[http][0]}}/html/user-activation/resources/activation-hierarchy-child.sub.html";
+    }, "Parent test");
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/resources/activation-hierarchy-child.sub.html b/third_party/blink/web_tests/external/wpt/html/user-activation/resources/activation-hierarchy-child.sub.html
new file mode 100644
index 0000000..ebccc8c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/resources/activation-hierarchy-child.sub.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body style="background: lightgreen;">
+  <h1>Child frame</h1>
+  <iframe id="grandchild" width="350" height="200"></iframe>
+  <script>
+    async_test(function(t) {
+        assert_false(navigator.userActivation.isActive);
+        assert_false(navigator.userActivation.hasBeenActive);
+
+        var grandchild = document.getElementById("grandchild");
+
+        window.addEventListener("click", t.step_func(event => {
+            // Child should be active when clicked.
+            assert_true(navigator.userActivation.isActive);
+            assert_true(navigator.userActivation.hasBeenActive);
+
+            // Ask grandchild to check its state (and notify top frame).
+            grandchild.contentWindow.postMessage('check-request', '*');
+
+            t.done();
+        }));
+
+        grandchild.src = "http://{{domains[www2]}}:{{ports[http][0]}}/html/user-activation/resources/activation-hierarchy-grandchild.html";
+    }, "Child test");
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/resources/activation-hierarchy-grandchild.html b/third_party/blink/web_tests/external/wpt/html/user-activation/resources/activation-hierarchy-grandchild.html
new file mode 100644
index 0000000..b9fe19a74
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/resources/activation-hierarchy-grandchild.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body style="background: lightgrey;">
+  <h1>Grandchild frame</h1>
+  <script>
+    async_test(function(t) {
+        assert_false(navigator.userActivation.isActive);
+        assert_false(navigator.userActivation.hasBeenActive);
+
+        window.addEventListener("message", event => {
+            if (event.source === parent && event.data === 'check-request') {
+                // Grandchild shouldn't be active after child is clicked.
+                assert_false(navigator.userActivation.isActive);
+                assert_false(navigator.userActivation.hasBeenActive);
+
+                // Notify top frame that checks are done.
+                parent.parent.postMessage('checked', '*');
+
+                t.done();
+            }
+        });
+
+    }, "Grandchild test");
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/inert/frame/button.html b/third_party/blink/web_tests/external/wpt/inert/frame/button.html
new file mode 100644
index 0000000..5867c75
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/inert/frame/button.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body {
+  overflow: hidden;
+}
+
+button#background {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 150px;
+  height: 40px;
+  line-height: 53px;
+}
+
+button.clicked {
+  background-color: red;
+}
+
+button.clicked::after {
+  content: " (clicked)";
+}
+
+</style>
+</head>
+<body>
+  <button id="background">background</button>
+<script>
+document.body.addEventListener('click', (e) => {
+  e.target.classList.add('clicked');
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/inert/inert-retargeting-iframe.tentative.html b/third_party/blink/web_tests/external/wpt/inert/inert-retargeting-iframe.tentative.html
new file mode 100644
index 0000000..78d7f6b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/inert/inert-retargeting-iframe.tentative.html
@@ -0,0 +1,285 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+button#foreground,
+button#background {
+  position: absolute;
+  top: 75px;
+  left: 50px;
+  width: 100px;
+  height: 20px;
+}
+
+button#background {
+  left: 75px;
+  width: 150px;
+  height: 40px;
+  top: 125px;
+  line-height: 53px;
+}
+
+.clicked {
+  background-color: red;
+}
+
+#container.clicked {
+  background-color: green;
+}
+
+#ancestorContainer {
+  position: relative;
+  width: 300px;
+  height: 300px;
+  background-color: blue;
+}
+
+#displacedAncestor {
+  position: absolute;
+  top: 13px;
+  left: 240px;
+  width: 300px;
+  height: 250px;
+  background-color: #ff89;
+}
+
+#displacedAncestor.clicked {
+  background-color: #f009;
+}
+
+#inertContainer {
+  background-color: #fff9;
+  position: absolute;
+  top: 35px;
+  left: -192px;
+  width: 200px;
+  height: 200px;
+}
+
+fieldset {
+  margin: 0;
+  padding: 0;
+  border: 1px solid black;
+}
+
+legend {
+  background-color: white;
+  border: 1px solid black;
+  margin-left: 5px;
+}
+
+button.clicked::after {
+  content: " (clicked)";
+}
+
+.clicked > legend::after {
+  content: " (clicked)";
+}
+</style>
+</head>
+<body>
+  <p>Click on "Inert button".</p>
+  <ul>
+    <li>The blue square ("Non-inert ancestor container") should turn green.</li>
+    <li>The yellow, semi-transparent square ("Non-inert, displaced container") should not turn red.</li>
+    <li>"Non-inert button" should not turn red.</li>
+  </ul>
+  <p>(The full test suite checks a range of events.)</p>
+  <fieldset id="ancestorContainer">
+    <legend>Non-inert ancestor container</legend>
+    <iframe id="background" src="frame/button.html"></iframe>
+    <fieldset id="displacedAncestor">
+      <legend>Non-inert, displaced ancestor</legend>
+      <fieldset id="inertContainer" inert>
+        <legend>Inert container</legend>
+        <button id="foreground">foreground</button>
+      </fieldset>
+    </fieldset>
+  </fieldset>
+
+  <script>
+document.body.addEventListener('click', (e) => {
+  e.target.classList.add('clicked');
+});
+
+function clickOn(element) {
+  return new test_driver.Actions()
+      .pointerMove(0, 0, {origin: element})
+      .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .send();
+}
+
+function auxClickOn(element) {
+  return new test_driver.Actions()
+      .pointerMove(0, 0, {origin: element})
+      .pointerDown({button: test_driver.Actions.prototype.ButtonType.RIGHT})
+      .pointerUp({button: test_driver.Actions.prototype.ButtonType.RIGHT})
+      .send();
+}
+
+function dblClickOn(element) {
+  return new test_driver.Actions()
+      .pointerMove(0, 0, {origin: element})
+      .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .send();
+}
+
+function movePointerOver(element) {
+  let rect = element.getBoundingClientRect();
+  return new test_driver.Actions()
+      .pointerMove(0, 0, { origin: element })
+      .send();
+}
+
+function movePointerTo(x, y) {
+  return new test_driver.Actions()
+      .pointerMove(x, y, { origin: "viewport" })
+      .send();
+}
+
+function expectEventsOn(events, element) {
+  let promises = [];
+  for (event of events) {
+    ((event, element) => {
+      var promise = new Promise((resolve, reject) => {
+        let f = (e) => {
+          assert_equals(e.type, event);
+          assert_equals(e.target, element);
+          resolve();
+        }
+        element.addEventListener(event, f, { capture: true, once: true });
+
+        setTimeout(() => {
+          element.removeEventListener(event, f, { capture: true });
+          reject("did not get " + event + " on " + element.id);
+        }, 1000);
+      });
+      promises.push(promise);
+    })(event, element);
+  }
+  return promises;
+}
+
+function unexpectEventsOn(events, element) {
+  let promises = [];
+  for (event of events) {
+    ((event, element) => {
+      var promise = new Promise((resolve, reject) => {
+        let f = (e) => {
+          assert_equals(e.type, event);
+          assert_equals(e.target, element);
+          reject("got " + e.type + " on " + e.target.id);
+        }
+        element.addEventListener(event, f, { capture: true, once: true });
+
+        setTimeout(() => {
+          element.removeEventListener(event, f, { capture: true });
+          resolve();
+        }, 1000);
+      });
+      promises.push(promise);
+    })(event, element);
+  }
+  return promises;
+}
+
+test(() => {
+  let rect = foreground.getBoundingClientRect();
+  let center_x = rect.left + (rect.width / 2);
+  let center_y = rect.top + (rect.height / 2);
+  assert_equals(document.elementsFromPoint(center_x, center_y)[0], foreground);
+}, "elementsFromPoint returns inert element");
+
+promise_test(async () => {
+  // Test mouse events on non-inert element - events should go to "foreground"
+  inertContainer.inert = false;
+  await movePointerTo(0, 0);
+  let promises = expectEventsOn(["mouseover", "mouseenter", "mousemove", "mousedown",
+                                 "mouseup", "click", "auxclick", "mouseout",
+                                 "mouseleave"],
+                                foreground);
+  await clickOn(foreground);
+  await auxClickOn(foreground);
+  await dblClickOn(foreground);
+  let ancestorBox = ancestorContainer.getBoundingClientRect();
+  let inertBox = inertContainer.getBoundingClientRect();
+  let x = ancestorBox.left + (ancestorBox.width / 2);
+  let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2);
+  await movePointerTo(x, y);
+  await Promise.all(promises);
+}, "Tests that any mouse event on a non-inert element is correctly targeted to that element");
+
+promise_test(async () => {
+  // Make the containing element inert - now events should go to "container"
+  // which is the non-inert ancestor at the same position
+  inertContainer.inert = true;
+  await movePointerTo(0, 0);
+
+  let promises = expectEventsOn(["mouseover", "mouseenter", "mousemove", "mousedown",
+                                 "mouseup", "click", "auxclick"],
+                                ancestorContainer);
+
+  promises = promises.concat(unexpectEventsOn(["mouseout", "mouseleave"],
+                                              ancestorContainer));
+
+  await clickOn(foreground);
+  await auxClickOn(foreground);
+  await dblClickOn(foreground);
+  let ancestorBox = ancestorContainer.getBoundingClientRect();
+  let inertBox = inertContainer.getBoundingClientRect();
+  let x = ancestorBox.left + (ancestorBox.width / 2);
+  let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2);
+  await movePointerTo(x, y);
+  await Promise.all(promises);
+}, 'Tests that any mouse event on an inert element is targeted to the nearest non-inert ancestor at the same coordinates');
+
+promise_test(async () => {
+  // Test pointer events on non-inert element - events should go to "foreground"
+  inertContainer.inert = false;
+  await movePointerTo(0, 0);
+
+  let promises = expectEventsOn(["pointerover", "pointerenter", "pointermove",
+                                 "pointerdown", "pointerup", "pointerout",
+                                 "pointerleave"],
+                                foreground);
+  await clickOn(foreground);
+  let ancestorBox = ancestorContainer.getBoundingClientRect();
+  let inertBox = inertContainer.getBoundingClientRect();
+  let x = ancestorBox.left + (ancestorBox.width / 2);
+  let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2);
+  await movePointerTo(x, y);
+  await Promise.all(promises);
+}, "Tests that any pointer event on a non-inert element is correctly targeted to that element");
+
+promise_test(async () => {
+  // Make the containing element inert - now events should go to "container"
+  // which is the non-inert ancestor at the same position
+  inertContainer.inert = true;
+  await movePointerTo(0, 0);
+
+  let promises = expectEventsOn(["pointerover", "pointerenter", "pointermove",
+                                 "pointerdown", "pointerup" ],
+                                ancestorContainer);
+
+  promises = promises.concat(unexpectEventsOn(["pointerout", "pointerleave"],
+                                              ancestorContainer));
+  await clickOn(foreground);
+  let ancestorBox = ancestorContainer.getBoundingClientRect();
+  let inertBox = inertContainer.getBoundingClientRect();
+  let x = ancestorBox.left + (ancestorBox.width / 2);
+  let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2);
+  await movePointerTo(x, y);
+  await Promise.all(promises);
+}, 'Tests that any pointer event on an inert element is targeted to the nearest non-inert ancestor at the same coordinates');
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/inert/inert-retargeting.tentative.html b/third_party/blink/web_tests/external/wpt/inert/inert-retargeting.tentative.html
new file mode 100644
index 0000000..d239a70
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/inert/inert-retargeting.tentative.html
@@ -0,0 +1,290 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+button#foreground,
+button#background {
+  position: absolute;
+  top: 75px;
+  left: 50px;
+  width: 100px;
+  height: 20px;
+}
+
+button#background {
+  left: 75px;
+  width: 150px;
+  height: 40px;
+  top: 125px;
+  line-height: 53px;
+}
+
+.clicked {
+  background-color: red;
+}
+
+#container.clicked {
+  background-color: green;
+}
+
+#ancestorContainer {
+  position: relative;
+  width: 300px;
+  height: 300px;
+  background-color: blue;
+}
+
+#displacedAncestor {
+  position: absolute;
+  top: 13px;
+  left: 240px;
+  width: 300px;
+  height: 250px;
+  background-color: #ff89;
+}
+
+#displacedAncestor.clicked {
+  background-color: #f009;
+}
+
+#inertContainer {
+  background-color: #fff9;
+  position: absolute;
+  top: 35px;
+  left: -192px;
+  width: 200px;
+  height: 200px;
+}
+
+fieldset {
+  margin: 0;
+  padding: 0;
+  border: 1px solid black;
+}
+
+legend {
+  background-color: white;
+  border: 1px solid black;
+  margin-left: 5px;
+}
+
+button.clicked::after {
+  content: " (clicked)";
+}
+
+.clicked > legend::after {
+  content: " (clicked)";
+}
+
+</style>
+</head>
+<body>
+  <p>Click on "foreground".</p>
+  <ul>
+    <li>The blue square ("Non-inert ancestor container") should turn green.</li>
+    <li>The yellow, semi-transparent square ("Non-inert, displaced container") should not turn red.</li>
+    <li>"Non-inert button" should not turn red.</li>
+  </ul>
+  <p>(The full test suite checks a range of events.)</p>
+  <fieldset id="ancestorContainer">
+    <legend>Non-inert ancestor container</legend>
+    <button id="background">background</button>
+    <fieldset id="displacedAncestor">
+      <legend>Non-inert, displaced ancestor</legend>
+      <fieldset id="inertContainer" inert>
+        <legend>Inert container</legend>
+        <button id="foreground">foreground</button>
+      </fieldset>
+    </fieldset>
+  </fieldset>
+
+  <script>
+document.body.addEventListener('click', (e) => {
+  e.target.classList.add('clicked');
+});
+
+function clickOn(element) {
+  return new test_driver.Actions()
+      .pointerMove(0, 0, {origin: element})
+      .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .send();
+}
+
+function auxClickOn(element) {
+  return new test_driver.Actions()
+      .pointerMove(0, 0, {origin: element})
+      .pointerDown({button: test_driver.Actions.prototype.ButtonType.RIGHT})
+      .pointerUp({button: test_driver.Actions.prototype.ButtonType.RIGHT})
+      .send();
+}
+
+function dblClickOn(element) {
+  return new test_driver.Actions()
+      .pointerMove(0, 0, {origin: element})
+      .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT})
+      .send();
+}
+
+function movePointerOver(element) {
+  let rect = element.getBoundingClientRect();
+  return new test_driver.Actions()
+      .pointerMove(0, 0, { origin: element })
+      .send();
+}
+
+function movePointerTo(x, y) {
+  return new test_driver.Actions()
+      .pointerMove(x, y, { origin: "viewport" })
+      .send();
+}
+
+function expectEventsOn(events, element) {
+  let promises = [];
+  for (event of events) {
+    ((event, element) => {
+      var promise = new Promise((resolve, reject) => {
+        let f = (e) => {
+          assert_equals(e.type, event);
+          assert_equals(e.target, element);
+          resolve();
+        }
+        element.addEventListener(event, f, { capture: true, once: true });
+
+        setTimeout(() => {
+          element.removeEventListener(event, f, { capture: true });
+          reject("did not get " + event + " on " + element.id);
+        }, 1000);
+      });
+      promises.push(promise);
+    })(event, element);
+  }
+  return promises;
+}
+
+function unexpectEventsOn(events, element) {
+  let promises = [];
+  for (event of events) {
+    ((event, element) => {
+      var promise = new Promise((resolve, reject) => {
+        let f = (e) => {
+          assert_equals(e.type, event);
+          assert_equals(e.target, element);
+          reject("got " + e.type + " on " + e.target.id);
+        }
+        element.addEventListener(event, f, { capture: true, once: true });
+
+        setTimeout(() => {
+          element.removeEventListener(event, f, { capture: true });
+          resolve();
+        }, 1000);
+      });
+      promises.push(promise);
+    })(event, element);
+  }
+  return promises;
+}
+
+test(() => {
+  let rect = foreground.getBoundingClientRect();
+  let center_x = rect.left + (rect.width / 2);
+  let center_y = rect.top + (rect.height / 2);
+  assert_equals(document.elementsFromPoint(center_x, center_y)[0], foreground);
+}, "elementsFromPoint returns inert element");
+
+promise_test(async () => {
+  // Test mouse events on non-inert element - events should go to "foreground"
+  inertContainer.inert = false;
+  await movePointerTo(0, 0);
+  let promises = expectEventsOn(["mouseover", "mouseenter", "mousemove", "mousedown",
+                                 "mouseup", "click", "auxclick", "mouseout",
+                                 "mouseleave"],
+                                foreground);
+  await clickOn(foreground);
+  await auxClickOn(foreground);
+  await dblClickOn(foreground);
+  let ancestorBox = ancestorContainer.getBoundingClientRect();
+  let inertBox = inertContainer.getBoundingClientRect();
+  let x = ancestorBox.left + (ancestorBox.width / 2);
+  let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2);
+  await movePointerTo(x, y);
+  await Promise.all(promises);
+}, "Tests that any mouse event on a non-inert element is correctly targeted to that element");
+
+promise_test(async () => {
+  // Make the containing element inert - now events should go to "container"
+  // which is the non-inert ancestor at the same position
+  inertContainer.inert = true;
+  await movePointerTo(0, 0);
+
+  let promises = expectEventsOn(["mouseover", "mouseenter", "mousemove", "mousedown",
+                                 "mouseup", "click", "auxclick"],
+                                ancestorContainer);
+
+  // TODO(aboxhall): We are getting these unexpected events. Why?
+  promises = promises.concat(unexpectEventsOn(["mouseout", "mouseleave"],
+                                              ancestorContainer));
+
+  await clickOn(foreground);
+  await auxClickOn(foreground);
+  await dblClickOn(foreground);
+  let ancestorBox = ancestorContainer.getBoundingClientRect();
+  let inertBox = inertContainer.getBoundingClientRect();
+  let x = ancestorBox.left + (ancestorBox.width / 2);
+  let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2);
+  await movePointerTo(x, y);
+  await Promise.all(promises);
+}, 'Tests that any mouse event on an inert element is targeted to the nearest non-inert ancestor at the same coordinates');
+
+promise_test(async () => {
+  // Test pointer events on non-inert element - events should go to "foreground"
+  inertContainer.inert = false;
+  await movePointerTo(0, 0);
+
+  let promises = expectEventsOn(["pointerover", "pointerenter", "pointermove",
+                                 "pointerdown", "pointerup", "pointerout",
+                                 "pointerleave"],
+                                foreground);
+  await clickOn(foreground);
+  let ancestorBox = ancestorContainer.getBoundingClientRect();
+  let inertBox = inertContainer.getBoundingClientRect();
+  let x = ancestorBox.left + (ancestorBox.width / 2);
+  let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2);
+  await movePointerTo(x, y);
+  await Promise.all(promises);
+}, "Tests that any pointer event on a non-inert element is correctly targeted to that element");
+
+promise_test(async () => {
+  // Make the containing element inert - now events should go to "container"
+  // which is the non-inert ancestor at the same position
+  inertContainer.inert = true;
+  await movePointerTo(0, 0);
+
+  let promises = expectEventsOn(["pointerover", "pointerenter", "pointermove",
+                                 "pointerdown", "pointerup" ],
+                                ancestorContainer);
+
+  // TODO(aboxhall): We are getting these unexpected events. Why?
+  promises = promises.concat(unexpectEventsOn(["pointerout", "pointerleave"],
+                                              ancestorContainer));
+
+  await clickOn(foreground);
+  let ancestorBox = ancestorContainer.getBoundingClientRect();
+  let inertBox = inertContainer.getBoundingClientRect();
+  let x = ancestorBox.left + (ancestorBox.width / 2);
+  let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2);
+  await movePointerTo(x, y);
+  await Promise.all(promises);
+}, 'Tests that any pointer event on an inert element is targeted to the nearest non-inert ancestor at the same coordinates');
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/animation-worklet.idl b/third_party/blink/web_tests/external/wpt/interfaces/animation-worklet.idl
index 159cabd..d223a74 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/animation-worklet.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/animation-worklet.idl
@@ -18,7 +18,7 @@
   any state();
 };
 
-[ Exposed=AnimationWorklet, Global=AnimationWorklet ]
+[ Exposed=(AnimationWorklet, Worklet), Global=AnimationWorklet ]
 interface AnimationWorkletGlobalScope : WorkletGlobalScope {
     void registerAnimator(DOMString name, AnimatorInstanceConstructor animatorCtor);
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/performance-timeline.idl b/third_party/blink/web_tests/external/wpt/interfaces/performance-timeline.idl
index 8ded59d..56c0ec4b 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/performance-timeline.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/performance-timeline.idl
@@ -32,7 +32,6 @@
 dictionary PerformanceObserverInit {
   sequence<DOMString> entryTypes;
   DOMString type;
-  boolean buffered;
 };
 
 [Exposed=(Window,Worker)]
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/service-workers.idl b/third_party/blink/web_tests/external/wpt/interfaces/service-workers.idl
index 89229e3..8e37060 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/service-workers.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/service-workers.idl
@@ -16,6 +16,7 @@
 ServiceWorker includes AbstractWorker;
 
 enum ServiceWorkerState {
+  "parsed",
   "installing",
   "installed",
   "activating",
@@ -95,6 +96,7 @@
 interface ServiceWorkerGlobalScope : WorkerGlobalScope {
   [SameObject] readonly attribute Clients clients;
   [SameObject] readonly attribute ServiceWorkerRegistration registration;
+  [SameObject] readonly attribute ServiceWorker serviceWorker;
 
   [NewObject] Promise<void> skipWaiting();
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl b/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
index cd039ad..c4fbf11 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
@@ -16,6 +16,7 @@
   attribute DOMString? ariaChecked;
   attribute DOMString? ariaColCount;
   attribute DOMString? ariaColIndex;
+  attribute DOMString? ariaColIndexText;
   attribute DOMString? ariaColSpan;
   attribute FrozenArray<Element>? ariaControlsElements;
   attribute DOMString? ariaCurrent;
@@ -47,6 +48,7 @@
   attribute DOMString? ariaRoleDescription;
   attribute DOMString? ariaRowCount;
   attribute DOMString? ariaRowIndex;
+  attribute DOMString? ariaRowIndexText;
   attribute DOMString? ariaRowSpan;
   attribute DOMString? ariaSelected;
   attribute DOMString? ariaSetSize;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webmidi.idl b/third_party/blink/web_tests/external/wpt/interfaces/webmidi.idl
index aa2ac7f2..58d6357f 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webmidi.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webmidi.idl
@@ -12,11 +12,11 @@
   boolean software;
 };
 
-interface MIDIInputMap {
+[SecureContext] interface MIDIInputMap {
   readonly maplike<DOMString, MIDIInput>;
 };
 
-interface MIDIOutputMap {
+[SecureContext] interface MIDIOutputMap {
   readonly maplike<DOMString, MIDIOutput>;
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webrtc-stats.idl b/third_party/blink/web_tests/external/wpt/interfaces/webrtc-stats.idl
index 981317f..f29e00d 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webrtc-stats.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webrtc-stats.idl
@@ -69,7 +69,9 @@
              DOMString receiverId;
              DOMString remoteId;
              unsigned long framesDecoded;
+             unsigned long keyFramesDecoded;
              unsigned long long qpSum;
+             double totalDecodeTime;
              DOMHighResTimeStamp lastPacketReceivedTimestamp;
              double averageRtcpInterval;
              unsigned long fecPacketsReceived;
@@ -109,6 +111,7 @@
              double targetBitrate;
              unsigned long long totalEncodedBytesTarget;
              unsigned long framesEncoded;
+             unsigned long keyFramesEncoded;
              unsigned long long qpSum;
              double totalEncodeTime;
              double totalPacketSendDelay;
@@ -187,7 +190,6 @@
              unsigned long framesCaptured;
              unsigned long framesSent;
              unsigned long hugeFramesSent;
-             unsigned long keyFramesSent;
 };
 
 dictionary RTCSenderVideoTrackAttachmentStats : RTCVideoSenderStats {
@@ -198,7 +200,6 @@
              double jitterBufferDelay;
              unsigned long long jitterBufferEmittedCount;
              unsigned long framesReceived;
-             unsigned long keyFramesReceived;
              unsigned long framesDecoded;
              unsigned long framesDropped;
              unsigned long partialFramesLost;
@@ -359,3 +360,11 @@
 partial dictionary RTCInboundRtpStreamStats {
           double fractionLost;
 };
+
+partial dictionary RTCVideoSenderStats {
+          unsigned long keyFramesSent;
+};
+
+partial dictionary RTCVideoReceiverStats {
+          unsigned long keyFramesReceived;
+};
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 4af1c96a..c78683ef 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
@@ -171,6 +171,7 @@
   [SameObject] readonly attribute XRSpace targetRaySpace;
   [SameObject] readonly attribute XRSpace? gripSpace;
   [SameObject] readonly attribute Gamepad? gamepad;
+  [SameObject] readonly attribute FrozenArray<DOMString> profiles;
 };
 
 [SecureContext, Exposed=Window]
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-image.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-image.html
index f989120..43cdfab0 100644
--- a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-image.html
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-image.html
@@ -5,21 +5,20 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script>
-  let beforeRender;
-  function computeTimestamp() {
-    beforeRender = performance.now();
-  }
   async_test(function (t) {
     if (!window.LargestContentfulPaint) {
       assert_unreached("LargestContentfulPaint is not implemented");
     }
+    let beforeRender = performance.now();
     const observer = new PerformanceObserver(
       t.step_func_done(function(entryList) {
         assert_equals(entryList.getEntries().length, 1);
         const entry = entryList.getEntries()[0];
         assert_equals(entry.entryType, 'largestContentfulPaint');
-        assert_greater_than_equal(entry.startTime, beforeRender);
-        assert_greater_than_equal(performance.now(), entry.startTime);
+        assert_greater_than_equal(entry.startTime, beforeRender,
+          'The rendering timestamp should occur after script starts running.');
+        assert_greater_than_equal(performance.now(), entry.startTime,
+          'The rendering timestamp should occur before the entry is dispatched to the observer.');
         assert_equals(entry.duration, 0);
         // blue.png is 133 x 106.
         assert_equals(entry.size, 14098);
@@ -29,5 +28,5 @@
   }, 'Element with elementtiming attribute is observable.');
 </script>
 
-<img src='/images/blue.png' onload='computeTimestamp()'/>
+<img src='/images/blue.png'/>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/lint.whitelist b/third_party/blink/web_tests/external/wpt/lint.whitelist
index 9966d7c..c344c3a 100644
--- a/third_party/blink/web_tests/external/wpt/lint.whitelist
+++ b/third_party/blink/web_tests/external/wpt/lint.whitelist
@@ -817,3 +817,6 @@
 WEB-PLATFORM.TEST:signed-exchange/resources/*.sxg
 WEB-PLATFORM.TEST:signed-exchange/appcache/resources/*.sxg
 WEB-PLATFORM.TEST:signed-exchange/resources/generate-test-sxgs.sh
+
+SET TIMEOUT: inert/inert-retargeting.tentative.html
+SET TIMEOUT: inert/inert-retargeting-iframe.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/idlharness.https.window.js b/third_party/blink/web_tests/external/wpt/mediacapture-streams/idlharness.https.window.js
index 976d31b6..5b255fca 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-streams/idlharness.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-streams/idlharness.https.window.js
@@ -16,7 +16,7 @@
   idl_array.add_dependency_idls(html);
   idl_array.add_dependency_idls(dom);
 
-  let input, media;
+  const devices = [];
   // Errors will be surfaced in idlharness.js's test_object below.
   try {
     const list = await navigator.mediaDevices.enumerateDevices();
@@ -24,9 +24,9 @@
       switch (item.kind) {
       case 'audioinput':
       case 'videoinput':
-        input = item;
       case 'audiooutput':
-        media = item;
+        self[item.kind] = item;
+        devices.push(item.kind);
       default:
         assert_unreached(
           'media.kind should be one of "audioinput", "videoinput", or "audiooutput".');
@@ -34,26 +34,21 @@
     }
   } catch (e) {}
 
-  let stream, track, trackEvent;
   try {
-    stream = await navigator.mediaDevices.getUserMedia({audio: true});
-    track = stream.getTracks()[0];
-    trackEvent = new MediaStreamTrackEvent("type", {
+    self.stream = await navigator.mediaDevices.getUserMedia({audio: true});
+    self.track = stream.getTracks()[0];
+    self.trackEvent = new MediaStreamTrackEvent("type", {
       track: track,
     });
   } catch (e) { throw e}
 
-  if (input) {
-    idl_array.add_objects({ InputDeviceInfo: [input] });
-  } else {
-    idl_array.add_objects({ MediaDeviceInfo: [media] });
-  }
   idl_array.add_objects({
-    MediaStream: [stream, 'new MediaStream()'],
+    InputDeviceInfo: devices,
+    MediaStream: ['stream', 'new MediaStream()'],
     Navigator: ['navigator'],
     MediaDevices: ['navigator.mediaDevices'],
-    MediaStreamTrack: [track],
-    MediaStreamTrackEvent: [trackEvent],
+    MediaStreamTrack: ['track'],
+    MediaStreamTrackEvent: ['trackEvent'],
   });
   idl_array.test();
 }, 'mediacapture-streams interfaces.');
diff --git a/third_party/blink/web_tests/external/wpt/preload/subresource-integrity.html b/third_party/blink/web_tests/external/wpt/preload/subresource-integrity.html
new file mode 100644
index 0000000..08c7854a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/subresource-integrity.html
@@ -0,0 +1,284 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Subresource Integrity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/sriharness.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/subresource-integrity/sri-test-helpers.sub.js"></script>
+
+<div id="log"></div>
+
+<div id="container"></div>
+<script>
+  // This is a list of information for each preload destination. The information
+  // is used in a loop iterating over the below tests, so that each test is run
+  // for each destination.
+  const preload_destination_info = [
+    {
+      destination: 'script', ext: '.js', supports_sri: true,
+      sha256: 'sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA=',
+      sha384: 'sha384-cINXh+nCzEHPWzXS7eoT+vYMBpyqczOybRLNU3XAButFWCRhHT5hLByIbPRqIm2f',
+      sha512: 'sha512-KZdenhzBd7X7Q/vmaOSyvFz1CGdoVt26xzCZjlkU9lfBEK+V/ougGys7iYDi0+tOHIQSQa87bIqx95R7GU7I9Q=='
+    },
+    {
+      destination: 'style', ext: '.css', supports_sri: true,
+      sha256: 'sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=',
+      sha384: 'sha384-wDAWxH4tOWBwAwHfBn9B7XuNmFxHTMeigAMwn0iVQ0zq3FtmYMLxihcGnU64CwcX',
+      sha512: 'sha512-9wXDjd6Wq3H6nPAhI9zOvG7mJkUr03MTxaO+8ztTKnfJif42laL93Be/IF6YYZHHF4esitVYxiwpY2HSZX4l6w=='
+    },
+    // TODO(domfarolino): Add more destinations.
+  ];
+
+  for (const info of preload_destination_info) {
+    const {destination, ext, supports_sri, sha256, sha384, sha512} = info;
+
+    // Preload + Subresource Integrity tests. These tests work by passing some
+    // destination-specific information (defined in |preload_destination_info|)
+    // to the below tests, which do the following:
+    // Create a <link rel="preload"> for the given destination, with the
+    // specified `integrity`. After this has either loaded or failed to load,
+    // the subresource element corresponding to |destination| will be created,
+    // attempting to re-use the preloaded resource. `integrity` may be specified
+    // on the subresource elements that support SRI as well. The subresource
+    // will either load or fail to load, and the result will be compared with an
+    // expectation passed to the test.
+    SRIPreloadTest(
+        true,                                                   /* preload_sri_success */
+        true,                                                   /* subresource_sri_success */
+        `Same-origin ${destination} with correct sha256 hash.`, /* name */
+        destination,                                            /* destination */
+        same_origin_prefix + destination + ext + `?${token()}`, /* resource_url (for preload + subresource) */
+        {integrity: sha256},                                    /* link_attrs */
+        {}                                                      /* subresource_attrs */
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with correct sha384 hash.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: sha384},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with correct sha512 hash.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: sha512},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with empty integrity.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {},
+        {}
+    )
+
+    SRIPreloadTest(
+        false,
+        false,
+        `Same-origin ${destination} with incorrect hash.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with multiple sha256 hashes, including correct.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: `${sha256} sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead`},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with multiple sha256 hashes, including unknown algorithm.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: `${sha256} foo666-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead`},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with sha256 mismatch, sha512 match`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: `${sha512} sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead`},
+        {}
+    )
+
+    SRIPreloadTest(
+        false,
+        false,
+        `Same-origin ${destination} with sha256 match, sha512 mismatch`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: `sha512-deadbeefspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg== ${sha256}`},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `<crossorigin='anonymous'> ${destination} with correct hash, ACAO: *`,
+        destination,
+        xorigin_prefix + destination + ext + `?${token()}` + anonymous,
+        {integrity: sha256, crossOrigin: 'anonymous'},
+        {crossOrigin: "anonymous"}
+    )
+
+    SRIPreloadTest(
+        false,
+        false,
+        `<crossorigin='anonymous'> ${destination} with incorrect hash, ACAO: *`,
+        destination,
+        xorigin_prefix + destination + ext + `?${token()}` + anonymous,
+        {integrity: "sha256-sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead", crossOrigin: "anonymous"},
+        {crossOrigin: "anonymous"}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `<crossorigin='use-credentials'> ${destination} with correct hash, CORS-eligible`,
+        destination,
+        xorigin_prefix + destination + ext + `?${token()}` + use_credentials,
+        {integrity: sha256, crossOrigin: "use-credentials"},
+        {crossOrigin: "use-credentials"}
+    )
+
+    SRIPreloadTest(
+        false,
+        false,
+        `<crossorigin='use-credentials'> ${destination} with incorrect hash CORS-eligible`,
+        destination,
+        xorigin_prefix + destination + ext + `?${token()}` + use_credentials,
+        {integrity: "sha256-deadbeef2S+pTRZgiw3DWrhC6JLDlt2zRyGpwH7unU8=", crossOrigin: "use-credentials"},
+        {crossOrigin: "use-credentials"}
+    )
+
+    SRIPreloadTest(
+        false,
+        false,
+        `<crossorigin='anonymous'> ${destination} with CORS-ineligible resource`,
+        destination,
+        // not piping ACAO header makes this CORS-ineligible
+        xorigin_prefix + destination + ext + `?${token()}`,
+        {integrity: sha256, crossOrigin: "anonymous"},
+        {crossOrigin: "anonymous"}
+    )
+
+    SRIPreloadTest(
+        false,
+        false,
+        `Cross-origin ${destination}, not CORS request, with correct hash`,
+        destination,
+        xorigin_prefix + destination + ext + `?${token()}` + anonymous,
+        {integrity: sha256},
+        {}
+    )
+
+    SRIPreloadTest(
+        false,
+        false,
+        `Cross-origin ${destination}, not CORS request, with hash mismatch`,
+        destination,
+        xorigin_prefix + destination + ext + `?${token()}` + anonymous,
+        {integrity: "sha256-deadbeef01Y0yKSx3/UoIKtIY2UQ9+H8WGyyMuOWOC0="},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Cross-origin ${destination}, empty integrity`,
+        destination,
+        xorigin_prefix + destination + ext + `?${token()}` + anonymous,
+        {},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with correct hash, options.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: `${sha256}?foo=bar?spam=eggs`},
+        {}
+    )
+
+    SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with unknown algorithm only.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: "foo666-8aBiAJl3ukQwSJ6eTs5wl6hGjnOtyXjcTRdAf89uIfY="},
+        {}
+    )
+
+    // The below tests are specific to subresource destinations that support
+    // SRI. See |supports_sri|.
+    if (supports_sri) {
+
+      SRIPreloadTest(
+        true,
+        true,
+        `Same-origin ${destination} with matching digest re-uses preload with matching digest.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: sha256},
+        {integrity: sha256}
+      )
+
+      SRIPreloadTest(
+        true,
+        false,
+        `Same-origin ${destination} with non-matching digest does not re-use preload with matching digest.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: sha256},
+        {integrity: "sha256-deadbeefQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="}
+      )
+
+      SRIPreloadTest(
+        false,
+        true,
+        `Same-origin ${destination} with matching digest does not re-use preload with non-matching digest.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: "sha256-deadbeefQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="},
+        {integrity: sha256}
+      )
+
+      SRIPreloadTest(
+        false,
+        false,
+        `Same-origin ${destination} with non-matching digest does not re-use preload with non-matching digest.`,
+        destination,
+        same_origin_prefix + destination + ext + `?${token()}`,
+        {integrity: "sha256-deadbeefQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="},
+        {integrity: "sha256-deaddeadbeefYHFvsYdWumweeFAw0hJDTFt9seErghA="}
+      )
+
+    } // if.
+
+  } // for-of.
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
index 5406b67..5ed96b8f 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
@@ -200,21 +200,21 @@
   }
 
   poseFromMatrix(m) {
-    let orientation = [];
-
     let m00 = m[0];
     let m11 = m[5];
     let m22 = m[10];
-    // The max( 0, ... ) is just a safeguard against rounding error.
-    orientation[3] = Math.sqrt(Math.max(0, 1 + m00 + m11 + m22)) / 2;
-    orientation[0] = Math.sqrt(Math.max(0, 1 + m00 - m11 - m22)) / 2;
-    orientation[1] = Math.sqrt(Math.max(0, 1 - m00 + m11 - m22)) / 2;
-    orientation[2] = Math.sqrt(Math.max(0, 1 - m00 - m11 + m22)) / 2;
 
-    let position = [];
-    position[0] = m[12];
-    position[1] = m[13];
-    position[2] = m[14];
+    // The max( 0, ... ) is just a safeguard against rounding error.
+    let orientation = new gfx.mojom.Quaternion();
+    orientation.w = Math.sqrt(Math.max(0, 1 + m00 + m11 + m22)) / 2;
+    orientation.x = Math.sqrt(Math.max(0, 1 + m00 - m11 - m22)) / 2;
+    orientation.y = Math.sqrt(Math.max(0, 1 - m00 + m11 - m22)) / 2;
+    orientation.z = Math.sqrt(Math.max(0, 1 - m00 - m11 + m22)) / 2;
+
+    let position = new gfx.mojom.Point3F();
+    position.x = m[12];
+    position.y = m[13];
+    position.z = m[14];
 
     return {
       orientation, position
@@ -249,7 +249,7 @@
           leftDegrees: 50.899,
           rightDegrees: 35.197
         },
-        offset: [-0.032, 0, 0],
+        offset: new gfx.mojom.Vector3dF(-0.032, 0, 0),
         renderWidth: 20,
         renderHeight: 20
       },
@@ -260,7 +260,7 @@
           leftDegrees: 50.899,
           rightDegrees: 35.197
         },
-        offset: [0.032, 0, 0],
+        offset: new gfx.mojom.Vector3dF(0.032, 0, 0),
         renderWidth: 20,
         renderHeight: 20
       },
@@ -293,7 +293,7 @@
         leftDegrees: toDegrees(leftTan),
         rightDegrees: toDegrees(rightTan)
       },
-      offset: [0, 0, 0],
+      offset: new gfx.mojom.Vector3dF(0, 0, 0),
       renderWidth: 20,
       renderHeight: 20
     };
@@ -320,7 +320,6 @@
           microseconds: now,
         },
         frameId: this.next_frame_id_++,
-        projectionMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
         bufferHolder: null,
         bufferSize: {}
       }
diff --git a/third_party/blink/web_tests/external/wpt/resources/sriharness.js b/third_party/blink/web_tests/external/wpt/resources/sriharness.js
index 9d7fa76..fe8ad2b 100644
--- a/third_party/blink/web_tests/external/wpt/resources/sriharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/sriharness.js
@@ -1,3 +1,5 @@
+// TODO(domfarolino): Refactor SRIScriptTest to just be a function instead of a
+// constructor, since there is no need to produce another object.
 var SRIScriptTest = function(pass, name, src, integrityValue, crossoriginValue, nonce) {
     this.pass = pass;
     this.name = "Script: " + name;
@@ -32,6 +34,100 @@
     document.body.appendChild(e);
 };
 
+function buildElementFromDestination(resource_url, destination, attrs) {
+  // Assert: |destination| is a valid destination.
+  let element;
+
+  // The below switch is responsible for:
+  //   1. Creating the correct subresource element
+  //   2. Setting said element's href, src, or fetch-instigating property
+  //      appropriately.
+  switch (destination) {
+    case "script":
+      element = document.createElement(destination);
+      element.src = resource_url;
+      break;
+    case "style":
+      element = document.createElement('link');
+      element.rel = 'stylesheet';
+      element.href = resource_url;
+      break;
+    case "image":
+      element = document.createElement('img');
+      element.src = resource_url;
+      break;
+    default:
+      assert_unreached("INVALID DESTINATION");
+  }
+
+  // Apply the rest of the attributes, if any.
+  for (const [attr_name, attr_val] of Object.entries(attrs)) {
+    element[attr_name] = attr_val;
+  }
+
+  return element;
+}
+
+const SRIPreloadTest = (preload_sri_success, subresource_sri_success, name,
+                        destination, resource_url, link_attrs,
+                        subresource_attrs) => {
+  const test = async_test(name);
+  const link = document.createElement('link');
+
+  // Early-fail in UAs that do not support `preload` links.
+  test.step_func(() => {
+    assert_true(link.relList.supports('preload'),
+      "This test is automatically failing because the browser does not" +
+      "support `preload` links.");
+  })();
+
+  // Build up the link.
+  link.rel = 'preload';
+  link.as = destination;
+  link.href = resource_url;
+  for (const [attr_name, attr_val] of Object.entries(link_attrs)) {
+    link[attr_name] = attr_val; // This may override `rel` to modulepreload.
+  }
+
+  // Preload + subresource success and failure loading functions.
+  const valid_preload_failed = test.step_func(() =>
+    { assert_unreached("Valid preload fired error handler.") });
+  const invalid_preload_succeeded = test.step_func(() =>
+    { assert_unreached("Invalid preload load succeeded.") });
+  const valid_subresource_failed = test.step_func(() =>
+    { assert_unreached("Valid subresource fired error handler.") });
+  const invalid_subresource_succeeded = test.step_func(() =>
+    { assert_unreached("Invalid subresource load succeeded.") });
+  const subresource_pass = test.step_func(() => { test.done(); });
+  const preload_pass = test.step_func(() => {
+    const subresource_element = buildElementFromDestination(
+      resource_url,
+      destination,
+      subresource_attrs
+    );
+
+    if (subresource_sri_success) {
+      subresource_element.onload = subresource_pass;
+      subresource_element.onerror = valid_subresource_failed;
+    } else {
+      subresource_element.onload = invalid_subresource_succeeded;
+      subresource_element.onerror = subresource_pass;
+    }
+
+    document.body.append(subresource_element);
+  });
+
+  if (preload_sri_success) {
+    link.onload = preload_pass;
+    link.onerror = valid_preload_failed;
+  } else {
+    link.onload = invalid_preload_succeeded;
+    link.onerror = preload_pass;
+  }
+
+  document.head.append(link);
+}
+
 // <link> tests
 // Style tests must be done synchronously because they rely on the presence
 // and absence of global style, which can affect later tests. Thus, instead
@@ -63,6 +159,8 @@
     var div = document.createElement("div");
     div.className = "testdiv";
     var e = document.createElement("link");
+
+    // The link relation is guaranteed to not be "preload" or "modulepreload".
     this.attrs.rel = this.attrs.rel || "stylesheet";
     for (var key in this.attrs) {
         if (this.attrs.hasOwnProperty(key)) {
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/interfaces-sw.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/interfaces-sw.https-expected.txt
index 5e51cf62..c368ef5 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/interfaces-sw.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/interfaces-sw.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 181 tests; 166 PASS, 15 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 183 tests; 166 PASS, 17 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Interfaces and attributes in ServiceWorkerGlobalScope
 PASS test setup (cache creation)
 FAIL Event constructors assert_equals: FetchEvent.isReload should not exist expected (undefined) undefined but got (boolean) false
@@ -66,6 +66,7 @@
 PASS ServiceWorkerGlobalScope interface: existence and properties of interface prototype object's @@unscopables property
 PASS ServiceWorkerGlobalScope interface: attribute clients
 PASS ServiceWorkerGlobalScope interface: attribute registration
+FAIL ServiceWorkerGlobalScope interface: attribute serviceWorker assert_own_property: The global object must have a property "serviceWorker" expected property "serviceWorker" missing
 PASS ServiceWorkerGlobalScope interface: operation skipWaiting()
 PASS ServiceWorkerGlobalScope interface: attribute oninstall
 PASS ServiceWorkerGlobalScope interface: attribute onactivate
@@ -82,6 +83,7 @@
 PASS Stringification of self
 PASS ServiceWorkerGlobalScope interface: self must inherit property "clients" with the proper type
 PASS ServiceWorkerGlobalScope interface: self must inherit property "registration" with the proper type
+FAIL ServiceWorkerGlobalScope interface: self must inherit property "serviceWorker" with the proper type assert_own_property: expected property "serviceWorker" missing
 PASS ServiceWorkerGlobalScope interface: self must inherit property "skipWaiting()" with the proper type
 PASS ServiceWorkerGlobalScope interface: self must inherit property "oninstall" with the proper type
 PASS ServiceWorkerGlobalScope interface: self must inherit property "onactivate" with the proper type
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/interfaces-sw.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/interfaces-sw.https.html
index 50c76d3..73ffb28 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/interfaces-sw.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/interfaces-sw.https.html
@@ -6,6 +6,8 @@
 <script>
 'use strict';
 
+// NOTE: affected when 'resources/interfaces-worker.sub.js' srcs change:
+// const srcs = ['dom', 'html', 'service-workers', 'dedicated-workers'];
 service_worker_test(
   'resources/interfaces-worker.sub.js',
   'Interfaces and attributes in ServiceWorkerGlobalScope');
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement-invalid-state.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement-invalid-state.html
deleted file mode 100644
index e777c96e..0000000
--- a/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement-invalid-state.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-
-const videoElementTests =
-    [
-      {
-        createDetector: () => { return new FaceDetector(); },
-        name: "Face - detect(HTMLVideoElement)",
-      },
-      {
-        createDetector: () => { return new BarcodeDetector(); },
-        name: "Barcode - detect(HTMLVideoElement)",
-      }
-    ];
-
-for (let videoElementTest of videoElementTests) {
-
-  // Detector's detect() rejects on a HAVE_NOTHING HTMLVideoElement.
-  promise_test(async t => {
-    const video = document.createElement("video");
-    video.src = "";
-    const videoWatcher = new EventWatcher(t, video, ["play", "error"]);
-    video.load();
-    await videoWatcher.wait_for("error");
-    assert_equals(video.readyState, video.HAVE_NOTHING);
-
-    const detector = videoElementTest.createDetector();
-    await promise_rejects(t, 'InvalidStateError', detector.detect(video));
-  }, `${videoElementTest.name} - HAVE_NOTHING`);
-
-  // Detector's detect() rejects on a HAVE_METADATA HTMLVideoElement.
-  promise_test(async t => {
-    const video = document.createElement("video");
-    video.src = "/media/white.webm";
-    video.loop = true;
-    video.autoplay = true;
-    const videoWatcher = new EventWatcher(t, video, ["loadedmetadata", "error"]);
-    video.load();
-    await videoWatcher.wait_for("loadedmetadata");
-    assert_equals(video.readyState, video.HAVE_METADATA);
-
-    const detector = videoElementTest.createDetector();
-    await promise_rejects(t, 'InvalidStateError', detector.detect(video));
-  }, `${videoElementTest.name} - HAVE_METADATA`);
-
-}
-
-</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.html b/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.html
index f4536926..c9d86430 100644
--- a/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.html
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.html
@@ -30,9 +30,9 @@
     img.src = IMAGE_URL;
     await imgWatcher.wait_for("load");
     const detector = crossOriginTest.createDetector();
-    await promise_rejects(t, "SecurityError", detector.detect(img));
-  }, `${crossOriginTest.detectorType} should reject cross-origin \
-HTMLImageElements with a SecurityError.`);
+    promise_rejects(t, "SecurityError", detector.detect(img));
+  }, crossOriginTest.detectorType
+  + " should reject cross-origin HTMLImageElements with a SecurityError.");
 
   // Verifies that Detector rejects a cross-origin ImageBitmap.
   promise_test(async t => {
@@ -42,9 +42,9 @@
     await imgWatcher.wait_for("load");
     const imgBitmap = await createImageBitmap(img);
     const detector = crossOriginTest.createDetector();
-    await promise_rejects(t, "SecurityError", detector.detect(imgBitmap));
-  }, `${crossOriginTest.detectorType} should reject cross-origin \
-ImageBitmaps with a SecurityError.`);
+    promise_rejects(t, "SecurityError", detector.detect(imgBitmap));
+  }, crossOriginTest.detectorType
+  + " should reject cross-origin ImageBitmaps with a SecurityError.");
 
   // Verifies that Detector rejects a cross-origin HTMLVideoElement.
   promise_test(async t => {
@@ -53,22 +53,9 @@
     video.src = VIDEO_URL;
     await videoWatcher.wait_for("loadeddata");
     const detector = crossOriginTest.createDetector();
-    await promise_rejects(t, "SecurityError", detector.detect(video));
-  }, `${crossOriginTest.detectorType} should reject cross-origin \
-HTMLVideoElements with a SecurityError.`);
-
-  // Verifies that Detector rejects a cross-origin HTMLCanvasElement.
-  promise_test(async t => {
-    const img = new Image();
-    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
-    img.src = IMAGE_URL;
-    await imgWatcher.wait_for("load");
-    const canvas = document.createElement("canvas");
-    canvas.getContext("2d").drawImage(img, 0, 0);
-    const detector = crossOriginTest.createDetector();
-    await promise_rejects(t, "SecurityError", detector.detect(canvas));
-  }, `${crossOriginTest.detectorType} should reject cross-origin \
-HTMLCanvasElement with a SecurityError.`);
+    promise_rejects(t, "SecurityError", detector.detect(video));
+  }, crossOriginTest.detectorType
+  + " should reject cross-origin HTMLVideoElements with a SecurityError.");
 
 }
 
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/attributes.html b/third_party/blink/web_tests/external/wpt/std-toast/attributes.html
index 9bfc65c..2aef41ae 100644
--- a/third_party/blink/web_tests/external/wpt/std-toast/attributes.html
+++ b/third_party/blink/web_tests/external/wpt/std-toast/attributes.html
@@ -113,4 +113,9 @@
         t.done();
     }, 2000);
 }, 'toggling open attribute does not start timeout');
+
+testToastElement((toast) => {
+    const permitted_properties = ['constructor', 'show', 'hide', 'toggle', 'open'];
+    assert_array_equals(permitted_properties.sort(), Object.getOwnPropertyNames(toast.__proto__).sort());
+}, 'toast only exposes certain properties');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/sri-test-helpers.sub.js b/third_party/blink/web_tests/external/wpt/subresource-integrity/sri-test-helpers.sub.js
index 22c9e9c..53102d4 100644
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/sri-test-helpers.sub.js
+++ b/third_party/blink/web_tests/external/wpt/subresource-integrity/sri-test-helpers.sub.js
@@ -9,19 +9,24 @@
 // Thus, we only want the Access-Control-Allow-Origin header to have
 // the port if it's not port 80 or 443, since the user agent will elide the
 // ports in those cases.
-const main_domain = "{{domains[]}}";
-const www_domain = "{{domains[www]}}";
-const default_port = (location.protocol === "https:") ? "{{ports[https][0]}}" :
-                                                        "{{ports[http][0]}}";
+const main_domain = '{{domains[]}}';
+const www_domain = '{{domains[www]}}';
+const default_port = (location.protocol === 'https:') ? '{{ports[https][0]}}' :
+                                                        '{{ports[http][0]}}';
 
-const port_string = (default_port !== "80" && default_port !== "443") ?
-                      `:${default_port}` : "";
+const port_string = (default_port !== '80' && default_port !== '443') ?
+                      `:${default_port}` : '';
 const www_host_and_port = www_domain + port_string;
 
 // General resource prefixes.
 const same_origin_prefix = '/subresource-integrity/';
 const xorigin_prefix = `${location.protocol}//${www_host_and_port}/subresource-integrity/`;
 
+// General resource suffixes, for piping CORS headers.
+const anonymous = '&pipe=header(Access-Control-Allow-Origin,*)';
+const use_credentials = "&pipe=header(Access-Control-Allow-Credentials,true)|" +
+                        "header(Access-Control-Allow-Origin," + location.origin + ")";
+
 // Note that all of these style URLs have query parameters started, so any
 // additional parameters should be appended starting with '&'.
 const xorigin_anon_style = location.protocol
diff --git a/third_party/blink/web_tests/external/wpt/svg/coordinate-systems/support/viewBox-scaling-text-001-ref.html b/third_party/blink/web_tests/external/wpt/svg/coordinate-systems/support/viewBox-scaling-text-001-ref.html
index 09321909..effaba5 100644
--- a/third_party/blink/web_tests/external/wpt/svg/coordinate-systems/support/viewBox-scaling-text-001-ref.html
+++ b/third_party/blink/web_tests/external/wpt/svg/coordinate-systems/support/viewBox-scaling-text-001-ref.html
@@ -4,19 +4,19 @@
   <title>Reference case for text scaled via SVG viewBox</title>
   <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
   <style>
-    body { margin: 0; }
     svg {
       width: 100px;
       height: 100px;
-      background: red;
+      background: lightgray;
+      overflow: visible;
     }
-    rect {
-      fill: lime;
+    text {
+      font: 100px/1 monospace;
     }
   </style>
 </head>
 <body>
   <svg>
-    <rect height="100%" width="100%"></rect>
+    <text x="0" y="100">X̂̂̂̂̂̂</text>
   </svg>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/svg/coordinate-systems/viewBox-scaling-text-001.html b/third_party/blink/web_tests/external/wpt/svg/coordinate-systems/viewBox-scaling-text-001.html
index 4250e4a2..30890430 100644
--- a/third_party/blink/web_tests/external/wpt/svg/coordinate-systems/viewBox-scaling-text-001.html
+++ b/third_party/blink/web_tests/external/wpt/svg/coordinate-systems/viewBox-scaling-text-001.html
@@ -1,31 +1,25 @@
 <!DOCTYPE html>
 <head>
   <meta charset="utf-8">
+  <meta name="assert" href="1px-tall-text should be visible when scaled up via the SVG viewBox attribute">
   <title>Testcase for text scaled via SVG viewBox</title>
   <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
   <link rel="help" href="https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute">
   <link rel="match" href="support/viewBox-scaling-text-001-ref.html">
-  <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
   <style>
-    body { margin: 0; }
     svg {
       width: 100px;
       height: 100px;
-      background: red;
+      background: lightgray;
+      overflow: visible;
     }
     text {
-      fill: lime;
-      font: 1px/1 Ahem;
+      font: 1px/1 monospace;
     }
   </style>
 </head>
 <body>
-  <!-- We position the <text> at y=0.8px, which is the alphabetic baseline for
-       the Ahem font. This puts the bottom of the rendered square glyph at
-       y=1px, i.e. the bottom of the SVG viewport. With that, the 1px-tall Ahem
-       square 'X' character should fully fill the SVG viewport (which is then
-       scaled up from 1x1 to 100x100). -->
   <svg viewBox="0 0 1 1">
-    <text x="0" y="0.8">X̂̂̂̂̂̂</text>
+    <text x="0" y="1">X̂̂̂̂̂̂</text>
   </svg>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
index 4bff465..bf29a9f 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 202 tests; 198 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 203 tests; 198 PASS, 5 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Navigator: original interface defined
 PASS Partial dictionary WebGLContextAttributes: original dictionary defined
@@ -145,6 +145,7 @@
 PASS XRInputSource interface: attribute targetRaySpace
 PASS XRInputSource interface: attribute gripSpace
 PASS XRInputSource interface: attribute gamepad
+FAIL XRInputSource interface: attribute profiles assert_true: The prototype object must have a property "profiles" expected true got false
 PASS XRInputSourceArray interface: existence and properties of interface object
 PASS XRInputSourceArray interface object length
 PASS XRInputSourceArray interface object name
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-createImageBitmap-video-resize.html b/third_party/blink/web_tests/fast/canvas/canvas-createImageBitmap-video-resize.html
index 3593d77..fae42c8 100644
--- a/third_party/blink/web_tests/fast/canvas/canvas-createImageBitmap-video-resize.html
+++ b/third_party/blink/web_tests/fast/canvas/canvas-createImageBitmap-video-resize.html
@@ -51,6 +51,7 @@
 
 // HTMLVideoElement
 var video = document.createElement("video");
+video.preload = "auto";
 video.oncanplaythrough = t.step_func(function() {
     return generateTest();
 });
diff --git a/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-cssom-expected.txt b/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-cssom-expected.txt
new file mode 100644
index 0000000..2ef69580
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-cssom-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL Rule is applied assert_equals: expected "rgb(0, 128, 0)" but got " green"
+FAIL Can read "syntax" from declaration assert_equals: expected (string) "\"<color> | none\"" but got (undefined) undefined
+FAIL Can read "inherits" from declaration assert_equals: expected (string) "false" but got (undefined) undefined
+FAIL Can read "initial-value" from declaration assert_equals: expected (string) " red" but got (undefined) undefined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-cssom.html b/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-cssom.html
new file mode 100644
index 0000000..08215ee
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-cssom.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!--TODO(andruud): Migrate to WPT once @property is in the specification. -->
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<style>
+  @property --test {
+    syntax: "<color> | none";
+    inherits: false;
+    initial-value: red;
+  }
+  #target {
+    --test: green;
+  }
+</style>
+<div id="target"></div>
+<script>
+
+let rule = document.styleSheets[0].cssRules[0];
+
+test(() => {
+  assert_equals(getComputedStyle(target).getPropertyValue('--test'), 'rgb(0, 128, 0)');
+}, 'Rule is applied');
+
+test(() => {
+  let syntax = rule.style['syntax'];
+  assert_equals(syntax, '"<color> | none"');
+}, 'Can read "syntax" from declaration');
+
+test(() => {
+  let inherits = rule.style['inherits'];
+  assert_equals(inherits, 'false');
+}, 'Can read "inherits" from declaration');
+
+test(() => {
+  let initialValue = rule.style['initial-value'];
+  assert_equals(initialValue, ' red');
+}, 'Can read "initial-value" from declaration');
+
+// TODO(https://crbug.com/978780): Test mutation through CSSOM once
+// we can unregister properties.
+
+</script>
diff --git a/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-expected.txt b/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-expected.txt
new file mode 100644
index 0000000..cc682635
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property-expected.txt
@@ -0,0 +1,63 @@
+This is a testharness.js-based test.
+FAIL Accepts valid value for 'syntax' descriptor ["<color>"] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'syntax' descriptor ["<color> | none"] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'syntax' descriptor ["<color># | <image> | none"] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'syntax' descriptor ["foo | bar | baz"] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'syntax' descriptor ["*"] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'syntax' descriptor ["notasyntax"] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'syntax' descriptor [red] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'syntax' descriptor [rgb(255, 0, 0)] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'syntax' descriptor [<color>] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'syntax' descriptor [foo | bar] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'initial-value' descriptor [10px] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'initial-value' descriptor [rgb(1, 2, 3)] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'initial-value' descriptor [red] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'initial-value' descriptor [foo] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'initial-value' descriptor [if(){}] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'initial-value' descriptor [var(--x)] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'inherits' descriptor [true] Cannot read property 'style' of undefined
+FAIL Accepts valid value for 'inherits' descriptor [false] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'inherits' descriptor [none] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'inherits' descriptor [0] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'inherits' descriptor [1] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'inherits' descriptor ["true"] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'inherits' descriptor ["false"] Cannot read property 'style' of undefined
+FAIL Rejects invalid value for 'inherits' descriptor [calc(0)] Cannot read property 'style' of undefined
+PASS Invalid property name does not parse [foo]
+PASS Invalid property name does not parse [-foo]
+FAIL Rule applied [*, if(){}, false] assert_equals: expected "if(){}" but got ""
+FAIL Rule applied [<angle>, 42deg, false] assert_equals: expected "42deg" but got ""
+FAIL Rule applied [<angle>, 1turn, false] assert_equals: expected "360deg" but got ""
+FAIL Rule applied [<color>, green, false] assert_equals: expected "rgb(0, 128, 0)" but got ""
+FAIL Rule applied [<color>, rgb(1, 2, 3), false] assert_equals: expected "rgb(1, 2, 3)" but got ""
+FAIL Rule applied [<image>, url("http://a/"), false] assert_equals: expected "url(\"http://a/\")" but got ""
+FAIL Rule applied [<integer>, 5, false] assert_equals: expected "5" but got ""
+FAIL Rule applied [<length-percentage>, 10px, false] assert_equals: expected "10px" but got ""
+FAIL Rule applied [<length-percentage>, 10%, false] assert_equals: expected "10%" but got ""
+FAIL Rule applied [<length-percentage>, calc(10% + 10px), false] assert_equals: expected "calc(10% + 10px)" but got ""
+FAIL Rule applied [<length>, 10px, false] assert_equals: expected "10px" but got ""
+FAIL Rule applied [<number>, 2.5, false] assert_equals: expected "2.5" but got ""
+FAIL Rule applied [<percentage>, 10%, false] assert_equals: expected "10%" but got ""
+FAIL Rule applied [<resolution>, 50dppx, false] assert_equals: expected "50dppx" but got ""
+FAIL Rule applied [<resolution>, 96dpi, false] assert_equals: expected "1dppx" but got ""
+FAIL Rule applied [<time>, 10s, false] assert_equals: expected "10s" but got ""
+FAIL Rule applied [<time>, 1000ms, false] assert_equals: expected "1s" but got ""
+FAIL Rule applied [<transform-function>, rotateX(0deg), false] assert_equals: expected "rotateX(0deg)" but got ""
+FAIL Rule applied [<transform-list>, rotateX(0deg), false] assert_equals: expected "rotateX(0deg)" but got ""
+FAIL Rule applied [<transform-list>, rotateX(0deg) translateX(10px), false] assert_equals: expected "rotateX(0deg) translateX(10px)" but got ""
+FAIL Rule applied [<url>, url("http://a/"), false] assert_equals: expected "url(\"http://a/\")" but got ""
+FAIL Rule applied [<color>, tomato, false] assert_equals: expected "rgb(255, 99, 71)" but got ""
+FAIL Rule applied [<color>, tomato, true] assert_equals: expected "rgb(255, 99, 71)" but got ""
+PASS Rule applied for "*", even with no initial value
+PASS Rule not applied [undefined, green, false]
+PASS Rule not applied [<color>, undefined, false]
+PASS Rule not applied [<color>, green, undefined]
+PASS Rule not applied [<gandalf>, grey, false]
+PASS Rule not applied [gandalf, grey, false]
+PASS Rule not applied [<color>, notacolor, false]
+PASS Rule not applied [<length>, 10em, false]
+FAIL Non-inherited properties do not inherit assert_equals: expected "40px" but got " 40px"
+FAIL Inherited properties inherit assert_equals: expected "40px" but got " 40px"
+FAIL Initial values substituted as computed value assert_equals: expected "rgb(0, 128, 0)" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property.html b/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property.html
new file mode 100644
index 0000000..ec38e30
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/css-properties-values-api/at-property.html
@@ -0,0 +1,218 @@
+<!DOCTYPE html>
+<!--TODO(andruud): Migrate to WPT once @property is in the specification. -->
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<div id="outer">
+  <div id="target"></div>
+</div>
+<script>
+
+let g_id = 0;
+
+function generate_name() {
+  g_id++;
+  return `--property-${g_id}`;
+}
+
+function with_style_node(text, fn) {
+  let node = document.createElement('style');
+  node.textContent = text;
+  try {
+    document.body.append(node);
+    fn(node);
+  } finally {
+    node.remove();
+  }
+}
+
+function with_at_rule(desc, fn) {
+  let name = typeof(desc.name) === 'undefined' ? generate_name() : desc.name;
+  let text = `@property ${name} {`;
+  if (typeof(desc.syntax) !== 'undefined')
+    text += `syntax:${desc.syntax};`;
+  if (typeof(desc.initialValue) !== 'undefined')
+    text += `initial-value:${desc.initialValue};`;
+  if (typeof(desc.inherits) !== 'undefined')
+    text += `inherits:${desc.inherits};`;
+  text += '}';
+  with_style_node(text, (node) => fn(name, node.sheet.rules[0]));
+}
+
+function test_with_at_rule(desc, fn, description) {
+  test(() => with_at_rule(desc, fn), description);
+}
+
+function test_with_style_node(text, fn, description) {
+  test(() => with_style_node(text, fn), description);
+}
+
+// Parsing:
+
+let uppercase_first = (x) => x.charAt(0).toUpperCase() + x.slice(1);
+let to_camel_case = (x) => x.split('-')[0] + x.split('-').slice(1).map(uppercase_first).join('');
+
+function test_parse_success(descriptor, value) {
+  test_with_at_rule({ [to_camel_case(descriptor)]: value }, (name, rule) => {
+    assert_equals(rule.style[to_camel_case(descriptor)], value);
+  }, `Accepts valid value for '${descriptor}' descriptor [${value}]`);
+}
+
+function test_parse_failure(descriptor, value) {
+  test_with_at_rule({ [to_camel_case(descriptor)]: value }, (name, rule) => {
+    assert_equals(rule.style[to_camel_case(descriptor)], '');
+  }, `Rejects invalid value for '${descriptor}' descriptor [${value}]`);
+}
+
+// syntax
+test_parse_success('syntax', '"<color>"');
+test_parse_success('syntax', '"<color> | none"');
+test_parse_success('syntax', '"<color># | <image> | none"');
+test_parse_success('syntax', '"foo | bar | baz"');
+test_parse_success('syntax', '"*"');
+test_parse_success('syntax', '"notasyntax"');
+
+test_parse_failure('syntax', 'red');
+test_parse_failure('syntax', 'rgb(255, 0, 0)');
+test_parse_failure('syntax', '<color>');
+test_parse_failure('syntax', 'foo | bar');
+
+// initial-value
+test_parse_success('initial-value', '10px');
+test_parse_success('initial-value', 'rgb(1, 2, 3)');
+test_parse_success('initial-value', 'red');
+test_parse_success('initial-value', 'foo');
+test_parse_success('initial-value', 'if(){}');
+test_parse_success('initial-value', 'var(--x)');
+
+// inherits
+test_parse_success('inherits', 'true');
+test_parse_success('inherits', 'false');
+
+test_parse_failure('inherits', 'none');
+test_parse_failure('inherits', '0');
+test_parse_failure('inherits', '1');
+test_parse_failure('inherits', '"true"');
+test_parse_failure('inherits', '"false"');
+test_parse_failure('inherits', 'calc(0)');
+
+test_with_style_node('@property foo { }', (node) => {
+  assert_equals(node.sheet.rules.length, 0);
+}, 'Invalid property name does not parse [foo]');
+
+test_with_style_node('@property -foo { }', (node) => {
+  assert_equals(node.sheet.rules.length, 0);
+}, 'Invalid property name does not parse [-foo]');
+
+// Applying @property rules
+
+function test_applied(syntax, initial, inherits, expected) {
+  test_with_at_rule({
+    syntax: `"${syntax}"`,
+    initialValue: initial,
+    inherits: inherits
+  }, (name, rule) => {
+    let actual = getComputedStyle(target).getPropertyValue(name);
+    assert_equals(actual, expected);
+  }, `Rule applied [${syntax}, ${initial}, ${inherits}]`);
+}
+
+function test_not_applied(syntax, initial, inherits) {
+  test_with_at_rule({
+    syntax: `"${syntax}"`,
+    initialValue: initial,
+    inherits: inherits
+  }, (name, rule) => {
+    let actual = getComputedStyle(target).getPropertyValue(name);
+    assert_equals(actual, '');
+  }, `Rule not applied [${syntax}, ${initial}, ${inherits}]`);
+}
+
+// syntax, initialValue, inherits, expected
+test_applied('*', 'if(){}', false, 'if(){}');
+test_applied('<angle>', '42deg', false, '42deg');
+test_applied('<angle>', '1turn', false, '360deg');
+test_applied('<color>', 'green', false, 'rgb(0, 128, 0)');
+test_applied('<color>', 'rgb(1, 2, 3)', false, 'rgb(1, 2, 3)');
+test_applied('<image>', 'url("http://a/")', false, 'url("http://a/")');
+test_applied('<integer>', '5', false, '5');
+test_applied('<length-percentage>', '10px', false, '10px');
+test_applied('<length-percentage>', '10%', false, '10%');
+test_applied('<length-percentage>', 'calc(10% + 10px)', false, 'calc(10% + 10px)');
+test_applied('<length>', '10px', false, '10px');
+test_applied('<number>', '2.5', false, '2.5');
+test_applied('<percentage>', '10%', false, '10%');
+test_applied('<resolution>', '50dppx', false, '50dppx');
+test_applied('<resolution>', '96dpi', false, '1dppx');
+test_applied('<time>', '10s', false, '10s');
+test_applied('<time>', '1000ms', false, '1s');
+test_applied('<transform-function>', 'rotateX(0deg)', false, 'rotateX(0deg)');
+test_applied('<transform-list>', 'rotateX(0deg)', false, 'rotateX(0deg)');
+test_applied('<transform-list>', 'rotateX(0deg) translateX(10px)', false, 'rotateX(0deg) translateX(10px)');
+test_applied('<url>', 'url("http://a/")', false, 'url("http://a/")');
+
+// inherits: true/false
+test_applied('<color>', 'tomato', false, 'rgb(255, 99, 71)');
+test_applied('<color>', 'tomato', true, 'rgb(255, 99, 71)');
+
+test_with_at_rule({ syntax: '"*"', inherits: true }, (name, rule) => {
+  try {
+    outer.style.setProperty(name, 'foo');
+    let actual = getComputedStyle(target).getPropertyValue(name);
+    assert_equals(actual, 'foo');
+  } finally {
+    outer.style = '';
+  }
+}, 'Rule applied for "*", even with no initial value');
+
+test_not_applied(undefined, 'green', false);
+test_not_applied('<color>', undefined, false);
+test_not_applied('<color>', 'green', undefined);
+test_not_applied('<gandalf>', 'grey', false);
+test_not_applied('gandalf', 'grey', false);
+test_not_applied('<color>', 'notacolor', false);
+test_not_applied('<length>', '10em', false);
+
+// Inheritance
+
+test_with_at_rule({
+  syntax: '"<length>"',
+  inherits: false,
+  initialValue: '0px'
+}, (name, rule) => {
+  try {
+    outer.style = `${name}: 40px`;
+    assert_equals(getComputedStyle(outer).getPropertyValue(name), '40px');
+    assert_equals(getComputedStyle(target).getPropertyValue(name), '0px');
+  } finally {
+    outer.style = '';
+  }
+}, 'Non-inherited properties do not inherit');
+
+test_with_at_rule({
+  syntax: '"<length>"',
+  inherits: true,
+  initialValue: '0px'
+}, (name, rule) => {
+  try {
+    outer.style = `${name}: 40px`;
+    assert_equals(getComputedStyle(outer).getPropertyValue(name), '40px');
+    assert_equals(getComputedStyle(target).getPropertyValue(name), '40px');
+  } finally {
+    outer.style = '';
+  }
+}, 'Inherited properties inherit');
+
+test_with_at_rule({
+  syntax: '"<color>"',
+  inherits: true,
+  initialValue: 'green'
+}, (name, rule) => {
+  try {
+    target.style = `--x:var(${name})`;
+    assert_equals(getComputedStyle(target).getPropertyValue(name), 'rgb(0, 128, 0)');
+  } finally {
+    target.style = '';
+  }
+}, 'Initial values substituted as computed value');
+
+</script>
diff --git a/third_party/blink/web_tests/fast/dynamic/011-expected.html b/third_party/blink/web_tests/fast/dynamic/011-expected.html
new file mode 100644
index 0000000..924868d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/dynamic/011-expected.html
@@ -0,0 +1,3 @@
+<html>
+<span id="foo">Bug #3810389.  Make sure we don't crash manipulating blocks inside inlines.  You should see the numbers 1 and 2 vertically stacked below this line. <div>1</div>2</span>
+<img>
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/scrollbars/nested-overlay-scrollbars-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
new file mode 100644
index 0000000..abd8db5
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
@@ -0,0 +1,129 @@
+{
+  "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": "LayoutBlockFlow (positioned) DIV id='outer'",
+      "position": [8, 8],
+      "bounds": [404, 404]
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [10, 10],
+      "bounds": [400, 400],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [10, 10],
+      "bounds": [400, 704],
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='inner'",
+      "position": [10, 510],
+      "bounds": [204, 204],
+      "transform": 1
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [12, 512],
+      "bounds": [200, 200],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [12, 512],
+      "bounds": [5000, 9000],
+      "transform": 1
+    },
+    {
+      "name": "Squashing Containment Layer",
+      "position": [10, 10],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='grey'",
+      "position": [12, 512],
+      "bounds": [100, 800],
+      "contentsOpaque": true,
+      "backgroundColor": "#808080",
+      "transform": 1
+    },
+    {
+      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='spacer')",
+      "position": [12, 2512],
+      "bounds": [5000, 1000],
+      "transform": 1
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [12, 512],
+      "bounds": [204, 204],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Horizontal Scrollbar Layer",
+      "position": [14, 707],
+      "bounds": [193, 7],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [207, 514],
+      "bounds": [7, 193],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Scroll Corner Layer",
+      "position": [207, 707],
+      "bounds": [7, 7],
+      "transform": 1
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [8, 8],
+      "bounds": [404, 404],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [403, 10],
+      "bounds": [7, 400],
+      "drawsContent": false
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -304, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/fast/dynamic/011-expected.png b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/fast/dynamic/011-expected.png
deleted file mode 100644
index 846fbab..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/fast/dynamic/011-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt
new file mode 100644
index 0000000..7e910706
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt
@@ -0,0 +1,67 @@
+{
+  "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": "LayoutBlockFlow (positioned) DIV class='outer'",
+      "bounds": [352, 294]
+    },
+    {
+      "name": "LayoutBlockFlow (relative positioned) DIV class='content'",
+      "position": [79, 79],
+      "bounds": [196, 212],
+      "contentsOpaque": true,
+      "backgroundColor": "#DDDDDD"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='outer' (foreground) Layer",
+      "bounds": [352, 294]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='scroller'",
+      "position": [32, 32],
+      "bounds": [290, 230],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [67, 67],
+      "bounds": [220, 160],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [67, 67],
+      "bounds": [220, 236],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [62, 62],
+      "bounds": [230, 170],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [280, 67],
+      "bounds": [7, 160],
+      "drawsContent": false
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/disable-blink-gen-property-trees/compositing/scrollbars/nested-overlay-scrollbars-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/disable-blink-gen-property-trees/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
new file mode 100644
index 0000000..abd8db5
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/disable-blink-gen-property-trees/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
@@ -0,0 +1,129 @@
+{
+  "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": "LayoutBlockFlow (positioned) DIV id='outer'",
+      "position": [8, 8],
+      "bounds": [404, 404]
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [10, 10],
+      "bounds": [400, 400],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [10, 10],
+      "bounds": [400, 704],
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='inner'",
+      "position": [10, 510],
+      "bounds": [204, 204],
+      "transform": 1
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [12, 512],
+      "bounds": [200, 200],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [12, 512],
+      "bounds": [5000, 9000],
+      "transform": 1
+    },
+    {
+      "name": "Squashing Containment Layer",
+      "position": [10, 10],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='grey'",
+      "position": [12, 512],
+      "bounds": [100, 800],
+      "contentsOpaque": true,
+      "backgroundColor": "#808080",
+      "transform": 1
+    },
+    {
+      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='spacer')",
+      "position": [12, 2512],
+      "bounds": [5000, 1000],
+      "transform": 1
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [12, 512],
+      "bounds": [204, 204],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Horizontal Scrollbar Layer",
+      "position": [14, 707],
+      "bounds": [193, 7],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [207, 514],
+      "bounds": [7, 193],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "Scroll Corner Layer",
+      "position": [207, 707],
+      "bounds": [7, 7],
+      "transform": 1
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [8, 8],
+      "bounds": [404, 404],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [403, 10],
+      "bounds": [7, 400],
+      "drawsContent": false
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -304, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/scrollbars/nested-overlay-scrollbars-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
new file mode 100644
index 0000000..1fa1391d
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/scrollbars/nested-overlay-scrollbars-expected.txt
@@ -0,0 +1,56 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='grey'",
+      "position": [2, 2],
+      "bounds": [100, 800],
+      "contentsOpaque": true,
+      "backgroundColor": "#808080",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='inner'",
+      "position": [2, 2],
+      "bounds": [200, 200],
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -304, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [2, 502, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/dynamic/011-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/dynamic/011-expected.png
deleted file mode 100644
index bf3fc2dc..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/dynamic/011-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/content_index/content-index.html b/third_party/blink/web_tests/http/tests/content_index/content-index.html
new file mode 100644
index 0000000..7fb15aa4
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/content_index/content-index.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Content Index: Behaviour of the add() function</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../serviceworker/resources/test-helpers.js"></script>
+<script src="resources.js"></script>
+<script>
+'use strict';
+
+contentIndexTest(async (t, index) => {
+  // Exposure of the interface and method.
+  assert_own_property(window, 'ContentIndex');
+  assert_own_property(ContentIndex.prototype, 'add');
+
+  assert_idl_attribute(index, 'add');
+  assert_idl_attribute(index, 'delete');
+  assert_idl_attribute(index, 'getDescriptions');
+
+}, 'The Content Index API is exposed');
+
+contentIndexTest(async (t, index) => {
+  await expectTypeErrorWithMessage(
+      index.add(createDescription({id: ''})),
+      'ID cannot be empty');
+  await expectTypeErrorWithMessage(
+      index.add(createDescription({title: ''})),
+      'Title cannot be empty');
+  await expectTypeErrorWithMessage(
+      index.add(createDescription({description:''})),
+      'Description cannot be empty');
+  await expectTypeErrorWithMessage(
+      index.add(createDescription({category: 'fake-category'})));
+
+  await expectTypeErrorWithMessage(
+      index.add(createDescription({iconUrl: '/non-existent-icon.png'})),
+      'Icon could not be loaded');
+  await expectTypeErrorWithMessage(
+      index.add(createDescription({iconUrl: '/resources/dummy.txt'})),
+      'Icon could not be loaded');
+
+  await index.add(createDescription({}));
+
+}, 'index.add parameters are validated.');
+
+contentIndexTest(async (t, index) => {
+  const description = createDescription({});
+
+  // Initially there are no descriptions.
+  assert_array_equals(await index.getDescriptions(), []);
+
+  await index.add(description);
+
+  const descriptions = await index.getDescriptions();
+  assert_equals(descriptions.length, 1);
+
+  assert_object_equals(descriptions[0], description);
+
+}, 'index.getDescriptions returns the same objects provided.');
+
+contentIndexTest(async (t, index) => {
+  const description1 = createDescription({title: 'title1'});
+  const description2 = createDescription({title: 'title2'});
+
+  await index.add(description1);
+  await index.add(description2);
+
+  // There should be one description.
+  const descriptions = await index.getDescriptions();
+  assert_equals(descriptions.length, 1);
+
+  assert_object_equals(descriptions[0], description2);
+
+}, 'index.add with same ID overwrites existing entry.');
+
+contentIndexTest(async (t, index) => {
+  const description1 = createDescription({id: 'id1'});
+  const description2 = createDescription({id: 'id2'});
+
+  await index.add(description1);
+  await index.add(description2);
+
+  // There should be two descriptions.
+  assert_equals((await index.getDescriptions()).length, 2);
+
+  await index.delete('id1');
+
+  // There should be one description.
+  const descriptions = await index.getDescriptions();
+  assert_equals(descriptions.length, 1);
+
+  assert_object_equals(descriptions[0], description2);
+
+}, 'index.delete removes entry.');
+
+contentIndexTest(async (t, index) => {
+  const descriptions = await index.getDescriptions();
+  assert_equals(descriptions.length, 0);
+
+  await index.delete('id');
+
+}, 'index.delete works on invalid ID.');
+
+</script>
diff --git a/third_party/blink/web_tests/http/tests/content_index/resources.js b/third_party/blink/web_tests/http/tests/content_index/resources.js
new file mode 100644
index 0000000..e66e90b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/content_index/resources.js
@@ -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.
+
+'use strict';
+
+const swUrl = '/resources/empty-worker.js';
+const scope = '/resources/';
+
+// Verifies that |func|, when invoked, throws a TypeError exception.
+async function expectTypeError(func) {
+  try {
+    await func();
+  } catch (e) {
+    assert_equals(e.name, 'TypeError');
+    return;
+  }
+
+  assert_unreached('expected a TypeError, but none was thrown');
+}
+
+async function expectTypeErrorWithMessage(promise, message) {
+  try {
+    await promise;
+    assert_unreached('Promise should have rejected');
+  } catch (e) {
+    assert_equals(e.name, 'TypeError');
+    if (message) {
+      assert_equals(e.message, message, 'Unexpected Error Message:');
+    }
+  }
+}
+
+function createDescription({id = 'id', title = 'title', description = 'description',
+                            category = 'homepage', iconUrl = '/resources/square.png',
+                            launchUrl = '/'}) {
+  return {id, title, description, category, iconUrl, launchUrl};
+}
+
+// Creates a Promise test for |func| given the |description|. The |func| will be
+// executed with the `index` object of an activated Service Worker Registration.
+function contentIndexTest(func, description) {
+  promise_test(async t => {
+    const registration = await service_worker_unregister_and_register(t, swUrl, scope);
+    await wait_for_state(t, registration.installing, 'activated');
+    return func(t, registration.index);
+  }, description);
+}
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-origin-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-origin-a11y-test-expected.txt
new file mode 100644
index 0000000..67299a43
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-origin-a11y-test-expected.txt
@@ -0,0 +1,78 @@
+aXe violations: [
+  {
+    "ruleDescription": "Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds",
+    "helpUrl": "https://dequeuniversity.com/rules/axe/3.0/color-contrast?application=axeAPI",
+    "ruleId": "color-contrast",
+    "impact": "serious",
+    "failedNodes": [
+      {
+        "target": [
+          "span:nth-child(2) > span.url-scheme-separator"
+        ],
+        "html": "<span class=\"url-scheme-separator\">://</span>"
+      },
+      {
+        "target": [
+          "#-blink-dev-tools > div:nth-child(6) > .widget.vbox > div > div > div > div > div > .security-origin-view > div:nth-child(4) > table > div:nth-child(1) > div:nth-child(1)"
+        ],
+        "html": "<div>Protocol</div>"
+      },
+      {
+        "target": [
+          "#-blink-dev-tools > div:nth-child(6) > .widget.vbox > div > div > div > div > div > .security-origin-view > div:nth-child(4) > table > div:nth-child(2) > div:nth-child(1)"
+        ],
+        "html": "<div>Key exchange</div>"
+      },
+      {
+        "target": [
+          "#-blink-dev-tools > div:nth-child(6) > .widget.vbox > div > div > div > div > div > .security-origin-view > div:nth-child(4) > table > div:nth-child(3) > div:nth-child(1)"
+        ],
+        "html": "<div>Cipher</div>"
+      },
+      {
+        "target": [
+          "#-blink-dev-tools > div:nth-child(6) > .widget.vbox > div > div > div > div > div > .security-origin-view > div:nth-child(5) > table > div:nth-child(1) > div:nth-child(1)"
+        ],
+        "html": "<div>Subject</div>"
+      },
+      {
+        "target": [
+          "#-blink-dev-tools > div:nth-child(6) > .widget.vbox > div > div > div > div > div > .security-origin-view > div:nth-child(5) > table > div:nth-child(2) > div:nth-child(1)"
+        ],
+        "html": "<div>SAN</div>"
+      },
+      {
+        "target": [
+          "#-blink-dev-tools > div:nth-child(6) > .widget.vbox > div > div > div > div > div > .security-origin-view > div:nth-child(5) > table > div:nth-child(3) > div:nth-child(1)"
+        ],
+        "html": "<div>Valid from</div>"
+      },
+      {
+        "target": [
+          "#-blink-dev-tools > div:nth-child(6) > .widget.vbox > div > div > div > div > div > .security-origin-view > div:nth-child(5) > table > div:nth-child(4) > div:nth-child(1)"
+        ],
+        "html": "<div>Valid until</div>"
+      },
+      {
+        "target": [
+          "#-blink-dev-tools > div:nth-child(6) > .widget.vbox > div > div > div > div > div > .security-origin-view > div:nth-child(5) > table > div:nth-child(5) > div:nth-child(1)"
+        ],
+        "html": "<div>Issuer</div>"
+      },
+      {
+        "target": [
+          "div:nth-child(6) > div.origin-view-section-notes"
+        ],
+        "html": "<div class=\"origin-view-section-notes\">This request complies with Chrome's Certificate Transparency policy.</div>"
+      },
+      {
+        "target": [
+          ".origin-view-notes > div"
+        ],
+        "html": "<div>The security details above are from the first inspected response.</div>"
+      }
+    ]
+  }
+]
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-origin-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-origin-a11y-test.js
new file mode 100644
index 0000000..65a904f0
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-origin-a11y-test.js
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium 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 () {
+  await TestRunner.loadModule('security_test_runner');
+  await TestRunner.loadModule('axe_core_test_runner');
+  await TestRunner.showPanel('security');
+
+  const request1 = new SDK.NetworkRequest(0, 'https://foo.test/', 'https://foo.test', 0, 0, null);
+  request1.setSecurityState(Protocol.Security.SecurityState.Secure);
+  const securityDetails = {
+    protocol : 'TLS 1.2',
+    keyExchange : 'Key_Exchange',
+    keyExchangeGroup : '',
+    cipher : 'Cypher',
+    mac : 'Mac',
+    subjectName : 'foo.test',
+    sanList : ['foo.test', '*.test'],
+    issuer : 'Super CA',
+    validFrom : 1490000000,
+    validTo : 2000000000,
+    CertificateId : 0,
+    signedCertificateTimestampList : [],
+    certificateTransparencyCompliance : Protocol.Network.CertificateTransparencyCompliance.Compliant
+  };
+
+  request1.setSecurityDetails(securityDetails);
+  SecurityTestRunner.dispatchRequestFinished(request1);
+  const securityPanel = runtime.sharedInstance(Security.SecurityPanel);
+
+  securityPanel.showOrigin("https://foo.test");
+  await AxeCoreTestRunner.runValidation(securityPanel.contentElement);
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-overview-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-overview-a11y-test-expected.txt
new file mode 100644
index 0000000..ee9ecd3
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-overview-a11y-test-expected.txt
@@ -0,0 +1,3 @@
+aXe violations: []
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-overview-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-overview-a11y-test.js
new file mode 100644
index 0000000..fe8d2628
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/security/security-overview-a11y-test.js
@@ -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.
+
+(async function() {
+  await TestRunner.loadModule('security_test_runner');
+  await TestRunner.loadModule('axe_core_test_runner');
+  await TestRunner.showPanel('security');
+
+  // Using an insecure request since the insecure overview page encompasses the most elements.
+  /** @type {!Protocol.Security.InsecureContentStatus} */
+  const insecureContentStatus = {
+    ranMixedContent: false,
+    displayedMixedContent: false,
+    ranContentWithCertErrors: false,
+    displayedContentWithCertErrors: true,
+    ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure,
+    displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral
+  };
+  const pageSecurityState = new Security.PageSecurityState(Protocol.Security.SecurityState.Secure, [], insecureContentStatus, null);
+
+  TestRunner.mainTarget.model(Security.SecurityModel).dispatchEventToListeners(
+    Security.SecurityModel.Events.SecurityStateChanged, pageSecurityState);
+  const request = new SDK.NetworkRequest(0, 'http://foo.test', 'https://foo.test', 0, 0, null);
+  request.setBlockedReason(Protocol.Network.BlockedReason.MixedContent);
+  request.mixedContentType = 'blockable';
+  SecurityTestRunner.dispatchRequestFinished(request);
+  const securityPanel = runtime.sharedInstance(Security.SecurityPanel);
+  await AxeCoreTestRunner.runValidation(securityPanel._mainView.contentElement);
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt
index 7e9e1e91c..15b9d88 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt
@@ -27,6 +27,8 @@
 Background Services
  Background Fetch
  Background Sync
+ Notifications
+ Push Messaging
 Frames
  top
 
@@ -53,6 +55,8 @@
 Background Services
  Background Fetch
  Background Sync
+ Notifications
+ Push Messaging
 Frames
  top
 
@@ -83,6 +87,8 @@
 Background Services
  Background Fetch
  Background Sync
+ Notifications
+ Push Messaging
 Frames
  top
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-on-navigation-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-on-navigation-expected.txt
index ae3f0d0..72af93d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-on-navigation-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-on-navigation-expected.txt
@@ -22,6 +22,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
 Visible view is a query view: true
@@ -47,6 +49,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
 Visible view is a query view: false
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-resource-preview-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-resource-preview-expected.txt
index 22d7554..521eee9 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-resource-preview-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-resource-preview-expected.txt
@@ -20,6 +20,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
 log: visible view: unknown
@@ -43,6 +45,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
     Images
@@ -71,6 +75,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
     Images
@@ -99,6 +105,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
     Images
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload-expected.txt
index 52a52fe..67dc802 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload-expected.txt
@@ -22,6 +22,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
 Selection: ["cookies://http://127.0.0.1:8000","category://Cookies"]
@@ -48,6 +50,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
 Selection: ["cookies://http://127.0.0.1:8000","category://Cookies"]
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-websql-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-websql-expected.txt
index 4cdaa99..0d56326 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-websql-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-websql-expected.txt
@@ -20,6 +20,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
 Found: true
@@ -44,6 +46,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
 Table added:
@@ -68,6 +72,8 @@
 Background Services
   Background Fetch
   Background Sync
+  Notifications
+  Push Messaging
 Frames
   top
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/copy-styles-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/copy-styles-expected.txt
new file mode 100644
index 0000000..5afb8ef5
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/copy-styles-expected.txt
@@ -0,0 +1,42 @@
+Tests node xPath construction
+
+
+<html>​
+InspectorFrontendHost.copyText:
+
+
+<head>​
+InspectorFrontendHost.copyText:
+
+
+<style>​
+InspectorFrontendHost.copyText:
+
+
+      button {
+        color: red;
+      }
+      body {
+        border: 1px solid black;
+        font-weight: bold;
+      }
+    
+
+<body>​
+InspectorFrontendHost.copyText:
+border: 1px solid black;
+font-weight: bold;
+
+<div style=​"padding:​ 5px">​ Hello ​</div>​
+InspectorFrontendHost.copyText:
+font-weight: bold;
+padding: 5px;
+
+<button>​ A red button ​</button>​
+InspectorFrontendHost.copyText:
+color: red;
+
+<button style=​"color:​ green">​ A green button ​</button>​
+InspectorFrontendHost.copyText:
+color: green;
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/copy-styles.js b/third_party/blink/web_tests/http/tests/devtools/elements/copy-styles.js
new file mode 100644
index 0000000..8fb585a
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/copy-styles.js
@@ -0,0 +1,49 @@
+// 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 node xPath construction\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+
+  await TestRunner.loadHTML(`
+    <style>
+      button {
+        color: red;
+      }
+      body {
+        border: 1px solid black;
+        font-weight: bold;
+      }
+    </style>
+    <div style="padding: 5px"> Hello </div>
+    <button> A red button </button>
+    <button style="color: green"> A green button </button>
+
+  `);
+  InspectorFrontendHost.copyText = text => {
+    TestRunner.addResult('InspectorFrontendHost.copyText:\n' + text)
+  };
+
+  await new Promise(x => ElementsTestRunner.expandElementsTree(x));
+
+
+  const element = ElementsTestRunner.firstElementsTreeOutline().rootElement();
+
+  await processElement(element);
+
+  TestRunner.completeTest();
+
+  async function processElement(element) {
+    if (element instanceof Elements.ElementsTreeElement && !element.isClosingTag() && element.node().nodeNameInCorrectCase() !== 'base') {
+      TestRunner.addResult('\n' + element.listItemElement.textContent);
+      await element._copyStyles();
+    }
+    for (const child of element.children()) {
+      await processElement(child);
+    }
+  }
+
+
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/node-xpath-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/node-xpath-expected.txt
index fec5d5fe..48e43b1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/node-xpath-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/node-xpath-expected.txt
@@ -1,25 +1,25 @@
 Tests node xPath construction
 
-'#document':'' - '/'
-  '#comment':' Pre-comment ' - '/comment()[1]'
-  'html':'' - '/html'
-    'head':'' - '/html/head'
-      'script':'' - '/html/head/script'
-        '#text':'\n// Comment\n//' - '/html/head/script/text()[1]'
-        '#cdata-section':'\nfunction f()\n{\n    document.write("<");\n}\n//' - '/html/head/script/text()[2]'
-    'body':'' - '/html/body'
-      'div':'' - '//*[@id="id1"]'
-      'div':'' - '//*[@id="id2"]'
-      'div':'' - '//*[@id="container"]'
-        'div':'' - '//*[@id="id3"]'
-          '#text':'3 Prefix ' - '//*[@id="id3"]/text()[1]'
-          '#cdata-section':'<greeting>Hello, world!</greeting>' - '//*[@id="id3"]/text()[2]'
-          '#text':' Suffix' - '//*[@id="id3"]/text()[3]'
-        'div':'' - '//*[@id="id4"]'
-          '#text':'4' - '//*[@id="id4"]/text()'
-        'div':'' - '//*[@id="id5"]'
-          '#text':'5' - '//*[@id="id5"]/text()'
-        'div':'' - '//*[@id="id6"]'
-          '#text':'6' - '//*[@id="id6"]/text()'
-  '#comment':' Post-comment ' - '/comment()[2]'
+'#document':'' - '/' - '/'
+  '#comment':' Pre-comment ' - '/comment()[1]' - '/comment()[1]'
+  'html':'' - '/html' - '/html'
+    'head':'' - '/html/head' - '/html/head'
+      'script':'' - '/html/head/script' - '/html/head/script'
+        '#text':'\n// Comment\n//' - '/html/head/script/text()[1]' - '/html/head/script/text()[1]'
+        '#cdata-section':'\nfunction f()\n{\n    document.write("<");\n}\n//' - '/html/head/script/text()[2]' - '/html/head/script/text()[2]'
+    'body':'' - '/html/body' - '/html/body'
+      'div':'' - '//*[@id="id1"]' - '/html/body/div[1]'
+      'div':'' - '//*[@id="id2"]' - '/html/body/div[2]'
+      'div':'' - '//*[@id="container"]' - '/html/body/div[3]'
+        'div':'' - '//*[@id="id3"]' - '/html/body/div[3]/div[1]'
+          '#text':'3 Prefix ' - '//*[@id="id3"]/text()[1]' - '/html/body/div[3]/div[1]/text()[1]'
+          '#cdata-section':'<greeting>Hello, world!</greeting>' - '//*[@id="id3"]/text()[2]' - '/html/body/div[3]/div[1]/text()[2]'
+          '#text':' Suffix' - '//*[@id="id3"]/text()[3]' - '/html/body/div[3]/div[1]/text()[3]'
+        'div':'' - '//*[@id="id4"]' - '/html/body/div[3]/div[2]'
+          '#text':'4' - '//*[@id="id4"]/text()' - '/html/body/div[3]/div[2]/text()'
+        'div':'' - '//*[@id="id5"]' - '/html/body/div[3]/div[3]'
+          '#text':'5' - '//*[@id="id5"]/text()' - '/html/body/div[3]/div[3]/text()'
+        'div':'' - '//*[@id="id6"]' - '/html/body/div[3]/div[4]'
+          '#text':'6' - '//*[@id="id6"]/text()' - '/html/body/div[3]/div[4]/text()'
+  '#comment':' Post-comment ' - '/comment()[2]' - '/comment()[2]'
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/node-xpath.js b/third_party/blink/web_tests/http/tests/devtools/elements/node-xpath.js
index 3e5ae079..4ab25ca 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/node-xpath.js
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/node-xpath.js
@@ -36,7 +36,8 @@
 
   function dumpNodeData(node, prefix) {
     var result = prefix + '\'' + node.nodeName() + '\':\'' + node.nodeValue() + '\' - \'' +
-        Elements.DOMPath.xPath(node, true) + '\'';
+        Elements.DOMPath.xPath(node, true) + '\' - \'' +
+        Elements.DOMPath.xPath(node, false) + '\'';
     TestRunner.addResult(result.replace(/\r?\n/g, '\\n'));
   }
 })();
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/blocked-mixed-content-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/blocked-mixed-content-expected.txt
index ced5f44..c682b8eb 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/blocked-mixed-content-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/blocked-mixed-content-expected.txt
@@ -10,7 +10,7 @@
         <DIV >
 Your page requested non-secure resources that were blocked.
         </DIV>
-        <DIV class=security-mixed-content link >
+        <DIV class=security-mixed-content devtools-link role=link tabindex=0 >
 View 1 request in Network Panel
         </DIV>
     </DIV>
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-active-and-passive-reload-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-active-and-passive-reload-expected.txt
index b34a37717..9924e1d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-active-and-passive-reload-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-active-and-passive-reload-expected.txt
@@ -44,7 +44,7 @@
         <DIV >
 Neutral Test Description
         </DIV>
-        <DIV class=security-mixed-content link >
+        <DIV class=security-mixed-content devtools-link role=link tabindex=0 >
 View 1 request in Network Panel
         </DIV>
     </DIV>
@@ -59,7 +59,7 @@
         <DIV >
 Insecure Test Description
         </DIV>
-        <DIV class=security-mixed-content link >
+        <DIV class=security-mixed-content devtools-link role=link tabindex=0 >
 View 1 request in Network Panel
         </DIV>
     </DIV>
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-reload-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-reload-expected.txt
index c0dd7e2..fa5ddbb 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-reload-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-reload-expected.txt
@@ -29,7 +29,7 @@
         <DIV >
 Neutral Test Description
         </DIV>
-        <DIV class=security-mixed-content link >
+        <DIV class=security-mixed-content devtools-link role=link tabindex=0 >
 View 1 request in Network Panel
         </DIV>
     </DIV>
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/origin-view-ct-compliance-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/origin-view-ct-compliance-expected.txt
index 54da9805..aa2aca3 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/origin-view-ct-compliance-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/origin-view-ct-compliance-expected.txt
@@ -7,7 +7,7 @@
     <STYLE type=text/css >
     </STYLE>
     <DIV class=title-section >
-        <DIV class=title-section-header >
+        <DIV class=title-section-header role=heading aria-level=1 >
 Origin
         </DIV>
         <DIV class=origin-display >
@@ -26,13 +26,13 @@
             </SPAN>
         </DIV>
         <DIV class=view-network-button >
-            <BUTTON class=origin-button text-button type=button >
+            <BUTTON class=origin-button text-button type=button role=link >
 View requests in Network Panel
             </BUTTON>
         </DIV>
     </DIV>
     <DIV class=origin-view-section >
-        <DIV class=origin-view-section-title >
+        <DIV class=origin-view-section-title role=heading aria-level=2 >
 Connection
         </DIV>
         <TABLE class=details-table >
@@ -63,7 +63,7 @@
         </TABLE>
     </DIV>
     <DIV class=origin-view-section >
-        <DIV class=origin-view-section-title >
+        <DIV class=origin-view-section-title role=heading aria-level=2 >
 Certificate
         </DIV>
         <TABLE class=details-table >
@@ -118,7 +118,7 @@
                 <DIV >
                 </DIV>
                 <DIV >
-                    <BUTTON class=origin-button text-button type=button >
+                    <BUTTON class=origin-button text-button type=button role=button aria-haspopup=true >
 Open full certificate details
                     </BUTTON>
                 </DIV>
@@ -126,7 +126,7 @@
         </TABLE>
     </DIV>
     <DIV class=origin-view-section >
-        <DIV class=origin-view-section-title >
+        <DIV class=origin-view-section-title role=heading aria-level=2 >
 Certificate Transparency
         </DIV>
         <TABLE class=details-table sct-summary >
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/origin-view-noncryptographic-secure-origin-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/origin-view-noncryptographic-secure-origin-expected.txt
index caba73a..acbd532 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/origin-view-noncryptographic-secure-origin-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/origin-view-noncryptographic-secure-origin-expected.txt
@@ -7,7 +7,7 @@
     <STYLE type=text/css >
     </STYLE>
     <DIV class=title-section >
-        <DIV class=title-section-header >
+        <DIV class=title-section-header role=heading aria-level=1 >
 Origin
         </DIV>
         <DIV class=origin-display >
@@ -26,13 +26,13 @@
             </SPAN>
         </DIV>
         <DIV class=view-network-button >
-            <BUTTON class=origin-button text-button type=button >
+            <BUTTON class=origin-button text-button type=button role=link >
 View requests in Network Panel
             </BUTTON>
         </DIV>
     </DIV>
     <DIV class=origin-view-section >
-        <DIV class=origin-view-section-title >
+        <DIV class=origin-view-section-title role=heading aria-level=2 >
 Secure
         </DIV>
         <DIV >
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/origin-view-then-interstitial-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/origin-view-then-interstitial-expected.txt
index a0982a65..d3ee9c1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/origin-view-then-interstitial-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/origin-view-then-interstitial-expected.txt
@@ -3,7 +3,7 @@
 Before selecting origin view:
 <DIV class=widget vbox security-main-view >
     <DIV class=security-summary >
-        <DIV class=security-summary-section-title >
+        <DIV class=security-summary-section-title role=heading aria-level=1 >
 Security overview
         </DIV>
         <DIV class=lock-spectrum >
@@ -20,7 +20,7 @@
                 </DIV>
             </DIV>
         </DIV>
-        <DIV class=security-summary-text >
+        <DIV class=security-summary-text role=heading aria-level=2 >
         </DIV>
     </DIV>
     <DIV class=security-explanation-list security-explanations-main >
@@ -35,7 +35,7 @@
     <STYLE type=text/css >
     </STYLE>
     <DIV class=title-section >
-        <DIV class=title-section-header >
+        <DIV class=title-section-header role=heading aria-level=1 >
 Origin
         </DIV>
         <DIV class=origin-display >
@@ -54,13 +54,13 @@
             </SPAN>
         </DIV>
         <DIV class=view-network-button >
-            <BUTTON class=origin-button text-button type=button >
+            <BUTTON class=origin-button text-button type=button role=link >
 View requests in Network Panel
             </BUTTON>
         </DIV>
     </DIV>
     <DIV class=origin-view-section >
-        <DIV class=origin-view-section-title >
+        <DIV class=origin-view-section-title role=heading aria-level=2 >
 Not secure
         </DIV>
         <DIV >
@@ -71,7 +71,7 @@
 After interstitial is shown:
 <DIV class=widget vbox security-main-view >
     <DIV class=security-summary >
-        <DIV class=security-summary-section-title >
+        <DIV class=security-summary-section-title role=heading aria-level=1 >
 Security overview
         </DIV>
         <DIV class=lock-spectrum >
@@ -88,7 +88,7 @@
                 </DIV>
             </DIV>
         </DIV>
-        <DIV class=security-summary-text >
+        <DIV class=security-summary-text role=heading aria-level=2 >
         </DIV>
     </DIV>
     <DIV class=security-explanation-list security-explanations-main >
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/security-blocked-mixed-content-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/security-blocked-mixed-content-expected.txt
index ced5f44..c682b8eb 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/security-blocked-mixed-content-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/security-blocked-mixed-content-expected.txt
@@ -10,7 +10,7 @@
         <DIV >
 Your page requested non-secure resources that were blocked.
         </DIV>
-        <DIV class=security-mixed-content link >
+        <DIV class=security-mixed-content devtools-link role=link tabindex=0 >
 View 1 request in Network Panel
         </DIV>
     </DIV>
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/security-details-updated-with-security-state-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/security-details-updated-with-security-state-expected.txt
index efa79d5..bf2aa7e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/security-details-updated-with-security-state-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/security-details-updated-with-security-state-expected.txt
@@ -51,7 +51,7 @@
     <STYLE type=text/css >
     </STYLE>
     <DIV class=title-section >
-        <DIV class=title-section-header >
+        <DIV class=title-section-header role=heading aria-level=1 >
 Origin
         </DIV>
         <DIV class=origin-display >
@@ -70,13 +70,13 @@
             </SPAN>
         </DIV>
         <DIV class=view-network-button >
-            <BUTTON class=origin-button text-button type=button >
+            <BUTTON class=origin-button text-button type=button role=link >
 View requests in Network Panel
             </BUTTON>
         </DIV>
     </DIV>
     <DIV class=origin-view-section >
-        <DIV class=origin-view-section-title >
+        <DIV class=origin-view-section-title role=heading aria-level=2 >
 Connection
         </DIV>
         <TABLE class=details-table >
@@ -107,7 +107,7 @@
         </TABLE>
     </DIV>
     <DIV class=origin-view-section >
-        <DIV class=origin-view-section-title >
+        <DIV class=origin-view-section-title role=heading aria-level=2 >
 Certificate
         </DIV>
         <TABLE class=details-table >
@@ -162,7 +162,7 @@
                 <DIV >
                 </DIV>
                 <DIV >
-                    <BUTTON class=origin-button text-button type=button >
+                    <BUTTON class=origin-button text-button type=button role=button aria-haspopup=true >
 Open full certificate details
                     </BUTTON>
                 </DIV>
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/security-explanation-ordering-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/security-explanation-ordering-expected.txt
index 56baad85..b77c3c3f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/security-explanation-ordering-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/security-explanation-ordering-expected.txt
@@ -10,7 +10,7 @@
         <DIV >
 The connection to this site is using a valid, trusted server certificate.
         </DIV>
-        <BUTTON class=security-certificate-button text-button type=button >
+        <BUTTON class=security-certificate-button text-button type=button role=button aria-haspopup=true >
 View certificate
         </BUTTON>
     </DIV>
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/security-summary-expected.txt b/third_party/blink/web_tests/http/tests/devtools/security/security-summary-expected.txt
index 24c6aac9..795e20bc 100644
--- a/third_party/blink/web_tests/http/tests/devtools/security/security-summary-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/security/security-summary-expected.txt
@@ -1,6 +1,6 @@
 Tests specifying a security summary for the Security panel overview.
 
-<DIV class=security-summary-text >
+<DIV class=security-summary-text role=heading aria-level=2 >
 Test: Summary Override Text
 </DIV>
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-navigation-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-navigation-expected.txt
index 8afcedea..3a89c90 100644
--- a/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-navigation-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-navigation-expected.txt
@@ -1,6 +1,6 @@
-Tests that Web Inspector will discard console message arguments and convert first argument into a string when iframe where the message was logged is navigated to a different page.
+Tests that Web Inspector will get the console message's all arguments.
 
-Message: "A message with first argument string", arguments: [string]
-Message: "2011", arguments: [string]
-Message: "[object Window]", arguments: [string]
+Message: "A message with first argument string Second argument which should not be discarded", arguments: [string]
+Message: "2011 A message with first argument integer", arguments: [string]
+Message: "[object Window] A message with first argument window", arguments: [string]
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-navigation.js b/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-navigation.js
index 6c436068..5fedd8e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-navigation.js
+++ b/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-navigation.js
@@ -5,7 +5,7 @@
 (async function() {
   await TestRunner.setupStartupTest('resources/console-clear-arguments-on-frame-navigation.html');
   TestRunner.addResult(
-      `Tests that Web Inspector will discard console message arguments and convert first argument into a string when iframe where the message was logged is navigated to a different page.\n`);
+      `Tests that Web Inspector will get the console message's all arguments.\n`);
   await TestRunner.loadModule('console_test_runner');
 
   for (var message of SDK.consoleModel.messages()) {
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-remove-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-remove-expected.txt
index 713da739..e3c7440 100644
--- a/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-remove-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-remove-expected.txt
@@ -1,6 +1,6 @@
-Tests that console message arguments will be cleared and first argument will be converted into a string when iframe where the messages were created is removed.
+Tests that console message arguments will be cleared and all arguments will be converted into a string when iframe where the messages were created is removed.
 
-Message: "A message with first argument string", arguments: [string]
-Message: "2011", arguments: [string]
-Message: "[object Window]", arguments: [string]
+Message: "A message with first argument string Second argument which should not be discarded", arguments: [string]
+Message: "2011 A message with first argument integer", arguments: [string]
+Message: "[object Window] A message with first argument window", arguments: [string]
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-remove.js b/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-remove.js
index 0c67680..b1593791 100644
--- a/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-remove.js
+++ b/third_party/blink/web_tests/http/tests/devtools/startup/console-clear-arguments-on-frame-remove.js
@@ -5,7 +5,7 @@
 (async function() {
   await TestRunner.setupStartupTest('resources/console-clear-arguments-on-frame-remove.html');
   TestRunner.addResult(
-      `Tests that console message arguments will be cleared and first argument will be converted into a string when iframe where the messages were created is removed.\n`);
+      `Tests that console message arguments will be cleared and all arguments will be converted into a string when iframe where the messages were created is removed.\n`);
   await TestRunner.loadModule('console_test_runner');
 
   for (var message of SDK.consoleModel.messages()) {
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/console-log-before-frame-navigation-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/console-log-before-frame-navigation-expected.txt
index fb8fba96..e46a8b8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/startup/console-log-before-frame-navigation-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/startup/console-log-before-frame-navigation-expected.txt
@@ -6,7 +6,7 @@
 Message: console-log-frame-after-navigation.html:4 Cross-process frame loaded
 Message: console-log-frame-after-navigation.html:5 Sending message to parent
 Message: console-log-frame-before-navigation.html:4 Frame loaded
-Message: console-log-frame-before-navigation.html:5 Console message (C) %d with element
+Message: console-log-frame-before-navigation.html:5 Console message (C) %d with element 2010 [object HTMLHtmlElement]
 Message: console-log-frame-before-navigation.html:8 Navigating frame cross-process
 TEST COMPLETE.
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/resources/console-clear-arguments-iframe.html b/third_party/blink/web_tests/http/tests/devtools/startup/resources/console-clear-arguments-iframe.html
index 31d0a79..f9bf5d88 100644
--- a/third_party/blink/web_tests/http/tests/devtools/startup/resources/console-clear-arguments-iframe.html
+++ b/third_party/blink/web_tests/http/tests/devtools/startup/resources/console-clear-arguments-iframe.html
@@ -1,5 +1,5 @@
 <script>
-console.log("A message with first argument string", "Second argument which should be discarded on iframe removal");
+console.log("A message with first argument string", "Second argument which should not be discarded");
 console.log(2011, "A message with first argument integer");
 console.log(window, "A message with first argument window");
 </script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/unit/object-properties-expand-recursively-expected.txt b/third_party/blink/web_tests/http/tests/devtools/unit/object-properties-expand-recursively-expected.txt
new file mode 100644
index 0000000..42084cc1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/unit/object-properties-expand-recursively-expected.txt
@@ -0,0 +1,13 @@
+Test that ObjectPropertiesSection expands recursively.
+
+JSON
+    foo: {bar: {baz: {quux: {corge: "plugh"}}}, quuz: {garply: "xyzzy", thud: {wibble: "wobble"}}}
+        bar: {baz: {quux: {corge: "plugh"}}}
+            baz: {quux: {corge: "plugh"}}
+                quux: {corge: "plugh"}
+                    corge: "plugh"
+        quuz: {garply: "xyzzy", thud: {wibble: "wobble"}}
+            garply: "xyzzy"
+            thud: {wibble: "wobble"}
+                wibble: "wobble"
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/unit/object-properties-expand-recursively.js b/third_party/blink/web_tests/http/tests/devtools/unit/object-properties-expand-recursively.js
new file mode 100644
index 0000000..dbf2e4f
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/unit/object-properties-expand-recursively.js
@@ -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.
+
+(async function() {
+  TestRunner.addResult(`Test that ObjectPropertiesSection expands recursively.\n`);
+  await TestRunner.loadModule('object_ui');
+
+  var object = {
+    "foo": {
+      "bar": {
+        "baz": {
+          "quux": {
+            "corge": "plugh"
+          }
+        }
+      },
+
+      "quuz": {
+        "garply": "xyzzy",
+        "thud": {
+          "wibble": "wobble"
+        }
+      }
+    }
+  }
+
+  var localObject = SDK.RemoteObject.fromLocalObject(object);
+  var propertiesSection = new ObjectUI.ObjectPropertiesSection(localObject, 'JSON');
+  await propertiesSection.objectTreeElement().expandRecursively();
+
+  TestRunner.addResult(TestRunner.textContentWithLineBreaks(propertiesSection.element));
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
index 256193a..7aebd890 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
@@ -316,25 +316,23 @@
   }
 
   async evaluate(code, ...args) {
-    if (typeof code === 'function') {
-      var argsString = args.map(JSON.stringify.bind(JSON)).join(', ');
-      code = `(${code.toString()})(${argsString})`;
-    }
-    var response = await this.protocol.Runtime.evaluate({expression: code, returnByValue: true});
-    if (response.error || response.result.exceptionDetails) {
-      this._testRunner.log(`Error while evaluating '${code}': ${JSON.stringify(response.error || response.result.exceptionDetails)}`);
-      this._testRunner.completeTest();
-    } else {
-      return response.result.result.value;
-    }
+    return this._innerEvaluate(false /* awaitPromise */, false /* userGesture */, code, ...args);
   }
 
   async evaluateAsync(code, ...args) {
+    return this._innerEvaluate(true /* awaitPromise */, false /* userGesture */, code, ...args);
+  }
+
+  async evaluateAsyncWithUserGesture(code, ...args) {
+    return this._innerEvaluate(true /* awaitPromise */, true /* userGesture */, code, ...args);
+  }
+
+  async _innerEvaluate(awaitPromise, userGesture, code, ...args) {
     if (typeof code === 'function') {
       var argsString = args.map(JSON.stringify.bind(JSON)).join(', ');
       code = `(${code.toString()})(${argsString})`;
     }
-    var response = await this.protocol.Runtime.evaluate({expression: code, returnByValue: true, awaitPromise: true});
+    var response = await this.protocol.Runtime.evaluate({expression: code, returnByValue: true, awaitPromise, userGesture});
     if (response.error) {
       this._testRunner.log(`Error while evaluating async '${code}': ${response.error}`);
       this._testRunner.completeTest();
diff --git a/third_party/blink/web_tests/http/tests/lazyload/invisible-image.html b/third_party/blink/web_tests/http/tests/lazyload/invisible-image.html
new file mode 100644
index 0000000..6d8b8b3
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/lazyload/invisible-image.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="placeholder.js"></script>
+
+<body>
+  <div style="height:10000px;"></div>
+  <img id="visibility_hidden" style="visibility:hidden;" src='../loading/resources/base-image1.png'>
+  <img id="display_none" style="display:none;" src='../loading/resources/base-image2.png'>
+  <img id="attribute_hidden" hidden src='../loading/resources/base-image3.png'>
+  <img id="js_hidden" src='../loading/resources/dup-image1.png'>
+  <script>
+    document.getElementById("js_hidden").style = 'display:none;';
+  </script>
+</body>
+
+<script>
+  var visibility_hidden_element = document.getElementById("visibility_hidden");
+  var display_none_element = document.getElementById("display_none");
+  var attribute_hidden_element = document.getElementById("attribute_hidden");
+  var js_hidden_element = document.getElementById("js_hidden");
+  async_test(function(t) {
+    window.addEventListener("load", t.step_func_done());
+  }, "Test that document load event is fired");
+  async_test(function(t) {
+    let image_fully_loaded_promise = (element) => {
+      return new Promise(resolve => {
+        element.addEventListener("load",
+          t.step_func(() => {
+            assert_true(is_image_fully_loaded(element));
+            resolve();
+        }));
+      });
+    }
+    Promise.all([image_fully_loaded_promise(visibility_hidden_element),
+      image_fully_loaded_promise(display_none_element),
+      image_fully_loaded_promise(attribute_hidden_element),
+      image_fully_loaded_promise(js_hidden_element)]).then(() => {
+        t.done();
+    });
+  }, "Test that invisible images are loaded");
+</script>
+
diff --git a/third_party/blink/web_tests/http/tests/permissions/test-midi-sysex-insecure-origin.html b/third_party/blink/web_tests/http/tests/permissions/test-midi-sysex-insecure-origin.html
deleted file mode 100644
index dae68a3..0000000
--- a/third_party/blink/web_tests/http/tests/permissions/test-midi-sysex-insecure-origin.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Permissions API: test midi sysex on insecure origins.</title>
-<script src='../resources/testharness.js'></script>
-<script src='../resources/testharnessreport.js'></script>
-<script src='../resources/get-host-info.js'></script>
-</head>
-<body>
-<script>
-// Midi SysEx is not available on insecure origins. This is testing that the
-// Permissions API matches navigator.requestMIDIAccess() behaviour.
-// In the context of Chromium's LayoutTests, this test is a dummy test because
-// the Permissions API and requestMIDIAccess calls are using a mock
-// implementation of the backend. However, the test can be used in different
-// context.
-if (window.location.origin != get_host_info().UNAUTHENTICATED_ORIGIN) {
-    window.location = get_host_info().UNAUTHENTICATED_ORIGIN + window.location.pathname;
-} else {
-  async_test(function() {
-    navigator.requestMIDIAccess({sysex:true}).then(this.step_func(function() {
-      assert_unreached('requesting midi access should fail');
-      this.done();
-    })).catch(this.step_func(function(e) {
-      assert_equals(e.name, 'SecurityError')
-      return navigator.permissions.query({name:'midi', sysex:true});
-    })).then(this.step_func(function(p) {
-      assert_equals(p.state, "denied");
-      this.done();
-    }));
-  }, 'requesting midi access and querying them should both return deny.');
-}
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/powerfulFeatureRestrictions/midi-on-insecure-origin.html b/third_party/blink/web_tests/http/tests/security/powerfulFeatureRestrictions/midi-on-insecure-origin.html
index 7ca0ed9f..1baaa4ba5 100644
--- a/third_party/blink/web_tests/http/tests/security/powerfulFeatureRestrictions/midi-on-insecure-origin.html
+++ b/third_party/blink/web_tests/http/tests/security/powerfulFeatureRestrictions/midi-on-insecure-origin.html
@@ -7,12 +7,8 @@
 if (window.location.origin != get_host_info().UNAUTHENTICATED_ORIGIN) {
     window.location = get_host_info().UNAUTHENTICATED_ORIGIN + window.location.pathname;
 } else {
-  promise_test(() => {
-    return navigator.requestMIDIAccess().then(() => {
-      assert_unreached('requestMIDIAccess should fail, but succeeded.');
-    }, error => {
-      assert_equals(error.message, 'An attempt was made to break through the security policy of the user agent.');
-    });
-  }, 'requestMIDIAccess on insecure origin');
+  test(t => {
+    assert_false('requestMIDIAccess' in navigator, 'navigator.requestMIDIAccess should not be present');
+  }, 'navigator.requestMIDIAccess requires a secure context');
 }
 </script>
diff --git a/third_party/blink/web_tests/http/tests/security/webgl-remote-read-remote-image-blocked-no-crossorigin.html b/third_party/blink/web_tests/http/tests/security/webgl-remote-read-remote-image-blocked-no-crossorigin.html
index b9b4eb80..a86c0f1 100644
--- a/third_party/blink/web_tests/http/tests/security/webgl-remote-read-remote-image-blocked-no-crossorigin.html
+++ b/third_party/blink/web_tests/http/tests/security/webgl-remote-read-remote-image-blocked-no-crossorigin.html
@@ -114,6 +114,7 @@
 testVideo = function ()
 {
     var video = document.createElement('video');
+    video.preload = "auto";
     video.oncanplay = testResource.bind(null, video, "video", finishUp);
     // No crossOrigin set here either.
     var name = "../../media/resources/test.ogv";
diff --git a/third_party/blink/web_tests/http/tests/shapedetection/shapedetection-cross-origin.html b/third_party/blink/web_tests/http/tests/shapedetection/shapedetection-cross-origin.html
index 593b06d1..16956f79 100644
--- a/third_party/blink/web_tests/http/tests/shapedetection/shapedetection-cross-origin.html
+++ b/third_party/blink/web_tests/http/tests/shapedetection/shapedetection-cross-origin.html
@@ -7,51 +7,74 @@
 const IMAGE_URL = "http://localhost:8080/security/resources/abe.png";
 const VIDEO_URL = "http://localhost:8080/external/wpt/media/white.webm";
 
+// Returns a Promise that is resolve()d if detect() is rejected. Needs an input
+// |element| (e.g. an HTMLImageElement or HTMLVideoElement) and a |url| to load.
+function detectTextOnElementAndExpectError(element, url) {
+  return new Promise(function(resolve, reject) {
+    var tryTextDetection = function() {
+      var textDetector = new TextDetector();
+      textDetector.detect(element)
+          .then(textDetectionResult => {
+            reject("Promise should have been rejected.");
+          })
+          .catch(error => {
+            resolve(error);
+          });
+    };
+    element.onload = tryTextDetection;
+    element.onerror = tryTextDetection;
+    element.src = url;
+  });
+}
+
+function detectTextOnImageBitmapAndExpectError(imageUrl) {
+  return new Promise(function(resolve, reject) {
+    var image = new Image();
+    image.onload = function() {
+      createImageBitmap(image)
+          .then(imageBitmap => {
+            var textDetector = new TextDetector();
+            return textDetector.detect(imageBitmap);
+          })
+          .then(textDetectionResult => {
+            reject("Promise should have been rejected.");
+          })
+          .catch(error => {
+            resolve(error);
+          });
+    };
+    image.onerror = () => {};  // Explicitly ignore expected error events.
+    image.src = imageUrl;
+  });
+}
+
 // Verifies that TextDetector rejects a cross-origin HTMLImageElement.
-promise_test(async t => {
-  const img = new Image();
-  const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
-  img.src = IMAGE_URL;
-  await imgWatcher.wait_for("load");
-  const detector = new TextDetector();
-  await promise_rejects(t, "SecurityError", detector.detect(img));
+promise_test(function(t) {
+  var image = new Image();
+  return detectTextOnElementAndExpectError(image, IMAGE_URL)
+      .then(error => {
+        assert_equals(error.name, "SecurityError");
+      });
 },
 "TextDetector should reject cross-origin HTMLImageElements with a SecurityError.");
 
 // Verifies that TextDetector rejects a cross-origin ImageBitmap.
-promise_test(async t => {
-  const img = new Image();
-  const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
-  img.src = IMAGE_URL;
-  await imgWatcher.wait_for("load");
-  const imgBitmap = await createImageBitmap(img);
-  const detector = new TextDetector();
-  await promise_rejects(t, "SecurityError", detector.detect(imgBitmap));
+promise_test(function(t) {
+  return detectTextOnImageBitmapAndExpectError(IMAGE_URL)
+      .then(error => {
+        assert_equals(error.name, "SecurityError");
+      });
 },
 "TextDetector should reject cross-origin ImageBitmaps with a SecurityError.");
 
 // Verifies that TextDetector rejects a cross-origin HTMLVideoElement.
-promise_test(async t => {
-  const video = document.createElement('video');
-  const videoWatcher = new EventWatcher(t, video, ["load", "error"]);
-  video.src = VIDEO_URL;
-  await videoWatcher.wait_for("error");
-  const detector = new TextDetector();
-  await promise_rejects(t, "SecurityError", detector.detect(video));
+promise_test(function(t) {
+  var video = document.createElement('video');
+  return detectTextOnElementAndExpectError(video, VIDEO_URL)
+      .then(error => {
+        assert_equals(error.name, "SecurityError");
+      });
 },
 "TextDetector should reject cross-origin HTMLVideoElements with a SecurityError.");
 
-// Verifies that TextDetector rejects a cross-origin HTMLCanvasElement.
-promise_test(async t => {
-  const img = new Image();
-  const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
-  img.src = IMAGE_URL;
-  await imgWatcher.wait_for("load");
-  const canvas = document.createElement("canvas");
-  canvas.getContext("2d").drawImage(img, 0, 0);
-  const detector = new TextDetector();
-  await promise_rejects(t, "SecurityError", detector.detect(canvas));
-},
-"TextDetector should reject cross-origin HTMLCanvasElements with a SecurityError.");
-
 </script>
diff --git a/third_party/blink/web_tests/http/tests/std-toast/roles-expected.txt b/third_party/blink/web_tests/http/tests/std-toast/roles-expected.txt
new file mode 100644
index 0000000..0e730e4
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/std-toast/roles-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS the toast has a status role
+FAIL the toast has no role attribute assert_false: expected false got true
+PASS the toast has manually set role
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/http/tests/std-toast/roles.html b/third_party/blink/web_tests/http/tests/std-toast/roles.html
new file mode 100644
index 0000000..9c5a8d90
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/std-toast/roles.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+
+<script type="module">
+import "std:elements/toast";
+</script>
+
+<std-toast id="test-toast" open>
+        Hello World!
+ </std-toast>
+
+<script type="module">
+
+test(t => {
+    assert_own_property(window, 'accessibilityController', 'Require accessibilityController');
+
+    const toast = accessibilityController.accessibleElementById('test-toast');
+
+    assert_not_equals(toast, undefined);
+    assert_equals(toast.role, "AXRole: AXStatus");
+}, 'the toast has a status role');
+
+test(t => {
+    const toast = document.querySelector('#test-toast');
+    assert_false(toast.hasAttribute('role'));
+}, 'the toast has no role attribute'); // WARNING: this should fail until elementInternals API is released
+
+test(t => {
+    const toast = document.createElement('std-toast');
+    toast.setAttribute('role', 'button');
+    document.body.appendChild(toast);
+
+    assert_equals(toast.getAttribute('role'), 'button');
+}, 'the toast has manually set role');
+
+</script>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/inspector-protocol/page/set-intercept-file-chooser-dialog-expected.txt b/third_party/blink/web_tests/inspector-protocol/page/set-intercept-file-chooser-dialog-expected.txt
new file mode 100644
index 0000000..3037081e
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/page/set-intercept-file-chooser-dialog-expected.txt
@@ -0,0 +1,61 @@
+Tests that Page.setInterceptFileChooserDialog works as expected
+
+Running test: testAcceptFile
+file chooser mode: selectSingle
+selected files: ["set-intercept-file-chooser-dialog.js"]
+
+Running test: testAcceptMultipleFiles
+file chooser mode: selectMultiple
+selected files: ["set-intercept-file-chooser-dialog.js","set-intercept-file-chooser-dialog-expected.txt"]
+
+Running test: testResetInput
+first selected files: ["set-intercept-file-chooser-dialog.js"]
+second selected files: []
+
+Running test: testErrors
+Try handling non-existing file chooser.
+{
+    error : {
+        code : -32000
+        message : No pending file chooser
+    }
+    id : <number>
+    sessionId : <string>
+}
+Try enabling file interception in multiclient
+{
+    error : {
+        code : -32000
+        message : Cannot enable file chooser interception because other protocol client already intercepts it
+    }
+    id : <number>
+    sessionId : <string>
+}
+Test file chooser fails when accepting multiple files for a non-multiple file chooser
+{
+    error : {
+        code : -32000
+        message : Expected to accept a single file
+    }
+    id : <number>
+    sessionId : <string>
+}
+Test wrong action
+{
+    error : {
+        code : -32602
+        message : Unknown action 'badaction'
+    }
+    id : <number>
+    sessionId : <string>
+}
+Test trying to handle already-handled file chooser
+{
+    error : {
+        code : -32000
+        message : No pending file chooser
+    }
+    id : <number>
+    sessionId : <string>
+}
+
diff --git a/third_party/blink/web_tests/inspector-protocol/page/set-intercept-file-chooser-dialog.js b/third_party/blink/web_tests/inspector-protocol/page/set-intercept-file-chooser-dialog.js
new file mode 100644
index 0000000..7520146
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/page/set-intercept-file-chooser-dialog.js
@@ -0,0 +1,129 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      'Tests that Page.setInterceptFileChooserDialog works as expected');
+
+  await dp.Page.enable();
+  await dp.Runtime.enable();
+  dp.Runtime.onConsoleAPICalled(event => {
+    testRunner.log(event.params.args[0].value);
+  });
+  await session.evaluate(() => {
+    window.getSelectedFiles = (picker) => JSON.stringify(Array.from(picker.files).map(file => file.name));
+    window.LOG = (...args) => console.log(args.join(' '));
+  });
+
+  await dp.Page.setInterceptFileChooserDialog({enabled: true});
+
+  // Note: this test must be run from the file:// scheme.
+  const path1 = window.location.href.replace(/.*test=/, '');
+  const path2 = path1.substring(0, path1.lastIndexOf('.')) + '-expected.txt';
+
+  testRunner.runTestSuite([
+    async function testAcceptFile() {
+      dp.Page.onceFileChooserOpened(event => {
+        testRunner.log('file chooser mode: ' + event.params.mode);
+        dp.Page.handleFileChooser({
+          action: 'accept',
+          files: [path1],
+        });
+        return true;
+      });
+      await session.evaluateAsyncWithUserGesture(async () => {
+        const picker = document.createElement('input');
+        picker.type = 'file';
+        picker.click();
+        await new Promise(x => picker.oninput = x);
+        LOG('selected files: ' + getSelectedFiles(picker));
+      });
+    },
+
+    async function testAcceptMultipleFiles() {
+      dp.Page.onceFileChooserOpened(event => {
+        testRunner.log('file chooser mode: ' + event.params.mode);
+        dp.Page.handleFileChooser({
+          action: 'accept',
+          files: [path1, path2],
+        });
+        return true;
+      });
+      await session.evaluateAsyncWithUserGesture(async () => {
+        const picker = document.createElement('input');
+        picker.type = 'file';
+        picker.setAttribute('multiple', true);
+        picker.click();
+        await new Promise(x => picker.oninput = x);
+        LOG('selected files: ' + getSelectedFiles(picker));
+      });
+    },
+
+    async function testResetInput() {
+      // Handle event twice: first to select files, then to reset them.
+      let counter = 0;
+      dp.Page.onceFileChooserOpened(event => {
+        ++counter;
+        dp.Page.handleFileChooser({
+          action: 'accept',
+          files: counter === 1 ? [path1] : [],
+        });
+        return counter === 2;
+      });
+      await session.evaluateAsyncWithUserGesture(async () => {
+        const picker = document.createElement('input');
+        picker.type = 'file';
+        // 1. Summon file chooser and check files
+        picker.click();
+        await new Promise(x => picker.oninput = x);
+        LOG('first selected files: ' + getSelectedFiles(picker));
+        // 2. Wait a new task: file chooser might be requested only once from a task.
+        await new Promise(x => setTimeout(x, 0));
+        // 3. Summon file chooser one more time.
+        picker.click();
+        await new Promise(x => picker.oninput = x);
+        LOG('second selected files: ' + getSelectedFiles(picker));
+      });
+    },
+
+    async function testErrors() {
+      testRunner.log('Try handling non-existing file chooser.');
+      testRunner.log(await dp.Page.handleFileChooser({
+          action: 'accept',
+          files: [path1, path2],
+      }));
+
+      testRunner.log('Try enabling file interception in multiclient');
+      const session2 = await page.createSession();
+      await session2.protocol.Page.enable();
+      testRunner.log(await session2.protocol.Page.setInterceptFileChooserDialog({enabled: true}));
+
+      // Trigger file chooser.
+      const [event] = await Promise.all([
+        dp.Page.onceFileChooserOpened(),
+        session.evaluateAsyncWithUserGesture(() => {
+          const picker = document.createElement('input');
+          picker.type = 'file';
+          picker.click();
+        }),
+      ]);
+
+      testRunner.log('Test file chooser fails when accepting multiple files for a non-multiple file chooser');
+      testRunner.log(await dp.Page.handleFileChooser({
+          action: 'accept',
+          files: [path1, path2],
+      }));
+
+      testRunner.log('Test wrong action');
+      testRunner.log(await dp.Page.handleFileChooser({
+          action: 'badaction',
+          files: [path1],
+      }));
+
+      testRunner.log('Test trying to handle already-handled file chooser');
+      // Try to handle file chooser twice.
+      await dp.Page.handleFileChooser({action: 'cancel'});
+      testRunner.log(await dp.Page.handleFileChooser({
+        action: 'accept',
+        files: []
+      }));
+    },
+  ]);
+})
diff --git a/third_party/blink/web_tests/platform/linux/fast/dynamic/011-expected.png b/third_party/blink/web_tests/platform/linux/fast/dynamic/011-expected.png
deleted file mode 100644
index bf3fc2dc..0000000
--- a/third_party/blink/web_tests/platform/linux/fast/dynamic/011-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/dynamic/011-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/dynamic/011-expected.png
deleted file mode 100644
index 85ad4f9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/dynamic/011-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/fast/dynamic/011-expected.png b/third_party/blink/web_tests/platform/mac-retina/fast/dynamic/011-expected.png
deleted file mode 100644
index 85ad4f9..0000000
--- a/third_party/blink/web_tests/platform/mac-retina/fast/dynamic/011-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/dynamic/011-expected.png b/third_party/blink/web_tests/platform/mac/fast/dynamic/011-expected.png
deleted file mode 100644
index e8aa156..0000000
--- a/third_party/blink/web_tests/platform/mac/fast/dynamic/011-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-expected.png b/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-expected.png
index d72ea11..c6a4b0b 100644
--- a/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-on-focus-expected.png b/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-on-focus-expected.png
new file mode 100644
index 0000000..7c6859a4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-on-focus-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/dynamic/011-expected.png b/third_party/blink/web_tests/platform/win/fast/dynamic/011-expected.png
deleted file mode 100644
index d987b4961..0000000
--- a/third_party/blink/web_tests/platform/win/fast/dynamic/011-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/std-switch/switch-appearance-on-focus-expected.png b/third_party/blink/web_tests/platform/win/std-switch/switch-appearance-on-focus-expected.png
new file mode 100644
index 0000000..7c6859a4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/std-switch/switch-appearance-on-focus-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/scrollbars/nested-scroll-overlay-scrollbar-expected.html b/third_party/blink/web_tests/scrollbars/nested-scroll-overlay-scrollbar-expected.html
new file mode 100644
index 0000000..337c2f68
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/nested-scroll-overlay-scrollbar-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<div id="outer" style="width: 200px; height: 200px; overflow: scroll">
+  <div style="background: green; height: 100px"></div>
+  <div style="height: 110px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/nested-scroll-overlay-scrollbar.html b/third_party/blink/web_tests/scrollbars/nested-scroll-overlay-scrollbar.html
new file mode 100644
index 0000000..9a9eba03
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/nested-scroll-overlay-scrollbar.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 200px; overflow: scroll">
+  <!-- The intermediate div doesn't have layout overflow. -->
+  <div style="height: 210px; overflow: scroll">
+    <div style="background: green; width: 300px; height: 100px"></div>
+  </div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-interleaving-z-order-expected.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-interleaving-z-order-expected.html
new file mode 100644
index 0000000..be7860a2
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-interleaving-z-order-expected.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<div id="container" style="width: 200px; height: 200px; overflow: scroll">
+  <div id="content" style="background: green; width: 210px; height: 150px"></div>
+  <div style="height: 60px"></div>
+</div>
+<div style="position: absolute; top: 80px; left: 80px; z-index: -1; background: blue; width: 200px; height: 200px"></div>
+<div style="position: absolute; top: 120px; left: 120px; z-index: 20; background: yellow; width: 200px; height: 200px"></div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-interleaving-z-order.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-interleaving-z-order.html
new file mode 100644
index 0000000..fe92316ea
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-interleaving-z-order.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<div id="container" style="width: 200px; height: 200px; overflow: scroll; position: relative">
+  <div id="content" style="position: absolute; z-index: 10; background: green; width: 210px; height: 150px"></div>
+  <div style="height: 210px"></div>
+</div>
+<div style="position: absolute; top: 80px; left: 80px; z-index: 5; background: blue; width: 200px; height: 200px"></div>
+<div style="position: absolute; top: 120px; left: 120px; z-index: 20; background: yellow; width: 200px; height: 200px"></div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-expected.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-expected.html
new file mode 100644
index 0000000..49c1ce68
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 200px; overflow: scroll">
+  <div style="background: green; width: 300px; height: 100px"></div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-1-expected.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-1-expected.html
new file mode 100644
index 0000000..0b104dd8
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-1-expected.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 200px; overflow: scroll">
+  <div style="background: green; width: 200px; height: 70px"></div>
+  <!-- This div let the top-level div have the same layout overflow as in the test. -->
+  <div style="width: 210px; height: 220px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-1.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-1.html
new file mode 100644
index 0000000..26445a4c
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<div style="position: relative; width: 200px; height: 200px; overflow: scroll">
+  <!-- the intermediate div doesn't have layout overflow. -->
+  <div style="position: relative; width: 210px; height: 80px; overflow: scroll">
+    <div style="position: absolute; z-index: 5; background: green; width: 200px; height: 70px"></div>
+  </div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-2-expected.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-2-expected.html
new file mode 100644
index 0000000..e4b3bae
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-2-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 200px; overflow: scroll">
+  <div style="background: green; width: 210px; height: 80px"></div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-2.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-2.html
new file mode 100644
index 0000000..30fdcc3
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+  #intermediate::-webkit-scrollbar { display: none; }
+</style>
+<div style="position: relative; width: 200px; height: 200px; overflow: scroll">
+  <!-- The intermediate div's scrollbars are hidden. -->
+  <div id="intermediate" style="position: relative; width: 210px; height: 80px; overflow: scroll">
+    <div style="position: absolute; z-index: 5; background: green; width: 300px; height: 100px"></div>
+  </div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-3-expected.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-3-expected.html
new file mode 100644
index 0000000..e4b3bae
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-3-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 200px; overflow: scroll">
+  <div style="background: green; width: 210px; height: 80px"></div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-3.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-3.html
new file mode 100644
index 0000000..e3b043e2
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-3.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<div style="position: relative; width: 200px; height: 200px; overflow: scroll">
+  <!-- The intermediate div's overflow is hidden. -->
+  <div style="position: relative; width: 210px; height: 80px; overflow: hidden">
+    <div style="position: absolute; z-index: 5; background: green; width: 300px; height: 100px"></div>
+  </div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-expected.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-expected.html
new file mode 100644
index 0000000..447fcfb6
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested-expected.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 200px; overflow: scroll">
+  <div style="width: 210px; height: 80px; overflow: scroll">
+    <div style="background: green; width: 300px; height: 200px"></div>
+  </div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested.html
new file mode 100644
index 0000000..a915f1c
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer-nested.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<div style="position: relative; width: 200px; height: 200px; overflow: scroll">
+  <div style="position: relative; width: 210px; height: 80px; overflow: scroll">
+    <div style="position: absolute; z-index: 5; background: green; width: 300px; height: 100px"></div>
+  </div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer.html
new file mode 100644
index 0000000..4220153
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-child-layer.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<div id="container" style="width: 200px; height: 200px; overflow: scroll">
+  <div id="content" style="position: relative; background: green; width: 300px; height: 100px"></div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-negative-z-child-layer-expected.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-negative-z-child-layer-expected.html
new file mode 100644
index 0000000..b136cfb
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-negative-z-child-layer-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<div id="container" style="width: 200px; height: 200px; overflow: scroll">
+  <div id="content" style="background: green; width: 300px; height: 100px"></div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-negative-z-child-layer.html b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-negative-z-child-layer.html
new file mode 100644
index 0000000..a8adb7f
--- /dev/null
+++ b/third_party/blink/web_tests/scrollbars/overlay-scrollbar-over-negative-z-child-layer.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<div id="container" style="width: 200px; height: 200px; overflow: scroll">
+  <div id="content" style="position: relative; z-index: -1; background: green; width: 300px; height: 100px"></div>
+  <div style="height: 210px"></div>
+</div>
+<script>
+if (window.internals) {
+  internals.runtimeFlags.overlayScrollbarsEnabled = true;
+  internals.settings.setMockScrollbarsEnabled(true);
+}
+</script>
diff --git a/third_party/blink/web_tests/security/srcdoc-include-file-sandboxed.html b/third_party/blink/web_tests/security/srcdoc-include-file-sandboxed.html
index 1dfbee9..1343c0e8 100644
--- a/third_party/blink/web_tests/security/srcdoc-include-file-sandboxed.html
+++ b/third_party/blink/web_tests/security/srcdoc-include-file-sandboxed.html
@@ -29,30 +29,25 @@
   };
 
   async_test(async function(t) {
-    // 1. During the initial navigation to about:srcdoc. The file is
-    //    successfully loaded.
-    //    TODO(https://crbug.com/977186): This should probably be blocked.
-    assert_equals("loaded", await message());
+    // 1. Initial navigation to about:srcdoc.
+    assert_equals("error", await message());
 
-    // 2. Repeating the same navigation, by setting the srcdoc attribute.
-    //    TODO(https://crbug.com/977186): This should probably be blocked.
-    iframe.srcdoc = iframe.srcdoc
-    assert_equals("loaded", await message());
+    // 2. Repeating the same navigation, by modifying the srcdoc attribute.
+    iframe.srcdoc = iframe.srcdoc;
+    assert_equals("error", await message());
 
-    // 3. History navigation to about:srcdoc. This time, it fails, probably
-    //    because of https://crbug.com/949510.
+    // 3. History navigation to about:srcdoc.
     //
     //    button.click() navigates to back.html which will execute
     //    history.back() and navigate back to the about:srcdoc page.
     button.click();
     assert_equals("error", await message());
 
-    // 4. Same as (2). This time its fails, because (3) removed the defaults
-    //    loaders.
-    iframe.srcdoc = iframe.srcdoc
+    // 4. Same as (2). This used to be useful, because (3) had side effects on
+    //    (4).
+    iframe.srcdoc = iframe.srcdoc;
     assert_equals("error", await message());
 
     t.done();
   });
 </script>
-
diff --git a/third_party/blink/web_tests/shapedetection/detection-HTMLVideoElement-invalid-state.html b/third_party/blink/web_tests/shapedetection/detection-HTMLVideoElement-invalid-state.html
deleted file mode 100644
index 115e685f..0000000
--- a/third_party/blink/web_tests/shapedetection/detection-HTMLVideoElement-invalid-state.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script>
-
-// Detector's detect() rejects on a HAVE_NOTHING HTMLVideoElement.
-promise_test(async t => {
-  const video = document.createElement("video");
-  video.src = "";
-  const videoWatcher = new EventWatcher(t, video, ["play", "error"]);
-  video.load();
-  await videoWatcher.wait_for("error");
-  assert_equals(video.readyState, video.HAVE_NOTHING);
-  const detector = new TextDetector();
-  await promise_rejects(t, 'InvalidStateError', detector.detect(video));
-}, "Text - detect(HTMLVideoElement) - HAVE_NOTHING");
-
-// Detector's detect() rejects on a HAVE_METADATA HTMLVideoElement.
-promise_test(async t => {
-  const video = document.createElement("video");
-  video.src = "../external/wpt/media/white.webm";
-  video.loop = true;
-  video.autoplay = true;
-  const videoWatcher = new EventWatcher(t, video, ["loadedmetadata", "error"]);
-  video.load();
-  await videoWatcher.wait_for("loadedmetadata");
-  assert_equals(video.readyState, video.HAVE_METADATA);
-  const detector = new TextDetector();
-  await promise_rejects(t, 'InvalidStateError', detector.detect(video));
-}, "Text - detect(HTMLVideoElement) - HAVE_METADATA");
-
-</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/std-switch/click-transition.html b/third_party/blink/web_tests/std-switch/click-transition.html
new file mode 100644
index 0000000..21f3c1c
--- /dev/null
+++ b/third_party/blink/web_tests/std-switch/click-transition.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/testdriver.js"></script>
+<script src="../resources/testdriver-vendor.js"></script>
+<body>
+<script type="module">
+import 'std:elements/switch';
+
+function singleFrame() {
+  return new Promise((resolve, reject) => {
+    requestAnimationFrame(resolve);
+  });
+}
+
+promise_test(async () => {
+  let switchElement = document.createElement('std-switch');
+  document.body.appendChild(switchElement);
+  assert_false(switchElement.on);
+  let thumb = internals.shadowRoot(switchElement).getElementById('thumb');
+  let transitionStartCounter = 0;
+  let transitionEndCounter = 0;
+  thumb.addEventListener('transitionstart', e => { ++transitionStartCounter; });
+  thumb.addEventListener('transitionend', e => { ++transitionEndCounter; });
+
+  await test_driver.click(switchElement);
+  assert_true(switchElement.on);
+  await singleFrame();
+  assert_true(transitionStartCounter > 0);
+  if (transitionStartCounter > transitionEndCounter) {
+    await new Promise((resolve, reject) => {
+      thumb.addEventListener('transitionend', resolve, {once: true});
+    });
+  }
+
+  // Changing the state with an IDL attribute should not animate.
+  transitionStartCounter = 0;
+  switchElement.on = false;
+  await singleFrame();
+  assert_equals(transitionStartCounter, 0, 'No additional events');
+}, 'Click event handler should start transition');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/std-switch/switch-appearance-on-focus.html b/third_party/blink/web_tests/std-switch/switch-appearance-on-focus.html
new file mode 100644
index 0000000..321452a7
--- /dev/null
+++ b/third_party/blink/web_tests/std-switch/switch-appearance-on-focus.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<body>
+<style>
+std-switch {
+  /* TODO(tkent): Should have margin in UA style? */
+  margin: 0.5em;
+}
+</style>
+<script type="module" async>
+import 'std:elements/switch';
+</script>
+
+<div>Focused: <std-switch id="focus" on></std-switch></div>
+<script>
+customElements.whenDefined('std-switch').then(() => {
+  document.getElementById('focus').focus();
+});
+</script>
+</body>
diff --git a/third_party/blink/web_tests/std-switch/switch-appearance.html b/third_party/blink/web_tests/std-switch/switch-appearance.html
index 9a1aeef4..a52f45ff 100644
--- a/third_party/blink/web_tests/std-switch/switch-appearance.html
+++ b/third_party/blink/web_tests/std-switch/switch-appearance.html
@@ -6,7 +6,7 @@
   margin: 0.5em;
 }
 </style>
-<script type="module" async>
+<script type="module">
 import 'std:elements/switch';
 
 
@@ -16,5 +16,8 @@
 <div><std-switch><div style="display:inline-block; text-align:right; width:100%;">off</div></std-switch></div>
 <div><std-switch on></std-switch></div>
 <div><std-switch on>on</std-switch></div>
-
+<div>Focused: <std-switch id="focus"></std-switch></div>
+<script type="module">
+document.getElementById('focus').focus();
+</script>
 </body>
diff --git a/third_party/blink/web_tests/svg/custom/radial-gradient-negative-radius-expected.png b/third_party/blink/web_tests/svg/custom/radial-gradient-negative-radius-expected.png
deleted file mode 100644
index 7f2dedd6..0000000
--- a/third_party/blink/web_tests/svg/custom/radial-gradient-negative-radius-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/svg/custom/radial-gradient-negative-radius.html b/third_party/blink/web_tests/svg/custom/radial-gradient-negative-radius.html
deleted file mode 100644
index 0304785..0000000
--- a/third_party/blink/web_tests/svg/custom/radial-gradient-negative-radius.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<head>
-<style>
-div {
-    background-image: repeating-radial-gradient(-100% -50% at center, red, green);
-}
-</style>
-</head>
-
-<body>
-  <div style="width: 100px; height: 100px;"></div>
-</body>
diff --git a/third_party/blink/web_tests/virtual/at-property/external/wpt/css/css-properties-values-api/README.txt b/third_party/blink/web_tests/virtual/at-property/external/wpt/css/css-properties-values-api/README.txt
new file mode 100644
index 0000000..f7533c5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/at-property/external/wpt/css/css-properties-values-api/README.txt
@@ -0,0 +1 @@
+Virtual test suite for @property. https://crbug.com/973830
diff --git a/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/README.txt b/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/README.txt
new file mode 100644
index 0000000..f7533c5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/README.txt
@@ -0,0 +1 @@
+Virtual test suite for @property. https://crbug.com/973830
diff --git a/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/at-property-cssom-expected.txt b/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/at-property-cssom-expected.txt
new file mode 100644
index 0000000..e123d49
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/at-property-cssom-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS Rule is applied
+PASS Can read "syntax" from declaration
+PASS Can read "inherits" from declaration
+PASS Can read "initial-value" from declaration
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/at-property-expected.txt b/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/at-property-expected.txt
new file mode 100644
index 0000000..1edca0df
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/at-property/fast/css/css-properties-values-api/at-property-expected.txt
@@ -0,0 +1,63 @@
+This is a testharness.js-based test.
+PASS Accepts valid value for 'syntax' descriptor ["<color>"]
+PASS Accepts valid value for 'syntax' descriptor ["<color> | none"]
+PASS Accepts valid value for 'syntax' descriptor ["<color># | <image> | none"]
+PASS Accepts valid value for 'syntax' descriptor ["foo | bar | baz"]
+PASS Accepts valid value for 'syntax' descriptor ["*"]
+PASS Accepts valid value for 'syntax' descriptor ["notasyntax"]
+PASS Rejects invalid value for 'syntax' descriptor [red]
+PASS Rejects invalid value for 'syntax' descriptor [rgb(255, 0, 0)]
+PASS Rejects invalid value for 'syntax' descriptor [<color>]
+PASS Rejects invalid value for 'syntax' descriptor [foo | bar]
+PASS Accepts valid value for 'initial-value' descriptor [10px]
+PASS Accepts valid value for 'initial-value' descriptor [rgb(1, 2, 3)]
+PASS Accepts valid value for 'initial-value' descriptor [red]
+PASS Accepts valid value for 'initial-value' descriptor [foo]
+PASS Accepts valid value for 'initial-value' descriptor [if(){}]
+PASS Accepts valid value for 'initial-value' descriptor [var(--x)]
+PASS Accepts valid value for 'inherits' descriptor [true]
+PASS Accepts valid value for 'inherits' descriptor [false]
+PASS Rejects invalid value for 'inherits' descriptor [none]
+PASS Rejects invalid value for 'inherits' descriptor [0]
+PASS Rejects invalid value for 'inherits' descriptor [1]
+PASS Rejects invalid value for 'inherits' descriptor ["true"]
+PASS Rejects invalid value for 'inherits' descriptor ["false"]
+PASS Rejects invalid value for 'inherits' descriptor [calc(0)]
+PASS Invalid property name does not parse [foo]
+PASS Invalid property name does not parse [-foo]
+PASS Rule applied [*, if(){}, false]
+PASS Rule applied [<angle>, 42deg, false]
+PASS Rule applied [<angle>, 1turn, false]
+PASS Rule applied [<color>, green, false]
+PASS Rule applied [<color>, rgb(1, 2, 3), false]
+PASS Rule applied [<image>, url("http://a/"), false]
+PASS Rule applied [<integer>, 5, false]
+PASS Rule applied [<length-percentage>, 10px, false]
+PASS Rule applied [<length-percentage>, 10%, false]
+PASS Rule applied [<length-percentage>, calc(10% + 10px), false]
+PASS Rule applied [<length>, 10px, false]
+PASS Rule applied [<number>, 2.5, false]
+PASS Rule applied [<percentage>, 10%, false]
+PASS Rule applied [<resolution>, 50dppx, false]
+PASS Rule applied [<resolution>, 96dpi, false]
+PASS Rule applied [<time>, 10s, false]
+PASS Rule applied [<time>, 1000ms, false]
+PASS Rule applied [<transform-function>, rotateX(0deg), false]
+PASS Rule applied [<transform-list>, rotateX(0deg), false]
+PASS Rule applied [<transform-list>, rotateX(0deg) translateX(10px), false]
+PASS Rule applied [<url>, url("http://a/"), false]
+PASS Rule applied [<color>, tomato, false]
+PASS Rule applied [<color>, tomato, true]
+PASS Rule applied for "*", even with no initial value
+PASS Rule not applied [undefined, green, false]
+PASS Rule not applied [<color>, undefined, false]
+PASS Rule not applied [<color>, green, undefined]
+PASS Rule not applied [<gandalf>, grey, false]
+PASS Rule not applied [gandalf, grey, false]
+PASS Rule not applied [<color>, notacolor, false]
+PASS Rule not applied [<length>, 10em, false]
+PASS Non-inherited properties do not inherit
+PASS Inherited properties inherit
+PASS Initial values substituted as computed value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.png b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.png
index 57f073e..d3100be 100644
--- a/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.png
+++ b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt
index 54bb628..d6d5879 100644
--- a/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt
+++ b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt
@@ -18,34 +18,22 @@
       "backgroundColor": "#FFFFFF"
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV class='outer'",
+      "name": "LayoutNGBlockFlow (positioned) DIV class='outer'",
       "bounds": [352, 294]
     },
     {
-      "name": "LayoutBlockFlow (relative positioned) DIV class='content'",
+      "name": "LayoutNGBlockFlow (relative positioned) DIV class='content'",
       "position": [79, 79],
       "bounds": [196, 212],
       "contentsOpaque": true,
       "backgroundColor": "#DDDDDD"
     },
     {
-      "name": "Overflow Controls Host Layer",
-      "position": [62, 62],
-      "bounds": [230, 170],
-      "drawsContent": false
-    },
-    {
-      "name": "Vertical Scrollbar Layer",
-      "position": [280, 67],
-      "bounds": [7, 160],
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutBlockFlow (positioned) DIV class='outer' (foreground) Layer",
+      "name": "LayoutNGBlockFlow (positioned) DIV class='outer' (foreground) Layer",
       "bounds": [352, 294]
     },
     {
-      "name": "LayoutBlockFlow DIV class='scroller'",
+      "name": "LayoutNGBlockFlow DIV class='scroller'",
       "position": [32, 32],
       "bounds": [290, 230],
       "backgroundColor": "#FFFFFF"
@@ -61,6 +49,18 @@
       "position": [67, 67],
       "bounds": [220, 236],
       "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [62, 62],
+      "bounds": [230, 170],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [280, 67],
+      "bounds": [7, 160],
+      "drawsContent": false
     }
   ]
 }
diff --git a/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/scrollbars/nested-overlay-scrollbars-expected.png b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/scrollbars/nested-overlay-scrollbars-expected.png
new file mode 100644
index 0000000..219172d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/compositing/scrollbars/nested-overlay-scrollbars-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-fullscreen-video.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-fullscreen-video.html
new file mode 100644
index 0000000..212ec3d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-fullscreen-video.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/third_party/blink/public/mojom/page/spatial_navigation.mojom.js"></script>
+<script src="../../../../../fast/spatial-navigation/resources/mock-snav-service.js"></script>
+<script src="../../../../../fast/spatial-navigation/resources/snav-testharness.js"></script>
+
+<video id="video" controls></video>
+<button id="button"></button>
+
+<script>
+  let video = document.getElementById("video");
+  let button = document.getElementById("button");
+
+  button.addEventListener('click', function() {
+    video.requestFullscreen();
+  });
+
+  promise_test(async () => {
+    button.focus();
+    eventSender.keyDown('Enter');
+
+    await snavCallback();
+
+    assert_equals(window.internals.interestedElement,
+                  video,
+                  "Video element should have interest.");
+    assert_equals(document.activeElement,
+                  video,
+                  "Video element should be focused.");
+
+    assert_true(mockSnavService.canExitFocus,
+                 "Should be able to exit focus.");
+    assert_true(mockSnavService.canSelectElement,
+                "Should be able to select element.");
+    assert_false(mockSnavService.isFormFocused,
+                 "Form should not be focused.");
+    assert_false(mockSnavService.hasNextFormElement,
+                 "Should be able to move to next form element.");
+    assert_true(mockSnavService.hasDefaultVideoControls,
+                "Should have default video controls active.");
+
+
+  }, "Spat-Nav resets gracefully after focused editable becomes display:none.");
+</script>
diff --git a/third_party/blink/web_tests/virtual/not-omt-sw-fetch/README.md b/third_party/blink/web_tests/virtual/not-omt-sw-fetch/README.md
new file mode 100644
index 0000000..a00c8fe
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/not-omt-sw-fetch/README.md
@@ -0,0 +1,4 @@
+# virtual/not-omt-sw-fetch
+
+Fetch toplevel service worker scripts on the main thread. This virtual tests
+and `virtual/omt-worker-fetch` contradict each other.
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/claim-worker-fetch.https-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/claim-worker-fetch.https-expected.txt
deleted file mode 100644
index 757707b6..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/claim-worker-fetch.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL fetch() in Worker should be intercepted after the client is claimed. assert_equals: fetch() in the worker should be intercepted. expected "Intercepted!" but got "a simple text file\n"
-FAIL fetch() in nested Worker should be intercepted after the client is claimed. assert_equals: fetch() in the worker should be intercepted. expected "Intercepted!" but got "a simple text file\n"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/clients-get-client-types.https-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/clients-get-client-types.https-expected.txt
new file mode 100644
index 0000000..37470a43
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/clients-get-client-types.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS Test Clients.get() with window and worker clients
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/worker-interception.https-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/worker-interception.https-expected.txt
new file mode 100644
index 0000000..8a05900d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/worker-interception.https-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS Verify a dedicated worker script request issued from a uncontrolled document is intercepted by worker's own service worker.
+PASS Verify a shared worker script request issued from a uncontrolled document is intercepted by worker's own service worker.
+PASS Verify a same-origin worker script served by a service worker succeeds in starting a dedicated worker.
+PASS Verify a same-origin worker script served by a service worker succeeds in starting a shared worker.
+PASS Verify a cors worker script served by a service worker fails dedicated worker start.
+PASS Verify a cors worker script served by a service worker fails shared worker start.
+PASS Verify a no-cors cross-origin worker script served by a service worker fails dedicated worker start.
+PASS Verify a no-cors cross-origin worker script served by a service worker fails shared worker start.
+PASS Verify subresource requests on a dedicated worker controlled by a service worker.
+PASS Verify subresource requests on a shared worker controlled by a service worker.
+FAIL Verify subresource requests on a nested dedicated worker created from a dedicated worker and controlled by a service worker. assert_equals: expected "finish" but got "failure:Error: assert_equals: expected \"This load was successfully intercepted.\" but got \"{\\\"error\\": {\\"message\\": \\"\\", \\"code\\": 404}}\""
+FAIL Verify subresource requests on a nested dedicated worker created from a shared worker and controlled by a service worker. assert_equals: expected "finish" but got "failure:Error: assert_equals: expected \"This load was successfully intercepted.\" but got \"Unexpected error! Worker is not defined\""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import-referrer-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import-referrer-expected.txt
deleted file mode 100644
index 10057389..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import-referrer-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-This is a testharness.js-based test.
-PASS Same-origin top-level module script loading with "no-referrer" referrer policy
-FAIL Same-origin top-level module script loading with "origin" referrer policy assert_equals: expected "http://web-platform.test:8001/" but got ""
-FAIL Same-origin top-level module script loading with "same-origin" referrer policy assert_equals: expected "http://web-platform.test:8001/workers/modules/resources/new-worker-window.html" but got ""
-PASS Same-origin static import with "no-referrer" referrer policy.
-PASS Same-origin static import with "origin" referrer policy.
-PASS Same-origin static import with "same-origin" referrer policy.
-PASS Cross-origin static import with "no-referrer" referrer policy.
-PASS Cross-origin static import with "origin" referrer policy.
-PASS Cross-origin static import with "same-origin" referrer policy.
-PASS Same-origin dynamic import with "no-referrer" referrer policy.
-PASS Same-origin dynamic import with "origin" referrer policy.
-PASS Same-origin dynamic import with "same-origin" referrer policy.
-PASS Cross-origin dynamic import with "no-referrer" referrer policy.
-PASS Cross-origin dynamic import with "origin" referrer policy.
-PASS Cross-origin dynamic import with "same-origin" referrer policy.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-clipping-2-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-clipping-2-expected.png
new file mode 100644
index 0000000..186e8a1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-clipping-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/vr/getEyeParameters_match.html b/third_party/blink/web_tests/vr/getEyeParameters_match.html
index b04ceeaf..968a39e 100644
--- a/third_party/blink/web_tests/vr/getEyeParameters_match.html
+++ b/third_party/blink/web_tests/vr/getEyeParameters_match.html
@@ -20,12 +20,13 @@
 
     function compareEyes(actual, expected) {
       t.step( () => {
-        assert_equals(actual.offset.length, expected.offset.length);
-        for (let i = 0; i < expected.offset.length; i++) {
-          assert_approx_equals(actual.offset[i], expected.offset[i],
-              FLOAT_EPSILON);
-        }
-
+        assert_equals(actual.offset.length, 3);
+        assert_approx_equals(actual.offset[0], expected.offset.x,
+            FLOAT_EPSILON);
+        assert_approx_equals(actual.offset[1], expected.offset.y,
+            FLOAT_EPSILON);
+        assert_approx_equals(actual.offset[2], expected.offset.z,
+            FLOAT_EPSILON);
         assert_equals(actual.renderWidth, expected.renderWidth * scale);
         assert_equals(actual.renderHeight, expected.renderHeight * scale);
       }, "Returned eye parameters match provided parameters");
diff --git a/third_party/blink/web_tests/vr/resources/vr-test-utils.js b/third_party/blink/web_tests/vr/resources/vr-test-utils.js
index 42224d7..2bac718 100644
--- a/third_party/blink/web_tests/vr/resources/vr-test-utils.js
+++ b/third_party/blink/web_tests/vr/resources/vr-test-utils.js
@@ -28,7 +28,17 @@
     };
     for (var field in pose) {
       if (this.pose_.hasOwnProperty(field)) {
-        this.pose_[field] = pose[field];
+        let val = pose[field];
+        if (field === "position") {
+          this.pose_[field] = new gfx.mojom.Point3F(val[0], val[1], val[2]);
+        } else if (field === "orientation") {
+          this.pose_[field] = new gfx.mojom.Quaternion(val[0], val[1], val[2], val[3]);
+        }else if (field === "angularVelocity" || field === "linearVelocity" ||
+                   field === "angularAcceleration" || field === "lienarAcceleration") {
+          this.pose_[field] = new gfx.mojom.Vector3dF(val[0], val[1], val[2]);
+        } else {
+          this.pose_[field] = pose[field];
+        }
       }
     }
   }
@@ -76,18 +86,24 @@
 
   let generic_left_eye = {
     fieldOfView : generic_left_fov,
-    offset : [-0.03, 0, 0],
+    offset : new gfx.mojom.Vector3dF(-0.03, 0, 0),
     renderWidth : 1024,
     renderHeight : 1024
   };
 
   let generic_right_eye = {
     fieldOfView :generic_right_fov,
-    offset : [0.03, 0, 0],
+    offset : new gfx.mojom.Vector3dF(0.03, 0, 0),
     renderWidth : 1024,
     renderHeight : 1024
   };
 
+  let genericStanding = new gfx.mojom.Transform();
+  genericStanding.matrix =  [0.0, 0.1, 0.2, 0.3,
+                             0.4, 0.5, 0.6, 0.7,
+                             0.8, 0.9, 1.0, 0.1,
+                             0.2, 0.3, 0.4, 0.5];
+
   return {
     FakeMagicWindowOnly: {
       displayName : "FakeVRDisplay",
@@ -111,10 +127,7 @@
         maxLayers : 1
       },
       stageParameters : {
-        standingTransform : [0.0, 0.1, 0.2, 0.3,
-                             0.4, 0.5, 0.6, 0.7,
-                             0.8, 0.9, 1.0, 0.1,
-                             0.2, 0.3, 0.4, 0.5],
+        standingTransform : genericStanding,
         sizeX : 5.0,
         sizeZ : 3.0,
       },
@@ -139,7 +152,7 @@
           leftDegrees : 35.197,
           rightDegrees : 50.899,
         },
-        offset : [-0.032, 0, 0],
+        offset : new gfx.mojom.Vector3dF(-0.032, 0, 0),
         renderWidth : 1920,
         renderHeight : 2160
       },
@@ -150,7 +163,7 @@
           leftDegrees: 50.899,
           rightDegrees: 35.197
         },
-        offset : [0.032, 0, 0],
+        offset : new gfx.mojom.Vector3dF(0.032, 0, 0),
         renderWidth : 1920,
         renderHeight : 2160
       },
diff --git a/third_party/blink/web_tests/vr/stageParameters_match.html b/third_party/blink/web_tests/vr/stageParameters_match.html
index e6bfdae7..58bd3164 100644
--- a/third_party/blink/web_tests/vr/stageParameters_match.html
+++ b/third_party/blink/web_tests/vr/stageParameters_match.html
@@ -22,11 +22,9 @@
     t.step( () => {
       assert_true(display.capabilities.hasExternalDisplay);
       assert_true(display.capabilities.hasPosition);
-      assert_equals(expectedParams.standingTransform.length,
-          actualParams.sittingToStandingTransform.length);
-      assert_equals(expectedParams.standingTransform.length, 16);
-      for (let i = 0; i < expectedParams.standingTransform.length; i++) {
-        assert_approx_equals(expectedParams.standingTransform[i],
+      assert_equals(actualParams.sittingToStandingTransform.length, 16);
+      for (let i = 0; i < actualParams.sittingToStandingTransform.length; i++) {
+        assert_approx_equals(expectedParams.standingTransform.matrix[i],
             actualParams.sittingToStandingTransform[i], FLOAT_EPSILON);
       }
 
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 1037fdf..48253350 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -696,6 +696,26 @@
 [Worker]     method constructor
 [Worker]     method decodingInfo
 [Worker]     method encodingInfo
+[Worker] interface MediaSource : EventTarget
+[Worker]     static method isTypeSupported
+[Worker]     attribute @@toStringTag
+[Worker]     getter activeSourceBuffers
+[Worker]     getter duration
+[Worker]     getter onsourceclose
+[Worker]     getter onsourceended
+[Worker]     getter onsourceopen
+[Worker]     getter readyState
+[Worker]     getter sourceBuffers
+[Worker]     method addSourceBuffer
+[Worker]     method clearLiveSeekableRange
+[Worker]     method constructor
+[Worker]     method endOfStream
+[Worker]     method removeSourceBuffer
+[Worker]     method setLiveSeekableRange
+[Worker]     setter duration
+[Worker]     setter onsourceclose
+[Worker]     setter onsourceended
+[Worker]     setter onsourceopen
 [Worker] interface MessageChannel
 [Worker]     attribute @@toStringTag
 [Worker]     getter port1
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 5bc47e4..185a304 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -678,6 +678,26 @@
 [Worker]     method constructor
 [Worker]     method decodingInfo
 [Worker]     method encodingInfo
+[Worker] interface MediaSource : EventTarget
+[Worker]     static method isTypeSupported
+[Worker]     attribute @@toStringTag
+[Worker]     getter activeSourceBuffers
+[Worker]     getter duration
+[Worker]     getter onsourceclose
+[Worker]     getter onsourceended
+[Worker]     getter onsourceopen
+[Worker]     getter readyState
+[Worker]     getter sourceBuffers
+[Worker]     method addSourceBuffer
+[Worker]     method clearLiveSeekableRange
+[Worker]     method constructor
+[Worker]     method endOfStream
+[Worker]     method removeSourceBuffer
+[Worker]     method setLiveSeekableRange
+[Worker]     setter duration
+[Worker]     setter onsourceclose
+[Worker]     setter onsourceended
+[Worker]     setter onsourceopen
 [Worker] interface MessageChannel
 [Worker]     attribute @@toStringTag
 [Worker]     getter port1
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-child-lock-ref.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-child-lock-ref.html
new file mode 100644
index 0000000..d7d6511
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-child-lock-ref.html
@@ -0,0 +1,29 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Display Locking: flex layout with child lock (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+#flexer {
+  display: flex;
+  width: 200px;
+  height: 300px;
+  background: lightgreen;
+}
+#container {
+  background: lightblue;
+}
+#sizer {
+  width: 123px;
+  height: 234px;
+}
+</style>
+
+<div id="flexer">
+  <div id="container">
+    <div id="sizer"></div>
+  </div>
+</div>
+
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-child-lock.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-child-lock.html
new file mode 100644
index 0000000..0a9af37
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-child-lock.html
@@ -0,0 +1,38 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>Display Locking: flex layout with child lock</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="flex-with-child-lock-ref.html">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+#flexer {
+  display: flex;
+  width: 200px;
+  height: 300px;
+  background: lightgreen;
+}
+#container {
+  contain: style layout;
+  background: lightblue;
+}
+</style>
+
+<div id="flexer">
+  <div id="container">
+    <div></div>
+  </div>
+</div>
+
+<script>
+async function runTest() {
+  const container = document.getElementById("container");
+  await container.displayLock.acquire({ timeout: Infinity, size: [123, 234] });
+  takeScreenshot();
+}
+
+window.onload = runTest;
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-descendant-lock-ref.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-descendant-lock-ref.html
new file mode 100644
index 0000000..48a09b8
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-descendant-lock-ref.html
@@ -0,0 +1,31 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Display Locking: flex layout with descendant lock (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+#flexer {
+  display: flex;
+  width: 200px;
+  height: 300px;
+  background: lightgreen;
+}
+#container {
+  background: lightblue;
+}
+#sizer {
+  width: 123px;
+  height: 234px;
+}
+</style>
+
+<div id="flexer">
+  <div>
+    <div id="container">
+      <div id="sizer"></div>
+    </div>
+  </div>
+</div>
+
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-descendant-lock.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-descendant-lock.html
new file mode 100644
index 0000000..471d9df
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/flex-with-descendant-lock.html
@@ -0,0 +1,40 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>Display Locking: flex layout with descendant lock</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="flex-with-descendant-lock-ref.html">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+#flexer {
+  display: flex;
+  width: 200px;
+  height: 300px;
+  background: lightgreen;
+}
+#container {
+  contain: style layout;
+  background: lightblue;
+}
+</style>
+
+<div id="flexer">
+  <div>
+    <div id="container">
+      <div></div>
+    </div>
+  </div>
+</div>
+
+<script>
+async function runTest() {
+  const container = document.getElementById("container");
+  await container.displayLock.acquire({ timeout: Infinity, size: [123, 234] });
+  takeScreenshot();
+}
+
+window.onload = runTest;
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update-and-commit.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update-and-commit.html
index b9e848b..a2ff170 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update-and-commit.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update-and-commit.html
@@ -14,7 +14,6 @@
 #outer {
   width: 100px;
   height: 100px;
-  background: lightblue;
 }
 #inner {
   width: 50px;
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update-ref.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update-ref.html
new file mode 100644
index 0000000..01c35a0
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update-ref.html
@@ -0,0 +1,9 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Display Locking: nested update (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<div id="log">PASS Element is nested under a locked element.</div>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update.html
index e8b1513..d22d7c4 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/nested-update.html
@@ -4,7 +4,7 @@
 <title>Display Locking: nested update</title>
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="pass-ref.html">
+<link rel="match" href="nested-update-ref.html">
 <script src="/common/reftest-wait.js"></script>
 
 <style>
@@ -14,7 +14,6 @@
 #outer {
   width: 100px;
   height: 100px;
-  background: lightblue;
 }
 #inner {
   width: 50px;
@@ -50,8 +49,8 @@
   // Dirty the inner layout
   inner.appendChild(document.createElement("div"));
   inner.displayLock.update().then(
-    () => { finishTest("PASS"); },
-    (e) => { finishTest("FAIL " + e.message); });
+    () => { finishTest("FAIL"); },
+    (e) => { finishTest("PASS " + e.message); });
 }
 
 window.onload = runTest;
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/tentative/click.html b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/click.html
new file mode 100644
index 0000000..acc806b
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/click.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body>
+<script type="module">
+import 'std:elements/switch';
+
+promise_test(async t => {
+  let switchElement = document.createElement('std-switch');
+  document.body.appendChild(switchElement);
+  assert_false(switchElement.on);
+
+  let inputEventCounter = 0;
+  let changeEventCounter = 0;
+  switchElement.oninput = t.step_func(e => {
+    ++inputEventCounter;
+    assert_true(e.bubbles);
+    assert_false(e.cancelable);
+    assert_false(e.composed);
+    assert_false(e.isTrusted);
+  });
+  switchElement.onchange = t.step_func(e => {
+    ++changeEventCounter;
+    assert_true(e.bubbles);
+    assert_false(e.cancelable);
+    assert_false(e.composed);
+    assert_false(e.isTrusted);
+  });
+
+  await test_driver.click(switchElement);
+  assert_true(switchElement.on);
+  assert_equals(inputEventCounter, 1);
+  assert_equals(changeEventCounter, 1);
+
+  await test_driver.click(switchElement);
+  assert_false(switchElement.on);
+  assert_equals(inputEventCounter, 2);
+  assert_equals(changeEventCounter, 2);
+
+  switchElement.disabled = true;
+  await test_driver.click(switchElement);
+  // No changes
+  assert_false(switchElement.on);
+  assert_equals(inputEventCounter, 2);
+  assert_equals(changeEventCounter, 2);
+}, 'Click should change the status, and dispatch input/change events.');
+
+test(() => {
+  let switchElement = document.createElement('std-switch');
+  let inputEventCounter = 0;
+  let changeEventCounter = 0;
+  switchElement.oninput = e => {
+    ++inputEventCounter;
+  };
+  switchElement.onchange = e => {
+    ++changeEventCounter;
+  };
+  switchElement.click();
+  assert_equals(inputEventCounter, 1);
+  assert_equals(changeEventCounter, 1);
+}, 'input/change events should be synchronous.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/tentative/focus-expected.txt b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/focus-expected.txt
new file mode 100644
index 0000000..da32ebe
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/focus-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS should be focus()-able by default
+FAIL should have no tabindex by default assert_false: expected false got true
+PASS should not be focusable with invalid tabindex
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/tentative/focus.html b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/focus.html
new file mode 100644
index 0000000..2b53540
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/focus.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script type="module">
+import 'std:elements/switch';
+
+test(() => {
+  let switchElement = document.createElement('std-switch');
+  document.body.appendChild(switchElement);
+  switchElement.focus();
+  assert_equals(document.activeElement, switchElement);
+  assert_true(switchElement.matches(':focus'));
+}, 'should be focus()-able by default');
+
+test(() => {
+  let switchElement = document.createElement('std-switch');
+  document.body.appendChild(switchElement);
+  assert_false(switchElement.hasAttribute('tabindex'));
+}, 'should have no tabindex by default');
+
+test(() => {
+  let switchElement = document.createElement('std-switch');
+  switchElement.setAttribute('tabindex', 'parse-error');
+  document.body.appendChild(switchElement);
+  switchElement.focus();
+  assert_not_equals(document.activeElement, switchElement);
+}, 'should not be focusable with invalid tabindex');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/xr/ar_hittest.html b/third_party/blink/web_tests/xr/ar_hittest.html
index cd4c130..aae505b6 100644
--- a/third_party/blink/web_tests/xr/ar_hittest.html
+++ b/third_party/blink/web_tests/xr/ar_hittest.html
@@ -30,7 +30,8 @@
     let ray = new XRRay({x : 0.0, y : 0.0, z : 0.0}, {x : 1.0, y : 0.0, z: 0.0});
 
     let hit = new device.mojom.XRHitResult();
-    hit.hitMatrix = expectedHitMatrix;
+    hit.hitMatrix = new gfx.mojom.Transform();
+    hit.hitMatrix.matrix = expectedHitMatrix;
 
     fakeDeviceController.setHitTestResults({ results: [hit] });
 
diff --git a/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js b/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js
index fb200746..a9942d7 100644
--- a/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js
+++ b/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js
@@ -4,8 +4,13 @@
  * for interal tests. The main mocked objects are found in
  * ../external/wpt/resources/chromium/webxr-test.js. */
 
+let default_standing = new gfx.mojom.Transform();
+default_standing.matrix = [1, 0, 0, 0,
+                           0, 1, 0, 0,
+                           0, 0, 1, 0,
+                           0, 1.65, 0, 1];
 const default_stage_parameters = {
-  standingTransform: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1.65, 0, 1],
+  standingTransform: default_standing,
   sizeX: 1.5,
   sizeZ: 1.5,
   bounds: null
@@ -59,7 +64,9 @@
   var hit_results = this.hittest_results_;
   if (!hit_results) {
     var hit = new device.mojom.XRHitResult();
-    hit.hitMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
+
+    // No change to the underlying matrix/leaving it null results in identity.
+    hit.hitMatrix = new gfx.mojom.Transform();
     hit_results = {results: [hit]};
   }
   return Promise.resolve(hit_results);
@@ -77,7 +84,8 @@
       this.displayInfo_.stageParameters = default_stage_parameters;
     }
 
-    this.displayInfo_.stageParameters.standingTransform = value;
+    this.displayInfo_.stageParameters.standingTransform = new gfx.mojom.Transform();
+    this.displayInfo_.stageParameters.standingTransform.matrix = value;
   } else if (this.displayInfo_.stageParameters) {
     this.displayInfo_.stageParameters = null;
   }
diff --git a/third_party/breakpad/BUILD.gn b/third_party/breakpad/BUILD.gn
index 61b6a162..aea6f2ae 100644
--- a/third_party/breakpad/BUILD.gn
+++ b/third_party/breakpad/BUILD.gn
@@ -719,6 +719,7 @@
 
     data_deps = [
       ":linux_dumper_unittest_helper",
+      "//testing/buildbot/filters:breakpad_unittests_filters",
     ]
 
     include_dirs = [
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 8ef69d1..1548ba39 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -75,6 +75,13 @@
 };
 
 /** @enum {string} */
+chrome.fileManagerPrivate.FormatFileSystemType = {
+  VFAT: 'vfat',
+  EXFAT: 'exfat',
+  NTFS: 'ntfs',
+};
+
+/** @enum {string} */
 chrome.fileManagerPrivate.TransferState = {
   IN_PROGRESS: 'in_progress',
   COMPLETED: 'completed',
@@ -738,8 +745,11 @@
 /**
  * Formats a mounted volume. |volumeId| ID of the volume to be formatted.
  * @param {string} volumeId
+ * @param {chrome.fileManagerPrivate.FormatFileSystemType} filesystem
+ * @param {string} volumeLabel
  */
-chrome.fileManagerPrivate.formatVolume = function(volumeId) {};
+chrome.fileManagerPrivate.formatVolume = function(volumeId, filesystem,
+    volumeLabel) {};
 
 /**
  * Renames a mounted volume. |volumeId| ID of the volume to be renamed to
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 974ef53..a8509af 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-0-72-g81445c034
-Revision: 81445c034aca36040b6311dc71a2cbed9548b262
+Version: VER-2-10-0-73-g7b1c7585d
+Revision: 7b1c7585d7ab929d9b29932d6697a22149162c13
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index d241a31..37d8e334 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -73,6 +73,7 @@
       "src/src/hb-algs.hh",
       "src/src/hb-array.hh",
       "src/src/hb-atomic.hh",
+      "src/src/hb-bimap.hh",
       "src/src/hb-blob.cc",
       "src/src/hb-blob.hh",
       "src/src/hb-buffer-deserialize-json.hh",
@@ -111,6 +112,7 @@
       "src/src/hb-ot-color-cbdt-table.hh",
       "src/src/hb-ot-color-colr-table.hh",
       "src/src/hb-ot-color-cpal-table.hh",
+      "src/src/hb-ot-face-table-list.hh",
       "src/src/hb-ot-face.cc",
       "src/src/hb-ot-face.hh",
       "src/src/hb-ot-font.cc",
@@ -213,6 +215,7 @@
 
     defines = [
       "HAVE_OT",
+      "HAVE_FREETYPE",
       "HAVE_ICU",
       "HAVE_ICU_BUILTIN",
       "HAVE_INTEL_ATOMIC_PRIMITIVES",
@@ -224,6 +227,9 @@
       "HB_NO_SUBSET_LAYOUT",
       "HB_NO_SUBSET_CFF",
 
+      # Fallback shaper not required, we only use the HarfBuzz internal OT shaper.
+      "HB_NO_FALLBACK_SHAPE",
+
       # Tells HarfBuzz to use ICU instead of the own mini UCDN implementation
       # that is part of HarfBuzz.
       "HB_NO_UCD",
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index f1e45a4..807337d9 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,9 +1,9 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 2.5.1-15
-Date: 20190605
-Revision: 659eeddb2df5b97cc01bd39e106381f65c9f41f1
+Version: 2.5.3-18
+Date: 20190627
+Revision: 7185bd6ffb4dd8c0efebdab5b930e62c5695e3ab
 Security Critical: yes
 License: MIT
 License File: src/COPYING
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 708cb1ab..03cf8ea 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 254277674
-Date: 2019/06/20 UTC
+Version: 255196992
+Date: 2019/06/26 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/system_profile.proto b/third_party/metrics_proto/system_profile.proto
index 3b7f068..a17a8ce4 100644
--- a/third_party/metrics_proto/system_profile.proto
+++ b/third_party/metrics_proto/system_profile.proto
@@ -195,6 +195,9 @@
       // Number of logical processors/cores on the current machine, which
       // includes hyperthreaded cores.
       optional uint32 num_cores = 3;
+
+      // Whether the CPU is running in a hypervisor.
+      optional bool is_hypervisor = 4;
     }
     optional CPU cpu = 13;
 
diff --git a/third_party/proguard/OWNERS b/third_party/proguard/OWNERS
index 3a28457..557bec58 100644
--- a/third_party/proguard/OWNERS
+++ b/third_party/proguard/OWNERS
@@ -1,3 +1,4 @@
 agrieve@chromium.org
+smaier@chromium.org
 torne@chromium.org
 yfriedman@chromium.org
diff --git a/third_party/proguard/README.chromium b/third_party/proguard/README.chromium
index bd0aceb..fead1bf 100644
--- a/third_party/proguard/README.chromium
+++ b/third_party/proguard/README.chromium
@@ -1,6 +1,6 @@
 Name: Proguard
 URL: http://proguard.sourceforge.net/
-Version: 5.2.1_and_6.0.3
+Version: 6.0.3
 Date: June 22, 2016
 License: GPL v2
 License File: NOT_SHIPPED
@@ -10,7 +10,3 @@
 This directory includes proguard.jar to allow Chromium to shrink, optimize java
 classes for Android, as well as retrace.jar, to allow for deobfuscation of stack
 traces.
-
-Proguard is kept at 5.2.1 because all newer versions were found to optimize in a
-way that breaks Chrome. Retrace is at 6.0.3 because version 5.2.1 was found to
-infinite loop when deobfuscating certainly strings.
diff --git a/third_party/proguard/cipd.yaml b/third_party/proguard/cipd.yaml
index ddcbd18..f98861f 100644
--- a/third_party/proguard/cipd.yaml
+++ b/third_party/proguard/cipd.yaml
@@ -6,6 +6,5 @@
 # cipd create --pkg-def cipd.yaml
 package: chromium/third_party/proguard
 data:
-  - file: lib/proguard.jar
   - file: lib/proguard603.jar
   - file: lib/retrace603.jar
diff --git a/third_party/webxr_test_pages/webxr-samples/js/hit-test.js b/third_party/webxr_test_pages/webxr-samples/js/hit-test.js
index 8f6b954..00cd512c9 100644
--- a/third_party/webxr_test_pages/webxr-samples/js/hit-test.js
+++ b/third_party/webxr_test_pages/webxr-samples/js/hit-test.js
@@ -117,8 +117,8 @@
 }
 
 // single plane hit test - doesn't take into account the plane's polygon
-function hitTestPlane(ray, plane, frameOfReference) {
-  const plane_pose = plane.getPose(frameOfReference);
+function hitTestPlane(frame, ray, plane, frameOfReference) {
+  const plane_pose = frame.getPose(plane.planeSpace, frameOfReference);
   const plane_normal = transform_point_by_matrix(
     plane_pose.transform.matrix, {x : 0, y : 1.0, z : 0, w : 0});
   const plane_center = normalize_perspective(
@@ -174,10 +174,12 @@
 }
 
 // multiple planes hit test
-export function hitTest(ray, planes, frameOfReference) {
+export function hitTest(frame, ray, frameOfReference) {
+  const planes = frame.worldInformation.detectedPlanes;
+
   let hit_test_results = [];
   planes.forEach(plane => {
-    let result = hitTestPlane(ray, plane, frameOfReference);
+    let result = hitTestPlane(frame, ray, plane, frameOfReference);
     if(result) {
       // throw away results with no intersection with plane
       hit_test_results.push(result);
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
index 073e3b2..122b27b 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
@@ -347,7 +347,7 @@
         }
       }
 
-      function addPlaneToScene(plane, timestamp) {
+      function addPlaneToScene(frame, plane, timestamp) {
         if(typeof XRPlane.counter == 'undefined') {
           XRPlane.counter = 1;
           XRPlane.colors = [
@@ -389,7 +389,7 @@
           plane_node.addNode(planeFrameOfReference);
 
           plane.scene_node = plane_node;
-          plane.scene_node.matrix = plane.getPose(xrRefSpace).transform.matrix;
+          plane.scene_node.matrix = frame.getPose(plane.planeSpace, xrRefSpace).transform.matrix;
 
           plane.extended_polygon = extendPolygon(plane.polygon);
           plane.extended_polygon_node = new PlaneNode({
@@ -406,7 +406,7 @@
         {
           // old plane that was updated in current frame
           plane.scene_node.onPlaneChanged(plane.polygon);
-          plane.scene_node.matrix = plane.getPose(xrRefSpace).transform.matrix;
+          plane.scene_node.matrix = frame.getPose(plane.planeSpace, xrRefSpace).transform.matrix;
           plane.extended_polygon = extendPolygon(plane.polygon);
           plane.extended_polygon_node.onPlaneChanged(plane.extended_polygon);
         }
@@ -450,7 +450,7 @@
 
         // Process all currently detected planes.
         detected_planes.forEach(plane => {
-          addPlaneToScene(plane, t);
+          addPlaneToScene(frame, plane, t);
         });
 
         // If requested, use the pose to cast a reticle into the scene using a
@@ -469,7 +469,7 @@
           rayObject.matrix = ray.matrix.slice();
           rayObject.visible = true;
 
-          const hitTestResults = hitTest(ray, frame.worldInformation.detectedPlanes, xrRefSpace);
+          const hitTestResults = hitTest(frame, ray, xrRefSpace);
           const hitTestFiltered = filterHitTestResults(hitTestResults,
                                                        extendPlanesEnabled.checked,
                                                        false,
diff --git a/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java b/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
index c2418f0..3e205ec8 100644
--- a/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
+++ b/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
@@ -4,6 +4,7 @@
 
 package org.chromium.customtabs.test;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.ComponentName;
@@ -30,8 +31,12 @@
 import android.widget.EditText;
 import android.widget.RadioButton;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Random;
 import java.util.Set;
 
 /** Activity used to benchmark Custom Tabs PLT.
@@ -279,6 +284,25 @@
     }
 
     /**
+     * Holds the file and the range for pinning. Used only in the 'Pinning Benchmark' mode.
+     */
+    private static class PinInfo {
+        public boolean pinningBenchmark;
+        public String fileName;
+        public int offset;
+        public int length;
+
+        public PinInfo() {}
+
+        public PinInfo(boolean pinningBenchmark, String fileName, int offset, int length) {
+            this.pinningBenchmark = pinningBenchmark;
+            this.fileName = fileName;
+            this.offset = offset;
+            this.length = length;
+        }
+    }
+
+    /**
      * Holds immutable parameters of the benchmark that are not needed after launching an intent.
      *
      * There are a few parameters that need to be written to the CSV line, those better fit in the
@@ -321,7 +345,21 @@
         if (speculationMode == null) speculationMode = "prerender";
         int timeoutSeconds = intent.getIntExtra("timeout", NONE);
 
-        if (parallelUrl != null && !warmup) {
+        PinInfo pinInfo;
+        if (!intent.getBooleanExtra("pinning_benchmark", false)) {
+            pinInfo = new PinInfo();
+        } else {
+            pinInfo = new PinInfo(true, intent.getStringExtra("pin_filename"),
+                    intent.getIntExtra("pin_offset", NONE), intent.getIntExtra("pin_length", NONE));
+        }
+        int extraBriefMemoryMb = intent.getIntExtra("extra_brief_memory_mb", 0);
+
+        if (parallelUrl != null && !parallelUrl.equals("") && !warmup) {
+            if (pinInfo.pinningBenchmark) {
+                String message = "Warming up while pinning is not interesting";
+                Log.e(TAG, message);
+                throw new RuntimeException(message);
+            }
             Log.w(TAG, "Parallel URL provided, forcing warmup");
             warmup = true;
             delayToLaunchUrl = Math.max(delayToLaunchUrl, PARALLEL_REQUEST_MIN_DELAY_AFTER_WARMUP);
@@ -329,8 +367,9 @@
                     Math.max(delayToMayLaunchUrl, PARALLEL_REQUEST_MIN_DELAY_AFTER_WARMUP);
         }
 
-        final CustomCallback cb = new CustomCallback(packageName, warmup, skipLauncherActivity,
-                speculationMode, delayToMayLaunchUrl, delayToLaunchUrl);
+        final CustomCallback cb =
+                new CustomCallback(packageName, warmup, skipLauncherActivity, speculationMode,
+                        delayToMayLaunchUrl, delayToLaunchUrl, pinInfo, extraBriefMemoryMb);
         launchCustomTabs(cb, new LaunchInfo(url, speculatedUrl, parallelUrl, timeoutSeconds));
     }
 
@@ -346,15 +385,20 @@
         public long pageLoadStartedMs = NONE;
         public long pageLoadFinishedMs = NONE;
         public long firstContentfulPaintMs = NONE;
+        public PinInfo pinInfo;
+        public long extraBriefMemoryMb;
 
         public CustomCallback(String packageName, boolean warmup, boolean skipLauncherActivity,
-                String speculationMode, int delayToMayLaunchUrl, int delayToLaunchUrl) {
+                String speculationMode, int delayToMayLaunchUrl, int delayToLaunchUrl,
+                PinInfo pinInfo, long extraBriefMemoryMb) {
             this.packageName = packageName;
             this.warmup = warmup;
             this.skipLauncherActivity = skipLauncherActivity;
             this.speculationMode = speculationMode;
             this.delayToMayLaunchUrl = delayToMayLaunchUrl;
             this.delayToLaunchUrl = delayToLaunchUrl;
+            this.pinInfo = pinInfo;
+            this.extraBriefMemoryMb = extraBriefMemoryMb;
         }
 
         public void recordIntentHasBeenSent() {
@@ -408,6 +452,9 @@
                     + speculationMode + "," + delayToMayLaunchUrl + "," + delayToLaunchUrl + ","
                     + intentSentMs + "," + pageLoadStartedMs + "," + pageLoadFinishedMs + ","
                     + firstContentfulPaintMs;
+            if (pinInfo.pinningBenchmark) {
+                logLine += ',' + extraBriefMemoryMb + ',' + pinInfo.length;
+            }
             Log.w(TAGCSV, logLine);
             logMemory(packageName, "AfterMetrics");
             MainActivity.this.finish();
@@ -503,6 +550,106 @@
         if (!ok) throw new RuntimeException("Cannot set the speculation mode");
     }
 
+    // Declare as public and volatile to prevent it from being optimized out.
+    private static volatile ArrayList<byte[]> sExtraArrays = new ArrayList<>();
+
+    private static final int MAX_ALLOCATION_ALLOWED = 1 << 23; // 8 MiB
+
+    private static byte[] createRandomlyFilledArray(int size, Random random) {
+        // Fill in small chunks to avoid allocating 2x the size.
+        byte[] array = new byte[size];
+        final int chunkSize = 1 << 15; // 32 KiB
+        byte[] randomBytes = new byte[chunkSize];
+        for (int i = 0; i < size / chunkSize; i++) {
+            random.nextBytes(randomBytes);
+            System.arraycopy(randomBytes /* src */, 0 /* srcPos */, array /* dest */,
+                    i * chunkSize /* destPos */, chunkSize /* length */);
+        }
+        return array;
+    }
+
+    // In order for this method to work, the Android system image needs to be modified to export
+    // PinnerService and allow any app to call pinRangeFromFile(). Usually pinning is requested by
+    // Chrome (in LibraryPrefetcher), but the call is reimplemented here to avoid restarting
+    // Chrome unnecessarily.
+    @SuppressLint("WrongConstant")
+    private boolean pinChrome(String fileName, int startOffset, int length) {
+        Context context = getApplicationContext();
+        Object pinner = context.getSystemService("pinner");
+        if (pinner == null) {
+            Log.w(TAG, "Cannot get PinnerService.");
+            return false;
+        }
+
+        try {
+            Method pinRangeFromFile = pinner.getClass().getMethod(
+                    "pinRangeFromFile", String.class, int.class, int.class);
+            boolean ok = (Boolean) pinRangeFromFile.invoke(pinner, fileName, startOffset, length);
+            if (!ok) {
+                Log.e(TAG, "Not allowed to call the method, should not happen");
+                return false;
+            } else {
+                Log.w(TAG, "Successfully pinned ordered code");
+            }
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
+            Log.w(TAG, "Error invoking the method. " + ex.getMessage());
+            return false;
+        }
+        return true;
+    }
+
+    // In order for this method to work, the Android system image needs to be modified to export
+    // PinnerService and allow any app to call unpinChromeFiles().
+    @SuppressLint("WrongConstant")
+    private boolean unpinChrome() {
+        Context context = getApplicationContext();
+        Object pinner = context.getSystemService("pinner");
+        if (pinner == null) {
+            Log.w(TAG, "Cannot get PinnerService for unpinning.");
+            return false;
+        }
+        try {
+            Method unpinChromeFiles = pinner.getClass().getMethod("unpinChromeFiles");
+            boolean ok = (Boolean) unpinChromeFiles.invoke(pinner);
+            if (!ok) {
+                Log.e(TAG, "Could not make a reflection call to unpinChromeFiles()");
+                return false;
+            } else {
+                Log.i(TAG, "Unpinned Chrome files");
+            }
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
+            Log.w(TAG, "Error invoking the method. " + ex.getMessage());
+            return false;
+        }
+        return true;
+    }
+
+    private void consumeExtraMemoryBriefly(long amountMb) {
+        // Allocate memory and fill with random data. Randomization is needed to avoid efficient
+        // compression of the data in ZRAM.
+        Log.i(TAG, "Consuming extra memory (MiB) = " + amountMb);
+        int bytesToUse = (int) (amountMb * (1 << 20));
+        long beforeFill = SystemClock.uptimeMillis();
+        Random random = new Random();
+        do {
+            // Limit every allocation in size in case there is a per-allocation limit.
+            int size = bytesToUse < MAX_ALLOCATION_ALLOWED ? bytesToUse : MAX_ALLOCATION_ALLOWED;
+            bytesToUse -= MAX_ALLOCATION_ALLOWED;
+            sExtraArrays.add(createRandomlyFilledArray(size, random));
+        } while (bytesToUse > 0);
+        long afterFill = SystemClock.uptimeMillis() - beforeFill;
+        Log.i(TAG, "Time to fill extra memory (ms) = " + afterFill);
+
+        // Allow a number of background apps to be killed.
+        int amountToWaitForBackgroundKilling = 3000;
+        syncSleepMs(amountToWaitForBackgroundKilling);
+
+        // Free up memory to give Chrome the room to start without killing even more background
+        // apps.
+        sExtraArrays.clear();
+        System.gc();
+    }
+
     private void onCustomTabsServiceConnected(
             CustomTabsClient client, CustomCallback cb, LaunchInfo launchInfo) {
         logMemory(cb.packageName, "OnServiceConnected");
@@ -530,21 +677,33 @@
                 cb.logMetricsAndFinishDelayed(launchInfo.timeoutSeconds * 1000);
         };
 
-        Runnable mayLaunchRunnable = () -> {
-            logMemory(cb.packageName, "BeforeMayLaunchUrl");
-            session.mayLaunchUrl(Uri.parse(launchInfo.speculatedUrl), null, null);
-            mHandler.postDelayed(launchRunnable, cb.delayToLaunchUrl);
-        };
-
-        if (cb.warmup) client.warmup(0);
-        if (cb.delayToMayLaunchUrl != NONE) {
-            mHandler.postDelayed(mayLaunchRunnable, cb.delayToMayLaunchUrl);
+        if (cb.pinInfo.pinningBenchmark) {
+            mHandler.post(launchRunnable); // Already waited for the delay.
         } else {
-            mHandler.postDelayed(launchRunnable, cb.delayToLaunchUrl);
+            if (cb.warmup) client.warmup(0);
+            if (cb.delayToMayLaunchUrl != NONE) {
+                final Runnable mayLaunchRunnable = () -> {
+                    logMemory(cb.packageName, "BeforeMayLaunchUrl");
+                    session.mayLaunchUrl(Uri.parse(launchInfo.speculatedUrl), null, null);
+                    mHandler.postDelayed(launchRunnable, cb.delayToLaunchUrl);
+                };
+                mHandler.postDelayed(mayLaunchRunnable, cb.delayToMayLaunchUrl);
+            } else {
+                mHandler.postDelayed(launchRunnable, cb.delayToLaunchUrl);
+            }
         }
     }
 
-    private void launchCustomTabs(CustomCallback cb, LaunchInfo launchInfo) {
+    private static void syncSleepMs(int delay) {
+        try {
+            Thread.sleep(delay);
+        } catch (InterruptedException e) {
+            Log.w(TAG, "Interrupted: " + e);
+        }
+    }
+
+    private void continueWithServiceConnection(
+            final CustomCallback cb, final LaunchInfo launchInfo) {
         CustomTabsClient.bindCustomTabsService(
                 this, cb.packageName, new CustomTabsServiceConnection() {
                     @Override
@@ -557,4 +716,31 @@
                     public void onServiceDisconnected(ComponentName name) {}
                 });
     }
+
+    private void launchCustomTabs(CustomCallback cb, LaunchInfo launchInfo) {
+        final PinInfo pinInfo = cb.pinInfo;
+        if (!pinInfo.pinningBenchmark) {
+            continueWithServiceConnection(cb, launchInfo);
+        } else {
+            // Execute off the UI thread to allow slow operations like pinning or eating RAM for
+            // dinner.
+            new Thread(() -> {
+                if (pinInfo.length > 0) {
+                    boolean ok = pinChrome(pinInfo.fileName, pinInfo.offset, pinInfo.length);
+                    if (!ok) throw new RuntimeException("Failed to pin Chrome file.");
+                } else {
+                    boolean ok = unpinChrome();
+                    if (!ok) throw new RuntimeException("Failed to unpin Chrome file.");
+                }
+                // Pinning is async, wait until hopefully it finishes.
+                syncSleepMs(3000);
+                if (cb.extraBriefMemoryMb != 0) {
+                    consumeExtraMemoryBriefly(cb.extraBriefMemoryMb);
+                }
+                Log.i(TAG, "Waiting for " + cb.delayToLaunchUrl + "ms before launching URL");
+                syncSleepMs(cb.delayToLaunchUrl);
+                continueWithServiceConnection(cb, launchInfo);
+            }).start();
+        }
+    }
 }
diff --git a/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py b/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
index d9a161fe..8385183 100755
--- a/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
+++ b/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
@@ -41,7 +41,9 @@
 
 def RunOnce(device, url, speculated_url, parallel_url, warmup,
             skip_launcher_activity, speculation_mode, delay_to_may_launch_url,
-            delay_to_launch_url, cold, chrome_args, reset_chrome_state):
+            delay_to_launch_url, cold, pinning_benchmark, pin_filename,
+            pin_offset, pin_length, extra_brief_memory_mb, chrome_args,
+            reset_chrome_state):
   """Runs a test on a device once.
 
   Args:
@@ -57,6 +59,12 @@
     delay_to_may_launch_url: (int) Delay to mayLaunchUrl() in ms.
     delay_to_launch_url: (int) Delay to launchUrl() in ms.
     cold: (bool) Whether the page cache should be dropped.
+    pinning_benchmark: (bool) Whether to perform the 'pinning benchmark'.
+    pin_filename: (str) The file to pin on the device.
+    pin_offset: (int) Start offset of the range to pin.
+    pin_length: (int) Number of bytes to pin.
+    extra_brief_memory_mb: (int) Number of MiB to consume before starting
+        Chrome. Applies only to the 'pinning benchmark' scenario.
     chrome_args: ([str]) List of arguments to pass to Chrome.
     reset_chrome_state: (bool) Whether to reset the Chrome local state before
         the run.
@@ -71,9 +79,9 @@
   if not device.HasRoot():
     device.EnableRoot()
 
-  timeout_s = 20
+  timeout_s = 64
   logcat_timeout = int(timeout_s + delay_to_may_launch_url / 1000.
-                       + delay_to_launch_url / 1000.) + 3;
+                       + delay_to_launch_url / 1000.);
 
   with flag_changer.CustomCommandLineFlags(
       device, _COMMAND_LINE_FILE, chrome_args):
@@ -89,6 +97,11 @@
                 'speculation_mode': str(speculation_mode),
                 'delay_to_may_launch_url': delay_to_may_launch_url,
                 'delay_to_launch_url': delay_to_launch_url,
+                'pinning_benchmark': pinning_benchmark,
+                'pin_filename': str(pin_filename),
+                'pin_offset': pin_offset,
+                'pin_length': pin_length,
+                'extra_brief_memory_mb': extra_brief_memory_mb,
                 'timeout': timeout_s})
     result_line_re = re.compile(r'CUSTOMTABSBENCHCSV.*: (.*)')
     logcat_monitor = device.GetLogcatMonitor(clear=True)
@@ -171,12 +184,17 @@
 
       result = RunOnce(device,
                        config['url'],
-                       config['speculated_url'],
-                       config['parallel_url'],
+                       config.get('speculated_url', config['url']),
+                       config.get('parallel_url', ''),
                        config['warmup'], config['skip_launcher_activity'],
                        config['speculation_mode'],
                        config['delay_to_may_launch_url'],
                        config['delay_to_launch_url'], config['cold'],
+                       config.get('pinning_benchmark', False),
+                       config.get('pin_filename', ''),
+                       config.get('pin_offset', -1),
+                       config.get('pin_length', -1),
+                       config.get('extra_brief_memory_mb', 0),
                        chrome_args, reset_chrome_state=True)
       if result is not None:
         out.write(result + '\n')
@@ -249,6 +267,21 @@
                     'stdout (this is the default)', default='-')
   parser.add_option('--once', help='Run only one iteration.',
                     action='store_true', default=False)
+  parser.add_option('--pinning_benchmark',
+                    help='Compare startup with/without a preliminary step '
+                    'that pins a range of bytes in the APK into memory with '
+                    'mlock(2).', default=False, action='store_true')
+  parser.add_option('--extra_brief_memory_mb', help='How much memory to '
+                    'consume in foreground for --pinning_benchmark.',
+                    type='int', default=0)
+  parser.add_option('--pin_filename', help='The file name on the device to pin '
+                    'to memory.', default='')
+  parser.add_option('--pin_offset', help='The start offset of the range to be '
+                    'pinned to memory.',
+                    type='int', default=-1)
+  parser.add_option('--pin_length', help='The length of the range being pinned,'
+                    ' where 0 results in no pinning.',
+                    type='int', default=-1)
 
   return parser
 
@@ -279,6 +312,11 @@
       'delay_to_may_launch_url': options.delay_to_may_launch_url,
       'delay_to_launch_url': options.delay_to_launch_url,
       'cold': options.cold,
+      'pinning_benchmark': options.pinning_benchmark,
+      'pin_filename': options.pin_filename,
+      'pin_offset': options.pin_offset,
+      'pin_length': options.pin_length,
+      'extra_brief_memory_mb': options.extra_brief_memory_mb,
   }
   LoopOnDevice(device, [config], options.output_file, once=options.once)
 
diff --git a/tools/android/customtabs_benchmark/scripts/pinning_config.json b/tools/android/customtabs_benchmark/scripts/pinning_config.json
new file mode 100644
index 0000000..482919c
--- /dev/null
+++ b/tools/android/customtabs_benchmark/scripts/pinning_config.json
@@ -0,0 +1,14 @@
+{
+  "url": "https://en.m.wikipedia.org/wiki/Science",
+  "warmup": false,
+  "skip_launcher_activity": false,
+  "speculation_mode": "disabled",
+  "delay_to_may_launch_url": -1,
+  "delay_to_launch_url": 3000,
+  "cold": false,
+  "pinning_benchmark": true,
+  "pin_filename": "/data/app/com.google.android.apps.chrome-tdw2k9YrmoNz01HnObBJHg==/base.apk",
+  "pin_offset": 18690048,
+  "extra_brief_memory_mb": [40, 40],
+  "pin_length": [0, 47816496]
+}
diff --git a/tools/android/customtabs_benchmark/scripts/run_benchmark.py b/tools/android/customtabs_benchmark/scripts/run_benchmark.py
index 5ddcab3..e48930b 100755
--- a/tools/android/customtabs_benchmark/scripts/run_benchmark.py
+++ b/tools/android/customtabs_benchmark/scripts/run_benchmark.py
@@ -27,7 +27,9 @@
 
 
 _KEYS = ['url', 'warmup', 'skip_launcher_activity', 'speculation_mode',
-         'delay_to_may_launch_url', 'delay_to_launch_url', 'cold']
+         'delay_to_may_launch_url', 'delay_to_launch_url', 'cold',
+         'pinning_benchmark', 'extra_brief_memory_mb', 'pin_filename',
+         'pin_offset', 'pin_length']
 
 
 def _ParseConfiguration(filename):
@@ -84,6 +86,7 @@
                     'output file is prefix_<device ID>.csv', default='result')
   parser.add_option('--once', help='Run only once.', default=False,
                     action='store_true')
+  parser.add_option('--adb_path', help='Path to ADB', default=None)
   return parser
 
 
@@ -121,7 +124,7 @@
   if options.config is None:
     logging.error('A configuration file must be provided.')
     sys.exit(0)
-  devil_chromium.Initialize()
+  devil_chromium.Initialize(adb_path=options.adb_path)
   configs = _ParseConfiguration(options.config)
   if options.once:
     device = device_utils.DeviceUtils.HealthyDevices()[0]
diff --git a/tools/grit/grit/node/include.py b/tools/grit/grit/node/include.py
index daf5e72..fd517bdd 100644
--- a/tools/grit/grit/node/include.py
+++ b/tools/grit/grit/node/include.py
@@ -73,14 +73,13 @@
     if os.path.isabs(os.path.expandvars(self.attrs['file'])):
       return self.attrs['file']
 
-    # We have no control over code that calles ToRealPath later, so convert
+    # We have no control over code that calls ToRealPath later, so convert
     # the path to be relative against our basedir.
     if self.attrs.get('use_base_dir', 'true') != 'true':
       # Normalize the directory path to use the appropriate OS separator.
       # GetBaseDir() may return paths\like\this or paths/like/this, since it is
       # read from the base_dir attribute in the grd file.
-      norm_base_dir = os.path.normpath(
-              self.GetRoot().GetBaseDir().replace('\\', '/'))
+      norm_base_dir = util.normpath(self.GetRoot().GetBaseDir())
       return os.path.relpath(self.attrs['file'], norm_base_dir)
 
     return self.attrs['file']
diff --git a/tools/grit/grit/node/structure.py b/tools/grit/grit/node/structure.py
index 48304b72..6b795d0 100644
--- a/tools/grit/grit/node/structure.py
+++ b/tools/grit/grit/node/structure.py
@@ -145,6 +145,7 @@
              'sconsdep' : 'false',
              'variables': '',
              'compress': 'false',
+             'use_base_dir': 'true',
              }
 
   def IsExcludedFromRc(self):
@@ -185,7 +186,8 @@
       return '\r'
     else:
       raise exception.UnexpectedAttribute(
-        "Attribute 'line_end' must be one of 'unix' (default), 'windows' or 'mac'")
+        "Attribute 'line_end' must be one of 'unix' (default), 'windows' or "
+        "'mac'")
 
   def GetCliques(self):
     return self.gatherer.GetCliques()
@@ -204,7 +206,24 @@
     return self.gatherer.GetHtmlResourceFilenames()
 
   def GetInputPath(self):
-    return self.gatherer.GetInputPath()
+    path = self.gatherer.GetInputPath()
+    if path is None:
+      return path
+
+    # Do not mess with absolute paths, that would make them invalid.
+    if os.path.isabs(os.path.expandvars(path)):
+      return path
+
+    # We have no control over code that calls ToRealPath later, so convert
+    # the path to be relative against our basedir.
+    if self.attrs.get('use_base_dir', 'true') != 'true':
+      # Normalize the directory path to use the appropriate OS separator.
+      # GetBaseDir() may return paths\like\this or paths/like/this, since it is
+      # read from the base_dir attribute in the grd file.
+      norm_base_dir = util.normpath(self.GetRoot().GetBaseDir())
+      return os.path.relpath(path, norm_base_dir)
+
+    return path
 
   def GetTextualIds(self):
     if not hasattr(self, 'gatherer'):
diff --git a/tools/grit/grit/node/structure_unittest.py b/tools/grit/grit/node/structure_unittest.py
index 19b9d6f..ed8290f 100755
--- a/tools/grit/grit/node/structure_unittest.py
+++ b/tools/grit/grit/node/structure_unittest.py
@@ -19,6 +19,8 @@
 import StringIO
 
 from grit import util
+from grit.node import empty
+from grit.node import misc
 from grit.node import structure
 from grit.format import rc
 
@@ -67,6 +69,31 @@
                             '</p>\n'), result)
     os.remove(filepath)
 
+  def testGetPath(self):
+    base_dir = util.PathFromRoot('grit/testdata')
+    grd = util.ParseGrdForUnittest('''
+        <structures>
+          <structure type="chrome_html" name="hello_tmpl" file="structure_variables.html" expand_variables="true" variables="GREETING=Hello,THINGS=foo,, bar,, baz,EQUATION=2+2==4,filename=simple" flattenhtml="true" use_base_dir="true"></structure>
+        </structures>''', base_dir)
+    grd.SetOutputLanguage('en')
+    grd.RunGatherers()
+    node, = grd.GetChildrenOfType(structure.StructureNode)
+    self.assertEqual(grd.ToRealPath(node.GetInputPath()),
+                     os.path.abspath(os.path.join(
+                         base_dir, ur'structure_variables.html')))
+
+  def testGetPathNoBasedir(self):
+    grd = util.ParseGrdForUnittest('''
+        <structures>
+          <structure type="chrome_html" name="hello_tmpl" file="structure_variables.html" expand_variables="true" variables="GREETING=Hello,THINGS=foo,, bar,, baz,EQUATION=2+2==4,filename=simple" flattenhtml="true" use_base_dir="false"></structure>
+        </structures>''', util.PathFromRoot('grit/testdata'))
+    grd.SetOutputLanguage('en')
+    grd.RunGatherers()
+    node, = grd.GetChildrenOfType(structure.StructureNode)
+    self.assertEqual(grd.ToRealPath(node.GetInputPath()),
+                     os.path.abspath(os.path.join(
+                         os.getcwd(), ur'structure_variables.html')))
+
   def testCompressGzip(self):
     test_data_root = util.PathFromRoot('grit/testdata')
     root = util.ParseGrdForUnittest('''
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index 11d7c99..f63bc5f 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -108,6 +108,13 @@
 
 grit_defines = []
 
+if (is_mac || is_win || is_linux || is_ios) {
+  grit_defines += [
+    "-D",
+    "scale_factors=2x",
+  ]
+}
+
 # Mac and iOS want Title Case strings.
 use_titlecase_in_grd_files = is_mac || is_ios
 if (use_titlecase_in_grd_files) {
@@ -137,8 +144,6 @@
   grit_defines += [
     "-D",
     "chromeos",
-    "-D",
-    "scale_factors=2x",
   ]
 }
 
@@ -184,13 +189,6 @@
   ]
 }
 
-if (is_mac || is_ios) {
-  grit_defines += [
-    "-D",
-    "scale_factors=2x",
-  ]
-}
-
 # When cross-compiling, explicitly pass the target system to grit.
 if (current_toolchain != host_toolchain) {
   if (is_android) {
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index bda2c60..a072f4d 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -314,7 +314,7 @@
     "messages": [23525],
   },
   "ash/components/ash_components_strings.grd": {
-    "messages": [24100],
+    "messages": [24130],
   },
   "ash/components/resources/ash_components_resources.grd": {
     "structures": [24350],
diff --git a/tools/infra/clobber.py b/tools/infra/clobber.py
index 9f493f6..f26b16b 100755
--- a/tools/infra/clobber.py
+++ b/tools/infra/clobber.py
@@ -73,14 +73,16 @@
 def main(raw_args):
   parser = argparse.ArgumentParser()
   parser.add_argument('--builder', required=True)
-  parser.add_argument('--pool', required=True, choices=['ci', 'try'])
+  parser.add_argument('--pool', required=True)
+  parser.add_argument(
+      '--project', default='chromium', choices=['chromium', 'infra'])
   parser.add_argument('-n', '--dry-run', action='store_true')
   args = parser.parse_args(raw_args)
 
   # Matches http://bit.ly/2WZO33P
-  h = hashlib.sha256('chromium/%s/%s' % (args.pool, args.builder))
+  h = hashlib.sha256('%s/%s/%s' % (args.project, args.pool, args.builder))
   cache_name = 'builder_%s_v2' % (h.hexdigest())
-  swarming_pool = 'luci.chromium.%s' % args.pool
+  swarming_pool = 'luci.%s.%s' % (args.project, args.pool)
 
   bots = _get_bots(swarming_pool, cache_name)
 
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 03472401..5f89146 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1193,6 +1193,7 @@
     msan = 'is_msan=true' in vals['gn_args']
     tsan = 'is_tsan=true' in vals['gn_args']
     cfi_diag = 'use_cfi_diag=true' in vals['gn_args']
+    java_coverage = 'jacoco_coverage=true' in vals['gn_args']
 
     test_type = isolate_map[target]['type']
 
@@ -1235,6 +1236,8 @@
           '--target', target,
           '--logdog-bin-cmd', '../../bin/logdog_butler',
           '--store-tombstones']
+      if java_coverage:
+        cmdline += ['--coverage-dir', '${ISOLATED_OUTDIR}']
     elif is_fuchsia and test_type != 'script':
       cmdline = [
           '../../testing/test_env.py',
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 8686b687..5898df7 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -13,6 +13,15 @@
   # config names (where each config name is a key in the 'configs' dict,
   # below). MB uses this dict to look up which config to use for a given bot.
   'masters': {
+    'chrome': {
+      'chromeos-amd64-generic-google-rel': 'official_cros_chrome_sdk',
+      'chromeos-betty-google-rel': 'official_cros_chrome_sdk_headless_ozone',
+      'linux-chromeos-google-rel': 'official_goma_chromeos_minimal_symbols',
+      'linux-google-rel': 'official_goma',
+      'mac-google-rel': 'official_goma',
+      'win-google-rel': 'official_goma_x86',
+    },
+
     'chromeos.chrome': {
       'chrome-tot-chromeos-amd64-generic': 'cros_chrome_sdk',
       'Chrome4CROS Packages': 'chromeos_with_codecs_release_bot',
@@ -78,6 +87,7 @@
       'x86 Emulator Tester': 'android_debug_static_bot_x86',
     },
 
+    # TODO(bpastene): Remove 'chromium.chrome' when it's been switched to 'chrome'
     'chromium.chrome': {
       'chromeos-amd64-generic-google-rel': 'official_cros_chrome_sdk',
       'chromeos-betty-google-rel': 'official_cros_chrome_sdk_headless_ozone',
@@ -471,7 +481,7 @@
       'WebKit Linux ASAN': 'asan_lsan_release_bot',
       'WebKit Linux Leak': 'release_bot',
       'WebKit Linux MSAN': 'msan_release_bot',
-      'android-asan': 'android_clang_asan_debug_bot',
+      'android-asan': 'android_clang_asan_release_bot',
       'win-asan': 'asan_clang_fuzzer_static_v8_heap_minimal_symbols_release',
     },
 
@@ -669,6 +679,13 @@
       'win_angle_deqp_rel_ng': 'deqp_release_trybot_x86',
     },
 
+    'tryserver.chrome': {
+      'chromeos-betty-chrome': 'official_cros_chrome_sdk_headless_ozone',
+      'linux-chrome': 'official_goma',
+      'linux-chromeos-chrome': 'official_goma_chromeos_minimal_symbols',
+      'win_chrome_official': 'official_goma_x86',
+    },
+
     'tryserver.chromium.chrome': {
       'chromeos-betty-chrome': 'official_cros_chrome_sdk_headless_ozone',
       'linux-chrome': 'official_goma',
@@ -900,6 +917,10 @@
       'android', 'clang', 'asan', 'debug_trybot', 'compile_only',
     ],
 
+    'android_clang_asan_release_bot': [
+      'android', 'clang', 'asan', 'release_bot', 'strip_debug_info', 'minimal_symbols',
+    ],
+
     'android_clang_tot_asan': [
       'android_without_codecs', 'clang_tot', 'asan', 'shared', 'debug', 'minimal_symbols',
       'strip_debug_info',
@@ -2271,7 +2292,7 @@
 
     # Used to pass the list of files to instrument for coverage to the compile
     # wrapper. See:
-    # https://cs.chromium.org/chromium/build/scripts/slave/recipe_modules/clang_coverage/api.py
+    # https://cs.chromium.org/chromium/build/scripts/slave/recipe_modules/code_coverage/api.py
     # and
     # https://cs.chromium.org/chromium/src/docs/clang_code_coverage_wrapper.md
     'partial_clang_instrumentation': {
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 56f8ad1..e98f8d6cb 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -538,6 +538,54 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="Accel_Desks_ActivateLeft">
+  <owner>afakhry@chromium.org</owner>
+  <description>
+    User activates the virtual desk positioned to the left of the currently
+    active one by pressing a keyboard shortcut.
+  </description>
+</action>
+
+<action name="Accel_Desks_ActivateRight">
+  <owner>afakhry@chromium.org</owner>
+  <description>
+    User activates the virtual desk positioned to the right of the currently
+    active one by pressing a keyboard shortcut.
+  </description>
+</action>
+
+<action name="Accel_Desks_MoveWindowLeft">
+  <owner>afakhry@chromium.org</owner>
+  <description>
+    User moves the currently active window (or the currently selected item in
+    overview mode) to an existing virtual desk to the left of the currently
+    active one by pressing a keyboard shortcut.
+  </description>
+</action>
+
+<action name="Accel_Desks_MoveWindowRight">
+  <owner>afakhry@chromium.org</owner>
+  <description>
+    User moves the currently active window (or the currently selected item in
+    overview mode) to an existing virtual desk to the right of the currently
+    active one by pressing a keyboard shortcut.
+  </description>
+</action>
+
+<action name="Accel_Desks_NewDesk">
+  <owner>afakhry@chromium.org</owner>
+  <description>
+    User creates a new virtual desk by pressing a keyboard shortcut.
+  </description>
+</action>
+
+<action name="Accel_Desks_RemoveDesk">
+  <owner>afakhry@chromium.org</owner>
+  <description>
+    User removes an existing virtual desk by pressing a keyboard shortcut.
+  </description>
+</action>
+
 <action name="Accel_Disable_Caps_Lock">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
@@ -21682,6 +21730,11 @@
   </description>
 </action>
 
+<action name="VoiceInteraction.Started.HomeButtonLongPress">
+  <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="VoiceInteraction.Started.Search_A">
   <owner>muyuanli@chromium.org</owner>
   <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f0a12c5..81db289e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -5965,6 +5965,11 @@
   <int value="1" label="Has slow paths with non-AA paint"/>
 </enum>
 
+<enum name="BooleanHasStrongValidators">
+  <int value="0" label="Lacks strong validators"/>
+  <int value="1" label="Has strong validators"/>
+</enum>
+
 <enum name="BooleanHasWithheldHosts">
   <int value="0" label="Does not have withheld hosts"/>
   <int value="1" label="Has withheld hosts"/>
@@ -7708,12 +7713,18 @@
 </enum>
 
 <enum name="ChromiumAndroidLinkerBrowserState">
+  <obsolete>
+    Deprecated as of 06/2019.
+  </obsolete>
   <int value="0" label="Normal, Random Address Load"/>
   <int value="1" label="Low memory, Fixed Address Load Success"/>
   <int value="2" label="Low memory, Fixed Address Load Failure"/>
 </enum>
 
 <enum name="ChromiumAndroidLinkerRendererState">
+  <obsolete>
+    Deprecated as of 06/2019.
+  </obsolete>
   <int value="0" label="Fixed Address Load Success"/>
   <int value="1" label="Fixed Address Load Failure"/>
   <int value="2" label="Fixed Address Load Not Attempted"/>
@@ -23774,6 +23785,8 @@
   <int value="2936" label="LazyLoadImageMissingDimensionsForLazy"/>
   <int value="2937" label="PeriodicBackgroundSyncGetTags"/>
   <int value="2938" label="PeriodicBackgroundSyncUnregister"/>
+  <int value="2939" label="CreateObjectURLMediaSourceFromWorker"/>
+  <int value="2940" label="CSSAtRuleProperty"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -25409,6 +25422,13 @@
   <int value="5" label="OS/2">Obsolete, no longer detected or supported.</int>
 </enum>
 
+<enum name="FTPStartResult">
+  <summary>Result of attempting to start an FTP request</summary>
+  <int value="0" label="Succeeded anonymously"/>
+  <int value="1" label="Succeeded with credentials"/>
+  <int value="2" label="Failed / credentials rejected"/>
+</enum>
+
 <enum name="GaiaSessionRestoreOutcome">
   <int value="0" label="Undefined"/>
   <int value="1" label="Success"/>
@@ -27776,6 +27796,11 @@
   <int value="2" label="Clicked, intent failed"/>
 </enum>
 
+<enum name="GoogleUpdateNotificationLaunchEvent">
+  <int value="0" label="Start"/>
+  <int value="1" label="Start activity failed"/>
+</enum>
+
 <enum name="GoogleUpdateUpgradeStatus">
   <int value="0" label="UPGRADE_STARTED"/>
   <int value="1" label="UPGRADE_CHECK_STARTED"/>
@@ -32893,6 +32918,9 @@
 </enum>
 
 <enum name="LibraryLoadFromApkStatus">
+  <obsolete>
+    Deprecated as of 06/2019.
+  </obsolete>
   <int value="0" label="Unknown"/>
   <int value="1" label="Not supported">obsolete</int>
   <int value="2" label="Supported">obsolete</int>
@@ -33733,6 +33761,7 @@
   <int value="-1703709912" label="enable-new-ntp"/>
   <int value="-1703308540" label="disable-webaudio"/>
   <int value="-1701123067" label="ShowManagedUi:enabled"/>
+  <int value="-1696619241" label="OmniboxWrapPopupPosition:disabled"/>
   <int value="-1696366449" label="disable-permissions-bubbles"/>
   <int value="-1695774453" label="skip-extra-ash-window-positioning"/>
   <int value="-1694353093" label="ArcVpn:disabled"/>
@@ -34425,6 +34454,8 @@
   <int value="-757282194" label="EnableFilesystemInIncognito:enabled"/>
   <int value="-750175757" label="ClientLoFi:enabled"/>
   <int value="-749048160" label="enable-panels"/>
+  <int value="-748571227"
+      label="AssistantEnableMediaSessionIntegration:enabled"/>
   <int value="-747463111" label="ContentSuggestionsNotifications:disabled"/>
   <int value="-746328467" label="ExpensiveBackgroundTimerThrottling:disabled"/>
   <int value="-744159181" label="disable-spdy-proxy-dev-auth-origin"/>
@@ -35236,6 +35267,7 @@
       label="enable-non-validating-reload-on-refresh-content"/>
   <int value="470011024" label="NonValidatingReloadOnNormalReload:enabled"/>
   <int value="471969274" label="MediaControlsExpandGesture:enabled"/>
+  <int value="473958125" label="OmniboxWrapPopupPosition:enabled"/>
   <int value="474743272" label="material-design-ink-drop"/>
   <int value="475858648"
       label="OverlayScrollbarFlashAfterAnyScrollUpdate:disabled"/>
@@ -36111,6 +36143,8 @@
   <int value="1785093465" label="enable-document-passive-event-listeners"/>
   <int value="1786229999" label="disable-md-downloads"/>
   <int value="1786386775" label="TranslateCompactUI:disabled"/>
+  <int value="1786692012"
+      label="AssistantEnableMediaSessionIntegration:disabled"/>
   <int value="1789517771" label="MacV2Sandbox:enabled"/>
   <int value="1789793147" label="HTTPSServerPreviewsUsingURLLoader:disabled"/>
   <int value="1792609232" label="NTPShortcuts:enabled"/>
@@ -38406,6 +38440,12 @@
   <int value="7" label="Generic"/>
 </enum>
 
+<enum name="MediaSourceExecutionContext">
+  <int value="0" label="Window"/>
+  <int value="1" label="Dedicated Worker"/>
+  <int value="2" label="Shared Worker"/>
+</enum>
+
 <enum name="MediaStatus">
   <int value="-30002" label="AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED"/>
   <int value="-30001" label="AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE"/>
@@ -45203,6 +45243,9 @@
 </enum>
 
 <enum name="PasswordManagerAndroidExportPasswordsProgressBarUsage">
+  <obsolete>
+    No longer recorded as of June 2019.
+  </obsolete>
   <int value="0" label="Not hidden because not shown"/>
   <int value="1" label="Hidden directly"/>
   <int value="2" label="Hidden with a delay"/>
@@ -45328,6 +45371,9 @@
     User is on an HTTP site where passwords are filled on account selection
     (FOAS).
   </int>
+  <int value="5" label="Touch To Fill">
+    User is browsing with the Touch To Fill feature enabled.
+  </int>
 </enum>
 
 <enum name="PasswordManagerHttpCredentialType">
@@ -54771,6 +54817,15 @@
   <int value="1" label="HAD_PREVIOUS_EXCEPTION"/>
 </enum>
 
+<enum name="SSLHandshakeDetails">
+  <int value="0" label="TLS 1.2 (or earlier) full handshake (2-RTT)"/>
+  <int value="1" label="TLS 1.2 (or earlier) resumption (1-RTT)"/>
+  <int value="2" label="TLS 1.2 full handshake with False Start (1-RTT)"/>
+  <int value="3" label="TLS 1.3 full handshake (1-RTT, usually)"/>
+  <int value="4" label="TLS 1.3 resumption handshake (1-RTT, usually)"/>
+  <int value="5" label="TLS 1.3 0-RTT handshake (0-RTT)"/>
+</enum>
+
 <enum name="SSLHashAlgorithm">
   <obsolete>
     Removed June 2016.
@@ -59157,7 +59212,7 @@
   <int value="10" label="ARGB"/>
   <int value="11" label="XRGB"/>
   <int value="12" label="RGB24"/>
-  <int value="13" label="RGB32"/>
+  <int value="13" label="RGB32 (Deprecated in M77)"/>
   <int value="14" label="MJPEG"/>
   <int value="15" label="MT21"/>
   <int value="16" label="YUV420P9"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2e697c6..98fe4870 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -2460,6 +2460,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.ManageSpace.ClearUnimportantTime" units="ms"
+    expires_after="2019-06-26">
+  <owner>dmurph@chromium.org</owner>
+  <owner>costan@chromium.org</owner>
+  <summary>
+    Records the time it takes to delete all unimportant sites when the user
+    initiates the 'Clear Unimportant Sites' action in the 'Manage Space' screen
+    for Chrome.
+  </summary>
+</histogram>
+
 <histogram name="Android.ManageSpace.TotalDiskUsageMB" units="MB">
   <owner>dmurph@chromium.org</owner>
   <summary>
@@ -8970,6 +8981,18 @@
   </summary>
 </histogram>
 
+<histogram name="Autofill.CardUnmask.CvcLength" units="digits"
+    expires_after="M83">
+  <owner>jsaul@google.com</owner>
+  <owner>jiahuiguo@chromium.org</owner>
+<!-- Name completed by histogram_suffixes name="AutofillCreditCardUnmaskReason" -->
+
+  <summary>
+    Records the length of CVCs sent to Payments when making card unmask calls,
+    to help determine if blank or invalid length CVCs are being sent.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.CardUploadDecision" enum="AutofillCardUploadDecision"
     expires_after="2016-02-08">
   <obsolete>
@@ -13609,9 +13632,13 @@
 
 <histogram base="true" name="Blink.ResourceLoadScheduler.PeakRequests"
     units="requests">
+  <obsolete>
+    Removed as of 6/2019.
+  </obsolete>
 <!-- Name completed by histogram_suffixes name="ResourceLoadScheduler.FrameType" -->
 
   <owner>ksakamoto@chromium.org</owner>
+  <owner>toyoshim@chromium.org</owner>
   <summary>
     The largest number of outstanding resource requests issued by a frame until
     the network 2-quiet (no more than 2 active network requests for 1 seconds).
@@ -13630,7 +13657,11 @@
 
 <histogram name="Blink.ResourceLoadScheduler.ThrottlingStateChangeCount"
     units="changes" expires_after="M77">
+  <obsolete>
+    Removed as of 6/2019.
+  </obsolete>
   <owner>toyoshim@chromium.org</owner>
+  <owner>ksakamoto@chromium.org</owner>
   <summary>
     Count how many times the scheduler has changed throttling status from the
     frame creation until network activity quiets.
@@ -13639,9 +13670,13 @@
 
 <histogram base="true" name="Blink.ResourceLoadScheduler.TotalDecodedBytes"
     units="bytes">
+  <obsolete>
+    Removed as of 6/2019.
+  </obsolete>
 <!-- Name completed by histogram_suffixes name="ResourceLoadScheduler.FrameType" -->
 
   <owner>toyoshim@chromium.org</owner>
+  <owner>altimin@chromium.org</owner>
   <summary>
     Total received data size in bytes to load resources from the frame creation
     until network activity quiets.
@@ -13661,9 +13696,13 @@
 
 <histogram base="true" name="Blink.ResourceLoadScheduler.TotalTrafficBytes"
     units="bytes">
+  <obsolete>
+    Removed as of 6/2019.
+  </obsolete>
 <!-- Name completed by histogram_suffixes name="ResourceLoadScheduler.FrameType" -->
 
   <owner>toyoshim@chromium.org</owner>
+  <owner>altimin@chromium.org</owner>
   <summary>
     Total traffic data in bytes transferred over networks to load resources from
     the frame creation until network activity quiets.
@@ -13838,6 +13877,16 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.ThreadedIconLoader.LoadTime" units="ms"
+    expires_after="2019-12-01">
+  <owner>nator@chromium.org</owner>
+  <owner>rayankans@chromium.org</owner>
+  <summary>
+    The number of milliseconds it took to finish successfully loading an icon
+    using ThreadedIconLoader.
+  </summary>
+</histogram>
+
 <histogram name="Blink.UseCounter.AnimatedCSSProperties"
     enum="MappedCSSProperties" expires_after="never">
 <!-- expires-never: used by Chrome Platform Status dashboard -->
@@ -15860,7 +15909,9 @@
 </histogram>
 
 <histogram name="BrowserRenderProcessHost.ChildLaunchFailureCodes"
-    enum="LaunchErrorCodes" expires_after="M77">
+    enum="LaunchErrorCodes" expires_after="never">
+<!-- expires-never: For monitoring stability regressions. -->
+
   <owner>wfh@chromium.org</owner>
   <summary>
     The launch error codes for failed renderer process launches.
@@ -15874,7 +15925,9 @@
 </histogram>
 
 <histogram name="BrowserRenderProcessHost.DisconnectedAlive"
-    enum="RendererType" expires_after="M77">
+    enum="RendererType" expires_after="never">
+<!-- expires-never: For monitoring stability regressions. -->
+
   <owner>wfh@chromium.org</owner>
   <summary>
     Count of renderer process crashes that we miscounted because we took the
@@ -17073,7 +17126,9 @@
 </histogram>
 
 <histogram name="ChildProcess.DisconnectedAlive2" enum="ProcessType2"
-    expires_after="M77">
+    expires_after="never">
+<!-- expires-never: For monitoring stability regressions. -->
+
   <owner>wfh@chromium.org</owner>
   <summary>
     Count of child process abnormal channel disconnects that are not classified
@@ -17146,6 +17201,9 @@
 
 <histogram name="ChildProcess.InvalidSandboxStateCrash.NoStartupWindow"
     enum="BooleanPresent" expires_after="M77">
+  <obsolete>
+    Removed 6/2019.
+  </obsolete>
   <owner>wfh@chromium.org</owner>
   <summary>
     Whether the browser command line had the switch --no-startup-window when a
@@ -18014,6 +18072,9 @@
 
 <histogram name="ChromiumAndroidLinker.BrowserStates"
     enum="ChromiumAndroidLinkerBrowserState" expires_after="M77">
+  <obsolete>
+    Deprecated as of 06/2019.
+  </obsolete>
   <owner>digit@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <owner>lizeb@chromium.org</owner>
@@ -18035,6 +18096,9 @@
 
 <histogram name="ChromiumAndroidLinker.LibraryLoadFromApkStatus"
     enum="LibraryLoadFromApkStatus" expires_after="M77">
+  <obsolete>
+    Deprecated as of 06/2019.
+  </obsolete>
   <owner>petrcermak@chromium.org</owner>
   <summary>
     Whether the device supports loading a library directly from the APK file.
@@ -18074,6 +18138,9 @@
 
 <histogram name="ChromiumAndroidLinker.RendererStates"
     enum="ChromiumAndroidLinkerRendererState" expires_after="M77">
+  <obsolete>
+    Deprecated as of 06/2019.
+  </obsolete>
   <owner>simonb@chromium.org</owner>
   <summary>
     Whether relro sharing was attempted for a renderer process, and if
@@ -18610,7 +18677,7 @@
 </histogram>
 
 <histogram name="ComponentUpdater.ChromeOS.MountTime" units="ms"
-    expires_after="M77">
+    expires_after="2021-06-26">
   <owner>xiaochu@chromium.org</owner>
   <summary>Chrome OS only. Time it takes to mount a component image.</summary>
 </histogram>
@@ -19804,6 +19871,9 @@
 
 <histogram name="ConnectivityDetector.FromSystem" enum="Boolean"
     expires_after="2019-03-30">
+  <obsolete>
+    Deprecated 6/2019. No longer used.
+  </obsolete>
   <owner>jianli@chromium.org</owner>
   <summary>
     Whether or not the network connectivity info, i.e. validated, is provided by
@@ -19816,6 +19886,9 @@
 
 <histogram name="ConnectivityDetector.Probe.HttpResponseCode"
     enum="HttpResponseCode" expires_after="2019-03-30">
+  <obsolete>
+    Deprecated 6/2019. No longer used.
+  </obsolete>
   <owner>jianli@chromium.org</owner>
   <summary>
     The HTTP response code returned from a HTTP probe for the purpose of
@@ -19825,6 +19898,9 @@
 
 <histogram name="ConnectivityDetector.Probe.Result" enum="ProbeResult"
     expires_after="2019-03-30">
+  <obsolete>
+    Deprecated 6/2019. No longer used.
+  </obsolete>
   <owner>jianli@chromium.org</owner>
   <summary>
     The result from a HTTP probe for the purpose of checking network
@@ -19834,6 +19910,9 @@
 
 <histogram name="ConnectivityDetector.Probe.ValidationTime" units="ms"
     expires_after="2019-03-30">
+  <obsolete>
+    Deprecated 6/2019. No longer used.
+  </obsolete>
   <owner>jianli@chromium.org</owner>
   <summary>
     The total duration measuring all the HTTP probes and backoff time taken when
@@ -19843,6 +19922,9 @@
 
 <histogram name="ConnectivityDetector.Probe.ValidationUrl" enum="ProbeUrlType"
     expires_after="2019-03-30">
+  <obsolete>
+    Deprecated 6/2019. No longer used.
+  </obsolete>
   <owner>jianli@chromium.org</owner>
   <summary>
     Records type of url, default or fallback URL, resulting in successful
@@ -29958,7 +30040,7 @@
 </histogram>
 
 <histogram base="true" name="Download.InsecureBlocking.Extensions"
-    expires_after="M80">
+    enum="InsecureDownloadExtensions" expires_after="M80">
   <owner>jdeblasio@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <owner>cthomp@chromium.org</owner>
@@ -30210,6 +30292,10 @@
 
 <histogram name="Download.MapErrorNetworkFailed" enum="NetErrorCodes"
     expires_after="M77">
+  <obsolete>
+    Deprecated in 04/2019 after network service is enabled by default. Use
+    Download.MapErrorNetworkFailed.NetworkService instead.
+  </obsolete>
   <owner>dtrainor@chromium.org</owner>
   <summary>
     Network error that produced a DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED
@@ -30217,6 +30303,17 @@
   </summary>
 </histogram>
 
+<histogram name="Download.MapErrorNetworkFailed.NetworkService"
+    enum="NetErrorCodes" expires_after="M78">
+  <owner>dtrainor@chromium.org</owner>
+  <owner>qinmin@chromium.org</owner>
+  <summary>
+    Network error that produced a DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED
+    result in DownloadResourceHandler::OnResponseCompleted(). Recorded when
+    running with the network service enabled.
+  </summary>
+</histogram>
+
 <histogram name="Download.MapWinShErrorAccessDenied"
     enum="SpecialShFileOperationCodes" expires_after="2018-08-14">
   <obsolete>
@@ -30506,6 +30603,15 @@
   </summary>
 </histogram>
 
+<histogram name="Download.ResumptionComplete.HasStrongValidators"
+    enum="BooleanHasStrongValidators" expires_after="2020-01-30">
+  <owner>qinmin@chromium.org</owner>
+  <owner>xingliu@chromium.org</owner>
+  <summary>
+    Records whether strong validators are present when download completes.
+  </summary>
+</histogram>
+
 <histogram name="Download.ResumptionRestart.Counts"
     enum="DownloadResumptionRestartCountType" expires_after="M79">
   <owner>qinmin@chromium.org</owner>
@@ -30526,6 +30632,15 @@
   </summary>
 </histogram>
 
+<histogram name="Download.ResumptionStart.HasStrongValidators"
+    enum="BooleanHasStrongValidators" expires_after="2020-01-30">
+  <owner>qinmin@chromium.org</owner>
+  <owner>xingliu@chromium.org</owner>
+  <summary>
+    Records whether strong validators are present when download is resumed.
+  </summary>
+</histogram>
+
 <histogram name="Download.SavePackage" enum="DownloadSavePackageEvent"
     expires_after="M77">
   <owner>dtrainor@chromium.org</owner>
@@ -31986,8 +32101,8 @@
 </histogram>
 
 <histogram name="EmbeddedWorkerInstance.ProcessCreated" enum="BooleanCreated"
-    expires_after="M77">
-  <owner>ksakamoto@chromium.org</owner>
+    expires_after="2020-06-30">
+  <owner>falken@chromium.org</owner>
   <summary>
     Whether a new renderer process was created for the EmbeddedWorkerInstance or
     existing one was used. Only recorded for installed workers.
@@ -36718,11 +36833,12 @@
 
 <histogram name="Event.VizHitTest.AsyncHitTestReasons"
     enum="AsyncHitTestReasons">
-  <owner>sunxd@chromium.org</owner>
-  <owner>event-targeting@chromium.org</owner>
+  <owner>yigu@chromium.org</owner>
   <summary>
     Tracks the reasons why sychronous hit testing could not be done for each hit
     test requests processd by HitTestQuery.
+
+    Team: event-targeting@chromium.org.
   </summary>
 </histogram>
 
@@ -36790,11 +36906,12 @@
 
 <histogram name="Event.VizHitTestSurfaceLayer.ResultsMatch"
     enum="VizHitTestResultsMatchEnums">
-  <owner>sunxd@chromium.org</owner>
-  <owner>event-targeting@chromium.org</owner>
+  <owner>yigu@chromium.org</owner>
   <summary>
     This tracks how often the cc layer based hit testing fast path result
     matches or doesn't match the asynchronous blink hit test result.
+
+    Team: event-targeting@chromium.org.
   </summary>
 </histogram>
 
@@ -37829,8 +37946,9 @@
 </histogram>
 
 <histogram name="Extensions.BackgroundPageLoadTime2" units="ms"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The time taken for an extension's persistent background page to load its
     initial URL.
@@ -37838,8 +37956,11 @@
 </histogram>
 
 <histogram name="Extensions.BackgroundPageType"
-    units="ExtensionBackgroundPageType" expires_after="M77">
+    units="ExtensionBackgroundPageType" expires_after="never">
+<!-- expires-never: Used for monitoring user extension usage. -->
+
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The type (if any) of background page the extension has. Recorded for
     installed extensions on startup.
@@ -38669,8 +38790,9 @@
 </histogram>
 
 <histogram name="Extensions.DidCreateScriptContext_Blessed" units="ms"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Part of the suite of Extensions.DidCreateScriptContext_* metrics. Records
     the time taken to install Extension JavaScript bindings per blessed
@@ -38680,8 +38802,9 @@
 </histogram>
 
 <histogram name="Extensions.DidCreateScriptContext_BlessedWebPage" units="ms"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Part of the suite of Extensions.DidCreateScriptContext_* metrics. Records
     the time taken to install Extension JavaScript bindings per blessed web page
@@ -38689,8 +38812,10 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.DidCreateScriptContext_ContentScript" units="ms">
+<histogram name="Extensions.DidCreateScriptContext_ContentScript" units="ms"
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Part of the suite of Extensions.DidCreateScriptContext_* metrics. Records
     the time taken to install Extension JavaScript bindings per content script
@@ -38699,8 +38824,9 @@
 </histogram>
 
 <histogram name="Extensions.DidCreateScriptContext_LockScreenExtension"
-    units="ms" expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Part of the suite of Extensions.DidCreateScriptContext_* metrics. Records
     the time taken to install Extension JavaScript bindings per content script
@@ -38711,8 +38837,9 @@
 </histogram>
 
 <histogram name="Extensions.DidCreateScriptContext_Unblessed" units="ms"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Part of the suite of Extensions.DidCreateScriptContext_* metrics. Records
     the time taken to install Extension JavaScript bindings per unblessed
@@ -38721,8 +38848,9 @@
 </histogram>
 
 <histogram name="Extensions.DidCreateScriptContext_Unspecified" units="ms"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Part of the suite of Extensions.DidCreateScriptContext_* metrics. Records
     the time taken to install Extension JavaScript bindings in an unknown type
@@ -38731,8 +38859,9 @@
 </histogram>
 
 <histogram name="Extensions.DidCreateScriptContext_WebPage" units="ms"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Part of the suite of Extensions.DidCreateScriptContext_* metrics. Records
     the time taken to install Extension JavaScript bindings per web page context
@@ -38741,8 +38870,9 @@
 </histogram>
 
 <histogram name="Extensions.DidCreateScriptContext_WebUI" units="ms"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Part of the suite of Extensions.DidCreateScriptContext_* metrics. Records
     the time taken to install Extension JavaScript bindings per WebUI context
@@ -38751,8 +38881,9 @@
 </histogram>
 
 <histogram name="Extensions.DidInitializeServiceWorkerContextOnWorkerThread"
-    units="ms" expires_after="M77">
+    units="ms" expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Records the time taken to install Extension JavaScript bindings per service
     worker context.
@@ -38944,8 +39075,10 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.EventPageIdleTime" units="ms" expires_after="M77">
+<histogram name="Extensions.EventPageIdleTime" units="ms"
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>The time an extension's event page has spent unloaded.</summary>
 </histogram>
 
@@ -40406,8 +40539,9 @@
 </histogram>
 
 <histogram name="Extensions.Messaging.MessageSize" units="bytes"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The size, in bytes, of a message sent from an extension using one of the
     messaging APIs (e.g. chrome.runtime.sendMessage). All message sizes are
@@ -40449,6 +40583,9 @@
 
 <histogram name="Extensions.NetworkDelayRegistryLoad" units="ms"
     expires_after="M77">
+  <obsolete>
+    Deprecated June 2019.
+  </obsolete>
   <owner>battre@chromium.org</owner>
   <summary>
     Time that network requests were blocked due to relevant rule registries
@@ -40987,8 +41124,9 @@
 </histogram>
 
 <histogram name="Extensions.ProcessManagerStartupHostsTime" units="ms"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The time taken to start up persistent background pages for extensions in
     ExtensionProcessManager when the extension system notifies that it is ready.
@@ -40996,7 +41134,7 @@
 </histogram>
 
 <histogram name="Extensions.ResetPermissionsIncrease" enum="Boolean"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -41415,8 +41553,9 @@
 </histogram>
 
 <histogram name="Extensions.UninstallDialogAction"
-    enum="ExtensionUninstallDialogAction" expires_after="M77">
+    enum="ExtensionUninstallDialogAction" expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The action that was taken from an extension uninstall dialog. Recorded once
     per dialog shown. Only recorded if all possible actions (including report
@@ -43305,7 +43444,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.AttemptsCountBeforeSuccess" units="count"
-    expires_after="2019-08-09">
+    expires_after="M85">
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
@@ -43314,7 +43453,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.AuthSuccessful" enum="BooleanSuccess"
-    expires_after="2019-08-09">
+    expires_after="M85">
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
@@ -43324,7 +43463,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.EnrolledFingerCount" units="count"
-    expires_after="2019-08-14">
+    expires_after="M85">
   <owner>norvez@chromium.org</owner>
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
@@ -43332,7 +43471,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.Match.Duration.Capture" units="ms"
-    expires_after="2019-08-14">
+    expires_after="M85">
   <owner>norvez@chromium.org</owner>
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
@@ -43343,7 +43482,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.Match.Duration.Matcher" units="ms"
-    expires_after="2019-08-14">
+    expires_after="M85">
   <owner>norvez@chromium.org</owner>
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
@@ -43353,7 +43492,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.Match.Duration.Overall" units="ms"
-    expires_after="2019-08-14">
+    expires_after="M85">
   <owner>norvez@chromium.org</owner>
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
@@ -43364,7 +43503,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.NoMatch.Duration.Capture" units="ms"
-    expires_after="2019-08-14">
+    expires_after="M85">
   <owner>norvez@chromium.org</owner>
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
@@ -43375,7 +43514,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.NoMatch.Duration.Matcher" units="ms"
-    expires_after="2019-08-14">
+    expires_after="M85">
   <owner>norvez@chromium.org</owner>
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
@@ -43385,7 +43524,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.NoMatch.Duration.Overall" units="ms"
-    expires_after="2019-08-14">
+    expires_after="M85">
   <owner>norvez@chromium.org</owner>
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
@@ -43396,7 +43535,7 @@
 </histogram>
 
 <histogram name="Fingerprint.UnlockEnabled" enum="BooleanEnabled"
-    expires_after="2019-08-09">
+    expires_after="M85">
   <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
@@ -43792,8 +43931,9 @@
 </histogram>
 
 <histogram name="GCM.CheckinRequestStatus" enum="GCMCheckinRequestStatus"
-    expires_after="M77">
+    expires_after="M88">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>Status code of the outcome of a GCM checkin request.</summary>
 </histogram>
 
@@ -43829,8 +43969,9 @@
   <summary>URL response and error codes from GCM connection attempts.</summary>
 </histogram>
 
-<histogram name="GCM.ConnectionLatency" units="ms" expires_after="M77">
+<histogram name="GCM.ConnectionLatency" units="ms" expires_after="M88">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     The time between the initiation of a connection and the successful
     completion of it.
@@ -43861,8 +44002,9 @@
 </histogram>
 
 <histogram name="GCM.Crypto.CreateKeySuccessRate" enum="BooleanSuccess"
-    expires_after="M77">
+    expires_after="M88">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     Success rate of writing new keying material to the GCM key store.
   </summary>
@@ -43870,6 +44012,7 @@
 
 <histogram name="GCM.Crypto.DecryptMessageResult" enum="GCMDecryptionResult">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     Result of decryption on a received GCM message, which includes unencrypted
     messages, successfully decrypted messages and decryption failures.
@@ -43878,6 +44021,7 @@
 
 <histogram name="GCM.Crypto.EncryptMessageResult" enum="GCMEncryptionResult">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     Result of encryption of outgoing GCM message, which includes successfully
     encrypted messages and encryption failures.
@@ -43885,7 +44029,7 @@
 </histogram>
 
 <histogram name="GCM.Crypto.GCMDatabaseUpgradeResult" enum="Boolean"
-    expires_after="M77">
+    expires_after="M80">
   <owner>nator@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <owner>rayankans@chromium.org</owner>
@@ -43898,8 +44042,9 @@
 </histogram>
 
 <histogram name="GCM.Crypto.GetKeySuccessRate" enum="BooleanSuccess"
-    expires_after="M77">
+    expires_after="M88">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     Success rate of reading keying material from the GCM key store.
   </summary>
@@ -43907,29 +44052,33 @@
 
 <histogram name="GCM.Crypto.InitKeyStoreSuccessRate" enum="BooleanSuccess">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     Success rate of initializing the LevelDB back-end of the GCM key store.
   </summary>
 </histogram>
 
 <histogram name="GCM.Crypto.LoadKeyStoreSuccessRate" enum="BooleanSuccess"
-    expires_after="M77">
+    expires_after="M88">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     Success rate of loading existing keying material in the GCM key store.
   </summary>
 </histogram>
 
 <histogram name="GCM.Crypto.RemoveKeySuccessRate" enum="BooleanSuccess"
-    expires_after="M77">
+    expires_after="M88">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     Success rate of removing keying material from the GCM key store.
   </summary>
 </histogram>
 
-<histogram name="GCM.Database.Open" enum="LevelDBStatus" expires_after="M77">
+<histogram name="GCM.Database.Open" enum="LevelDBStatus" expires_after="M88">
   <owner>cmumford@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
   <summary>The result of a database open attempt by the GCM store.</summary>
 </histogram>
 
@@ -43941,8 +44090,9 @@
 </histogram>
 
 <histogram name="GCM.DataMessageReceived" enum="BooleanReceived"
-    expires_after="M77">
+    expires_after="M88">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>
     Number of DATA_MESSAGE messages received from Google Cloud Messaging for
     which a matching registration exists. Recorded when processing the received
@@ -43988,6 +44138,9 @@
 
 <histogram name="GCM.FirstReceivedDataMessageLatencyAfterConnection" units="ms"
     expires_after="M77">
+  <obsolete>
+    Removed in June 2019.
+  </obsolete>
   <owner>peter@chromium.org</owner>
   <summary>
     The time between the successful completion of the connection and the arrival
@@ -44073,6 +44226,9 @@
 
 <histogram name="GCM.ReceivedDataMessageIntervalWithinBurst" units="ms"
     expires_after="M77">
+  <obsolete>
+    Removed in June 2019.
+  </obsolete>
   <owner>peter@chromium.org</owner>
   <summary>
     Interval between messages within a received GCM data message burst.
@@ -44080,7 +44236,7 @@
 </histogram>
 
 <histogram name="GCM.RegistrationCacheStatus" enum="GCMRegistrationCacheStatus"
-    expires_after="M77">
+    expires_after="M80">
   <owner>nator@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <owner>rayankans@chromium.org</owner>
@@ -44229,8 +44385,9 @@
 </histogram>
 
 <histogram name="GCM.UnregistrationRequestStatus"
-    enum="GCMUnregistrationRequestStatus" expires_after="M77">
+    enum="GCMUnregistrationRequestStatus" expires_after="M88">
   <owner>peter@chromium.org</owner>
+  <owner>zea@chromium.org</owner>
   <summary>Status code of the outcome of a GCM unregistration request.</summary>
 </histogram>
 
@@ -44656,6 +44813,9 @@
 
 <histogram name="GooglePlayServices.ConnectionResult"
     enum="GooglePlayServicesConnectionResult" expires_after="M77">
+  <obsolete>
+    Removed from code June 2019. See https://crbug.com/490710 for context.
+  </obsolete>
   <owner>wnwen@chromium.org</owner>
   <summary>
     ConnectionResult error codes resulting from attempts to check whether or not
@@ -44667,6 +44827,9 @@
 
 <histogram name="GooglePlayServices.ErrorHandlerAction"
     enum="GooglePlayServicesErrorHandlerAction" expires_after="M77">
+  <obsolete>
+    Removed from code June 2019. See https://crbug.com/490710 for context.
+  </obsolete>
   <owner>wnwen@chromium.org</owner>
   <summary>
     Types of action taken in response to Google Play Services user-recoverable
@@ -44859,6 +45022,15 @@
   </summary>
 </histogram>
 
+<histogram name="GoogleUpdate.Notification.LaunchEvent"
+    enum="GoogleUpdateNotificationLaunchEvent" expires_after="2020-06-26">
+  <owner>xingliu@chromium.org</owner>
+  <summary>
+    (Android-only) Records events when try to launch the update work flow via an
+    update notification.
+  </summary>
+</histogram>
+
 <histogram name="GoogleUpdate.Result.Session" enum="BooleanSuccess"
     expires_after="2019-11-30">
 <!-- Name completed by histogram_suffixes name="GoogleUpdate.Result.UpdateType" -->
@@ -51923,7 +52095,7 @@
 </histogram>
 
 <histogram name="Launch.WebAppDisplayMode" enum="WebAppDisplayMode"
-    expires_after="M77">
+    expires_after="M87">
   <owner>peter@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
   <summary>
@@ -53693,8 +53865,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.StreamBrokerDisconnectReason"
-    enum="AudioInputStreamDisconnectReason" expires_after="2019-08-01">
-  <owner>jonasolsson@chromium.org</owner>
+    enum="AudioInputStreamDisconnectReason" expires_after="2020-08-01">
   <owner>maxmorin@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -53851,8 +54022,9 @@
 </histogram>
 
 <histogram name="Media.Audio.CoreAudioDispatchOverrideLookupEvent"
-    enum="CoreAudioDispatchOverrideLookupEvent" expires_after="M77">
+    enum="CoreAudioDispatchOverrideLookupEvent" expires_after="2020-02-01">
   <owner>ossu@chromium.org</owner>
+  <owner>grunell@chromium.org</owner>
   <summary>
     Logged whenever the CoreAudio dispatch override hotfix needs to look up the
     calling function using dladdr(). Once ResumeIO and PauseIO have been
@@ -54049,7 +54221,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.CaptureDelayMs" units="ms"
-    expires_after="M77">
+    expires_after="2019-12-01">
   <owner>ossu@chromium.org</owner>
   <owner>grunell@chromium.org</owner>
   <summary>
@@ -54060,7 +54232,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.CaptureDelayVarianceMs"
-    units="ms squared" expires_after="M77">
+    units="ms squared" expires_after="2019-12-01">
   <owner>ossu@chromium.org</owner>
   <owner>grunell@chromium.org</owner>
   <summary>
@@ -54071,7 +54243,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.RenderDelayMs" units="ms"
-    expires_after="M77">
+    expires_after="2019-12-01">
   <owner>ossu@chromium.org</owner>
   <owner>grunell@chromium.org</owner>
   <summary>
@@ -54082,7 +54254,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.RenderDelayVarianceMs"
-    units="ms squared" expires_after="M77">
+    units="ms squared" expires_after="2019-12-01">
   <owner>ossu@chromium.org</owner>
   <owner>grunell@chromium.org</owner>
   <summary>
@@ -54093,7 +54265,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.TotalDelayMs" units="ms"
-    expires_after="M77">
+    expires_after="2019-12-01">
   <owner>ossu@chromium.org</owner>
   <owner>grunell@chromium.org</owner>
   <summary>
@@ -54103,7 +54275,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.TotalDelayVarianceMs"
-    units="ms squared" expires_after="M77">
+    units="ms squared" expires_after="2019-12-01">
   <owner>ossu@chromium.org</owner>
   <owner>grunell@chromium.org</owner>
   <summary>
@@ -54173,6 +54345,9 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.BrowserCallbackRegularity">
+  <obsolete>
+    Deprecated June 2019.
+  </obsolete>
   <owner>olka@chromium.org</owner>
   <summary>
     Reflects how regularly browser issues audio requests to renderer. Depends on
@@ -55513,6 +55688,9 @@
 </histogram>
 
 <histogram name="Media.Duration2" units="ms" expires_after="M77">
+  <obsolete>
+    Removed 06/2019. Not useful.
+  </obsolete>
   <owner>dalecurtis@chromium.org</owner>
   <summary>Duration in milliseconds of HTML5 media (when known).</summary>
 </histogram>
@@ -56763,6 +56941,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.MSE.ExecutionContext" enum="MediaSourceExecutionContext"
+    expires_after="M81">
+  <owner>wolenetz@chromium.org</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    For each MediaSource instance, records the type of thread that created the
+    instance.
+  </summary>
+</histogram>
+
 <histogram name="Media.MSE.LateAudioFrames"
     units="late frames per million frames" expires_after="2016-10-03">
   <obsolete>
@@ -57771,7 +57959,9 @@
 </histogram>
 
 <histogram name="Media.URLScheme2" enum="URLSchemeForHistogram"
-    expires_after="M77">
+    expires_after="never">
+<!-- expires-never: Codec support planning metric. -->
+
   <owner>media-dev@chromium.org</owner>
   <summary>
     URL scheme used with HTML5 media; only recorded for src=URL playbacks and
@@ -58583,6 +58773,9 @@
 
 <histogram name="Media.VideoPixelFormatUnion" enum="VideoPixelFormatUnion"
     expires_after="M77">
+  <obsolete>
+    Pixel format has been removed from demuxers. 06/2019
+  </obsolete>
   <owner>mcasas@chromium.org</owner>
   <owner>emircan@chromium.org</owner>
   <summary>
@@ -63086,7 +63279,7 @@
 </histogram>
 
 <histogram name="MobileStartup.IntentToCreationTime" units="ms"
-    expires_after="M77">
+    expires_after="M90">
   <owner>twellington@chromium.org</owner>
   <summary>
     Android: The time it takes from creating an intent for ChromeActivity to
@@ -69626,6 +69819,16 @@
   </summary>
 </histogram>
 
+<histogram name="Net.FTP.StartResult" enum="FTPStartResult" expires_after="M79">
+  <owner>tervay@google.com</owner>
+  <owner>asanka@chromium.org</owner>
+  <summary>
+    Each bucket is the result of whether or not the initiated FTP transaction.
+    Successful transactions are recorded as either authenticated or
+    unauthenticated.
+  </summary>
+</histogram>
+
 <histogram name="Net.FtpDataConnectionErrorCount" enum="FtpDataConnectionError"
     expires_after="2019-04-23">
   <obsolete>
@@ -75122,6 +75325,16 @@
   </summary>
 </histogram>
 
+<histogram name="Net.SSLHandshakeDetails" enum="SSLHandshakeDetails">
+  <owner>davidben@chromium.org</owner>
+  <summary>
+    For each successful TLS handshake, what kind of handshake was used. This
+    metric distinguishes TLS 1.2 (or earlier) and TLS 1.3 as they have very
+    different resumption and round-trip behaviors. It also distinguishes full
+    handshakes, resumption, 0-RTT, and False Start.
+  </summary>
+</histogram>
+
 <histogram name="Net.SSLHostInfoDNSLookup" units="ms"
     expires_after="2015-11-11">
   <obsolete>
@@ -77085,8 +77298,10 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.3GPPRegistrationDelayedDrop"
-    enum="NetworkCellular3GPPRegistrationDelayedDrop" expires_after="M77">
+    enum="NetworkCellular3GPPRegistrationDelayedDrop"
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network diagnostic metric sampling the number of cellular network
     flakes. A network flake occurs when the signal strength goes below detection
@@ -77095,8 +77310,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.AutoConnectTotalTime" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network diagnostic metric sampling the total amount of time spent
     from the start of the first auto-connect request until when the cellular
@@ -77104,8 +77320,10 @@
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Cellular.AutoConnectTries" expires_after="M77">
+<histogram name="Network.Shill.Cellular.AutoConnectTries"
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network diagnostic metric sampling the number of auto-connect
     tries that were attempted before the cellular modem successfully connected
@@ -77114,8 +77332,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.DevicePresenceStatus"
-    enum="BooleanPresent" expires_after="M77">
+    enum="BooleanPresent" expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network metric that tracks the presence of a Cellular device in
     the system. A sample is emitted once every 3 minutes.
@@ -77137,8 +77356,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.Disconnect"
-    enum="NetworkDisconnectType">
+    enum="NetworkDisconnectType" expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network usage metric that tracks whether the cellular network was
     disconnected due to an error or was explicitly disconnected by the user.
@@ -77146,8 +77366,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.Drop" enum="NetworkCellularTechnology"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS cellular network metric that tracks the number of drops based on
     the network technology.
@@ -77170,8 +77391,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.ExpiredLeaseLengthSeconds2"
-    units="seconds">
+    units="seconds" expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric that tracks the length of a lease for a
     cellular network at the time it expired without the DHCP client being able
@@ -77180,8 +77402,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.IPv6ConnectivityStatus"
-    enum="IPv6ConnectivityStatus" expires_after="M77">
+    enum="IPv6ConnectivityStatus" expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network metric that tracks the presence of complete IPv6
     configuration at the time when cellular connection is established.
@@ -77189,8 +77412,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.NetworkConnectionIPType"
-    enum="NetworkConnectionIPType" expires_after="M77">
+    enum="NetworkConnectionIPType" expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network metric that tracks the types of IP configuration used for
     establishing cellular connections.
@@ -77198,16 +77422,19 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.OutOfCreditsReason"
-    enum="NetworkCellularOutOfCreditsReason">
+    enum="NetworkCellularOutOfCreditsReason" expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS cellular network metric that tracks the number of out-of-credits
     detected based on the cause that triggered the out-of-credits.
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Cellular.PortalAttempts" expires_after="M77">
+<histogram name="Network.Shill.Cellular.PortalAttempts"
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network diagnostic metric sampling the number of portal detection
     attempts per pass for a cellular network. This includes failure, timeout and
@@ -77216,8 +77443,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.PortalAttemptsToOnline"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network diagnostic metric sampling the total number of portal
     detection attempts performed for a cellular network between the Connected
@@ -77226,8 +77454,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.PortalResult"
-    enum="NetworkPortalResult" expires_after="M77">
+    enum="NetworkPortalResult" expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network diagnostic metric sampling the result of portal detections
     for a cellular network.
@@ -77235,16 +77464,19 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.SignalStrengthBeforeDrop"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network metric sampling the signal strength (0-100) of the
     cellular modem before it dropped from the network.
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Cellular.TimeOnline" units="seconds">
+<histogram name="Network.Shill.Cellular.TimeOnline" units="seconds"
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network metric sampling the time spent using cellular to transport
     data. These data are mostly useful when summed and compared to TimeOnline
@@ -77253,8 +77485,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.TimeToConfig" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to join a cellular
     network and configure Layer 3 state.
@@ -77262,8 +77495,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.TimeToConnect" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to connect a cellular
     modem.
@@ -77271,8 +77505,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.TimeToDisable" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to disable a cellular
     modem.
@@ -77280,8 +77515,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.TimeToEnable" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to enable a cellular
     modem.
@@ -77289,8 +77525,9 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.TimeToInitialize" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to initialize a
     cellular modem.
@@ -77298,16 +77535,19 @@
 </histogram>
 
 <histogram name="Network.Shill.Cellular.TimeToOnline" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that a
     cellular network is online after configuring Layer 3 state.
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Cellular.TimeToPortal" units="ms">
+<histogram name="Network.Shill.Cellular.TimeToPortal" units="ms"
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that a
     cellular network is in a captive portal after configuring Layer 3 state.
@@ -77325,8 +77565,10 @@
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Cellular.TimeToScan" units="ms">
+<histogram name="Network.Shill.Cellular.TimeToScan" units="ms"
+    expires_after="2020-06-01">
   <owner>benchan@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to scan a cellular
     network and register a modem.
@@ -77792,8 +78034,10 @@
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.PPPMTUValue" units="bytes" expires_after="M77">
-  <owner>gdk@chromium.org</owner>
+<histogram name="Network.Shill.PPPMTUValue" units="bytes"
+    expires_after="2020-06-01">
+  <owner>akhouderchah@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network diagnostic metric sampling the MTU value provided by the
     upstream PPP peer. A sample is emitted each time the client successfully
@@ -77951,8 +78195,10 @@
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Vpn.Driver" enum="VPNDriver">
-  <owner>cernekee@chromium.org</owner>
+<histogram name="Network.Shill.Vpn.Driver" enum="VPNDriver"
+    expires_after="2020-06-01">
+  <owner>akhouderchah@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network usage metric sampled on each successful VPN connection
     that tracks the VPN connection type.
@@ -77960,16 +78206,19 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.RemoteAuthenticationType"
-    enum="VPNRemoteAuthenticationType">
-  <owner>cernekee@chromium.org</owner>
+    enum="VPNRemoteAuthenticationType" expires_after="2020-06-01">
+  <owner>akhouderchah@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network usage metric sampled on each successful VPN connection
     that tracks the remote authentication method.
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Vpn.TimeOnline" units="ms" expires_after="M77">
-  <owner>cernekee@chromium.org</owner>
+<histogram name="Network.Shill.Vpn.TimeOnline" units="ms"
+    expires_after="2020-06-01">
+  <owner>akhouderchah@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network metric sampling the time spent using VPN to transport
     data. These data are mostly useful when summed and compared to TimeOnline
@@ -77980,25 +78229,30 @@
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Vpn.TimeToConfig" units="ms" expires_after="M77">
-  <owner>cernekee@chromium.org</owner>
+<histogram name="Network.Shill.Vpn.TimeToConfig" units="ms"
+    expires_after="2020-06-01">
+  <owner>akhouderchah@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to configure Layer 3
     state on a VPN network (typically acquire a DHCP lease).
   </summary>
 </histogram>
 
-<histogram name="Network.Shill.Vpn.TimeToOnline" units="ms" expires_after="M77">
-  <owner>cernekee@chromium.org</owner>
+<histogram name="Network.Shill.Vpn.TimeToOnline" units="ms"
+    expires_after="2020-06-01">
+  <owner>akhouderchah@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that a
-    WiMax network is online after configuring Layer 3 state.
+    VPN connection is online after configuring Layer 3 state.
   </summary>
 </histogram>
 
 <histogram name="Network.Shill.Vpn.UserAuthenticationType"
-    enum="VPNUserAuthenticationType" expires_after="M77">
-  <owner>cernekee@chromium.org</owner>
+    enum="VPNUserAuthenticationType" expires_after="2020-06-01">
+  <owner>akhouderchah@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
   <summary>
     Chrome OS network usage metric sampled on each successful VPN connection
     that tracks the user authentication method.
@@ -78006,7 +78260,7 @@
 </histogram>
 
 <histogram name="Network.Shill.WiFi.Ap80211kSupport" enum="WiFiAp80211kSupport"
-    expires_after="M77">
+    expires_after="2020-12-31">
   <owner>matthewmwang@chromium.org</owner>
   <summary>
     Chrome OS network metric sampling the number of Wireless Access Points that
@@ -78023,7 +78277,7 @@
 </histogram>
 
 <histogram name="Network.Shill.WiFi.Ap80211vBSSMaxIdlePeriodSupport"
-    enum="WiFiAp80211vBSSMaxIdlePeriodSupport" expires_after="M77">
+    enum="WiFiAp80211vBSSMaxIdlePeriodSupport" expires_after="2020-12-31">
   <owner>matthewmwang@chromium.org</owner>
   <summary>
     Chrome OS network metric sampling the number of Wireless Access Points that
@@ -78033,7 +78287,7 @@
 </histogram>
 
 <histogram name="Network.Shill.WiFi.Ap80211vBSSTransitionSupport"
-    enum="WiFiAp80211vBSSTransitionSupport" expires_after="M77">
+    enum="WiFiAp80211vBSSTransitionSupport" expires_after="2020-12-31">
   <owner>matthewmwang@chromium.org</owner>
   <summary>
     Chrome OS network metric sampling the number of Wireless Access Points that
@@ -78043,7 +78297,7 @@
 </histogram>
 
 <histogram name="Network.Shill.WiFi.Ap80211vDMSSupport"
-    enum="WiFiAp80211vDMSSupport" expires_after="M77">
+    enum="WiFiAp80211vDMSSupport" expires_after="2020-12-31">
   <owner>matthewmwang@chromium.org</owner>
   <summary>
     Chrome OS network metric sampling the number of Wireless Access Points that
@@ -78052,7 +78306,7 @@
 </histogram>
 
 <histogram name="Network.Shill.WiFi.ApChannelSwitch" enum="WiFiApChannelSwitch"
-    expires_after="M77">
+    expires_after="2020-12-31">
   <owner>matthewmwang@chromium.org</owner>
   <summary>
     Chrome OS network usage metric sampled when an AP switches channels. Shows
@@ -78774,6 +79028,9 @@
 
 <histogram name="Network.Shill.Wimax.DevicePresenceStatus"
     enum="BooleanPresent" expires_after="M77">
+  <obsolete>
+    Wimax support has been dropped from shill since M76.
+  </obsolete>
   <owner>benchan@chromium.org</owner>
   <summary>
     Chrome OS network metric that tracks the presence of a WiMax device in the
@@ -78812,6 +79069,9 @@
 
 <histogram name="Network.Shill.Wimax.ExpiredLeaseLengthSeconds2"
     units="seconds">
+  <obsolete>
+    Wimax support has been dropped from shill since M76.
+  </obsolete>
   <owner>benchan@chromium.org</owner>
   <summary>
     Chrome OS network performance metric that tracks the length of a lease for a
@@ -78822,6 +79082,9 @@
 
 <histogram name="Network.Shill.Wimax.TimeToConfig" units="ms"
     expires_after="2019-12-31">
+  <obsolete>
+    Wimax support has been dropped from shill since M76.
+  </obsolete>
   <owner>benchan@chromium.org</owner>
   <summary>
     Chrome OS network performance metric sampling the time to configure Layer 3
@@ -78831,6 +79094,9 @@
 
 <histogram name="Network.Shill.Wimax.TimeToInitialize" units="ms"
     expires_after="2019-12-31">
+  <obsolete>
+    Wimax support has been dropped from shill since M76.
+  </obsolete>
   <owner>benchan@chromium.org</owner>
   <summary>
     Chrome OS network performance metric sampling the time to initialize a WiMax
@@ -78840,6 +79106,9 @@
 
 <histogram name="Network.Shill.Wimax.TimeToOnline" units="ms"
     expires_after="2019-12-31">
+  <obsolete>
+    Wimax support has been dropped from shill since M76.
+  </obsolete>
   <owner>benchan@chromium.org</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that a
@@ -79131,7 +79400,7 @@
 </histogram>
 
 <histogram name="NetworkService.MdnsResponder.ServiceError"
-    enum="MdnsResponderServiceError" expires_after="M77">
+    enum="MdnsResponderServiceError" expires_after="M79">
   <owner>qingsi@chromium.org</owner>
   <owner>jeroendb@chromium.org</owner>
   <summary>
@@ -81984,6 +82253,9 @@
 </histogram>
 
 <histogram name="Notifications.LoadFailTime" units="ms">
+  <obsolete>
+    Deprecated in June 2019.
+  </obsolete>
 <!-- Name completed by histogram_suffixes name="NotificationImageTypes" -->
 
   <owner>peter@chromium.org</owner>
@@ -81994,6 +82266,9 @@
 </histogram>
 
 <histogram name="Notifications.LoadFileSize" units="bytes">
+  <obsolete>
+    Deprecated in June 2019.
+  </obsolete>
 <!-- Name completed by histogram_suffixes name="NotificationImageTypes" -->
 
   <owner>peter@chromium.org</owner>
@@ -82003,6 +82278,9 @@
 </histogram>
 
 <histogram name="Notifications.LoadFinishTime" units="ms">
+  <obsolete>
+    Deprecated in June 2019.
+  </obsolete>
 <!-- Name completed by histogram_suffixes name="NotificationImageTypes" -->
 
   <owner>peter@chromium.org</owner>
@@ -82013,6 +82291,9 @@
 </histogram>
 
 <histogram name="Notifications.LoadScaleDownTime" units="ms">
+  <obsolete>
+    Deprecated in June 2019.
+  </obsolete>
 <!-- Name completed by histogram_suffixes name="NotificationImageTypes" -->
 
   <owner>peter@chromium.org</owner>
@@ -85746,7 +86027,7 @@
 </histogram>
 
 <histogram name="Omnibox.HistoryQuickHistoryIDSetFromWords" units="ms"
-    expires_after="M77">
+    expires_after="M86">
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
@@ -86000,6 +86281,9 @@
 
 <histogram name="Omnibox.QueryGeolocationAcquisitionTime" units="ms"
     expires_after="M77">
+  <obsolete>
+    Removed in June 2019 for M-78.
+  </obsolete>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <owner>stkhapugin@chromium.org</owner>
@@ -86011,6 +86295,9 @@
 
 <histogram name="Omnibox.QueryGeolocationHorizontalAccuracy" units="meters"
     expires_after="M77">
+  <obsolete>
+    Removed in June 2019 for M-78.
+  </obsolete>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <owner>stkhapugin@chromium.org</owner>
@@ -86076,6 +86363,9 @@
 
 <histogram name="Omnibox.SaveStateForTabSwitch.UserInputInProgress"
     units="count" expires_after="M77">
+  <obsolete>
+    Removed in June 2019 for M-78.
+  </obsolete>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
@@ -86106,6 +86396,9 @@
 
 <histogram name="Omnibox.SearchProvider.AddHistoryResultsTime" units="ms"
     expires_after="M77">
+  <obsolete>
+    Removed in June 2019 for M-78.
+  </obsolete>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
@@ -86914,6 +87207,59 @@
   <summary>Time spent on specific OOBE screen.</summary>
 </histogram>
 
+<histogram name="OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount"
+    units="total host count" expires_after="M80">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Records the number of hosts selected for sending a OnePlatform client hint
+    request. This will be captured when any OnePlatform client hint request is
+    initiated.
+  </summary>
+</histogram>
+
+<histogram name="OptimizationGuide.HintsFetcher.GetHintsRequest.NetErrorCode"
+    enum="NetErrorCodes" expires_after="M80">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Net error codes for HintsFetch requests to the Optimization Guide Service on
+    success and failure.
+  </summary>
+</histogram>
+
+<histogram name="OptimizationGuide.HintsFetcher.GetHintsRequest.Status"
+    enum="HttpResponseCode" expires_after="M80">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    For each HintsFetch request to the Optimization Guide Service, log the HTTP
+    response code on success and failure.
+  </summary>
+</histogram>
+
+<histogram
+    name="OptimizationGuide.HintsFetcher.TopHostProvider.BlacklistSize.OnInitialize"
+    units="total host count" expires_after="M80">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Records the number of hosts placed on the HintsFetcherTopHostBlacklist when
+    it is initialized.
+  </summary>
+</histogram>
+
+<histogram
+    name="OptimizationGuide.HintsFetcher.TopHostProvider.BlacklistSize.OnRequest"
+    units="total host count" expires_after="M80">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Records the number of hosts on the HintsFetcherTopHostBlacklist when top
+    hosts are requested.
+  </summary>
+</histogram>
+
 <histogram name="OptimizationGuide.HintsLoadedPercentage" units="%"
     expires_after="2019-12-31">
   <owner>dougarnett@chromium.org</owner>
@@ -91741,6 +92087,9 @@
 <histogram name="PasswordManager.Android.ExportPasswordsProgressBarUsage"
     enum="PasswordManagerAndroidExportPasswordsProgressBarUsage"
     expires_after="M77">
+  <obsolete>
+    No longer recorded as of June 2019.
+  </obsolete>
   <owner>fhorschig@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -91750,36 +92099,37 @@
 </histogram>
 
 <histogram name="PasswordManager.Android.PasswordCredentialEntry"
-    enum="PasswordManagerAndroidPasswordEntryActions" expires_after="M77">
+    enum="PasswordManagerAndroidPasswordEntryActions" expires_after="M82">
   <owner>fhorschig@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
-    Records the action taken with a password credential entry on Android.
+    Records the action taken with a password credential entry in the preferences
+    on Android.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.Android.PasswordCredentialEntry.Password"
-    enum="PasswordManagerAndroidPasswordActions" expires_after="M77">
+    enum="PasswordManagerAndroidPasswordActions" expires_after="M82">
   <owner>fhorschig@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
-    Records the action taken with a password of a password credential entry on
-    Android.
+    Records the action taken with a password of a password credential entry in
+    the preferences on Android.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.Android.PasswordCredentialEntry.Username"
-    enum="PasswordManagerAndroidUsernameActions" expires_after="M77">
+    enum="PasswordManagerAndroidUsernameActions" expires_after="M82">
   <owner>fhorschig@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
-    Records the action taken with a username of a password credential entry on
-    Android.
+    Records the action taken with a username of a password credential entry in
+    the preferences on Android.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.Android.PasswordCredentialEntry.Website"
-    enum="PasswordManagerAndroidWebsiteActions" expires_after="M77">
+    enum="PasswordManagerAndroidWebsiteActions" expires_after="M82">
   <owner>fhorschig@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -91789,26 +92139,30 @@
 </histogram>
 
 <histogram name="PasswordManager.Android.PasswordExceptionEntry"
-    enum="PasswordManagerAndroidPasswordEntryActions" expires_after="M77">
+    enum="PasswordManagerAndroidPasswordEntryActions" expires_after="M82">
   <owner>fhorschig@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
-    Records the action taken with a password exception entry on Android.
+    Records the action taken with a password exception entry in the preferences
+    on Android.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.Android.PasswordExceptionEntry.Website"
-    enum="PasswordManagerAndroidWebsiteActions" expires_after="M77">
+    enum="PasswordManagerAndroidWebsiteActions" expires_after="M82">
   <owner>fhorschig@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
-    Records the action taken with a website of a password exception entry on
-    Android.
+    Records the action taken with a website of a password exception entry in the
+    preferences on Android.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.Android.PasswordSearchTriggered"
     enum="Boolean" expires_after="M77">
+  <obsolete>
+    No longer recorded as of June 2019.
+  </obsolete>
   <owner>fhorschig@chromium.org</owner>
   <summary>
     Records whether a user triggered the search function while visiting the
@@ -94296,6 +94650,9 @@
 
 <histogram name="Pepper.BrokerAction" enum="PepperBrokerAction"
     expires_after="M77">
+  <obsolete>
+    Deprecated as of 5/2019.
+  </obsolete>
   <owner>raymes@chromium.org</owner>
   <summary>
     Counts various actions related to the Pepper Broker interface.
@@ -101803,7 +102160,11 @@
 </histogram>
 
 <histogram name="Previews.HintsFetcher.GetHintsRequest.HostCount"
-    units="total host count" expires_after="M79">
+    units="total host count" expires_after="M77">
+  <obsolete>
+    Replaced by OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount in
+    06/2019.
+  </obsolete>
   <owner>mcrouse@chromium.org</owner>
   <owner>dougarnett@chromium.org</owner>
   <summary>
@@ -101814,7 +102175,11 @@
 </histogram>
 
 <histogram name="Previews.HintsFetcher.GetHintsRequest.NetErrorCode"
-    enum="NetErrorCodes" expires_after="M79">
+    enum="NetErrorCodes" expires_after="M77">
+  <obsolete>
+    Replaced by OptimizationGuide.HintsFetcher.GetHintsRequest.NetErrorCode in
+    06/2019.
+  </obsolete>
   <owner>mcrouse@chromium.org</owner>
   <owner>dougarnett@chromium.org</owner>
   <summary>
@@ -101824,7 +102189,11 @@
 </histogram>
 
 <histogram name="Previews.HintsFetcher.GetHintsRequest.Status"
-    enum="HttpResponseCode" expires_after="M79">
+    enum="HttpResponseCode" expires_after="M77">
+  <obsolete>
+    Replaced by OptimizationGuide.HintsFetcher.GetHintsRequest.Status in
+    06/2019.
+  </obsolete>
   <owner>mcrouse@chromium.org</owner>
   <owner>dougarnett@chromium.org</owner>
   <summary>
@@ -101835,7 +102204,12 @@
 
 <histogram
     name="Previews.HintsFetcher.TopHostProvider.BlacklistSize.OnInitialize"
-    units="total host count" expires_after="M80">
+    units="total host count" expires_after="M77">
+  <obsolete>
+    Replaced by
+    OptimizationGuide.HintsFetcher.TopHostProvider.BlacklistSize.OnInitialize in
+    06/2019.
+  </obsolete>
   <owner>mcrouse@chromium.org</owner>
   <owner>dougarnett@chromium.org</owner>
   <summary>
@@ -101845,7 +102219,12 @@
 </histogram>
 
 <histogram name="Previews.HintsFetcher.TopHostProvider.BlacklistSize.OnRequest"
-    units="total host count" expires_after="M80">
+    units="total host count" expires_after="M77">
+  <obsolete>
+    Replaced by
+    OptimizationGuide.HintsFetcher.TopHostProvider.BlacklistSize.OnRequest in
+    06/2019.
+  </obsolete>
   <owner>mcrouse@chromium.org</owner>
   <owner>dougarnett@chromium.org</owner>
   <summary>
@@ -105543,6 +105922,9 @@
 
 <histogram name="Renderer4.ImageDecodeMipLevel" units="mip level"
     expires_after="M77">
+  <obsolete>
+    Deprecated 06/2019.
+  </obsolete>
   <owner>khushalsagar@chromium.org</owner>
   <summary>
     The mip level at which images are decoded for rasterization in the renderer.
@@ -110110,8 +110492,10 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Time" units="ms"
-    expires_after="M77">
+<histogram name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Time" units="ms">
+  <obsolete>
+    Removed in M77 due to lack of use.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -110136,7 +110520,10 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4ProcessFullUpdate.DecodeAdditions.Time"
-    units="ms" expires_after="M77">
+    units="ms">
+  <obsolete>
+    Removed in M77 due to lack of use.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -110182,7 +110569,10 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Time"
-    units="ms" expires_after="M77">
+    units="ms">
+  <obsolete>
+    Removed in M77 due to lack of use.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -110209,7 +110599,10 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.DecodeAdditions.Time"
-    units="ms" expires_after="M77">
+    units="ms">
+  <obsolete>
+    Removed in M77 due to lack of use.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -110230,7 +110623,10 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.DecodeRemovals.Time"
-    units="ms" expires_after="M77">
+    units="ms">
+  <obsolete>
+    Removed in M77 due to lack of use.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -110276,8 +110672,10 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.V4ReadFromDisk.ApplyUpdate.Time" units="ms"
-    expires_after="M77">
+<histogram name="SafeBrowsing.V4ReadFromDisk.ApplyUpdate.Time" units="ms">
+  <obsolete>
+    Removed in M77 due to lack of use.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -110301,8 +110699,10 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.V4ReadFromDisk.DecodeAdditions.Time" units="ms"
-    expires_after="M77">
+<histogram name="SafeBrowsing.V4ReadFromDisk.DecodeAdditions.Time" units="ms">
+  <obsolete>
+    Removed in M77 due to lack of use.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -112989,6 +113389,28 @@
   </summary>
 </histogram>
 
+<histogram name="SBDownloadFeedback.FileErrors"
+    enum="CombinedHttpResponseAndNetErrorCode">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    The net error or response code that caused the TwoPhaseUploader to fail
+    while uploading the file. This is logged whenever the TwoPhaseUploader fails
+    while uploading the file.
+  </summary>
+</histogram>
+
+<histogram name="SBDownloadFeedback.MetadataErrors"
+    enum="CombinedHttpResponseAndNetErrorCode">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    The net error or response code that caused the TwoPhaseUploader to fail
+    while uploading metadata. This is logged whenever the TwoPhaseUploader fails
+    while uploading metadata.
+  </summary>
+</histogram>
+
 <histogram name="SBDownloadFeedback.Shown" enum="DownloadItem.DangerType"
     expires_after="2013-11-02">
   <obsolete>
@@ -113032,6 +113454,16 @@
   </summary>
 </histogram>
 
+<histogram name="SBDownloadFeedback.UploadDuration" units="ms"
+    expires_after="M78">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    The time it takes for the TwoPhaseUploader to complete. This is logged on
+    every upload.
+  </summary>
+</histogram>
+
 <histogram name="SBDownloadFeedback.UploadRequestedByServer"
     enum="DownloadUploadRequestedByServer" expires_after="M78">
   <owner>vakh@chromium.org</owner>
@@ -117049,16 +117481,9 @@
 </histogram>
 
 <histogram name="SendTabToSelf.AndroidShareSheet.ClickResult"
-    enum="SendTabToSelfNotification" expires_after="M84">
+    enum="SendTabToSelfClickResult" expires_after="M84">
   <owner>tgupta@chromium.org</owner>
-  <summary>
-    The use of Android notification for SendTabToSelf.
-
-    These notifications are tied to a sync type so there is a distinction
-    between user initiated actions (Opened, Dismissed) and actions that are
-    initiated by changes to the sync model or Android (Shown,
-    DismissedRemotely).
-  </summary>
+  <summary>Tracks the user flow for sending a tab for SendTabToSelf.</summary>
 </histogram>
 
 <histogram base="true" name="SendTabToSelf.ClickResult"
@@ -118369,7 +118794,7 @@
   </summary>
 </histogram>
 
-<histogram name="ServiceWorker.Runtime" units="ms" expires_after="M77">
+<histogram name="ServiceWorker.Runtime" units="ms" expires_after="2020-07-30">
   <owner>falken@chromium.org</owner>
   <summary>
     The amount of time a service worker ran for (wall time). Starts recording
@@ -118380,8 +118805,8 @@
 </histogram>
 
 <histogram name="ServiceWorker.ScriptCachedMetadataSize" units="bytes"
-    expires_after="M78">
-  <owner>horo@chromium.org</owner>
+    expires_after="2020-06-30">
+  <owner>falken@chromium.org</owner>
   <summary>
     The length of cached metadata of Service Worker scripts. Logged on each load
     of Service Worker script only when the cached metadata is available. It
@@ -118391,8 +118816,8 @@
 </histogram>
 
 <histogram name="ServiceWorker.ScriptCachedMetadataTotalSize" units="bytes"
-    expires_after="M77">
-  <owner>horo@chromium.org</owner>
+    expires_after="2020-06-30">
+  <owner>falken@chromium.org</owner>
   <summary>
     The total length of cached metadata of Service Worker scripts. Logged on
     each start of Service Worker only when the cached metadata is available. It
@@ -118401,8 +118826,9 @@
   </summary>
 </histogram>
 
-<histogram name="ServiceWorker.ScriptCount" units="count" expires_after="M77">
-  <owner>horo@chromium.org</owner>
+<histogram name="ServiceWorker.ScriptCount" units="count"
+    expires_after="2020-06-30">
+  <owner>falken@chromium.org</owner>
   <summary>
     The counts of scripts per Service Worker. Logged on each start of Service
     Worker. It includes the main script and imported scripts. This histogram is
@@ -118427,8 +118853,9 @@
   </summary>
 </histogram>
 
-<histogram name="ServiceWorker.ScriptSize" units="bytes" expires_after="M77">
-  <owner>ksakamoto@chromium.org</owner>
+<histogram name="ServiceWorker.ScriptSize" units="bytes"
+    expires_after="2020-06-30">
+  <owner>falken@chromium.org</owner>
   <summary>
     The length of Service Worker scripts. Logged on each load of Service Worker
     script. It doesn't include the size of imported scripts. This histogram is
@@ -118437,8 +118864,8 @@
 </histogram>
 
 <histogram name="ServiceWorker.ScriptTotalSize" units="bytes"
-    expires_after="M77">
-  <owner>horo@chromium.org</owner>
+    expires_after="2020-06-30">
+  <owner>falken@chromium.org</owner>
   <summary>
     The total length of Service Worker scripts. Logged on each start of Service
     Worker. It includes the main script and imported scripts. This histogram is
@@ -118523,7 +118950,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.StartTiming.ClockConsistency"
-    enum="CrossProcessTimeDelta" expires_after="2019-07-30">
+    enum="CrossProcessTimeDelta" expires_after="2020-07-30">
   <owner>falken@chromium.org</owner>
   <owner>shimazu@chromium.org</owner>
   <summary>
@@ -118664,7 +119091,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.StartWorker.FailureStreakEnded" units="count"
-    expires_after="M77">
+    expires_after="2020-07-30">
   <owner>falken@chromium.org</owner>
   <summary>
     When a worker startup succeeded after failing, the number of times the
@@ -118686,7 +119113,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.StartWorker.Purpose"
-    enum="ServiceWorkerMetrics.EventType" expires_after="M77">
+    enum="ServiceWorkerMetrics.EventType" expires_after="2020-07-30">
   <owner>falken@chromium.org</owner>
   <summary>
     The purpose for starting up a service worker. Recorded only for installed
@@ -118756,7 +119183,8 @@
   </summary>
 </histogram>
 
-<histogram name="ServiceWorker.StopWorker.Time" units="ms" expires_after="M77">
+<histogram name="ServiceWorker.StopWorker.Time" units="ms"
+    expires_after="2020-07-30">
   <owner>falken@chromium.org</owner>
   <summary>The time taken to stop a Service Worker.</summary>
 </histogram>
@@ -120778,6 +121206,11 @@
 
 <histogram name="Setup.Install.NumDeleteOldVersionsAttemptsBeforeAbort"
     units="Attempts" expires_after="M77">
+  <obsolete>
+    Deprecated 06/2019. In 88% of cases, we aborted after the maximum number of
+    attemps was reached. The remaining 12% of cases occured when another process
+    tried to acquire the setup singleton.
+  </obsolete>
   <owner>fdoray@chromium.org</owner>
   <summary>
     Number of calls to DeleteOldVerions() made by a --delete-old-versions
@@ -120793,6 +121226,10 @@
 
 <histogram name="Setup.Install.NumDeleteOldVersionsAttemptsBeforeSuccess"
     units="Attempts" expires_after="M77">
+  <obsolete>
+    Deprecated 06/2019. 50th percentile: 1.5. 75th percentile: 1.77. 99th
+    percentile: 2.75.
+  </obsolete>
   <owner>fdoray@chromium.org</owner>
   <summary>
     Number of calls to DeleteOldVersions() made by a --delete-old-versions
@@ -120857,6 +121294,9 @@
 
 <histogram name="Setup.Install.SingletonAcquisitionResult"
     enum="SetupSingletonAcquisitionResult" expires_after="M77">
+  <obsolete>
+    Deprecated 06/2019 because it is not used.
+  </obsolete>
   <owner>fdoray@chromium.org</owner>
   <summary>
     The result of trying to acquire a setup singleton. On Windows, a setup.exe
@@ -123565,6 +124005,17 @@
   </summary>
 </histogram>
 
+<histogram name="SiteIsolation.CORBProtection.SensitiveWithRangeSupport"
+    enum="BooleanSupported" expires_after="M79">
+  <owner>krstnmnlsn@chromium.org</owner>
+  <owner>creis@chromium.org</owner>
+  <summary>
+    True if the response has an Accept-Ranges header, which indicates the server
+    supports range requests on the resource. Only reported if the response
+    looked sensitive under the CORS or CORB heuristics.
+  </summary>
+</histogram>
+
 <histogram name="SiteIsolation.CurrentRendererProcessCount">
   <owner>creis@chromium.org</owner>
   <summary>
@@ -123855,6 +124306,7 @@
 <histogram name="SiteIsolation.XSD.Browser.Action"
     enum="SiteIsolationResponseAction" expires_after="2020-06-01">
   <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <summary>
     Various actions related to cross-site document blocking in the browser
     process, indicating whether the response was blocked from the renderer.
@@ -123863,8 +124315,14 @@
 </histogram>
 
 <histogram name="SiteIsolation.XSD.Browser.Allowed.ContentScript"
-    enum="ContentResourceType2" expires_after="2019-06-01">
+    enum="ContentResourceType2" expires_after="M77">
+  <obsolete>
+    Removed in June 2019 / M77. Some old data have been saved in a
+    Google-internal doc at
+    https://docs.google.com/document/d/1hgPpFD5GpxgWsTQvrA0eLunrG0mwb_uJgWWJmG_kVN4
+  </obsolete>
   <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <summary>
     The total count of responses that were would be blocked by the cross-site
     document blocking logic in the browser process, but were only allowed
@@ -123874,8 +124332,6 @@
     Note that this histogram is not reimplemented in the NetworkService version
     of Cross-Origin Read Blocking feature. This should be okay since we hope to
     gather enough data before NetworkService ships.
-
-    TODO(lukasza): Around Q4 2018: Remove code after we've gathered enough data.
   </summary>
 </histogram>
 
@@ -123887,6 +124343,7 @@
     https://docs.google.com/document/d/1hgPpFD5GpxgWsTQvrA0eLunrG0mwb_uJgWWJmG_kVN4
   </obsolete>
   <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <summary>
     The total count of responses that were blocked by the cross-site document
     blocking logic in the browser process. Recorded with a resource type (0-17)
@@ -123916,6 +124373,7 @@
     https://docs.google.com/document/d/1hgPpFD5GpxgWsTQvrA0eLunrG0mwb_uJgWWJmG_kVN4
   </obsolete>
   <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <summary>
     For each responses blocked by the cross-site document blocking logic in the
     browser process, logs network::ResourceResponseInfo::content_length of the
@@ -123932,6 +124390,7 @@
     https://docs.google.com/document/d/1hgPpFD5GpxgWsTQvrA0eLunrG0mwb_uJgWWJmG_kVN4
   </obsolete>
   <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <summary>
     For each responses blocked by the cross-site document blocking logic in the
     browser process, logs whether network::ResourceResponseInfo::content_length
@@ -123948,6 +124407,7 @@
     https://docs.google.com/document/d/1hgPpFD5GpxgWsTQvrA0eLunrG0mwb_uJgWWJmG_kVN4
   </obsolete>
   <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <summary>
     The total count of responses that were blocked by the cross-site document
     blocking logic in the browser process, due to the presence of a Javascript
@@ -123966,6 +124426,7 @@
     https://docs.google.com/document/d/1hgPpFD5GpxgWsTQvrA0eLunrG0mwb_uJgWWJmG_kVN4
   </obsolete>
   <owner>creis@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
   <summary>
     The number of bytes of the network response buffered for sniffing purposes,
     when attempting to sniff the response to determine if it should be blocked
@@ -128010,6 +128471,17 @@
   </summary>
 </histogram>
 
+<histogram name="Storage.ImportantSites.GenerationTime" units="ms"
+    expires_after="2019-06-26">
+  <owner>dmurph@chromium.org</owner>
+  <owner>costan@chromium.org</owner>
+  <summary>
+    The time it takes to generate the list of 'important' sites. This list is
+    generated when the user enters the 'Clear Browsing Dialog' or the 'Manage
+    Space' screens for Chrome, both of which are on Android.
+  </summary>
+</histogram>
+
 <histogram name="Storage.IndexedDB.PutBlobSizeKB" units="KB">
   <owner>dmurph@chromium.org</owner>
   <summary>
@@ -129373,6 +129845,17 @@
   </summary>
 </histogram>
 
+<histogram name="Sync.CanDecryptNigoriKeybagWithBase64DecodedKeys"
+    enum="Boolean" expires_after="2019-10-01">
+  <owner>mmoskvitin@google.com</owner>
+  <owner>mastiz@chromium.org</owner>
+  <summary>
+    Records whether Nigori keybag can be decrypted with base64 decoded keystore
+    keys, when Nigori is not migrated to keystore and cryptographer is not
+    ready.
+  </summary>
+</histogram>
+
 <histogram name="Sync.ClearServerDataEvents" enum="ClearServerDataEvents"
     expires_after="2019-02-19">
   <obsolete>
@@ -134487,8 +134970,10 @@
   </summary>
 </histogram>
 
-<histogram name="TabsApi.RequestedWindowState" enum="RequestedWindowState"
-    expires_after="M77">
+<histogram name="TabsApi.RequestedWindowState" enum="RequestedWindowState">
+  <obsolete>
+    Removed 2019-06.
+  </obsolete>
   <owner>afakhry@chromium.org</owner>
   <summary>
     The requested window creation state from the tabs extensions API when it's
@@ -135479,8 +135964,9 @@
 </histogram>
 
 <histogram name="Toolbar.ActionsModel.ToolbarActionsVisible"
-    expires_after="M77">
+    expires_after="2020-12-01">
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The number of visible toolbar icons in the Browser Actions Container
     (visible as in number of icons not in the overflow bucket). 0 means all
@@ -140701,6 +141187,19 @@
   </summary>
 </histogram>
 
+<histogram name="V8.WasmTierUpModuleMicroSeconds" units="microseconds"
+    expires_after="2020-03-31">
+  <owner>titzer@chromium.org</owner>
+  <owner>adamk@chromium.org</owner>
+  <owner>clemensh@chromium.org</owner>
+  <summary>
+    Time to tier-up a WebAssembly module, i.e. the time between baseline
+    compilation finishes and top-tier compilation finishes. Recorded whenever an
+    asynchronously compiled module finishes top-tier compilation, but only if a
+    high-resolution clock is available.
+  </summary>
+</histogram>
+
 <histogram name="Variations.CreateTrials.SeedExpiry"
     enum="VariationsSeedExpiry">
   <owner>asvitkine@chromium.org</owner>
@@ -142556,7 +143055,7 @@
 </histogram>
 
 <histogram name="Webapp.Install.DisplayMode" enum="WebAppDisplayMode"
-    expires_after="M77">
+    expires_after="M87">
   <owner>peter@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
   <summary>
@@ -142627,6 +143126,15 @@
   </summary>
 </histogram>
 
+<histogram name="Webapp.InstallResult" enum="WebAppInstallResultCode"
+    expires_after="2020-06-30">
+<!-- Name completed by histogram_suffixes name="WebAppType" -->
+
+  <owner>calamity@chromium.org</owner>
+  <owner>ortuno@chromium.org</owner>
+  <summary>Records the result code of Web App installs.</summary>
+</histogram>
+
 <histogram name="Webapp.NavigationStatus" enum="BooleanSuccess">
   <owner>peter@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
@@ -145427,7 +145935,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.EchoCanceller.CaptureSaturation" enum="Boolean"
-    expires_after="M77">
+    expires_after="M84">
   <owner>peah@chromium.org</owner>
   <summary>
     This histogram logs a value every time the WebRTC echo canceller has
@@ -145536,7 +146044,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.EchoCanceller.FilterDelay" units="Blocks"
-    expires_after="M77">
+    expires_after="M84">
   <owner>peah@chromium.org</owner>
   <summary>
     This histogram logs the estimated echo path delay in 64 sample blocks as
@@ -145616,7 +146124,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.EchoCanceller.ReliableDelayEstimates"
-    enum="WebRTCAecDelayEstimateReliability" expires_after="M77">
+    enum="WebRTCAecDelayEstimateReliability" expires_after="M84">
   <owner>peah@chromium.org</owner>
   <summary>
     This histogram logs the assessed reliability of the delay estimates produced
@@ -145665,7 +146173,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.EchoCanceller.UsableLinearEstimate"
-    enum="Boolean" expires_after="M77">
+    enum="Boolean" expires_after="M84">
   <owner>peah@chromium.org</owner>
   <summary>
     This histogram logs a value every time the WebRTC echo canceller deems that
@@ -148145,7 +148653,7 @@
 </histogram>
 
 <histogram base="true" name="WebRtcTextLogging"
-    enum="WebRtcLoggingWebAppIdHash" expires_after="2019-07-01">
+    enum="WebRtcLoggingWebAppIdHash" expires_after="2020-01-01">
   <owner>grunell@chromium.org</owner>
   <summary>
     Counts the number of WebRTC text log events per web application. Suffixed by
@@ -148154,7 +148662,7 @@
 </histogram>
 
 <histogram name="WebRtcTextLogging.UploadFailureNetErrorCode"
-    enum="NetErrorCodes" expires_after="2019-07-01">
+    enum="NetErrorCodes" expires_after="2020-01-01">
   <owner>grunell@chromium.org</owner>
   <summary>
     Network error codes for WebRTC text log upload failures. Recorded when an
@@ -148163,7 +148671,7 @@
 </histogram>
 
 <histogram name="WebRtcTextLogging.UploadFailureReason"
-    enum="WebRtcLoggingUploadFailureReason" expires_after="2019-07-01">
+    enum="WebRtcLoggingUploadFailureReason" expires_after="2020-01-01">
   <owner>grunell@chromium.org</owner>
   <summary>
     Counts upload failures for WebRTC text log. Error codes for network errors
@@ -148699,6 +149207,9 @@
 
 <histogram name="Windows.InCompatibilityMode" enum="BooleanCompatibilityMode"
     expires_after="M77">
+  <obsolete>
+    Removed 06/2019
+  </obsolete>
   <owner>wfh@chromium.org</owner>
   <owner>brucedawson@chromium.org</owner>
   <summary>
@@ -150192,6 +150703,12 @@
   <affected-histogram name="Autofill.StoredCreditCardDisusedCount"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="AutofillCreditCardUnmaskReason" separator=".">
+  <suffix name="ForAutofill" label="Autofill"/>
+  <suffix name="ForPaymentRequest" label="Payment Request"/>
+  <affected-histogram name="Autofill.CardUnmask.CvcLength"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="AutofillDataAvailability" separator=".">
   <suffix name="WithBothServerAndLocalData"
       label="both server and local autofill data"/>
@@ -151503,6 +152020,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="ConnectivityDetectorProbe" separator=".">
+  <obsolete>
+    Deprecated 6/2019. No longer used.
+  </obsolete>
   <suffix name="Default"/>
   <suffix name="Fallback"/>
   <affected-histogram name="ConnectivityDetector.Probe.HttpResponseCode"/>
@@ -157769,6 +158289,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="NotificationImageTypes" separator=".">
+  <obsolete>
+    Deprecated in June 2019.
+  </obsolete>
   <suffix name="ActionIcon"/>
   <suffix name="Badge"/>
   <suffix name="Icon"/>
@@ -164290,6 +164813,13 @@
   <affected-histogram name="WebApp.Engagement"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="WebappType" separator=".">
+  <suffix name="Default" label="Default-installed app"/>
+  <suffix name="Policy" label="Policy-installed app"/>
+  <suffix name="System" label="System-installed app"/>
+  <affected-histogram name="Webapp.InstallResult"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="WebBluetoothMacOSAPIs" separator=".">
   <suffix name="DidDisconnectPeripheral" label="Disconnected from peripheral"/>
   <suffix name="DidDiscoverCharacteristics" label="Discovered characteristics"/>
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index e9dd2738..6102c8a 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -1189,6 +1189,9 @@
 
 <rappor-metric name="Extensions.CrossOriginFetchFromContentScript"
     type="UMA_RAPPOR_TYPE">
+  <obsolete>
+    Logging code was removed in June 2019 / in M77.
+  </obsolete>
   <owner>lukasza@chromium.org</owner>
   <summary>
     The ID of an extension which triggers a cross-origin request from its
@@ -1200,8 +1203,6 @@
     version of Cross-Origin Read Blocking feature. This should be okay since we
     hope to gather enough data before NetworkService ships.
 
-    TODO(lukasza): Around Q4 2018: Remove code after we've gathered enough data.
-
     DEPRECATED: This metric incorrectly covers more than just requests from
     content scripts - for example it also covers requests from GuestViews
     (initiated by a Chrome App injecting scripts into GuestView). For now
@@ -1212,6 +1213,16 @@
 
 <rappor-metric name="Extensions.CrossOriginFetchFromContentScript2"
     type="UMA_RAPPOR_TYPE">
+  <obsolete>
+    Logging code was removed in June 2019 / in M77.
+
+    The Rappor data may be found in a Google-internal documents at:
+    https://docs.google.com/document/d/1ReuTBgn6HDIO0QKsjzBZz2Bz9F9_sEW4X-rz6IogURo
+
+    The allowlist built based on the Rappor data may be found in a
+    Google-internal table at:
+    https://docs.google.com/spreadsheets/d/1EfOYXomD3uNa_WSjVijuHIhmadrXFjxY-Hurd_TUEic
+  </obsolete>
   <owner>lukasza@chromium.org</owner>
   <summary>
     The ID of an extension which triggers a cross-origin request from its
@@ -1222,8 +1233,6 @@
     Note that gathering of this data is not reimplemented in the NetworkService
     version of Cross-Origin Read Blocking feature. This should be okay since we
     hope to gather enough data before NetworkService ships.
-
-    TODO(lukasza): Around Q4 2018: Remove code after we've gathered enough data.
   </summary>
 </rappor-metric>
 
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index a3d07c8e..744ad53a 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -5437,70 +5437,25 @@
       changes in form control types.
     </summary>
   </metric>
-  <metric name="Fill.FirstFillingResultInRenderer">
+  <metric name="Fill.FirstFillingResultInRenderer"
+      enum="PasswordManagerFirstRendererFillingResult">
     <summary>
       Records whether the PasswordAutofillAgent in the renderer manages to fill
       credentials as instructed by the browser or records a failure reason
       otherwise. Only the outcome of the first attempt to fill is recorded.
 
-      0: Success
-
-      1: No password element - The password element to be filled has not been
-      found.
-
-      2: Blocked by frame hierarchy - Filling only happens in iframes if all
-      parent frames PSL-match the security origin of the iframe containing the
-      password field.
-
-      3: Password element is not autocompleteable - Passwords are not filled
-      into fields that are not editable.
-
-      4: Username prefilled with incompatible value - The username field
-      contains a string that does not match the username of any available
-      credential. This typically indicates placeholder values.
-
-      5: Found no password for username - No credential was filled due to
-      mismatches with the username. This can happen in a number of cases: In
-      case the username field is empty and readonly. In case of a
-      username-first-flow where a user's credentials do contain a username but
-      the form contains only a password field and no username field. In case of
-      change password forms that contain no username field. In case the user
-      name is given on a page but only PSL matched credentials exist for this
-      username. There may be further cases.
-
-      6: Wait for username - Renderer was instructed to wait until user has
-      manually picked a credential. This happens for example if the session is
-      an incognito session, the credendial's URL matches the mainframe only via
-      the PSL, the site is on HTTP, or the form has no current password field.
-      Fill.FirstWaitForUsernameReason records the root causes.
-
-      7: No fillable elements found - No fillable elements were found, only
-      possible for the old form parser.
-
       This is replicated as PasswordManager.FirstFillingResultInRenderer in UMA.
     </summary>
   </metric>
-  <metric name="Fill.FirstWaitForUsernameReason">
+  <metric name="Fill.FirstWaitForUsernameReason"
+      enum="PasswordManagerFirstWaitForUsernameReason">
     <summary>
       This metric records why the browser instructs the renderer not to fill the
       credentials on page load but to wait for the user to confirm the
       credential to be filled. Only the value of the first decision to fill is
       recorded.
 
-      0: Don't wait
-
-      1: Incognito mode - User is browsing in incognito mode.
-
-      2: Public suffix match - A credential exists for a PSL matched site but
-      not for the current security origin.
-
-      3: Form not good for filling - Form is suspected to be a password change
-      form. (Only recorded for old form parser)
-
-      4: FOAS on HTTP - User is on an HTTP site where passwords are filled on
-      account selection (FOAS).
-
-      This is replicated PasswordManager.FirstWaitForUsernameReason in UMA.
+      This is replicated as PasswordManager.FirstWaitForUsernameReason in UMA.
     </summary>
   </metric>
   <metric name="FillOnLoad">
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index cad8206b..91a4b40 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -37,7 +37,6 @@
 passthrough_command_buffer_perftests,net-dev@chromium.org,Internals>Network,,
 performance_browser_tests,miu@chromium.org,Internals>Media>ScreenCapture,,
 power.desktop,brucedawson@chromium.org,,https://bit.ly/power-benchmarks,
-power.typical_10_mobile,perezju@chromium.org,,https://bit.ly/power-benchmarks,
 rasterize_and_record_micro.partial_invalidation,vmpstr@chromium.org,Internals>Compositing>Rasterization,https://bit.ly/rasterize-and-record-benchmark,
 rasterize_and_record_micro.top_25,vmpstr@chromium.org,Internals>Compositing>Rasterization,https://bit.ly/rasterize-and-record-benchmark,
 rendering.desktop,"sadrul@chromium.org, vmiura@chromium.org",Internals>GPU>Metrics,https://bit.ly/rendering-benchmarks,"backdrop_filter,gpu_rasterization,image_decoding,key_desktop_move,maps,repaint_desktop,representative_mac_desktop,representative_mobile,representative_win_desktop,required_webgl,top_real_world_desktop,tough_animation,tough_canvas,tough_compositor,tough_filters,tough_image_decode,tough_path_rendering,tough_pinch_zoom,tough_scheduling,tough_scrolling,tough_texture_upload,tough_webgl,use_fake_camera_device"
diff --git a/tools/perf/benchmarks/power.py b/tools/perf/benchmarks/power.py
index 127264a5..cb790ecb 100644
--- a/tools/perf/benchmarks/power.py
+++ b/tools/perf/benchmarks/power.py
@@ -4,7 +4,6 @@
 
 from core import perf_benchmark
 
-from measurements import power
 import page_sets
 from telemetry import benchmark
 from telemetry import story
@@ -12,22 +11,6 @@
 from telemetry.web_perf import timeline_based_measurement
 
 
-@benchmark.Info(emails=['perezju@chromium.org'],
-                documentation_url='https://bit.ly/power-benchmarks')
-class PowerTypical10Mobile(perf_benchmark.PerfBenchmark):
-  """Android typical 10 mobile power test."""
-  test = power.Power
-  page_set = page_sets.Typical10MobilePageSet
-  SUPPORTED_PLATFORMS = [story.expectations.ALL_MOBILE]
-
-  def SetExtraBrowserOptions(self, options):
-    options.full_performance_mode = False
-
-  @classmethod
-  def Name(cls):
-    return 'power.typical_10_mobile'
-
-
 @benchmark.Info(emails=['brucedawson@chromium.org'],
                 documentation_url='https://bit.ly/power-benchmarks')
 class PowerDesktop(perf_benchmark.PerfBenchmark):
diff --git a/tools/perf/cli_tools/pinboard/pinboard.py b/tools/perf/cli_tools/pinboard/pinboard.py
index 1a3f899..ebe810e 100644
--- a/tools/perf/cli_tools/pinboard/pinboard.py
+++ b/tools/perf/cli_tools/pinboard/pinboard.py
@@ -43,6 +43,28 @@
     'V8-Only:duration',
 ])
 
+# Compute averages over a fixed set of active stories. These may need to be
+# periodically updated.
+ACTIVE_STORIES = set([
+    'browse:chrome:newtab',
+    'browse:chrome:omnibox',
+    'browse:media:facebook_photos',
+    'browse:media:googleplaystore:2019',
+    'browse:media:imgur',
+    'browse:media:youtube',
+    'browse:news:cricbuzz',
+    'browse:news:toi',
+    'browse:shopping:amazon',
+    'browse:shopping:lazada',
+    'browse:social:facebook',
+    'browse:social:instagram',
+    'browse:tools:maps',
+    'load:media:facebook_photos',
+    'load:media:youtube:2018',
+    'load:news:irctc',
+    'load:news:wikipedia:2018'
+])
+
 
 def StartPinpointJobs(state, date):
   """Start new pinpoint jobs for the last commit on the given date."""
@@ -179,8 +201,9 @@
   assert df['change'].str.contains(item['revision']).all(), (
       'Not all results match the expected git revision')
 
-  # Filter out and keep only the measurements we want.
+  # Filter out and keep only the measurements and stories that we want.
   df = df[df['name'].isin(MEASUREMENTS)]
+  df = df[df['story'].isin(ACTIVE_STORIES)]
 
   if not df.empty:
     # Aggregate over the results of individual stories.
diff --git a/tools/perf/cli_tools/pinboard/pinboard_unittest.py b/tools/perf/cli_tools/pinboard/pinboard_unittest.py
index d3352b3..0eb3d6d 100644
--- a/tools/perf/cli_tools/pinboard/pinboard_unittest.py
+++ b/tools/perf/cli_tools/pinboard/pinboard_unittest.py
@@ -168,7 +168,10 @@
     with open(filename, 'w') as f:
       f.writelines(csv)
 
-    df = pinboard.GetRevisionResults(item)
+    with mock.patch('cli_tools.pinboard.pinboard.ACTIVE_STORIES',
+                    new=['story1', 'story2']):
+      df = pinboard.GetRevisionResults(item)
+
     self.assertEqual(len(df.index), 2)  # Only two rows of output.
     self.assertTrue((df['revision'] == '2a66ba').all())
     self.assertTrue((df['benchmark'] == 'loading').all())
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 55738ba..e63c206 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -106,14 +106,13 @@
 _MAC_LOW_END_BENCHMARK_NAMES = OFFICIAL_BENCHMARK_NAMES
 _WIN_10_BENCHMARK_NAMES = _OFFICIAL_EXCEPT_DISPLAY_LOCKING
 _WIN_10_LOW_END_HP_CANDIDATE_BENCHMARK_NAMES = frozenset([
-    'rendering.desktop'])
+    'v8.browsing_desktop'])
 _WIN_7_BENCHMARK_NAMES = _OFFICIAL_EXCEPT_DISPLAY_LOCKING
 _WIN_7_GPU_BENCHMARK_NAMES = _OFFICIAL_EXCEPT_DISPLAY_LOCKING
 _ANDROID_GO_BENCHMARK_NAMES = frozenset([
     'memory.top_10_mobile',
     'system_health.memory_mobile',
     'system_health.common_mobile',
-    'power.typical_10_mobile',
     'startup.mobile',
     'system_health.webview_startup',
     'v8.browsing_mobile',
@@ -206,7 +205,7 @@
 ANDROID_PIXEL2_PERF_FYI = PerfPlatform(
     'android-pixel2-perf-fyi', 'Android OPM1.171019.021',
     _ANDROID_PIXEL2_FYI_BENCHMARK_NAMES,
-    num_shards=7, platform_os='android', is_fyi=True)
+    num_shards=4, platform_os='android', is_fyi=True)
 
 # TODO(crbug.com/902089): Add linux-perf-fyi once the bot is configured to use
 # the sharding map.
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 30e65771..caba819 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -118,7 +118,7 @@
           #'--run-ref-build',
           '--test-shard-map-filename=android-pixel2-perf-fyi_map.json',
         ],
-        'num_shards': 7
+        'num_shards': 4
       }
     ],
     'platform': 'android-chrome',
diff --git a/tools/perf/core/shard_maps/android-go-perf_map.json b/tools/perf/core/shard_maps/android-go-perf_map.json
index ab2f34d..7509d3854 100644
--- a/tools/perf/core/shard_maps/android-go-perf_map.json
+++ b/tools/perf/core/shard_maps/android-go-perf_map.json
@@ -15,7 +15,6 @@
     },
     "2": {
         "benchmarks": {
-            "power.typical_10_mobile": {},
             "speedometer": {},
             "speedometer2": {}
         }
diff --git a/tools/perf/core/shard_maps/android-go_webview-perf_map.json b/tools/perf/core/shard_maps/android-go_webview-perf_map.json
index abcc62c..2f1db86 100644
--- a/tools/perf/core/shard_maps/android-go_webview-perf_map.json
+++ b/tools/perf/core/shard_maps/android-go_webview-perf_map.json
@@ -18,18 +18,11 @@
         "benchmarks": {
             "memory.top_10_mobile": {
                 "begin": 14
-            },
-            "power.typical_10_mobile": {
-                "end": 1
             }
         }
     },
     "3": {
-        "benchmarks": {
-            "power.typical_10_mobile": {
-                "begin": 1
-            }
-        }
+        "benchmarks": {}
     },
     "4": {
         "benchmarks": {
diff --git a/tools/perf/core/shard_maps/android-nexus5x-perf_map.json b/tools/perf/core/shard_maps/android-nexus5x-perf_map.json
index 3876b8b..c6c6e48 100644
--- a/tools/perf/core/shard_maps/android-nexus5x-perf_map.json
+++ b/tools/perf/core/shard_maps/android-nexus5x-perf_map.json
@@ -64,7 +64,6 @@
     },
     "5": {
         "benchmarks": {
-            "power.typical_10_mobile": {},
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
             "rendering.mobile": {
@@ -190,4 +189,4 @@
         "shard #14": 5554.0,
         "shard #15": 5740.0
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel2-perf-fyi_map.json b/tools/perf/core/shard_maps/android-pixel2-perf-fyi_map.json
index b7f488ad..748d333e 100644
--- a/tools/perf/core/shard_maps/android-pixel2-perf-fyi_map.json
+++ b/tools/perf/core/shard_maps/android-pixel2-perf-fyi_map.json
@@ -6,53 +6,29 @@
             "speedometer2": {},
             "startup.mobile": {},
             "system_health.common_mobile": {
-                "end": 37
+                "end": 55
             }
         }
     },
     "1": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 37
+                "begin": 55
             },
             "system_health.memory_mobile": {
-                "end": 4
+                "end": 33
             }
         }
     },
     "2": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 4,
-                "end": 19
-            }
-        }
-    },
-    "3": {
-        "benchmarks": {
-            "system_health.memory_mobile": {
-                "begin": 19,
-                "end": 34
-            }
-        }
-    },
-    "4": {
-        "benchmarks": {
-            "system_health.memory_mobile": {
-                "begin": 34,
-                "end": 48
-            }
-        }
-    },
-    "5": {
-        "benchmarks": {
-            "system_health.memory_mobile": {
-                "begin": 48,
+                "begin": 33,
                 "end": 63
             }
         }
     },
-    "6": {
+    "3": {
         "benchmarks": {
             "system_health.memory_mobile": {
                 "begin": 63
@@ -62,16 +38,13 @@
     },
     "extra_infos": {
         "num_stories": 173,
-        "predicted_min_shard_time": 84,
-        "predicted_min_shard_index": 4,
-        "predicted_max_shard_time": 90,
-        "predicted_max_shard_index": 2,
-        "shard #0": 88,
-        "shard #1": 86,
-        "shard #2": 90,
-        "shard #3": 90,
-        "shard #4": 84,
-        "shard #5": 90,
-        "shard #6": 90
+        "predicted_min_shard_time": 5100.0,
+        "predicted_min_shard_index": 2,
+        "predicted_max_shard_time": 5246.0,
+        "predicted_max_shard_index": 1,
+        "shard #0": 5218.0,
+        "shard #1": 5246.0,
+        "shard #2": 5100.0,
+        "shard #3": 5226.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel2-perf_map.json b/tools/perf/core/shard_maps/android-pixel2-perf_map.json
index 3bb0b1c2..33bd8c5 100644
--- a/tools/perf/core/shard_maps/android-pixel2-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel2-perf_map.json
@@ -125,17 +125,11 @@
             "memory.top_10_mobile": {
                 "begin": 16
             },
-            "octane": {},
-            "power.typical_10_mobile": {
-                "end": 10
-            }
+            "octane": {}
         }
     },
     "14": {
         "benchmarks": {
-            "power.typical_10_mobile": {
-                "begin": 10
-            },
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
             "rendering.mobile": {
diff --git a/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json b/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json
index bcd1c4bf..74b3e52c 100644
--- a/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json
@@ -96,17 +96,11 @@
             "memory.top_10_mobile": {
                 "begin": 9
             },
-            "octane": {},
-            "power.typical_10_mobile": {
-                "end": 1
-            }
+            "octane": {}
         }
     },
     "10": {
         "benchmarks": {
-            "power.typical_10_mobile": {
-                "begin": 1
-            },
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {
                 "end": 17
diff --git a/tools/perf/core/shard_maps/android_nexus5_perf_map.json b/tools/perf/core/shard_maps/android_nexus5_perf_map.json
index 8ae65b5..5078b91e 100644
--- a/tools/perf/core/shard_maps/android_nexus5_perf_map.json
+++ b/tools/perf/core/shard_maps/android_nexus5_perf_map.json
@@ -67,7 +67,6 @@
                 "begin": 11
             },
             "octane": {},
-            "power.typical_10_mobile": {},
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
             "rendering.mobile": {
diff --git a/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json b/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
index 57a85c5a..fc05f9f 100644
--- a/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
+++ b/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
@@ -58,8 +58,7 @@
             "memory.top_10_mobile": {
                 "begin": 3
             },
-            "octane": {},
-            "power.typical_10_mobile": {}
+            "octane": {}
         }
     },
     "5": {
diff --git a/tools/perf/core/shard_maps/android_nexus6_webview_perf_map.json b/tools/perf/core/shard_maps/android_nexus6_webview_perf_map.json
index 845884d..5557e09 100644
--- a/tools/perf/core/shard_maps/android_nexus6_webview_perf_map.json
+++ b/tools/perf/core/shard_maps/android_nexus6_webview_perf_map.json
@@ -49,17 +49,11 @@
             },
             "media.mobile": {},
             "memory.top_10_mobile": {},
-            "octane": {},
-            "power.typical_10_mobile": {
-                "end": 1
-            }
+            "octane": {}
         }
     },
     "4": {
         "benchmarks": {
-            "power.typical_10_mobile": {
-                "begin": 1
-            },
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
             "rendering.mobile": {
diff --git a/tools/perf/core/shard_maps/mac-10_12_laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/mac-10_12_laptop_low_end-perf_map.json
index 8add746..21f4fcf1 100644
--- a/tools/perf/core/shard_maps/mac-10_12_laptop_low_end-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-10_12_laptop_low_end-perf_map.json
@@ -277,4 +277,4 @@
         "shard #24": 2318.0,
         "shard #25": 2330.0
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel2-perf-fyi_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel2-perf-fyi_timing.json
index 0637a08..87dd6f8 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel2-perf-fyi_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel2-perf-fyi_timing.json
@@ -1 +1,634 @@
-[]
\ No newline at end of file
+[
+    {
+        "duration": "273.0",
+        "name": "jetstream/http://browserbench.org/JetStream/"
+    },
+    {
+        "duration": "59.0",
+        "name": "octane/http://chromium.github.io/octane/index.html?auto=1"
+    },
+    {
+        "duration": "57.0",
+        "name": "speedometer2/Speedometer2"
+    },
+    {
+        "duration": "119.0",
+        "name": "startup.mobile/cct:coldish:bbc"
+    },
+    {
+        "duration": "126.0",
+        "name": "startup.mobile/intent:coldish:bbc"
+    },
+    {
+        "duration": "99.0",
+        "name": "startup.mobile/intent:warm:bbc"
+    },
+    {
+        "duration": "90.0",
+        "name": "startup.mobile/maps_pwa:with_http_cache"
+    },
+    {
+        "duration": "23.0",
+        "name": "system_health.common_mobile/background:media:imgur"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.common_mobile/background:news:nytimes"
+    },
+    {
+        "duration": "23.0",
+        "name": "system_health.common_mobile/background:search:google"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/background:social:facebook"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.common_mobile/background:tools:gmail"
+    },
+    {
+        "duration": "80.0",
+        "name": "system_health.common_mobile/browse:chrome:newtab"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.common_mobile/browse:chrome:omnibox"
+    },
+    {
+        "duration": "77.0",
+        "name": "system_health.common_mobile/browse:media:facebook_photos"
+    },
+    {
+        "duration": "46.0",
+        "name": "system_health.common_mobile/browse:media:flickr_infinite_scroll"
+    },
+    {
+        "duration": "45.0",
+        "name": "system_health.common_mobile/browse:media:googleplaystore:2019"
+    },
+    {
+        "duration": "72.0",
+        "name": "system_health.common_mobile/browse:media:imgur"
+    },
+    {
+        "duration": "90.0",
+        "name": "system_health.common_mobile/browse:media:youtube"
+    },
+    {
+        "duration": "57.0",
+        "name": "system_health.common_mobile/browse:news:cnn:2018"
+    },
+    {
+        "duration": "48.0",
+        "name": "system_health.common_mobile/browse:news:cricbuzz"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.common_mobile/browse:news:globo"
+    },
+    {
+        "duration": "52.0",
+        "name": "system_health.common_mobile/browse:news:qq"
+    },
+    {
+        "duration": "50.0",
+        "name": "system_health.common_mobile/browse:news:reddit"
+    },
+    {
+        "duration": "92.0",
+        "name": "system_health.common_mobile/browse:news:toi"
+    },
+    {
+        "duration": "50.0",
+        "name": "system_health.common_mobile/browse:news:washingtonpost"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.common_mobile/browse:search:amp:2018"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.common_mobile/browse:search:amp:sxg:2019"
+    },
+    {
+        "duration": "58.0",
+        "name": "system_health.common_mobile/browse:shopping:amazon"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.common_mobile/browse:shopping:avito"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.common_mobile/browse:shopping:flipkart"
+    },
+    {
+        "duration": "32.0",
+        "name": "system_health.common_mobile/browse:shopping:lazada"
+    },
+    {
+        "duration": "66.0",
+        "name": "system_health.common_mobile/browse:social:facebook"
+    },
+    {
+        "duration": "90.0",
+        "name": "system_health.common_mobile/browse:social:facebook_infinite_scroll"
+    },
+    {
+        "duration": "77.0",
+        "name": "system_health.common_mobile/browse:social:facebook_infinite_scroll:2018"
+    },
+    {
+        "duration": "78.0",
+        "name": "system_health.common_mobile/browse:social:instagram"
+    },
+    {
+        "duration": "76.0",
+        "name": "system_health.common_mobile/browse:social:pinterest_infinite_scroll"
+    },
+    {
+        "duration": "74.0",
+        "name": "system_health.common_mobile/browse:social:tumblr_infinite_scroll:2018"
+    },
+    {
+        "duration": "50.0",
+        "name": "system_health.common_mobile/browse:social:twitter"
+    },
+    {
+        "duration": "58.0",
+        "name": "system_health.common_mobile/browse:tech:discourse_infinite_scroll"
+    },
+    {
+        "duration": "75.0",
+        "name": "system_health.common_mobile/browse:tech:discourse_infinite_scroll:2018"
+    },
+    {
+        "duration": "46.0",
+        "name": "system_health.common_mobile/browse:tools:maps"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:chrome:blank"
+    },
+    {
+        "duration": "24.0",
+        "name": "system_health.common_mobile/load:games:bubbles"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:games:lazors"
+    },
+    {
+        "duration": "28.0",
+        "name": "system_health.common_mobile/load:games:spychase:2018"
+    },
+    {
+        "duration": "24.0",
+        "name": "system_health.common_mobile/load:media:dailymotion"
+    },
+    {
+        "duration": "21.0",
+        "name": "system_health.common_mobile/load:media:facebook_photos"
+    },
+    {
+        "duration": "24.0",
+        "name": "system_health.common_mobile/load:media:flickr:2018"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:media:google_images:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.common_mobile/load:media:imgur:2018"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:media:soundcloud:2018"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:media:youtube:2018"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.common_mobile/load:news:cnn:2018"
+    },
+    {
+        "duration": "23.0",
+        "name": "system_health.common_mobile/load:news:irctc"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.common_mobile/load:news:nytimes"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:news:qq"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:news:reddit"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:news:washingtonpost"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:news:wikipedia:2018"
+    },
+    {
+        "duration": "24.0",
+        "name": "system_health.common_mobile/load:search:baidu:2018"
+    },
+    {
+        "duration": "24.0",
+        "name": "system_health.common_mobile/load:search:ebay:2018"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:search:google:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.common_mobile/load:search:taobao"
+    },
+    {
+        "duration": "21.0",
+        "name": "system_health.common_mobile/load:search:yahoo:2018"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:search:yandex:2018"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:social:twitter"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:tools:docs"
+    },
+    {
+        "duration": "23.0",
+        "name": "system_health.common_mobile/load:tools:drive"
+    },
+    {
+        "duration": "21.0",
+        "name": "system_health.common_mobile/load:tools:dropbox"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.common_mobile/load:tools:gmail"
+    },
+    {
+        "duration": "22.0",
+        "name": "system_health.common_mobile/load:tools:stackoverflow:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.common_mobile/load:tools:weather"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.common_mobile/long_running:tools:gmail-background"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.common_mobile/long_running:tools:gmail-foreground"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/background:media:imgur"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.memory_mobile/background:search:google"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/background:social:facebook"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.memory_mobile/background:tools:gmail"
+    },
+    {
+        "duration": "79.0",
+        "name": "system_health.memory_mobile/browse:chrome:newtab"
+    },
+    {
+        "duration": "33.0",
+        "name": "system_health.memory_mobile/browse:chrome:omnibox"
+    },
+    {
+        "duration": "49.0",
+        "name": "system_health.memory_mobile/browse:media:flickr_infinite_scroll"
+    },
+    {
+        "duration": "45.0",
+        "name": "system_health.memory_mobile/browse:media:googleplaystore:2019"
+    },
+    {
+        "duration": "71.0",
+        "name": "system_health.memory_mobile/browse:media:imgur"
+    },
+    {
+        "duration": "84.0",
+        "name": "system_health.memory_mobile/browse:media:youtube"
+    },
+    {
+        "duration": "52.0",
+        "name": "system_health.memory_mobile/browse:news:qq"
+    },
+    {
+        "duration": "51.0",
+        "name": "system_health.memory_mobile/browse:news:reddit"
+    },
+    {
+        "duration": "51.0",
+        "name": "system_health.memory_mobile/browse:news:washingtonpost"
+    },
+    {
+        "duration": "28.0",
+        "name": "system_health.memory_mobile/browse:search:amp:2018"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.memory_mobile/browse:search:amp:sxg:2019"
+    },
+    {
+        "duration": "73.0",
+        "name": "system_health.memory_mobile/browse:social:facebook_infinite_scroll:2018"
+    },
+    {
+        "duration": "76.0",
+        "name": "system_health.memory_mobile/browse:social:instagram"
+    },
+    {
+        "duration": "72.0",
+        "name": "system_health.memory_mobile/browse:social:pinterest_infinite_scroll"
+    },
+    {
+        "duration": "69.0",
+        "name": "system_health.memory_mobile/browse:social:tumblr_infinite_scroll:2018"
+    },
+    {
+        "duration": "51.0",
+        "name": "system_health.memory_mobile/browse:social:twitter"
+    },
+    {
+        "duration": "47.0",
+        "name": "system_health.memory_mobile/browse:tools:maps"
+    },
+    {
+        "duration": "24.0",
+        "name": "system_health.memory_mobile/load:games:bubbles"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/load:games:lazors"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.memory_mobile/load:games:spychase:2018"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.memory_mobile/load:media:dailymotion"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/load:media:facebook_photos"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.memory_mobile/load:media:flickr:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:media:google_images:2018"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.memory_mobile/load:media:imgur:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:media:soundcloud:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:media:youtube:2018"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.memory_mobile/load:news:cnn:2018"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/load:news:irctc"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.memory_mobile/load:news:nytimes"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/load:news:qq"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/load:news:reddit"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/load:news:washingtonpost"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:news:wikipedia:2018"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.memory_mobile/load:search:baidu:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:search:ebay:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:search:google:2018"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.memory_mobile/load:search:taobao"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:search:yahoo:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:search:yandex:2018"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:social:twitter"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/load:tools:docs"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.memory_mobile/load:tools:drive"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:tools:dropbox"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.memory_mobile/load:tools:gmail"
+    },
+    {
+        "duration": "25.0",
+        "name": "system_health.memory_mobile/load:tools:stackoverflow:2018"
+    },
+    {
+        "duration": "28.0",
+        "name": "system_health.memory_mobile/load:tools:weather"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.memory_mobile/long_running:tools:gmail-background"
+    },
+    {
+        "duration": "0.0",
+        "name": "system_health.memory_mobile/long_running:tools:gmail-foreground"
+    },
+    {
+        "duration": "94.0",
+        "name": "v8.browsing_mobile/browse:chrome:newtab"
+    },
+    {
+        "duration": "31.0",
+        "name": "v8.browsing_mobile/browse:chrome:omnibox"
+    },
+    {
+        "duration": "83.0",
+        "name": "v8.browsing_mobile/browse:media:facebook_photos"
+    },
+    {
+        "duration": "52.0",
+        "name": "v8.browsing_mobile/browse:media:flickr_infinite_scroll"
+    },
+    {
+        "duration": "50.0",
+        "name": "v8.browsing_mobile/browse:media:googleplaystore:2019"
+    },
+    {
+        "duration": "78.0",
+        "name": "v8.browsing_mobile/browse:media:imgur"
+    },
+    {
+        "duration": "97.0",
+        "name": "v8.browsing_mobile/browse:media:youtube"
+    },
+    {
+        "duration": "71.0",
+        "name": "v8.browsing_mobile/browse:news:cnn:2018"
+    },
+    {
+        "duration": "52.0",
+        "name": "v8.browsing_mobile/browse:news:cricbuzz"
+    },
+    {
+        "duration": "0.0",
+        "name": "v8.browsing_mobile/browse:news:globo"
+    },
+    {
+        "duration": "59.0",
+        "name": "v8.browsing_mobile/browse:news:qq"
+    },
+    {
+        "duration": "55.0",
+        "name": "v8.browsing_mobile/browse:news:reddit"
+    },
+    {
+        "duration": "0.0",
+        "name": "v8.browsing_mobile/browse:news:toi"
+    },
+    {
+        "duration": "56.0",
+        "name": "v8.browsing_mobile/browse:news:washingtonpost"
+    },
+    {
+        "duration": "30.0",
+        "name": "v8.browsing_mobile/browse:search:amp:2018"
+    },
+    {
+        "duration": "27.0",
+        "name": "v8.browsing_mobile/browse:search:amp:sxg:2019"
+    },
+    {
+        "duration": "65.0",
+        "name": "v8.browsing_mobile/browse:shopping:amazon"
+    },
+    {
+        "duration": "0.0",
+        "name": "v8.browsing_mobile/browse:shopping:avito"
+    },
+    {
+        "duration": "0.0",
+        "name": "v8.browsing_mobile/browse:shopping:flipkart"
+    },
+    {
+        "duration": "34.0",
+        "name": "v8.browsing_mobile/browse:shopping:lazada"
+    },
+    {
+        "duration": "75.0",
+        "name": "v8.browsing_mobile/browse:social:facebook"
+    },
+    {
+        "duration": "114.0",
+        "name": "v8.browsing_mobile/browse:social:facebook_infinite_scroll"
+    },
+    {
+        "duration": "96.0",
+        "name": "v8.browsing_mobile/browse:social:facebook_infinite_scroll:2018"
+    },
+    {
+        "duration": "89.0",
+        "name": "v8.browsing_mobile/browse:social:instagram"
+    },
+    {
+        "duration": "87.0",
+        "name": "v8.browsing_mobile/browse:social:pinterest_infinite_scroll"
+    },
+    {
+        "duration": "83.0",
+        "name": "v8.browsing_mobile/browse:social:tumblr_infinite_scroll:2018"
+    },
+    {
+        "duration": "55.0",
+        "name": "v8.browsing_mobile/browse:social:twitter"
+    },
+    {
+        "duration": "72.0",
+        "name": "v8.browsing_mobile/browse:tech:discourse_infinite_scroll"
+    },
+    {
+        "duration": "94.0",
+        "name": "v8.browsing_mobile/browse:tech:discourse_infinite_scroll:2018"
+    },
+    {
+        "duration": "50.0",
+        "name": "v8.browsing_mobile/browse:tools:maps"
+    }
+]
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_hp-candidate_map.json b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_hp-candidate_map.json
index 251990d..94b05b8 100644
--- a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_hp-candidate_map.json
+++ b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_hp-candidate_map.json
@@ -1,8 +1,8 @@
 {
     "0": {
         "benchmarks": {
-            "rendering.desktop": {}
+            "v8.browsing_desktop": {}
         }
     },
     "extra_infos": {}
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/sharding_map_generator.py b/tools/perf/core/sharding_map_generator.py
index 1600244..b42f16b 100644
--- a/tools/perf/core/sharding_map_generator.py
+++ b/tools/perf/core/sharding_map_generator.py
@@ -89,7 +89,7 @@
       candidate_story, candidate_story_duration = story_timing_list[-1]
       new_diff = abs(total_time_scheduled + candidate_story_duration -
                      expected_total_time)
-      if new_diff < last_diff or i == final_shard_index:
+      if new_diff <= last_diff or i == final_shard_index:
         story_timing_list.pop()
         total_time_scheduled += candidate_story_duration
         time_per_shard += candidate_story_duration
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 4959fee..dffdf0b 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -34,6 +34,15 @@
 crbug.com/784540 [ Nexus_5X ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
 crbug.com/784540 [ Nexus_5 ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
 crbug.com/784540 [ Nexus_5X ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/978500 [ Nexus5X_Webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ Nexus5X_Webview ] blink_perf.canvas/draw-dynamic-webgl-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-dynamic-webgl-to-hw-accelerated-canvas-2d.html [ Skip ]
+crbug.com/978500 [ Nexus5X_Webview ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ Nexus5X_Webview ] blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/978500 [ Pixel2_Webview ] blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
 crbug.com/853738 [ Android_Webview ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d.html [ Skip ]
 crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-sub-texture.html [ Skip ]
 crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-texture.html [ Skip ]
diff --git a/tools/perf/measurements/power.py b/tools/perf/measurements/power.py
deleted file mode 100644
index 722c166..0000000
--- a/tools/perf/measurements/power.py
+++ /dev/null
@@ -1,31 +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.
-
-from metrics import power
-from telemetry.page import legacy_page_test
-
-
-class Power(legacy_page_test.LegacyPageTest):
-  """Measures power draw and idle wakeups during the page's interactions."""
-
-  def __init__(self):
-    super(Power, self).__init__()
-    self._power_metric = None
-
-  def WillStartBrowser(self, platform):
-    self._power_metric = power.PowerMetric(platform)
-
-  def WillNavigateToPage(self, page, tab):
-    pass
-
-  def DidNavigateToPage(self, page, tab):
-    self._power_metric.Start(page, tab)
-
-  def ValidateAndMeasurePage(self, page, tab, results):
-    self._power_metric.Stop(page, tab)
-    self._power_metric.AddResults(tab, results)
-
-  def DidRunPage(self, platform):
-    del platform  # unused
-    self._power_metric.Close()
diff --git a/tools/perf/metrics/power.py b/tools/perf/metrics/power.py
deleted file mode 100644
index d9b4155f..0000000
--- a/tools/perf/metrics/power.py
+++ /dev/null
@@ -1,222 +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.
-
-import logging
-import time
-
-from telemetry.util import process_statistic_timeline_data
-from telemetry.value import scalar
-
-from metrics import Metric
-
-
-FUELGAUGE_POWER_LABEL = 'fuel_gauge_energy_consumption_mwh'
-APP_POWER_LABEL = 'application_energy_consumption_mwh'
-TOTAL_POWER_LABEL = 'energy_consumption_mwh'
-
-
-class PowerMetric(Metric):
-  """A metric for measuring power usage."""
-
-  # System power draw while idle.
-  _quiescent_power_draw_mwh = 0
-
-  def __init__(self, platform, quiescent_measurement_time_s=0):
-    """PowerMetric Constructor.
-
-    Args:
-      platform: platform object to use.
-      quiescent_measurement_time_s: time to measure quiescent power,
-          in seconds. 0 means don't measure quiescent power."""
-    super(PowerMetric, self).__init__()
-    self._browser = None
-    self._platform = platform
-    self._running = False
-    self._starting_cpu_stats = None
-    self._results = None
-    self._MeasureQuiescentPower(quiescent_measurement_time_s)
-    # TODO(yolandyan): Remove this and the following info loggings after
-    # crbug/573302 is fixed
-    logging.info('PowerMetric created, not running')
-
-  def _StopInternal(self):
-    """Stop monitoring power if measurement is running. This function is
-    idempotent."""
-    if not self._running:
-      logging.info('Attempting to stop non-running monitor')
-      return
-    self._running = False
-    logging.info('StopInternal: PowerMetric running')
-    self._results = self._platform.StopMonitoringPower()
-    if self._results:  # StopMonitoringPower() can return None.
-      self._results['cpu_stats'] = (
-          _SubtractCpuStats(self._browser.cpu_stats, self._starting_cpu_stats))
-
-  def _MeasureQuiescentPower(self, measurement_time_s):
-    """Measure quiescent power draw for the system."""
-    if (not self._platform.CanMonitorPower() or
-        self._platform.CanMeasurePerApplicationPower() or
-        not measurement_time_s):
-      return
-
-    # Only perform quiescent measurement once per run.
-    if PowerMetric._quiescent_power_draw_mwh:
-      return
-
-    self._platform.StartMonitoringPower(self._browser)
-    time.sleep(measurement_time_s)
-    power_results = self._platform.StopMonitoringPower()
-    PowerMetric._quiescent_power_draw_mwh = (
-        power_results.get(TOTAL_POWER_LABEL, 0))
-
-  def Start(self, _, tab):
-    self._browser = tab.browser
-
-    if not self._platform.CanMonitorPower():
-      return
-
-    if not self._browser.supports_power_metrics:
-      logging.warning('Power metrics not supported.')
-      return
-
-    self._results = None
-    self._StopInternal()
-
-    # This line invokes top a few times, call before starting power
-    # measurement.
-    self._starting_cpu_stats = self._browser.cpu_stats
-    self._platform.StartMonitoringPower(self._browser)
-
-    self._running = True
-    logging.info('Start: PowerMetric running')
-
-  def Stop(self, _, tab):
-    if (not self._platform.CanMonitorPower() or
-        not self._browser.supports_power_metrics):
-      return
-
-    self._StopInternal()
-
-  def Close(self):
-    # TODO(rnephew): Remove when crbug.com/553601 is solved.
-    logging.info('Closing power monitors')
-    if self._platform.IsMonitoringPower():
-      self._platform.StopMonitoringPower()
-      self._running = False
-
-  def AddResults(self, _, results):
-    """Add the collected power data into the results object.
-
-    This function needs to be robust in the face of differing power data on
-    various platforms. Therefore data existence needs to be checked when
-    building up the results. Additionally 0 is a valid value for many of the
-    metrics here which is why there are plenty of checks for 'is not None'
-    below.
-    """
-    if not self._results:
-      return
-
-    application_energy_consumption_mwh = self._results.get(APP_POWER_LABEL)
-    total_energy_consumption_mwh = self._results.get(TOTAL_POWER_LABEL)
-    fuel_gauge_energy_consumption_mwh = self._results.get(
-        FUELGAUGE_POWER_LABEL)
-
-    if (PowerMetric._quiescent_power_draw_mwh and
-        application_energy_consumption_mwh is None and
-        total_energy_consumption_mwh is not None):
-      application_energy_consumption_mwh = max(
-          total_energy_consumption_mwh - PowerMetric._quiescent_power_draw_mwh,
-          0)
-
-    if fuel_gauge_energy_consumption_mwh is not None:
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, FUELGAUGE_POWER_LABEL, 'mWh',
-          fuel_gauge_energy_consumption_mwh))
-
-    if total_energy_consumption_mwh is not None:
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, TOTAL_POWER_LABEL, 'mWh',
-          total_energy_consumption_mwh))
-
-    if application_energy_consumption_mwh is not None:
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, APP_POWER_LABEL, 'mWh',
-          application_energy_consumption_mwh))
-
-    component_utilization = self._results.get('component_utilization', {})
-    # GPU Frequency.
-    gpu_power = component_utilization.get('gpu', {})
-    gpu_freq_hz = gpu_power.get('average_frequency_hz')
-    if gpu_freq_hz is not None:
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, 'gpu_average_frequency_hz', 'hz', gpu_freq_hz,
-          important=False))
-
-    # Add idle wakeup numbers for all processes.
-    for (process_type, stats) in self._results.get('cpu_stats', {}).items():
-      trace_name_for_process = 'idle_wakeups_%s' % (process_type.lower())
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, trace_name_for_process, 'count', stats,
-          important=False))
-
-    # Add temperature measurements.
-    platform_info_utilization = self._results.get('platform_info', {})
-    board_temperature_c = platform_info_utilization.get(
-        'average_temperature_c')
-    if board_temperature_c is not None:
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, 'board_temperature', 'celsius',
-          board_temperature_c, important=False))
-
-    # Add CPU frequency measurements.
-    frequency_hz = platform_info_utilization.get('frequency_percent')
-    if frequency_hz is not None:
-      frequency_sum = 0.0
-      for freq, percent in frequency_hz.iteritems():
-        frequency_sum += freq * (percent / 100.0)
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, 'cpu_average_frequency_hz', 'Hz',
-          frequency_sum, important=False))
-
-    # Add CPU c-state residency measurements.
-    cstate_percent = platform_info_utilization.get('cstate_residency_percent')
-    if cstate_percent is not None:
-      for state, percent in cstate_percent.iteritems():
-        results.AddValue(scalar.ScalarValue(
-            results.current_page, 'cpu_cstate_%s_residency_percent' % state,
-            '%', percent, important=False))
-
-    self._results = None
-
-
-def _SubtractCpuStats(cpu_stats, start_cpu_stats):
-  """Computes number of idle wakeups that occurred over measurement period.
-
-  Each of the two cpu_stats arguments is a dict as returned by the
-  Browser.cpu_stats call.
-
-  Returns:
-    A dict of process type names (Browser, Renderer, etc.) to idle wakeup count
-    over the period recorded by the input.
-  """
-  cpu_delta = {}
-  total = 0
-  for process_type in cpu_stats:
-    assert process_type in start_cpu_stats, 'Mismatching process types'
-    # Skip any process_types that are empty.
-    if (not cpu_stats[process_type]) or (not start_cpu_stats[process_type]):
-      continue
-    # Skip if IdleWakeupCount is not present.
-    if ('IdleWakeupCount' not in cpu_stats[process_type] or
-        'IdleWakeupCount' not in start_cpu_stats[process_type]):
-      continue
-
-    assert isinstance(cpu_stats[process_type]['IdleWakeupCount'],
-                      process_statistic_timeline_data.IdleWakeupTimelineData)
-    idle_wakeup_delta = (cpu_stats[process_type]['IdleWakeupCount'] -
-                         start_cpu_stats[process_type]['IdleWakeupCount'])
-    cpu_delta[process_type] = idle_wakeup_delta.total_sum()
-    total = total + cpu_delta[process_type]
-  cpu_delta['Total'] = total
-  return cpu_delta
diff --git a/tools/perf/metrics/unittest_data/2ch_repeat_timeline.json b/tools/perf/metrics/unittest_data/2ch_repeat_timeline.json
deleted file mode 100644
index fd0be8b..0000000
--- a/tools/perf/metrics/unittest_data/2ch_repeat_timeline.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"children":[{"data":{"requestId":"3176.3","requestMethod":"GET","url":"http:\/\/2ch.net\/"},"frameId":"3176.1","startTime":1377648369729.2,"type":"ResourceSendRequest","usedHeapSize":2055348}],"data":[],"endTime":1377648369730.2,"startTime":1377648369728.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369737.2,"startTime":1377648369736.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369737.2,"startTime":1377648369737.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"text\/html","requestId":"3176.3","statusCode":200},"endTime":1377648369738.2,"frameId":"3176.1","startTime":1377648369738.2,"type":"ResourceReceiveResponse","usedHeapSize":2055348}],"data":[],"endTime":1377648369738.2,"startTime":1377648369737.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369739.2,"startTime":1377648369739.2,"type":"Program"},{"children":[{"children":[{"children":[],"data":{"elementCount":0},"endTime":1377648369747.2,"frameId":"3176.1","startTime":1377648369747.2,"type":"RecalculateStyles","usedHeapSize":2137748}],"data":{"encodedDataLength":0,"requestId":"3176.3"},"endTime":1377648369783.2,"frameId":"3176.1","startTime":1377648369741.2,"type":"ResourceReceivedData","usedHeapSize":2468412,"usedHeapSizeDelta":413064}],"data":[],"endTime":1377648369783.2,"startTime":1377648369741.2,"type":"Program","usedHeapSizeDelta":413064},{"children":[],"data":[],"endTime":1377648369784.2,"startTime":1377648369784.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369784.2,"startTime":1377648369784.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369784.2,"startTime":1377648369784.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648369736.2,"requestId":"3176.3"},"frameId":"3176.1","startTime":1377648369785.2,"type":"ResourceFinish","usedHeapSize":2468412}],"data":[],"endTime":1377648369789.2,"startTime":1377648369784.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369790.2,"startTime":1377648369790.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369790.2,"startTime":1377648369790.2,"type":"Program"},{"children":[{"children":[{"children":[{"children":[{"children":[{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"lastError"},"endTime":1377648369801.2,"frameId":"3176.1","stackTrace":[{"columnNumber":17,"functionName":"","lineNumber":5,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377648369800.2,"type":"FunctionCall","usedHeapSize":2798804,"usedHeapSizeDelta":3956},{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"utils"},"endTime":1377648369802.2,"frameId":"3176.1","stackTrace":[{"columnNumber":22,"functionName":"","lineNumber":41,"url":"json_schema"},{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"}],"startTime":1377648369802.2,"type":"FunctionCall","usedHeapSize":2835092,"usedHeapSizeDelta":1744}],"data":{"scriptLine":1,"scriptName":"json_schema"},"endTime":1377648369802.2,"frameId":"3176.1","stackTrace":[{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"}],"startTime":1377648369802.2,"type":"FunctionCall","usedHeapSize":2840456,"usedHeapSizeDelta":11128}],"data":{"scriptLine":1,"scriptName":"schemaUtils"},"endTime":1377648369802.2,"frameId":"3176.1","stackTrace":[{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377648369801.2,"type":"FunctionCall","usedHeapSize":2841368,"usedHeapSizeDelta":31052}],"data":{"scriptLine":1,"scriptName":"sendRequest"},"endTime":1377648369802.2,"frameId":"3176.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377648369799.2,"type":"FunctionCall","usedHeapSize":2842004,"usedHeapSizeDelta":50976},{"children":[],"data":{"scriptLine":1,"scriptName":"unload_event"},"endTime":1377648369803.2,"frameId":"3176.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":11,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377648369802.2,"type":"FunctionCall","usedHeapSize":2845236,"usedHeapSizeDelta":684}],"data":{"scriptLine":1,"scriptName":"event_bindings"},"endTime":1377648369803.2,"frameId":"3176.1","stackTrace":[{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377648369799.2,"type":"FunctionCall","usedHeapSize":2851656,"usedHeapSizeDelta":73956}],"data":{"scriptLine":1,"scriptName":"binding"},"endTime":1377648369812.2,"frameId":"3176.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377648369798.2,"type":"FunctionCall","usedHeapSize":2874460,"usedHeapSizeDelta":114384},{"children":[],"data":{"scriptLine":1,"scriptName":"miscellaneous_bindings"},"endTime":1377648369823.2,"frameId":"3176.1","stackTrace":[{"columnNumber":20,"functionName":"","lineNumber":11,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377648369817.2,"type":"FunctionCall","usedHeapSize":2897596,"usedHeapSizeDelta":7220},{"children":[],"data":{"scriptLine":1,"scriptName":"runtime"},"endTime":1377648369832.2,"frameId":"3176.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":82,"url":"extension"},{"columnNumber":16,"functionName":"target.(anonymous function)","lineNumber":20,"url":"extensions::SafeBuiltins"},{"columnNumber":10,"functionName":"","lineNumber":79,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":190,"url":"binding"},{"columnNumber":16,"functionName":"target.(anonymous function)","lineNumber":20,"url":"extensions::SafeBuiltins"}],"startTime":1377648369826.2,"type":"FunctionCall","usedHeapSize":2989292,"usedHeapSizeDelta":42396}],"data":{"scriptLine":1,"scriptName":"extension"},"endTime":1377648369832.2,"frameId":"3176.1","stackTrace":[{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377648369796.2,"type":"FunctionCall","usedHeapSize":2993548,"usedHeapSizeDelta":251252}],"data":{"lineNumber":1,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"},"endTime":1377648369836.2,"frameId":"3176.1","startTime":1377648369796.2,"type":"EvaluateScript","usedHeapSize":3070056,"usedHeapSizeDelta":338260},{"data":{"requestId":"3176.4","requestMethod":"GET","url":"http:\/\/2ch.net\/2ch_top.css"},"frameId":"3176.1","startTime":1377648369839.2,"type":"ResourceSendRequest","usedHeapSize":3070056}],"data":{"endLine":18,"startLine":0},"endTime":1377648369839.2,"frameId":"3176.1","startTime":1377648369790.2,"type":"ParseHTML","usedHeapSize":3070056,"usedHeapSizeDelta":601644}],"data":[],"endTime":1377648369839.2,"startTime":1377648369790.2,"type":"Program","usedHeapSizeDelta":601644},{"children":[{"data":{"requestId":"3176.5","requestMethod":"GET","url":"http:\/\/2ch.net\/images\/2ch_logo.gif"},"frameId":"3176.1","startTime":1377648369841.2,"type":"ResourceSendRequest","usedHeapSize":3070056},{"data":{"requestId":"3176.6","requestMethod":"GET","url":"http:\/\/2ch.net\/images\/senna88x31.gif"},"frameId":"3176.1","startTime":1377648369841.2,"type":"ResourceSendRequest","usedHeapSize":3070056},{"data":{"requestId":"3176.7","requestMethod":"GET","url":"http:\/\/2ch.net\/images\/search_button.gif"},"frameId":"3176.1","startTime":1377648369842.2,"type":"ResourceSendRequest","usedHeapSize":3070056},{"data":{"requestId":"3176.8","requestMethod":"GET","url":"http:\/\/img.bbchat.tv\/images\/bannar\/46860-3.gif"},"frameId":"3176.1","startTime":1377648369842.2,"type":"ResourceSendRequest","usedHeapSize":3070056},{"data":{"requestId":"3176.9","requestMethod":"GET","url":"http:\/\/www.dd.iij4u.or.jp\/~cap\/y_3copyok.jpg"},"frameId":"3176.1","startTime":1377648369843.2,"type":"ResourceSendRequest","usedHeapSize":3070056},{"data":{"requestId":"3176.10","requestMethod":"GET","url":"http:\/\/count.2ch.net\/ct.php\/index"},"frameId":"3176.1","startTime":1377648369843.2,"type":"ResourceSendRequest","usedHeapSize":3070056}],"data":[],"endTime":1377648369843.2,"startTime":1377648369841.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369844.2,"startTime":1377648369844.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369844.2,"startTime":1377648369844.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369844.2,"startTime":1377648369844.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369848.2,"startTime":1377648369848.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"text\/css","requestId":"3176.4","statusCode":200},"endTime":1377648369849.2,"frameId":"3176.1","startTime":1377648369849.2,"type":"ResourceReceiveResponse","usedHeapSize":3070056}],"data":[],"endTime":1377648369849.2,"startTime":1377648369848.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369850.2,"startTime":1377648369850.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.4"},"endTime":1377648369859.2,"frameId":"3176.1","startTime":1377648369851.2,"type":"ResourceReceivedData","usedHeapSize":3070056}],"data":[],"endTime":1377648369859.2,"startTime":1377648369851.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369860.2,"startTime":1377648369860.2,"type":"Program"},{"children":[{"children":[],"data":{"elementCount":12},"endTime":1377648369866.2,"frameId":"3176.1","startTime":1377648369866.2,"type":"RecalculateStyles","usedHeapSize":3070056},{"children":[],"data":{"lineNumber":15,"url":"http:\/\/2ch.net\/"},"endTime":1377648369868.2,"frameId":"3176.1","startTime":1377648369867.2,"type":"EvaluateScript","usedHeapSize":3070792,"usedHeapSizeDelta":692},{"children":[{"data":[],"frameId":"3176.1","startTime":1377648369869.2,"type":"InvalidateLayout","usedHeapSize":3070792},{"data":[],"frameId":"3176.1","startTime":1377648369888.2,"type":"ScheduleStyleRecalculation","usedHeapSize":3076468},{"children":[],"data":{"elementCount":0},"endTime":1377648369900.2,"frameId":"3176.4","startTime":1377648369900.2,"type":"RecalculateStyles","usedHeapSize":3076468},{"children":[{"data":[],"frameId":"3176.4","startTime":1377648369900.2,"type":"ScheduleStyleRecalculation","usedHeapSize":3076468},{"data":[],"frameId":"3176.4","startTime":1377648369900.2,"type":"InvalidateLayout","usedHeapSize":3076468}],"data":{"endLine":0,"startLine":0},"endTime":1377648369900.2,"frameId":"3176.4","startTime":1377648369900.2,"type":"ParseHTML","usedHeapSize":3076468},{"data":[],"frameId":"3176.4","startTime":1377648369901.2,"type":"InvalidateLayout","usedHeapSize":3076468},{"children":[],"data":{"elementCount":3},"endTime":1377648369901.2,"frameId":"3176.4","startTime":1377648369901.2,"type":"RecalculateStyles","usedHeapSize":3076468},{"data":{"isMainFrame":false},"frameId":"3176.4","startTime":1377648369901.2,"type":"MarkDOMContent","usedHeapSize":3076468},{"data":{"requestId":"3176.11","requestMethod":"GET","url":"http:\/\/cast.texpo.jp\/2chtop\/main_frame.html"},"frameId":"3176.4","startTime":1377648369904.2,"type":"ResourceSendRequest","usedHeapSize":3076468},{"children":[],"data":{"elementCount":0},"endTime":1377648369911.2,"frameId":"3176.6","startTime":1377648369911.2,"type":"RecalculateStyles","usedHeapSize":3076468},{"children":[{"data":[],"frameId":"3176.6","startTime":1377648369912.2,"type":"ScheduleStyleRecalculation","usedHeapSize":3076468},{"data":[],"frameId":"3176.6","startTime":1377648369913.2,"type":"InvalidateLayout","usedHeapSize":3076468}],"data":{"endLine":0,"startLine":0},"endTime":1377648369913.2,"frameId":"3176.6","startTime":1377648369911.2,"type":"ParseHTML","usedHeapSize":3076468},{"data":[],"frameId":"3176.6","startTime":1377648369913.2,"type":"InvalidateLayout","usedHeapSize":3076468},{"children":[],"data":{"elementCount":3},"endTime":1377648369913.2,"frameId":"3176.6","startTime":1377648369913.2,"type":"RecalculateStyles","usedHeapSize":3076468},{"data":{"isMainFrame":false},"frameId":"3176.6","startTime":1377648369913.2,"type":"MarkDOMContent","usedHeapSize":3076468},{"data":{"requestId":"3176.12","requestMethod":"GET","url":"http:\/\/cast.texpo.jp\/2chtop\/moritapo_frame.html"},"frameId":"3176.6","startTime":1377648369913.2,"type":"ResourceSendRequest","usedHeapSize":3076468},{"data":[],"frameId":"3176.1","startTime":1377648369914.2,"type":"InvalidateLayout","usedHeapSize":3076468},{"children":[],"data":{"elementCount":4},"endTime":1377648369939.2,"frameId":"3176.1","startTime":1377648369914.2,"type":"RecalculateStyles","usedHeapSize":3076468},{"data":{"isMainFrame":true},"frameId":"3176.1","startTime":1377648369940.2,"type":"MarkDOMContent","usedHeapSize":3076468}],"data":{"endLine":0,"startLine":18},"endTime":1377648369941.2,"frameId":"3176.1","startTime":1377648369868.2,"type":"ParseHTML","usedHeapSize":3076468,"usedHeapSizeDelta":5676},{"data":{"didFail":false,"networkTime":1377648369846.2,"requestId":"3176.4"},"frameId":"3176.1","startTime":1377648369941.2,"type":"ResourceFinish","usedHeapSize":3076468}],"data":[],"endTime":1377648369942.2,"startTime":1377648369860.2,"type":"Program","usedHeapSizeDelta":6412},{"children":[],"data":[],"endTime":1377648369942.2,"startTime":1377648369942.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/gif","requestId":"3176.5","statusCode":200},"endTime":1377648369942.2,"frameId":"3176.1","startTime":1377648369942.2,"type":"ResourceReceiveResponse","usedHeapSize":3076468}],"data":[],"endTime":1377648369944.2,"startTime":1377648369942.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369945.2,"startTime":1377648369945.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/gif","requestId":"3176.6","statusCode":200},"endTime":1377648369947.2,"frameId":"3176.1","startTime":1377648369947.2,"type":"ResourceReceiveResponse","usedHeapSize":3076468}],"data":[],"endTime":1377648369947.2,"startTime":1377648369947.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369947.2,"startTime":1377648369947.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.5"},"endTime":1377648369949.2,"frameId":"3176.1","startTime":1377648369948.2,"type":"ResourceReceivedData","usedHeapSize":3076468}],"data":[],"endTime":1377648369949.2,"startTime":1377648369948.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369949.2,"startTime":1377648369949.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648369859.2,"requestId":"3176.5"},"frameId":"3176.1","startTime":1377648369952.2,"type":"ResourceFinish","usedHeapSize":3076468}],"data":[],"endTime":1377648369955.2,"startTime":1377648369951.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369956.2,"startTime":1377648369956.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/gif","requestId":"3176.7","statusCode":200},"endTime":1377648369961.2,"frameId":"3176.1","startTime":1377648369961.2,"type":"ResourceReceiveResponse","usedHeapSize":3076468}],"data":[],"endTime":1377648369965.2,"startTime":1377648369958.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369968.2,"startTime":1377648369967.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/gif","requestId":"3176.8","statusCode":200},"endTime":1377648369971.2,"frameId":"3176.1","startTime":1377648369970.2,"type":"ResourceReceiveResponse","usedHeapSize":3076468}],"data":[],"endTime":1377648369972.2,"startTime":1377648369969.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369975.2,"startTime":1377648369974.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/jpeg","requestId":"3176.9","statusCode":200},"endTime":1377648369978.2,"frameId":"3176.1","startTime":1377648369978.2,"type":"ResourceReceiveResponse","usedHeapSize":3076468}],"data":[],"endTime":1377648369982.2,"startTime":1377648369976.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369983.2,"startTime":1377648369982.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.6"},"endTime":1377648369984.2,"frameId":"3176.1","startTime":1377648369983.2,"type":"ResourceReceivedData","usedHeapSize":3076468}],"data":[],"endTime":1377648369984.2,"startTime":1377648369983.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369984.2,"startTime":1377648369984.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648369862.2,"requestId":"3176.6"},"frameId":"3176.1","startTime":1377648369984.2,"type":"ResourceFinish","usedHeapSize":3076468}],"data":[],"endTime":1377648369984.2,"startTime":1377648369984.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369985.2,"startTime":1377648369984.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.7"},"endTime":1377648369985.2,"frameId":"3176.1","startTime":1377648369985.2,"type":"ResourceReceivedData","usedHeapSize":3076468}],"data":[],"endTime":1377648369985.2,"startTime":1377648369985.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369986.2,"startTime":1377648369986.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648369863.2,"requestId":"3176.7"},"frameId":"3176.1","startTime":1377648369986.2,"type":"ResourceFinish","usedHeapSize":3076468}],"data":[],"endTime":1377648369986.2,"startTime":1377648369986.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369987.2,"startTime":1377648369987.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.8"},"endTime":1377648369987.2,"frameId":"3176.1","startTime":1377648369987.2,"type":"ResourceReceivedData","usedHeapSize":3076468}],"data":[],"endTime":1377648369987.2,"startTime":1377648369987.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369988.2,"startTime":1377648369988.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648369864.2,"requestId":"3176.8"},"frameId":"3176.1","startTime":1377648369988.2,"type":"ResourceFinish","usedHeapSize":3076468}],"data":[],"endTime":1377648369988.2,"startTime":1377648369988.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369989.2,"startTime":1377648369989.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.9"},"endTime":1377648369990.2,"frameId":"3176.1","startTime":1377648369989.2,"type":"ResourceReceivedData","usedHeapSize":3076468}],"data":[],"endTime":1377648369990.2,"startTime":1377648369989.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369990.2,"startTime":1377648369990.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648369865.2,"requestId":"3176.9"},"frameId":"3176.1","startTime":1377648369991.2,"type":"ResourceFinish","usedHeapSize":3076468}],"data":[],"endTime":1377648369991.2,"startTime":1377648369991.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369991.2,"startTime":1377648369991.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369991.2,"startTime":1377648369991.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369991.2,"startTime":1377648369991.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"text\/html","requestId":"3176.11","statusCode":200},"endTime":1377648369991.2,"frameId":"3176.4","startTime":1377648369991.2,"type":"ResourceReceiveResponse","usedHeapSize":3076468}],"data":[],"endTime":1377648369991.2,"startTime":1377648369991.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369991.2,"startTime":1377648369991.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"text\/html","requestId":"3176.12","statusCode":200},"endTime":1377648369991.2,"frameId":"3176.6","startTime":1377648369991.2,"type":"ResourceReceiveResponse","usedHeapSize":3076468}],"data":[],"endTime":1377648369991.2,"startTime":1377648369991.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369995.2,"startTime":1377648369991.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648369996.2,"startTime":1377648369996.2,"type":"Program"},{"children":[{"children":[{"children":[],"data":{"elementCount":0},"endTime":1377648369996.2,"frameId":"3176.4","startTime":1377648369996.2,"type":"RecalculateStyles","usedHeapSize":3076468}],"data":{"encodedDataLength":0,"requestId":"3176.11"},"endTime":1377648370015.2,"frameId":"3176.4","startTime":1377648369996.2,"type":"ResourceReceivedData","usedHeapSize":3421624,"usedHeapSizeDelta":345156}],"data":[],"endTime":1377648370015.2,"startTime":1377648369996.2,"type":"Program","usedHeapSizeDelta":345156},{"children":[],"data":[],"endTime":1377648370015.2,"startTime":1377648370015.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648369936.2,"requestId":"3176.11"},"frameId":"3176.4","startTime":1377648370016.2,"type":"ResourceFinish","usedHeapSize":3421624}],"data":[],"endTime":1377648370016.2,"startTime":1377648370015.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370016.2,"startTime":1377648370016.2,"type":"Program"},{"children":[{"children":[{"children":[],"data":{"elementCount":0},"endTime":1377648370018.2,"frameId":"3176.6","startTime":1377648370018.2,"type":"RecalculateStyles","usedHeapSize":3421624}],"data":{"encodedDataLength":0,"requestId":"3176.12"},"endTime":1377648370029.2,"frameId":"3176.6","startTime":1377648370016.2,"type":"ResourceReceivedData","usedHeapSize":3761036,"usedHeapSizeDelta":339412}],"data":[],"endTime":1377648370029.2,"startTime":1377648370016.2,"type":"Program","usedHeapSizeDelta":339412},{"children":[],"data":[],"endTime":1377648370029.2,"startTime":1377648370029.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648369936.2,"requestId":"3176.12"},"frameId":"3176.6","startTime":1377648370030.2,"type":"ResourceFinish","usedHeapSize":3761036}],"data":[],"endTime":1377648370031.2,"startTime":1377648370029.2,"type":"Program"},{"children":[{"children":[{"data":{"requestId":"3176.13","requestMethod":"GET","url":"http:\/\/cast.texpo.jp\/2chtop\/2ch_top.css"},"frameId":"3176.4","startTime":1377648370034.2,"type":"ResourceSendRequest","usedHeapSize":3761036},{"data":{"requestId":"3176.14","requestMethod":"GET","url":"http:\/\/cast.texpo.jp\/2chtop\/images\/topimage.gif"},"frameId":"3176.4","startTime":1377648370035.2,"type":"ResourceSendRequest","usedHeapSize":3761036},{"data":{"requestId":"3176.15","requestMethod":"GET","url":"http:\/\/cast.texpo.jp\/2chtop\/images\/2t2013banner03.jpg"},"frameId":"3176.4","startTime":1377648370036.2,"type":"ResourceSendRequest","usedHeapSize":3761036}],"data":{"endLine":23,"startLine":0},"endTime":1377648370036.2,"frameId":"3176.4","startTime":1377648370033.2,"type":"ParseHTML","usedHeapSize":3761036}],"data":[],"endTime":1377648370036.2,"startTime":1377648370033.2,"type":"Program"},{"children":[{"children":[{"children":[],"data":{"dirtyObjects":1,"partialLayout":false,"root":[0,0,997,0,997,50,0,50],"rootNode":-4,"totalObjects":1},"endTime":1377648370255.2,"frameId":"3176.6","startTime":1377648370252.2,"type":"Layout","usedHeapSize":3761036},{"children":[],"data":{"dirtyObjects":2,"partialLayout":false,"root":[0,0,997,0,997,550,0,550],"rootNode":-5,"totalObjects":2},"endTime":1377648370258.2,"frameId":"3176.4","startTime":1377648370256.2,"type":"Layout","usedHeapSize":3761036}],"data":{"dirtyObjects":107,"partialLayout":false,"root":[0,0,997,0,997,650,0,650],"rootNode":-6,"totalObjects":107},"endTime":1377648370258.2,"frameId":"3176.1","startTime":1377648370036.2,"type":"Layout","usedHeapSize":3761036}],"data":[],"endTime":1377648370259.2,"startTime":1377648370036.2,"type":"Program"},{"children":[{"children":[{"data":{"isMainFrame":false},"frameId":"3176.4","startTime":1377648370263.2,"type":"MarkDOMContent","usedHeapSize":3761036}],"data":{"endLine":0,"startLine":23},"endTime":1377648370264.2,"frameId":"3176.4","startTime":1377648370261.2,"type":"ParseHTML","usedHeapSize":3761036}],"data":[],"endTime":1377648370264.2,"startTime":1377648370261.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370267.2,"startTime":1377648370266.2,"type":"Program"},{"children":[{"children":[],"data":{"endLine":16,"startLine":0},"endTime":1377648370270.2,"frameId":"3176.6","startTime":1377648370268.2,"type":"ParseHTML","usedHeapSize":3761036}],"data":[],"endTime":1377648370270.2,"startTime":1377648370268.2,"type":"Program"},{"children":[{"children":[{"data":{"isMainFrame":false},"frameId":"3176.6","startTime":1377648370271.2,"type":"MarkLoad","usedHeapSize":3761036},{"children":[],"data":{"dirtyObjects":2,"partialLayout":false,"root":[0,0,997,0,997,50,0,50],"rootNode":-4,"totalObjects":2},"endTime":1377648370271.2,"frameId":"3176.6","startTime":1377648370271.2,"type":"Layout","usedHeapSize":3761036},{"data":{"isMainFrame":false},"frameId":"3176.6","startTime":1377648370271.2,"type":"MarkDOMContent","usedHeapSize":3761036}],"data":{"endLine":0,"startLine":16},"endTime":1377648370271.2,"frameId":"3176.6","startTime":1377648370270.2,"type":"ParseHTML","usedHeapSize":3761036}],"data":[],"endTime":1377648370271.2,"startTime":1377648370270.2,"type":"Program"},{"children":[{"data":[],"startTime":1377648370271.2,"type":"BeginFrame","usedHeapSize":3761036},{"children":[{"children":[],"data":{"imageType":"GIF"},"endTime":1377648370297.2,"startTime":1377648370297.2,"type":"DecodeImage","usedHeapSize":3761036},{"children":[],"data":{"imageType":"GIF"},"endTime":1377648370317.2,"startTime":1377648370317.2,"type":"DecodeImage","usedHeapSize":3761036},{"children":[],"data":{"imageType":"GIF"},"endTime":1377648370318.2,"startTime":1377648370318.2,"type":"DecodeImage","usedHeapSize":3761036},{"children":[],"data":{"clip":[0,98,997,98,997,648,0,648],"layerRootNode":-5},"endTime":1377648370319.2,"frameId":"3176.4","startTime":1377648370319.2,"type":"Paint","usedHeapSize":3761036}],"data":{"clip":[0,0,997,0,997,650,0,650],"layerRootNode":-6},"endTime":1377648370319.2,"frameId":"3176.1","startTime":1377648370288.2,"type":"Paint","usedHeapSize":3761036}],"data":[],"endTime":1377648370341.2,"startTime":1377648370271.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370343.2,"startTime":1377648370343.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370344.2,"startTime":1377648370343.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/gif","requestId":"3176.10","statusCode":200},"endTime":1377648370344.2,"frameId":"3176.1","startTime":1377648370344.2,"type":"ResourceReceiveResponse","usedHeapSize":3761036}],"data":[],"endTime":1377648370344.2,"startTime":1377648370344.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370345.2,"startTime":1377648370345.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":1024,"requestId":"3176.10"},"endTime":1377648370346.2,"frameId":"3176.1","startTime":1377648370345.2,"type":"ResourceReceivedData","usedHeapSize":3761036}],"data":[],"endTime":1377648370346.2,"startTime":1377648370345.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":782,"requestId":"3176.10"},"endTime":1377648370346.2,"frameId":"3176.1","startTime":1377648370346.2,"type":"ResourceReceivedData","usedHeapSize":3761036}],"data":[],"endTime":1377648370346.2,"startTime":1377648370346.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370346.2,"startTime":1377648370346.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648370039.2,"requestId":"3176.10"},"frameId":"3176.1","startTime":1377648370347.2,"type":"ResourceFinish","usedHeapSize":3761036}],"data":[],"endTime":1377648370347.2,"startTime":1377648370346.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370347.2,"startTime":1377648370347.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370349.2,"startTime":1377648370349.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"text\/css","requestId":"3176.13","statusCode":200},"endTime":1377648370350.2,"frameId":"3176.4","startTime":1377648370350.2,"type":"ResourceReceiveResponse","usedHeapSize":3761036}],"data":[],"endTime":1377648370350.2,"startTime":1377648370349.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370351.2,"startTime":1377648370351.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/gif","requestId":"3176.14","statusCode":200},"endTime":1377648370351.2,"frameId":"3176.4","startTime":1377648370351.2,"type":"ResourceReceiveResponse","usedHeapSize":3761036}],"data":[],"endTime":1377648370352.2,"startTime":1377648370351.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370352.2,"startTime":1377648370352.2,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/jpeg","requestId":"3176.15","statusCode":200},"endTime":1377648370353.2,"frameId":"3176.4","startTime":1377648370353.2,"type":"ResourceReceiveResponse","usedHeapSize":3761036}],"data":[],"endTime":1377648370353.2,"startTime":1377648370352.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370354.2,"startTime":1377648370354.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.13"},"endTime":1377648370354.2,"frameId":"3176.4","startTime":1377648370354.2,"type":"ResourceReceivedData","usedHeapSize":3761036}],"data":[],"endTime":1377648370354.2,"startTime":1377648370354.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370354.2,"startTime":1377648370354.2,"type":"Program"},{"children":[{"children":[{"data":[],"frameId":"3176.6","startTime":1377648370359.2,"type":"InvalidateLayout","usedHeapSize":3761036}],"data":{"elementCount":12},"endTime":1377648370360.2,"frameId":"3176.6","startTime":1377648370358.2,"type":"RecalculateStyles","usedHeapSize":3761036},{"data":[],"frameId":"3176.6","startTime":1377648370361.2,"type":"InvalidateLayout","usedHeapSize":3761036},{"data":[],"frameId":"3176.6","startTime":1377648370361.2,"type":"InvalidateLayout","usedHeapSize":3761036},{"children":[{"data":[],"frameId":"3176.4","startTime":1377648370364.2,"type":"InvalidateLayout","usedHeapSize":3761036}],"data":{"elementCount":18},"endTime":1377648370364.2,"frameId":"3176.4","startTime":1377648370364.2,"type":"RecalculateStyles","usedHeapSize":3761036},{"data":[],"frameId":"3176.4","startTime":1377648370364.2,"type":"InvalidateLayout","usedHeapSize":3761036},{"data":[],"frameId":"3176.4","startTime":1377648370364.2,"type":"InvalidateLayout","usedHeapSize":3761036},{"data":{"didFail":false,"networkTime":1377648370044.2,"requestId":"3176.13"},"frameId":"3176.4","startTime":1377648370364.2,"type":"ResourceFinish","usedHeapSize":3761036}],"data":[],"endTime":1377648370365.2,"startTime":1377648370354.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370366.2,"startTime":1377648370365.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.14"},"endTime":1377648370369.2,"frameId":"3176.4","startTime":1377648370366.2,"type":"ResourceReceivedData","usedHeapSize":3761036}],"data":[],"endTime":1377648370369.2,"startTime":1377648370366.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370370.2,"startTime":1377648370369.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.15"},"endTime":1377648370371.2,"frameId":"3176.4","startTime":1377648370370.2,"type":"ResourceReceivedData","usedHeapSize":3761036}],"data":[],"endTime":1377648370371.2,"startTime":1377648370370.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370371.2,"startTime":1377648370371.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648370046.2,"requestId":"3176.15"},"frameId":"3176.4","startTime":1377648370372.2,"type":"ResourceFinish","usedHeapSize":3761036}],"data":[],"endTime":1377648370373.2,"startTime":1377648370372.2,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"3176.14"},"endTime":1377648370374.2,"frameId":"3176.4","startTime":1377648370373.2,"type":"ResourceReceivedData","usedHeapSize":3761036}],"data":[],"endTime":1377648370374.2,"startTime":1377648370373.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370374.2,"startTime":1377648370374.2,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377648370047.2,"requestId":"3176.14"},"frameId":"3176.4","startTime":1377648370375.2,"type":"ResourceFinish","usedHeapSize":3761036},{"data":{"isMainFrame":false},"frameId":"3176.4","startTime":1377648370375.2,"type":"MarkLoad","usedHeapSize":3761036},{"children":[],"data":{"dirtyObjects":19,"partialLayout":false,"root":[0,0,997,0,997,550,0,550],"rootNode":-5,"totalObjects":19},"endTime":1377648370392.2,"frameId":"3176.4","startTime":1377648370376.2,"type":"Layout","usedHeapSize":3761036},{"children":[{"children":[],"data":{"scriptLine":101,"scriptName":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"},"endTime":1377648370404.2,"frameId":"3176.1","startTime":1377648370394.2,"type":"FunctionCall","usedHeapSize":3819172,"usedHeapSizeDelta":48852},{"children":[{"data":{"usedHeapSizeDelta":249880},"endTime":1377648370412.2,"stackTrace":[{"columnNumber":139,"functionName":"onload","lineNumber":22,"url":"http:\/\/2ch.net\/"}],"startTime":1377648370406.2,"type":"GCEvent","usedHeapSize":3661148},{"data":[],"frameId":"3176.1","stackTrace":[{"columnNumber":139,"functionName":"onload","lineNumber":22,"url":"http:\/\/2ch.net\/"}],"startTime":1377648370418.2,"type":"ScheduleStyleRecalculation","usedHeapSize":3661228},{"children":[],"data":{"elementCount":2},"endTime":1377648370418.2,"frameId":"3176.1","stackTrace":[{"columnNumber":139,"functionName":"onload","lineNumber":22,"url":"http:\/\/2ch.net\/"}],"startTime":1377648370418.2,"type":"RecalculateStyles","usedHeapSize":3662168}],"data":{"scriptLine":22,"scriptName":"http:\/\/2ch.net\/"},"endTime":1377648370421.2,"frameId":"3176.1","startTime":1377648370405.2,"type":"FunctionCall","usedHeapSize":3662168,"usedHeapSizeDelta":4294792768}],"data":{"type":"load"},"endTime":1377648370421.2,"frameId":"3176.1","startTime":1377648370392.2,"type":"EventDispatch","usedHeapSize":3662168,"usedHeapSizeDelta":4294868428},{"data":{"isMainFrame":true},"frameId":"3176.1","startTime":1377648370421.2,"type":"MarkLoad","usedHeapSize":3662168}],"data":[],"endTime":1377648370423.2,"startTime":1377648370374.2,"type":"Program","usedHeapSizeDelta":4294868428},{"children":[{"children":[],"data":{"dirtyObjects":20,"partialLayout":false,"root":[0,0,997,0,997,50,0,50],"rootNode":-4,"totalObjects":20},"endTime":1377648370431.2,"frameId":"3176.6","startTime":1377648370423.2,"type":"Layout","usedHeapSize":3662168}],"data":[],"endTime":1377648370432.2,"startTime":1377648370423.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370432.2,"startTime":1377648370432.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370432.2,"startTime":1377648370432.2,"type":"Program"},{"children":[{"data":[],"startTime":1377648370432.2,"type":"BeginFrame","usedHeapSize":3662168},{"children":[{"children":[{"children":[],"data":{"imageType":"GIF"},"endTime":1377648370458.2,"startTime":1377648370448.2,"type":"DecodeImage","usedHeapSize":3662168},{"children":[],"data":{"imageType":"JPEG"},"endTime":1377648370460.2,"startTime":1377648370459.2,"type":"DecodeImage","usedHeapSize":3662168}],"data":{"clip":[0,98,997,98,997,648,0,648],"layerRootNode":-5},"endTime":1377648370461.2,"frameId":"3176.4","startTime":1377648370448.2,"type":"Paint","usedHeapSize":3662168}],"data":{"clip":[0,60,997,60,997,648,0,648],"layerRootNode":-6},"endTime":1377648370461.2,"frameId":"3176.1","startTime":1377648370443.2,"type":"Paint","usedHeapSize":3662168}],"data":[],"endTime":1377648370462.2,"startTime":1377648370432.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370463.2,"startTime":1377648370463.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370463.2,"startTime":1377648370463.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370463.2,"startTime":1377648370463.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370463.2,"startTime":1377648370463.2,"type":"Program"},{"children":[{"children":[{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"lastError"},"endTime":1377648370467.2,"frameId":"3176.1","stackTrace":[{"columnNumber":17,"functionName":"","lineNumber":5,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370467.2,"type":"FunctionCall","usedHeapSize":3717324,"usedHeapSizeDelta":4720},{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"utils"},"endTime":1377648370468.2,"frameId":"3176.1","stackTrace":[{"columnNumber":22,"functionName":"","lineNumber":41,"url":"json_schema"},{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370468.2,"type":"FunctionCall","usedHeapSize":3753120,"usedHeapSizeDelta":2272}],"data":{"scriptLine":1,"scriptName":"json_schema"},"endTime":1377648370468.2,"frameId":"3176.1","stackTrace":[{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370468.2,"type":"FunctionCall","usedHeapSize":3758752,"usedHeapSizeDelta":11156}],"data":{"scriptLine":1,"scriptName":"schemaUtils"},"endTime":1377648370468.2,"frameId":"3176.1","stackTrace":[{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370468.2,"type":"FunctionCall","usedHeapSize":3759932,"usedHeapSizeDelta":31252}],"data":{"scriptLine":1,"scriptName":"sendRequest"},"endTime":1377648370468.2,"frameId":"3176.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370467.2,"type":"FunctionCall","usedHeapSize":3760836,"usedHeapSizeDelta":51920},{"children":[],"data":{"scriptLine":1,"scriptName":"unload_event"},"endTime":1377648370468.2,"frameId":"3176.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":11,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370468.2,"type":"FunctionCall","usedHeapSize":3764204,"usedHeapSizeDelta":676}],"data":{"scriptLine":1,"scriptName":"event_bindings"},"endTime":1377648370469.2,"frameId":"3176.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370467.2,"type":"FunctionCall","usedHeapSize":3770056,"usedHeapSizeDelta":74288}],"data":{"scriptLine":1,"scriptName":"miscellaneous_bindings"},"endTime":1377648370469.2,"frameId":"3176.1","startTime":1377648370467.2,"type":"FunctionCall","usedHeapSize":3777272,"usedHeapSizeDelta":100164},{"children":[],"data":{"scriptLine":76,"scriptName":"miscellaneous_bindings"},"endTime":1377648370469.2,"frameId":"3176.1","startTime":1377648370469.2,"type":"FunctionCall","usedHeapSize":3777440},{"children":[{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"lastError"},"endTime":1377648370470.2,"frameId":"3176.4","stackTrace":[{"columnNumber":17,"functionName":"","lineNumber":5,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370470.2,"type":"FunctionCall","usedHeapSize":3832756,"usedHeapSizeDelta":4756},{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"utils"},"endTime":1377648370471.2,"frameId":"3176.4","stackTrace":[{"columnNumber":22,"functionName":"","lineNumber":41,"url":"json_schema"},{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370471.2,"type":"FunctionCall","usedHeapSize":3869432,"usedHeapSizeDelta":3152}],"data":{"scriptLine":1,"scriptName":"json_schema"},"endTime":1377648370471.2,"frameId":"3176.4","stackTrace":[{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370471.2,"type":"FunctionCall","usedHeapSize":3875220,"usedHeapSizeDelta":12192}],"data":{"scriptLine":1,"scriptName":"schemaUtils"},"endTime":1377648370471.2,"frameId":"3176.4","stackTrace":[{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370471.2,"type":"FunctionCall","usedHeapSize":3876412,"usedHeapSizeDelta":32300}],"data":{"scriptLine":1,"scriptName":"sendRequest"},"endTime":1377648370471.2,"frameId":"3176.4","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370470.2,"type":"FunctionCall","usedHeapSize":3877328,"usedHeapSizeDelta":53016},{"children":[],"data":{"scriptLine":1,"scriptName":"unload_event"},"endTime":1377648370474.2,"frameId":"3176.4","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":11,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370473.2,"type":"FunctionCall","usedHeapSize":3880852,"usedHeapSizeDelta":676}],"data":{"scriptLine":1,"scriptName":"event_bindings"},"endTime":1377648370474.2,"frameId":"3176.4","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370470.2,"type":"FunctionCall","usedHeapSize":3886708,"usedHeapSizeDelta":75544}],"data":{"scriptLine":1,"scriptName":"miscellaneous_bindings"},"endTime":1377648370476.2,"frameId":"3176.4","startTime":1377648370470.2,"type":"FunctionCall","usedHeapSize":3893936,"usedHeapSizeDelta":101400},{"children":[],"data":{"scriptLine":76,"scriptName":"miscellaneous_bindings"},"endTime":1377648370476.2,"frameId":"3176.4","startTime":1377648370476.2,"type":"FunctionCall","usedHeapSize":3894104},{"children":[{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"lastError"},"endTime":1377648370480.2,"frameId":"3176.6","stackTrace":[{"columnNumber":17,"functionName":"","lineNumber":5,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370480.2,"type":"FunctionCall","usedHeapSize":3949888,"usedHeapSizeDelta":4756},{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"utils"},"endTime":1377648370484.2,"frameId":"3176.6","stackTrace":[{"columnNumber":22,"functionName":"","lineNumber":41,"url":"json_schema"},{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370483.2,"type":"FunctionCall","usedHeapSize":3985716,"usedHeapSizeDelta":2304}],"data":{"scriptLine":1,"scriptName":"json_schema"},"endTime":1377648370484.2,"frameId":"3176.6","stackTrace":[{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370481.2,"type":"FunctionCall","usedHeapSize":3991504,"usedHeapSizeDelta":11344}],"data":{"scriptLine":1,"scriptName":"schemaUtils"},"endTime":1377648370484.2,"frameId":"3176.6","stackTrace":[{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370481.2,"type":"FunctionCall","usedHeapSize":3992696,"usedHeapSizeDelta":31452}],"data":{"scriptLine":1,"scriptName":"sendRequest"},"endTime":1377648370484.2,"frameId":"3176.6","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370479.2,"type":"FunctionCall","usedHeapSize":3993612,"usedHeapSizeDelta":52168},{"children":[],"data":{"scriptLine":1,"scriptName":"unload_event"},"endTime":1377648370484.2,"frameId":"3176.6","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":11,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370484.2,"type":"FunctionCall","usedHeapSize":3997136,"usedHeapSizeDelta":676}],"data":{"scriptLine":1,"scriptName":"event_bindings"},"endTime":1377648370484.2,"frameId":"3176.6","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377648370479.2,"type":"FunctionCall","usedHeapSize":4003272,"usedHeapSizeDelta":74976}],"data":{"scriptLine":1,"scriptName":"miscellaneous_bindings"},"endTime":1377648370484.2,"frameId":"3176.6","startTime":1377648370476.2,"type":"FunctionCall","usedHeapSize":4010500,"usedHeapSizeDelta":101300},{"children":[],"data":{"scriptLine":76,"scriptName":"miscellaneous_bindings"},"endTime":1377648370484.2,"frameId":"3176.6","startTime":1377648370484.2,"type":"FunctionCall","usedHeapSize":4010668},{"children":[],"data":{"scriptLine":76,"scriptName":"miscellaneous_bindings"},"endTime":1377648370484.2,"frameId":"3176.1","startTime":1377648370484.2,"type":"FunctionCall","usedHeapSize":4010788},{"children":[],"data":{"scriptLine":270,"scriptName":"miscellaneous_bindings"},"endTime":1377648370485.2,"frameId":"3176.1","startTime":1377648370484.2,"type":"FunctionCall","usedHeapSize":4013340,"usedHeapSizeDelta":2404}],"data":[],"endTime":1377648370485.2,"startTime":1377648370463.2,"type":"Program","usedHeapSizeDelta":351172},{"children":[],"data":[],"endTime":1377648370486.2,"startTime":1377648370486.2,"type":"Program"},{"children":[{"children":[],"data":{"scriptLine":253,"scriptName":"miscellaneous_bindings"},"endTime":1377648370487.2,"frameId":"3176.1","startTime":1377648370487.2,"type":"FunctionCall","usedHeapSize":4014652,"usedHeapSizeDelta":1180},{"children":[],"data":{"scriptLine":253,"scriptName":"miscellaneous_bindings"},"endTime":1377648370487.2,"frameId":"3176.4","startTime":1377648370487.2,"type":"FunctionCall","usedHeapSize":4014948,"usedHeapSizeDelta":164},{"children":[],"data":{"scriptLine":253,"scriptName":"miscellaneous_bindings"},"endTime":1377648370487.2,"frameId":"3176.6","startTime":1377648370487.2,"type":"FunctionCall","usedHeapSize":4015248,"usedHeapSizeDelta":168},{"children":[],"data":{"scriptLine":253,"scriptName":"miscellaneous_bindings"},"endTime":1377648370487.2,"frameId":"3176.1","startTime":1377648370487.2,"type":"FunctionCall","usedHeapSize":4015588,"usedHeapSizeDelta":208}],"data":[],"endTime":1377648370487.2,"startTime":1377648370486.2,"type":"Program","usedHeapSizeDelta":2248},{"children":[],"data":[],"endTime":1377648370487.2,"startTime":1377648370487.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370488.2,"startTime":1377648370488.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370489.2,"startTime":1377648370488.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370547.2,"startTime":1377648370547.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370547.2,"startTime":1377648370547.2,"type":"Program"},{"children":[{"data":{"usedHeapSizeDelta":1436604},"endTime":1377648370560.2,"startTime":1377648370548.2,"type":"GCEvent","usedHeapSize":2578984}],"data":[],"endTime":1377648370560.2,"startTime":1377648370547.2,"type":"Program","usedHeapSizeDelta":4293530692},{"children":[],"data":[],"endTime":1377648370895.2,"startTime":1377648370895.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370923.2,"startTime":1377648370923.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370952.2,"startTime":1377648370952.2,"type":"Program"},{"children":[{"data":[],"startTime":1377648370952.2,"type":"BeginFrame","usedHeapSize":2578984},{"children":[],"data":{"clip":[544,64,547,64,547,82,544,82],"layerRootNode":-6},"endTime":1377648370954.2,"frameId":"3176.1","startTime":1377648370953.2,"type":"Paint","usedHeapSize":2578984}],"data":[],"endTime":1377648370954.2,"startTime":1377648370952.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370955.2,"startTime":1377648370955.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648370956.2,"startTime":1377648370956.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648371484.2,"startTime":1377648371484.2,"type":"Program"},{"children":[{"data":[],"startTime":1377648371484.2,"type":"BeginFrame","usedHeapSize":2578984},{"children":[],"data":{"clip":[544,64,547,64,547,82,544,82],"layerRootNode":-6},"endTime":1377648371485.2,"frameId":"3176.1","startTime":1377648371484.2,"type":"Paint","usedHeapSize":2578984}],"data":[],"endTime":1377648371485.2,"startTime":1377648371484.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648371486.2,"startTime":1377648371486.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648371488.2,"startTime":1377648371487.2,"type":"Program"},{"children":[],"data":[],"endTime":1377648371785.2,"startTime":1377648371785.2,"type":"Program"}]
\ No newline at end of file
diff --git a/tools/perf/metrics/unittest_data/baidu_repeat_timeline.json b/tools/perf/metrics/unittest_data/baidu_repeat_timeline.json
deleted file mode 100644
index 09293789..0000000
--- a/tools/perf/metrics/unittest_data/baidu_repeat_timeline.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"children":[{"data":{"requestId":"2664.3","requestMethod":"GET","url":"http:\/\/www.baidu.com\/"},"frameId":"2664.1","startTime":1377637870418.1,"type":"ResourceSendRequest","usedHeapSize":2001444}],"data":[],"endTime":1377637870418.1,"startTime":1377637870416.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637870424.1,"startTime":1377637870424.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637870452.1,"startTime":1377637870452.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637870885.1,"startTime":1377637870880.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637870885.1,"startTime":1377637870885.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"text\/html","requestId":"2664.3","statusCode":200},"endTime":1377637870887.1,"frameId":"2664.1","startTime":1377637870887.1,"type":"ResourceReceiveResponse","usedHeapSize":2001444}],"data":[],"endTime":1377637870887.1,"startTime":1377637870885.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637870888.1,"startTime":1377637870887.1,"type":"Program"},{"children":[{"children":[{"children":[],"data":{"elementCount":0},"endTime":1377637870894.1,"frameId":"2664.1","startTime":1377637870894.1,"type":"RecalculateStyles","usedHeapSize":2083844}],"data":{"encodedDataLength":2461,"requestId":"2664.3"},"endTime":1377637870906.1,"frameId":"2664.1","startTime":1377637870888.1,"type":"ResourceReceivedData","usedHeapSize":2412332,"usedHeapSizeDelta":410888}],"data":[],"endTime":1377637870906.1,"startTime":1377637870888.1,"type":"Program","usedHeapSizeDelta":410888},{"children":[],"data":[],"endTime":1377637870906.1,"startTime":1377637870906.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637870906.1,"startTime":1377637870906.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":2400,"requestId":"2664.3"},"endTime":1377637870906.1,"frameId":"2664.1","startTime":1377637870906.1,"type":"ResourceReceivedData","usedHeapSize":2412332}],"data":[],"endTime":1377637870929.1,"startTime":1377637870906.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637870929.1,"startTime":1377637870929.1,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377637870892.1,"requestId":"2664.3"},"frameId":"2664.1","startTime":1377637870929.1,"type":"ResourceFinish","usedHeapSize":2412332}],"data":[],"endTime":1377637870929.1,"startTime":1377637870929.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637870929.1,"startTime":1377637870929.1,"type":"Program"},{"children":[{"children":[{"children":[{"children":[{"children":[{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"lastError"},"endTime":1377637871009.1,"frameId":"2664.1","stackTrace":[{"columnNumber":17,"functionName":"","lineNumber":5,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377637871009.1,"type":"FunctionCall","usedHeapSize":2744596,"usedHeapSizeDelta":3956},{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"utils"},"endTime":1377637871010.1,"frameId":"2664.1","stackTrace":[{"columnNumber":22,"functionName":"","lineNumber":41,"url":"json_schema"},{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"}],"startTime":1377637871010.1,"type":"FunctionCall","usedHeapSize":2789072,"usedHeapSizeDelta":1744}],"data":{"scriptLine":1,"scriptName":"json_schema"},"endTime":1377637871010.1,"frameId":"2664.1","stackTrace":[{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"}],"startTime":1377637871009.1,"type":"FunctionCall","usedHeapSize":2794436,"usedHeapSizeDelta":11128}],"data":{"scriptLine":1,"scriptName":"schemaUtils"},"endTime":1377637871010.1,"frameId":"2664.1","stackTrace":[{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377637871009.1,"type":"FunctionCall","usedHeapSize":2795348,"usedHeapSizeDelta":31052}],"data":{"scriptLine":1,"scriptName":"sendRequest"},"endTime":1377637871010.1,"frameId":"2664.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377637871008.1,"type":"FunctionCall","usedHeapSize":2795984,"usedHeapSizeDelta":59164},{"children":[],"data":{"scriptLine":1,"scriptName":"unload_event"},"endTime":1377637871022.1,"frameId":"2664.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":11,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377637871021.1,"type":"FunctionCall","usedHeapSize":2799216,"usedHeapSizeDelta":684}],"data":{"scriptLine":1,"scriptName":"event_bindings"},"endTime":1377637871023.1,"frameId":"2664.1","stackTrace":[{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377637871008.1,"type":"FunctionCall","usedHeapSize":2805636,"usedHeapSizeDelta":82144}],"data":{"scriptLine":1,"scriptName":"binding"},"endTime":1377637871023.1,"frameId":"2664.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377637871008.1,"type":"FunctionCall","usedHeapSize":2828440,"usedHeapSizeDelta":122572},{"children":[],"data":{"scriptLine":1,"scriptName":"miscellaneous_bindings"},"endTime":1377637871024.1,"frameId":"2664.1","stackTrace":[{"columnNumber":20,"functionName":"","lineNumber":11,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377637871023.1,"type":"FunctionCall","usedHeapSize":2851576,"usedHeapSizeDelta":7220},{"children":[],"data":{"scriptLine":1,"scriptName":"runtime"},"endTime":1377637871038.1,"frameId":"2664.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":82,"url":"extension"},{"columnNumber":16,"functionName":"target.(anonymous function)","lineNumber":20,"url":"extensions::SafeBuiltins"},{"columnNumber":10,"functionName":"","lineNumber":79,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":190,"url":"binding"},{"columnNumber":16,"functionName":"target.(anonymous function)","lineNumber":20,"url":"extensions::SafeBuiltins"}],"startTime":1377637871025.1,"type":"FunctionCall","usedHeapSize":2936844,"usedHeapSizeDelta":42012}],"data":{"scriptLine":1,"scriptName":"extension"},"endTime":1377637871038.1,"frameId":"2664.1","stackTrace":[{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377637871006.1,"type":"FunctionCall","usedHeapSize":2941100,"usedHeapSizeDelta":252980}],"data":{"lineNumber":1,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"},"endTime":1377637871040.1,"frameId":"2664.1","startTime":1377637871006.1,"type":"EvaluateScript","usedHeapSize":3017608,"usedHeapSizeDelta":339956},{"children":[],"data":{"elementCount":5},"endTime":1377637871043.1,"frameId":"2664.1","startTime":1377637871043.1,"type":"RecalculateStyles","usedHeapSize":3017608},{"children":[],"data":{"lineNumber":1,"url":"http:\/\/www.baidu.com\/"},"endTime":1377637871043.1,"frameId":"2664.1","startTime":1377637871043.1,"type":"EvaluateScript","usedHeapSize":3018428,"usedHeapSizeDelta":776}],"data":{"endLine":0,"startLine":0},"endTime":1377637871043.1,"frameId":"2664.1","startTime":1377637870965.1,"type":"ParseHTML","usedHeapSize":3018428,"usedHeapSizeDelta":606096}],"data":[],"endTime":1377637871043.1,"startTime":1377637870965.1,"type":"Program","usedHeapSizeDelta":606096},{"children":[{"children":[{"data":[],"frameId":"2664.1","startTime":1377637871044.1,"type":"InvalidateLayout","usedHeapSize":3018428},{"data":{"requestId":"2664.4","requestMethod":"GET","url":"http:\/\/www.baidu.com\/img\/bdlogo.gif"},"frameId":"2664.1","startTime":1377637871047.1,"type":"ResourceSendRequest","usedHeapSize":3018428},{"data":{"requestId":"2664.5","requestMethod":"GET","url":"http:\/\/www.baidu.com\/img\/i-1.0.0.png"},"frameId":"2664.1","startTime":1377637871049.1,"type":"ResourceSendRequest","usedHeapSize":3024104},{"data":[],"frameId":"2664.1","startTime":1377637871082.1,"type":"InvalidateLayout","usedHeapSize":3024104}],"data":{"endLine":0,"startLine":0},"endTime":1377637871082.1,"frameId":"2664.1","startTime":1377637871044.1,"type":"ParseHTML","usedHeapSize":3024104,"usedHeapSizeDelta":5676}],"data":[],"endTime":1377637871082.1,"startTime":1377637871044.1,"type":"Program","usedHeapSizeDelta":5676},{"children":[{"children":[{"data":{"requestId":"2664.6","requestMethod":"GET","url":"http:\/\/www.baidu.com\/cache\/global\/img\/gs.gif"},"frameId":"2664.1","startTime":1377637871100.1,"type":"ResourceSendRequest","usedHeapSize":3024104},{"children":[],"data":{"lineNumber":1,"url":"http:\/\/www.baidu.com\/"},"endTime":1377637871136.1,"frameId":"2664.1","startTime":1377637871136.1,"type":"EvaluateScript","usedHeapSize":3026432,"usedHeapSizeDelta":2284}],"data":{"endLine":0,"startLine":0},"endTime":1377637871137.1,"frameId":"2664.1","startTime":1377637871099.1,"type":"ParseHTML","usedHeapSize":3026432,"usedHeapSizeDelta":2328}],"data":[],"endTime":1377637871137.1,"startTime":1377637871099.1,"type":"Program","usedHeapSizeDelta":2328},{"children":[{"children":[{"data":{"requestId":"2664.7","requestMethod":"GET","url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"},"frameId":"2664.1","startTime":1377637871146.1,"type":"ResourceSendRequest","usedHeapSize":3026432}],"data":{"endLine":0,"startLine":0},"endTime":1377637871146.1,"frameId":"2664.1","startTime":1377637871138.1,"type":"ParseHTML","usedHeapSize":3026432}],"data":[],"endTime":1377637871146.1,"startTime":1377637871138.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637871146.1,"startTime":1377637871146.1,"type":"Program"},{"children":[{"data":{"requestId":"2664.8","requestMethod":"GET","url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/tangram-1.3.4c1.0_07038476.js"},"frameId":"2664.1","startTime":1377637871146.1,"type":"ResourceSendRequest","usedHeapSize":3026432}],"data":[],"endTime":1377637871147.1,"startTime":1377637871146.1,"type":"Program"},{"children":[{"data":{"requestId":"2664.9","requestMethod":"GET","url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/user\/js\/u_3603075f.js"},"frameId":"2664.1","startTime":1377637871147.1,"type":"ResourceSendRequest","usedHeapSize":3026432}],"data":[],"endTime":1377637871147.1,"startTime":1377637871147.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637871147.1,"startTime":1377637871147.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637871147.1,"startTime":1377637871147.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637871147.1,"startTime":1377637871147.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637871147.1,"startTime":1377637871147.1,"type":"Program"},{"children":[{"children":[{"children":[],"data":{"dirtyObjects":0,"partialLayout":false,"root":[0,0,1014,0,1014,650,0,650],"rootNode":-4,"totalObjects":88},"endTime":1377637871964.1,"frameId":"2664.1","startTime":1377637871952.1,"type":"Layout","usedHeapSize":3026432}],"data":{"dirtyObjects":88,"partialLayout":false,"root":[0,0,1014,0,1014,650,0,650],"rootNode":-4,"totalObjects":88},"endTime":1377637871964.1,"frameId":"2664.1","startTime":1377637871147.1,"type":"Layout","usedHeapSize":3026432}],"data":[],"endTime":1377637871965.1,"startTime":1377637871147.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637871973.1,"startTime":1377637871968.1,"type":"Program"},{"children":[{"data":[],"startTime":1377637871976.1,"type":"BeginFrame","usedHeapSize":3026432},{"children":[],"data":{"clip":[0,0,1014,0,1014,650,0,650],"layerRootNode":-4},"endTime":1377637872091.1,"frameId":"2664.1","startTime":1377637872003.1,"type":"Paint","usedHeapSize":3026432}],"data":[],"endTime":1377637872092.1,"startTime":1377637871974.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872092.1,"startTime":1377637872092.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872093.1,"startTime":1377637872092.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872093.1,"startTime":1377637872093.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/gif","requestId":"2664.4","statusCode":200},"endTime":1377637872094.1,"frameId":"2664.1","startTime":1377637872094.1,"type":"ResourceReceiveResponse","usedHeapSize":3026432}],"data":[],"endTime":1377637872095.1,"startTime":1377637872093.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872095.1,"startTime":1377637872095.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872095.1,"startTime":1377637872095.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872095.1,"startTime":1377637872095.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/png","requestId":"2664.5","statusCode":200},"endTime":1377637872096.1,"frameId":"2664.1","startTime":1377637872096.1,"type":"ResourceReceiveResponse","usedHeapSize":3026432}],"data":[],"endTime":1377637872097.1,"startTime":1377637872095.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872098.1,"startTime":1377637872098.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872100.1,"startTime":1377637872099.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"2664.4"},"endTime":1377637872100.1,"frameId":"2664.1","startTime":1377637872100.1,"type":"ResourceReceivedData","usedHeapSize":3026432}],"data":[],"endTime":1377637872100.1,"startTime":1377637872100.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872100.1,"startTime":1377637872100.1,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377637871424.1,"requestId":"2664.4"},"frameId":"2664.1","startTime":1377637872100.1,"type":"ResourceFinish","usedHeapSize":3026432}],"data":[],"endTime":1377637872100.1,"startTime":1377637872100.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872102.1,"startTime":1377637872102.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"2664.5"},"endTime":1377637872105.1,"frameId":"2664.1","startTime":1377637872103.1,"type":"ResourceReceivedData","usedHeapSize":3026432}],"data":[],"endTime":1377637872105.1,"startTime":1377637872103.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872105.1,"startTime":1377637872105.1,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377637871427.1,"requestId":"2664.5"},"frameId":"2664.1","startTime":1377637872105.1,"type":"ResourceFinish","usedHeapSize":3026432}],"data":[],"endTime":1377637872105.1,"startTime":1377637872105.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"image\/gif","requestId":"2664.6","statusCode":200},"endTime":1377637872106.1,"frameId":"2664.1","startTime":1377637872106.1,"type":"ResourceReceiveResponse","usedHeapSize":3026432}],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"application\/javascript","requestId":"2664.7","statusCode":200},"endTime":1377637872106.1,"frameId":"2664.1","startTime":1377637872106.1,"type":"ResourceReceiveResponse","usedHeapSize":3026432}],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"application\/javascript","requestId":"2664.8","statusCode":200},"endTime":1377637872106.1,"frameId":"2664.1","startTime":1377637872106.1,"type":"ResourceReceiveResponse","usedHeapSize":3026432}],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[{"children":[{"data":[],"frameId":"2664.1","startTime":1377637872106.1,"type":"InvalidateLayout","usedHeapSize":3026432}],"data":{"encodedDataLength":0,"requestId":"2664.6"},"endTime":1377637872106.1,"frameId":"2664.1","startTime":1377637872106.1,"type":"ResourceReceivedData","usedHeapSize":3026432}],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377637871461.1,"requestId":"2664.6"},"frameId":"2664.1","startTime":1377637872106.1,"type":"ResourceFinish","usedHeapSize":3026432}],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"application\/javascript","requestId":"2664.9","statusCode":200},"endTime":1377637872106.1,"frameId":"2664.1","startTime":1377637872106.1,"type":"ResourceReceiveResponse","usedHeapSize":3026432}],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"2664.7"},"endTime":1377637872106.1,"frameId":"2664.1","startTime":1377637872106.1,"type":"ResourceReceivedData","usedHeapSize":3026432}],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872106.1,"startTime":1377637872106.1,"type":"Program"},{"children":[{"children":[{"data":[],"frameId":"2664.1","stackTrace":[{"columnNumber":706,"functionName":"","lineNumber":28,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"},{"columnNumber":797,"functionName":"","lineNumber":28,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"}],"startTime":1377637872144.1,"type":"ScheduleStyleRecalculation","usedHeapSize":3156584},{"data":{"singleShot":true,"timeout":3000,"timerId":1},"frameId":"2664.1","stackTrace":[{"columnNumber":800,"functionName":"","lineNumber":28,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"}],"startTime":1377637872145.1,"type":"TimerInstall","usedHeapSize":3157616},{"children":[],"data":{"scriptLine":1,"scriptName":"InjectedScript"},"endTime":1377637872152.1,"frameId":"2664.1","stackTrace":[{"columnNumber":230,"functionName":"","lineNumber":29,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"}],"startTime":1377637872152.1,"type":"FunctionCall","usedHeapSize":3247084,"usedHeapSizeDelta":3220},{"children":[],"data":{"scriptLine":1,"scriptName":"InjectedScript"},"endTime":1377637872153.1,"frameId":"2664.1","stackTrace":[{"columnNumber":349,"functionName":"","lineNumber":29,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"}],"startTime":1377637872153.1,"type":"FunctionCall","usedHeapSize":3250672,"usedHeapSizeDelta":2360},{"children":[],"data":{"scriptLine":1,"scriptName":"InjectedScript"},"endTime":1377637872153.1,"frameId":"2664.1","stackTrace":[{"columnNumber":349,"functionName":"","lineNumber":29,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"}],"startTime":1377637872153.1,"type":"FunctionCall","usedHeapSize":3250928,"usedHeapSizeDelta":80},{"children":[],"data":{"scriptLine":1,"scriptName":"InjectedScript"},"endTime":1377637872153.1,"frameId":"2664.1","stackTrace":[{"columnNumber":442,"functionName":"","lineNumber":29,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"}],"startTime":1377637872153.1,"type":"FunctionCall","usedHeapSize":3252108,"usedHeapSizeDelta":80}],"data":{"lineNumber":1,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"},"endTime":1377637872153.1,"frameId":"2664.1","startTime":1377637872106.1,"type":"EvaluateScript","usedHeapSize":3252204,"usedHeapSizeDelta":225728},{"children":[{"children":[{"data":{"requestId":"2664.10","requestMethod":"GET","url":"http:\/\/suggestion.baidu.com\/su?wd=&cb=window.bdsug.sugPreRequest&sid=&t=1377637872165"},"frameId":"2664.1","stackTrace":[{"columnNumber":568,"functionName":"bdsug.initSug","lineNumber":23,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"},{"columnNumber":765,"functionName":"bds.se.sug","lineNumber":26,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/home_299f7566.js"},{"columnNumber":6538,"functionName":"","lineNumber":1,"url":"http:\/\/www.baidu.com\/"}],"startTime":1377637872172.1,"type":"ResourceSendRequest","usedHeapSize":3383472}],"data":{"lineNumber":1,"url":"http:\/\/www.baidu.com\/"},"endTime":1377637872180.1,"frameId":"2664.1","startTime":1377637872153.1,"type":"EvaluateScript","usedHeapSize":3393016,"usedHeapSizeDelta":140768}],"data":{"endLine":0,"startLine":0},"endTime":1377637872181.1,"frameId":"2664.1","startTime":1377637872153.1,"type":"ParseHTML","usedHeapSize":3393016,"usedHeapSizeDelta":140812},{"data":{"didFail":false,"networkTime":1377637871462.1,"requestId":"2664.7"},"frameId":"2664.1","startTime":1377637872181.1,"type":"ResourceFinish","usedHeapSize":3393016}],"data":[],"endTime":1377637872182.1,"startTime":1377637872106.1,"type":"Program","usedHeapSizeDelta":366584},{"children":[],"data":[],"endTime":1377637872185.1,"startTime":1377637872184.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"2664.8"},"endTime":1377637872185.1,"frameId":"2664.1","startTime":1377637872185.1,"type":"ResourceReceivedData","usedHeapSize":3393016}],"data":[],"endTime":1377637872185.1,"startTime":1377637872185.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872185.1,"startTime":1377637872185.1,"type":"Program"},{"children":[{"children":[],"data":{"lineNumber":1,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/tangram-1.3.4c1.0_07038476.js"},"endTime":1377637872258.1,"frameId":"2664.1","startTime":1377637872193.1,"type":"EvaluateScript","usedHeapSize":3487452,"usedHeapSizeDelta":94392},{"children":[],"data":{"endLine":0,"startLine":0},"endTime":1377637872259.1,"frameId":"2664.1","startTime":1377637872258.1,"type":"ParseHTML","usedHeapSize":3487452},{"data":{"didFail":false,"networkTime":1377637871462.1,"requestId":"2664.8"},"frameId":"2664.1","startTime":1377637872259.1,"type":"ResourceFinish","usedHeapSize":3487452}],"data":[],"endTime":1377637872259.1,"startTime":1377637872185.1,"type":"Program","usedHeapSizeDelta":94436},{"children":[],"data":[],"endTime":1377637872259.1,"startTime":1377637872259.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":0,"requestId":"2664.9"},"endTime":1377637872259.1,"frameId":"2664.1","startTime":1377637872259.1,"type":"ResourceReceivedData","usedHeapSize":3487452}],"data":[],"endTime":1377637872259.1,"startTime":1377637872259.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872259.1,"startTime":1377637872259.1,"type":"Program"},{"children":[{"children":[{"data":{"requestId":"2664.11","requestMethod":"GET","url":"http:\/\/passport.baidu.com\/passApi\/js\/uni_login_wrapper.js?cdnversion=1377637872254"},"frameId":"2664.1","stackTrace":[{"columnNumber":967,"functionName":"baidu.sio.callByBrowser","lineNumber":19,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/tangram-1.3.4c1.0_07038476.js"},{"columnNumber":454,"functionName":"","lineNumber":1,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/user\/js\/u_3603075f.js"}],"startTime":1377637872262.1,"type":"ResourceSendRequest","usedHeapSize":3630736}],"data":{"lineNumber":1,"url":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/user\/js\/u_3603075f.js"},"endTime":1377637872263.1,"frameId":"2664.1","startTime":1377637872259.1,"type":"EvaluateScript","usedHeapSize":3642560,"usedHeapSizeDelta":155064},{"children":[{"children":[],"data":{"lineNumber":1,"url":"http:\/\/www.baidu.com\/"},"endTime":1377637872263.1,"frameId":"2664.1","startTime":1377637872263.1,"type":"EvaluateScript","usedHeapSize":3647844,"usedHeapSizeDelta":5240},{"children":[],"data":{"lineNumber":2,"url":"http:\/\/www.baidu.com\/"},"endTime":1377637872263.1,"frameId":"2664.1","startTime":1377637872263.1,"type":"EvaluateScript","usedHeapSize":3648468,"usedHeapSizeDelta":580},{"data":[],"frameId":"2664.1","startTime":1377637872263.1,"type":"InvalidateLayout","usedHeapSize":3648468},{"children":[],"data":{"elementCount":3},"endTime":1377637872264.1,"frameId":"2664.1","startTime":1377637872263.1,"type":"RecalculateStyles","usedHeapSize":3648468},{"data":{"isMainFrame":true},"frameId":"2664.1","startTime":1377637872264.1,"type":"MarkDOMContent","usedHeapSize":3648468}],"data":{"endLine":0,"startLine":0},"endTime":1377637872264.1,"frameId":"2664.1","startTime":1377637872263.1,"type":"ParseHTML","usedHeapSize":3648468,"usedHeapSizeDelta":5908},{"data":{"didFail":false,"networkTime":1377637871462.1,"requestId":"2664.9"},"frameId":"2664.1","startTime":1377637872264.1,"type":"ResourceFinish","usedHeapSize":3648468}],"data":[],"endTime":1377637872265.1,"startTime":1377637872259.1,"type":"Program","usedHeapSizeDelta":161016},{"children":[{"data":{"usedHeapSizeDelta":1144644},"endTime":1377637872379.1,"startTime":1377637872265.1,"type":"GCEvent","usedHeapSize":2503824}],"data":[],"endTime":1377637872379.1,"startTime":1377637872265.1,"type":"Program","usedHeapSizeDelta":4293822652},{"children":[],"data":[],"endTime":1377637872379.1,"startTime":1377637872379.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872379.1,"startTime":1377637872379.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872379.1,"startTime":1377637872379.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872379.1,"startTime":1377637872379.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872379.1,"startTime":1377637872379.1,"type":"Program"},{"children":[{"children":[],"data":{"dirtyObjects":11,"partialLayout":false,"root":[0,0,1014,0,1014,650,0,650],"rootNode":-4,"totalObjects":90},"endTime":1377637872381.1,"frameId":"2664.1","startTime":1377637872379.1,"type":"Layout","usedHeapSize":2503824}],"data":[],"endTime":1377637872381.1,"startTime":1377637872379.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872423.1,"startTime":1377637872423.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"baiduapp\/json","requestId":"2664.10","statusCode":200},"endTime":1377637872424.1,"frameId":"2664.1","startTime":1377637872424.1,"type":"ResourceReceiveResponse","usedHeapSize":2503824}],"data":[],"endTime":1377637872424.1,"startTime":1377637872424.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872425.1,"startTime":1377637872425.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":259,"requestId":"2664.10"},"endTime":1377637872425.1,"frameId":"2664.1","startTime":1377637872425.1,"type":"ResourceReceivedData","usedHeapSize":2503824}],"data":[],"endTime":1377637872425.1,"startTime":1377637872425.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872425.1,"startTime":1377637872425.1,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377637872422.1,"requestId":"2664.10"},"frameId":"2664.1","startTime":1377637872425.1,"type":"ResourceFinish","usedHeapSize":2503824}],"data":[],"endTime":1377637872425.1,"startTime":1377637872425.1,"type":"Program"},{"children":[{"children":[],"data":{"lineNumber":1,"url":"http:\/\/suggestion.baidu.com\/su?wd=&cb=window.bdsug.sugPreRequest&sid=&t=1377637872165"},"endTime":1377637872426.1,"frameId":"2664.1","startTime":1377637872425.1,"type":"EvaluateScript","usedHeapSize":2506308,"usedHeapSizeDelta":2356}],"data":[],"endTime":1377637872426.1,"startTime":1377637872425.1,"type":"Program","usedHeapSizeDelta":2484},{"children":[],"data":[],"endTime":1377637872464.1,"startTime":1377637872464.1,"type":"Program"},{"children":[{"data":[],"startTime":1377637872464.1,"type":"BeginFrame","usedHeapSize":2506308},{"children":[{"children":[],"data":{"imageType":"GIF"},"endTime":1377637872472.1,"startTime":1377637872472.1,"type":"DecodeImage","usedHeapSize":2506308}],"data":{"clip":[372,24,642,24,642,153,372,153],"layerRootNode":-4},"endTime":1377637872472.1,"frameId":"2664.1","startTime":1377637872472.1,"type":"Paint","usedHeapSize":2506308},{"children":[{"children":[],"data":{"imageType":"PNG"},"endTime":1377637872472.1,"startTime":1377637872472.1,"type":"DecodeImage","usedHeapSize":2506308}],"data":{"clip":[257,180,677,180,677,211,257,211],"layerRootNode":-4},"endTime":1377637872472.1,"frameId":"2664.1","startTime":1377637872472.1,"type":"Paint","usedHeapSize":2506308},{"children":[],"data":{"clip":[682,179,779,179,779,213,682,213],"layerRootNode":-4},"endTime":1377637872473.1,"frameId":"2664.1","startTime":1377637872472.1,"type":"Paint","usedHeapSize":2506308},{"children":[],"data":{"clip":[147,506,867,506,867,521,147,521],"layerRootNode":-4},"endTime":1377637872473.1,"frameId":"2664.1","startTime":1377637872473.1,"type":"Paint","usedHeapSize":2506308},{"children":[{"children":[],"data":{"imageType":"GIF"},"endTime":1377637872473.1,"startTime":1377637872473.1,"type":"DecodeImage","usedHeapSize":2506308}],"data":{"clip":[147,557,867,557,867,586,147,586],"layerRootNode":-4},"endTime":1377637872473.1,"frameId":"2664.1","startTime":1377637872473.1,"type":"Paint","usedHeapSize":2506308}],"data":[],"endTime":1377637872473.1,"startTime":1377637872464.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872573.1,"startTime":1377637872573.1,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"text\/javascript","requestId":"2664.11","statusCode":200},"endTime":1377637872574.1,"frameId":"2664.1","startTime":1377637872574.1,"type":"ResourceReceiveResponse","usedHeapSize":2506308}],"data":[],"endTime":1377637872574.1,"startTime":1377637872573.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872580.1,"startTime":1377637872580.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872581.1,"startTime":1377637872580.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":1024,"requestId":"2664.11"},"endTime":1377637872582.1,"frameId":"2664.1","startTime":1377637872581.1,"type":"ResourceReceivedData","usedHeapSize":2506308}],"data":[],"endTime":1377637872582.1,"startTime":1377637872581.1,"type":"Program"},{"children":[{"children":[],"data":{"encodedDataLength":281,"requestId":"2664.11"},"endTime":1377637872582.1,"frameId":"2664.1","startTime":1377637872582.1,"type":"ResourceReceivedData","usedHeapSize":2506308}],"data":[],"endTime":1377637872582.1,"startTime":1377637872582.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872589.1,"startTime":1377637872589.1,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377637872577.1,"requestId":"2664.11"},"frameId":"2664.1","startTime":1377637872589.1,"type":"ResourceFinish","usedHeapSize":2506308}],"data":[],"endTime":1377637872589.1,"startTime":1377637872589.1,"type":"Program"},{"children":[{"children":[],"data":{"lineNumber":1,"url":"http:\/\/passport.baidu.com\/passApi\/js\/uni_login_wrapper.js?cdnversion=1377637872254"},"endTime":1377637872589.1,"frameId":"2664.1","startTime":1377637872589.1,"type":"EvaluateScript","usedHeapSize":2509964,"usedHeapSizeDelta":3612},{"children":[{"children":[],"data":{"scriptLine":19,"scriptName":"http:\/\/s1.bdstatic.com\/r\/www\/cache\/static\/global\/js\/tangram-1.3.4c1.0_07038476.js"},"endTime":1377637872589.1,"frameId":"2664.1","startTime":1377637872589.1,"type":"FunctionCall","usedHeapSize":2557444,"usedHeapSizeDelta":28980}],"data":{"type":"load"},"endTime":1377637872589.1,"frameId":"2664.1","startTime":1377637872589.1,"type":"EventDispatch","usedHeapSize":2557444,"usedHeapSizeDelta":47480},{"children":[{"children":[],"data":{"scriptLine":101,"scriptName":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"},"endTime":1377637872598.1,"frameId":"2664.1","startTime":1377637872589.1,"type":"FunctionCall","usedHeapSize":2630832,"usedHeapSizeDelta":58436},{"children":[{"data":[],"frameId":"2664.1","stackTrace":[{"columnNumber":9452,"functionName":"","lineNumber":1,"url":"http:\/\/www.baidu.com\/"}],"startTime":1377637872599.1,"type":"ScheduleStyleRecalculation","usedHeapSize":2631672},{"children":[],"data":{"elementCount":2},"endTime":1377637872599.1,"frameId":"2664.1","stackTrace":[{"columnNumber":9452,"functionName":"","lineNumber":1,"url":"http:\/\/www.baidu.com\/"}],"startTime":1377637872599.1,"type":"RecalculateStyles","usedHeapSize":2632612}],"data":{"scriptLine":1,"scriptName":"http:\/\/www.baidu.com\/"},"endTime":1377637872600.1,"frameId":"2664.1","startTime":1377637872598.1,"type":"FunctionCall","usedHeapSize":2632612,"usedHeapSizeDelta":1700}],"data":{"type":"load"},"endTime":1377637872600.1,"frameId":"2664.1","startTime":1377637872589.1,"type":"EventDispatch","usedHeapSize":2632612,"usedHeapSizeDelta":75168},{"data":{"isMainFrame":true},"frameId":"2664.1","startTime":1377637872600.1,"type":"MarkLoad","usedHeapSize":2632612}],"data":[],"endTime":1377637872601.1,"startTime":1377637872589.1,"type":"Program","usedHeapSizeDelta":126304},{"children":[],"data":[],"endTime":1377637872601.1,"startTime":1377637872601.1,"type":"Program"},{"children":[{"data":[],"startTime":1377637872601.1,"type":"BeginFrame","usedHeapSize":2632612},{"children":[],"data":{"clip":[264,184,670,184,670,208,264,208],"layerRootNode":-4},"endTime":1377637872603.1,"frameId":"2664.1","startTime":1377637872602.1,"type":"Paint","usedHeapSize":2632612}],"data":[],"endTime":1377637872603.1,"startTime":1377637872601.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872604.1,"startTime":1377637872604.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872604.1,"startTime":1377637872604.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872611.1,"startTime":1377637872611.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872611.1,"startTime":1377637872611.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637872613.1,"startTime":1377637872613.1,"type":"Program"},{"children":[{"children":[{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"lastError"},"endTime":1377637872619.1,"frameId":"2664.1","stackTrace":[{"columnNumber":17,"functionName":"","lineNumber":5,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377637872619.1,"type":"FunctionCall","usedHeapSize":2690684,"usedHeapSizeDelta":4332},{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"utils"},"endTime":1377637872620.1,"frameId":"2664.1","stackTrace":[{"columnNumber":22,"functionName":"","lineNumber":41,"url":"json_schema"},{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377637872620.1,"type":"FunctionCall","usedHeapSize":2726332,"usedHeapSizeDelta":1736}],"data":{"scriptLine":1,"scriptName":"json_schema"},"endTime":1377637872620.1,"frameId":"2664.1","stackTrace":[{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377637872620.1,"type":"FunctionCall","usedHeapSize":2731688,"usedHeapSizeDelta":10404}],"data":{"scriptLine":1,"scriptName":"schemaUtils"},"endTime":1377637872620.1,"frameId":"2664.1","stackTrace":[{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377637872619.1,"type":"FunctionCall","usedHeapSize":2732592,"usedHeapSizeDelta":30284}],"data":{"scriptLine":1,"scriptName":"sendRequest"},"endTime":1377637872620.1,"frameId":"2664.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377637872619.1,"type":"FunctionCall","usedHeapSize":2733220,"usedHeapSizeDelta":50616},{"children":[],"data":{"scriptLine":1,"scriptName":"unload_event"},"endTime":1377637872620.1,"frameId":"2664.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":11,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377637872620.1,"type":"FunctionCall","usedHeapSize":2736372,"usedHeapSizeDelta":676}],"data":{"scriptLine":1,"scriptName":"event_bindings"},"endTime":1377637872620.1,"frameId":"2664.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377637872619.1,"type":"FunctionCall","usedHeapSize":2742224,"usedHeapSizeDelta":73588}],"data":{"scriptLine":1,"scriptName":"miscellaneous_bindings"},"endTime":1377637872620.1,"frameId":"2664.1","startTime":1377637872619.1,"type":"FunctionCall","usedHeapSize":2749580,"usedHeapSizeDelta":101184},{"children":[],"data":{"scriptLine":76,"scriptName":"miscellaneous_bindings"},"endTime":1377637872620.1,"frameId":"2664.1","startTime":1377637872620.1,"type":"FunctionCall","usedHeapSize":2749748},{"children":[],"data":{"scriptLine":76,"scriptName":"miscellaneous_bindings"},"endTime":1377637872620.1,"frameId":"2664.1","startTime":1377637872620.1,"type":"FunctionCall","usedHeapSize":2749868},{"children":[],"data":{"scriptLine":270,"scriptName":"miscellaneous_bindings"},"endTime":1377637872621.1,"frameId":"2664.1","startTime":1377637872620.1,"type":"FunctionCall","usedHeapSize":2751892,"usedHeapSizeDelta":1876}],"data":[],"endTime":1377637872621.1,"startTime":1377637872619.1,"type":"Program","usedHeapSizeDelta":119280},{"children":[{"children":[],"data":{"scriptLine":253,"scriptName":"miscellaneous_bindings"},"endTime":1377637872621.1,"frameId":"2664.1","startTime":1377637872621.1,"type":"FunctionCall","usedHeapSize":2753204,"usedHeapSizeDelta":1180},{"children":[],"data":{"scriptLine":253,"scriptName":"miscellaneous_bindings"},"endTime":1377637872621.1,"frameId":"2664.1","startTime":1377637872621.1,"type":"FunctionCall","usedHeapSize":2753500,"usedHeapSizeDelta":164}],"data":[],"endTime":1377637872621.1,"startTime":1377637872621.1,"type":"Program","usedHeapSizeDelta":1608},{"children":[],"data":[],"endTime":1377637872943.1,"startTime":1377637872943.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637873089.1,"startTime":1377637873089.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637873130.1,"startTime":1377637873130.1,"type":"Program"},{"children":[{"data":[],"startTime":1377637873130.1,"type":"BeginFrame","usedHeapSize":2753500},{"children":[],"data":{"clip":[264,184,267,184,267,208,264,208],"layerRootNode":-4},"endTime":1377637873130.1,"frameId":"2664.1","startTime":1377637873130.1,"type":"Paint","usedHeapSize":2753500}],"data":[],"endTime":1377637873131.1,"startTime":1377637873130.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637873131.1,"startTime":1377637873131.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637873131.1,"startTime":1377637873131.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637873662.1,"startTime":1377637873662.1,"type":"Program"},{"children":[{"data":[],"startTime":1377637873662.1,"type":"BeginFrame","usedHeapSize":2753500},{"children":[],"data":{"clip":[264,184,267,184,267,208,264,208],"layerRootNode":-4},"endTime":1377637873662.1,"frameId":"2664.1","startTime":1377637873662.1,"type":"Paint","usedHeapSize":2753500}],"data":[],"endTime":1377637873662.1,"startTime":1377637873662.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637873663.1,"startTime":1377637873663.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637873663.1,"startTime":1377637873663.1,"type":"Program"},{"children":[],"data":[],"endTime":1377637874100.1,"startTime":1377637874100.1,"type":"Program"}]
\ No newline at end of file
diff --git a/tools/perf/metrics/unittest_data/cern_repeat_timeline.json b/tools/perf/metrics/unittest_data/cern_repeat_timeline.json
deleted file mode 100644
index c1a2cd1..0000000
--- a/tools/perf/metrics/unittest_data/cern_repeat_timeline.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"children":[{"data":{"requestId":"1312.3","requestMethod":"GET","url":"http:\/\/info.cern.ch\/hypertext\/WWW\/TheProject.html"},"frameId":"1312.1","startTime":1377574729484.4,"type":"ResourceSendRequest","usedHeapSize":2055348}],"data":[],"endTime":1377574729484.4,"startTime":1377574729483.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729500.4,"startTime":1377574729500.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729501.4,"startTime":1377574729501.4,"type":"Program"},{"children":[{"children":[],"data":{"mimeType":"text\/html","requestId":"1312.3","statusCode":200},"endTime":1377574729501.4,"frameId":"1312.1","startTime":1377574729501.4,"type":"ResourceReceiveResponse","usedHeapSize":2055348}],"data":[],"endTime":1377574729502.4,"startTime":1377574729501.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729502.4,"startTime":1377574729502.4,"type":"Program"},{"children":[{"children":[{"children":[],"data":{"elementCount":0},"endTime":1377574729506.4,"frameId":"1312.1","startTime":1377574729506.4,"type":"RecalculateStyles","usedHeapSize":2137748}],"data":{"encodedDataLength":0,"requestId":"1312.3"},"endTime":1377574729526.4,"frameId":"1312.1","startTime":1377574729502.4,"type":"ResourceReceivedData","usedHeapSize":2468416,"usedHeapSizeDelta":413068}],"data":[],"endTime":1377574729526.4,"startTime":1377574729502.4,"type":"Program","usedHeapSizeDelta":413068},{"children":[],"data":[],"endTime":1377574729531.4,"startTime":1377574729531.4,"type":"Program"},{"children":[{"data":{"didFail":false,"networkTime":1377574729502.4,"requestId":"1312.3"},"frameId":"1312.1","startTime":1377574729531.4,"type":"ResourceFinish","usedHeapSize":2468416}],"data":[],"endTime":1377574729531.4,"startTime":1377574729531.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729531.4,"startTime":1377574729531.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729531.4,"startTime":1377574729531.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729531.4,"startTime":1377574729531.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729540.4,"startTime":1377574729540.4,"type":"Program"},{"children":[{"children":[{"data":[],"frameId":"1312.1","startTime":1377574729540.4,"type":"ScheduleStyleRecalculation","usedHeapSize":2468416},{"children":[{"children":[{"children":[{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"lastError"},"endTime":1377574729559.4,"frameId":"1312.1","stackTrace":[{"columnNumber":17,"functionName":"","lineNumber":5,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377574729558.4,"type":"FunctionCall","usedHeapSize":2798808,"usedHeapSizeDelta":3956},{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"utils"},"endTime":1377574729560.4,"frameId":"1312.1","stackTrace":[{"columnNumber":22,"functionName":"","lineNumber":41,"url":"json_schema"},{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"}],"startTime":1377574729560.4,"type":"FunctionCall","usedHeapSize":2835096,"usedHeapSizeDelta":1744}],"data":{"scriptLine":1,"scriptName":"json_schema"},"endTime":1377574729561.4,"frameId":"1312.1","stackTrace":[{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"}],"startTime":1377574729559.4,"type":"FunctionCall","usedHeapSize":2840460,"usedHeapSizeDelta":11128}],"data":{"scriptLine":1,"scriptName":"schemaUtils"},"endTime":1377574729561.4,"frameId":"1312.1","stackTrace":[{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377574729559.4,"type":"FunctionCall","usedHeapSize":2841372,"usedHeapSizeDelta":31052}],"data":{"scriptLine":1,"scriptName":"sendRequest"},"endTime":1377574729561.4,"frameId":"1312.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377574729558.4,"type":"FunctionCall","usedHeapSize":2842008,"usedHeapSizeDelta":50976},{"children":[],"data":{"scriptLine":1,"scriptName":"unload_event"},"endTime":1377574729561.4,"frameId":"1312.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":11,"url":"event_bindings"},{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377574729561.4,"type":"FunctionCall","usedHeapSize":2845240,"usedHeapSizeDelta":684}],"data":{"scriptLine":1,"scriptName":"event_bindings"},"endTime":1377574729561.4,"frameId":"1312.1","stackTrace":[{"columnNumber":13,"functionName":"","lineNumber":5,"url":"binding"},{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377574729558.4,"type":"FunctionCall","usedHeapSize":2851660,"usedHeapSizeDelta":73956}],"data":{"scriptLine":1,"scriptName":"binding"},"endTime":1377574729564.4,"frameId":"1312.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":7,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377574729558.4,"type":"FunctionCall","usedHeapSize":2874464,"usedHeapSizeDelta":114384},{"children":[],"data":{"scriptLine":1,"scriptName":"miscellaneous_bindings"},"endTime":1377574729565.4,"frameId":"1312.1","stackTrace":[{"columnNumber":20,"functionName":"","lineNumber":11,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377574729565.4,"type":"FunctionCall","usedHeapSize":2897600,"usedHeapSizeDelta":7220},{"children":[],"data":{"scriptLine":1,"scriptName":"runtime"},"endTime":1377574729570.4,"frameId":"1312.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":82,"url":"extension"},{"columnNumber":16,"functionName":"target.(anonymous function)","lineNumber":20,"url":"extensions::SafeBuiltins"},{"columnNumber":10,"functionName":"","lineNumber":79,"url":"extension"},{"columnNumber":7,"functionName":"","lineNumber":190,"url":"binding"},{"columnNumber":16,"functionName":"target.(anonymous function)","lineNumber":20,"url":"extensions::SafeBuiltins"}],"startTime":1377574729566.4,"type":"FunctionCall","usedHeapSize":2989296,"usedHeapSizeDelta":42396}],"data":{"scriptLine":1,"scriptName":"extension"},"endTime":1377574729571.4,"frameId":"1312.1","stackTrace":[{"columnNumber":7,"functionName":"","lineNumber":188,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"}],"startTime":1377574729557.4,"type":"FunctionCall","usedHeapSize":2993552,"usedHeapSizeDelta":251252}],"data":{"lineNumber":1,"url":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"},"endTime":1377574729572.4,"frameId":"1312.1","startTime":1377574729555.4,"type":"EvaluateScript","usedHeapSize":3070060,"usedHeapSizeDelta":338260},{"data":[],"frameId":"1312.1","startTime":1377574729572.4,"type":"InvalidateLayout","usedHeapSize":3070060}],"data":{"endLine":73,"startLine":0},"endTime":1377574729577.4,"frameId":"1312.1","startTime":1377574729540.4,"type":"ParseHTML","usedHeapSize":3070060,"usedHeapSizeDelta":601644}],"data":[],"endTime":1377574729577.4,"startTime":1377574729540.4,"type":"Program","usedHeapSizeDelta":601644},{"children":[{"children":[{"data":[],"frameId":"1312.1","startTime":1377574729578.4,"type":"InvalidateLayout","usedHeapSize":3070060},{"children":[],"data":{"elementCount":52},"endTime":1377574729578.4,"frameId":"1312.1","startTime":1377574729578.4,"type":"RecalculateStyles","usedHeapSize":3070060},{"children":[{"children":[],"data":{"scriptLine":101,"scriptName":"chrome-extension:\/\/ejfgipfkkpdadoidbhfedneaabgolbno\/wpt\/script.js"},"endTime":1377574729614.4,"frameId":"1312.1","startTime":1377574729578.4,"type":"FunctionCall","usedHeapSize":3128104,"usedHeapSizeDelta":48916}],"data":{"type":"load"},"endTime":1377574729614.4,"frameId":"1312.1","startTime":1377574729578.4,"type":"EventDispatch","usedHeapSize":3128104,"usedHeapSizeDelta":58044},{"data":{"isMainFrame":true},"frameId":"1312.1","startTime":1377574729614.4,"type":"MarkLoad","usedHeapSize":3128104},{"children":[{"children":[],"data":{"dirtyObjects":0,"partialLayout":false,"root":[0,0,1014,0,1014,650,0,650],"rootNode":-3,"totalObjects":115},"endTime":1377574729708.4,"frameId":"1312.1","startTime":1377574729702.4,"type":"Layout","usedHeapSize":3128104}],"data":{"dirtyObjects":115,"partialLayout":false,"root":[0,0,1014,0,1014,650,0,650],"rootNode":-3,"totalObjects":115},"endTime":1377574729709.4,"frameId":"1312.1","startTime":1377574729616.4,"type":"Layout","usedHeapSize":3128104},{"data":{"isMainFrame":true},"frameId":"1312.1","startTime":1377574729709.4,"type":"MarkDOMContent","usedHeapSize":3128104}],"data":{"endLine":0,"startLine":73},"endTime":1377574729791.4,"frameId":"1312.1","startTime":1377574729578.4,"type":"ParseHTML","usedHeapSize":3128104,"usedHeapSizeDelta":58044}],"data":[],"endTime":1377574729791.4,"startTime":1377574729578.4,"type":"Program","usedHeapSizeDelta":58044},{"children":[],"data":[],"endTime":1377574729792.4,"startTime":1377574729792.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729792.4,"startTime":1377574729792.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729795.4,"startTime":1377574729794.4,"type":"Program"},{"children":[{"data":[],"startTime":1377574729795.4,"type":"BeginFrame","usedHeapSize":3128104},{"children":[],"data":{"clip":[0,0,1014,0,1014,650,0,650],"layerRootNode":-3},"endTime":1377574729863.4,"frameId":"1312.1","startTime":1377574729811.4,"type":"Paint","usedHeapSize":3128104}],"data":[],"endTime":1377574729864.4,"startTime":1377574729795.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729864.4,"startTime":1377574729864.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574729864.4,"startTime":1377574729864.4,"type":"Program"},{"children":[{"children":[],"data":{"scriptLine":76,"scriptName":"miscellaneous_bindings"},"endTime":1377574729897.4,"frameId":"1312.1","startTime":1377574729897.4,"type":"FunctionCall","usedHeapSize":3128224},{"children":[],"data":{"scriptLine":270,"scriptName":"miscellaneous_bindings"},"endTime":1377574729903.4,"frameId":"1312.1","startTime":1377574729897.4,"type":"FunctionCall","usedHeapSize":3130740,"usedHeapSizeDelta":2368},{"children":[{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"lastError"},"endTime":1377574729904.4,"frameId":"1312.1","stackTrace":[{"columnNumber":17,"functionName":"","lineNumber":5,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377574729904.4,"type":"FunctionCall","usedHeapSize":3186488,"usedHeapSizeDelta":4720},{"children":[{"children":[{"children":[],"data":{"scriptLine":1,"scriptName":"utils"},"endTime":1377574729905.4,"frameId":"1312.1","stackTrace":[{"columnNumber":22,"functionName":"","lineNumber":41,"url":"json_schema"},{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377574729905.4,"type":"FunctionCall","usedHeapSize":3222284,"usedHeapSizeDelta":2272}],"data":{"scriptLine":1,"scriptName":"json_schema"},"endTime":1377574729905.4,"frameId":"1312.1","stackTrace":[{"columnNumber":27,"functionName":"","lineNumber":8,"url":"schemaUtils"},{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377574729905.4,"type":"FunctionCall","usedHeapSize":3227916,"usedHeapSizeDelta":11156}],"data":{"scriptLine":1,"scriptName":"schemaUtils"},"endTime":1377574729905.4,"frameId":"1312.1","stackTrace":[{"columnNumber":16,"functionName":"","lineNumber":9,"url":"sendRequest"},{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377574729905.4,"type":"FunctionCall","usedHeapSize":3229096,"usedHeapSizeDelta":31252}],"data":{"scriptLine":1,"scriptName":"sendRequest"},"endTime":1377574729905.4,"frameId":"1312.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":8,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377574729904.4,"type":"FunctionCall","usedHeapSize":3230000,"usedHeapSizeDelta":51920},{"children":[],"data":{"scriptLine":1,"scriptName":"unload_event"},"endTime":1377574729905.4,"frameId":"1312.1","stackTrace":[{"columnNumber":21,"functionName":"","lineNumber":11,"url":"event_bindings"},{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377574729905.4,"type":"FunctionCall","usedHeapSize":3233368,"usedHeapSizeDelta":676}],"data":{"scriptLine":1,"scriptName":"event_bindings"},"endTime":1377574729905.4,"frameId":"1312.1","stackTrace":[{"columnNumber":15,"functionName":"","lineNumber":12,"url":"miscellaneous_bindings"}],"startTime":1377574729903.4,"type":"FunctionCall","usedHeapSize":3239220,"usedHeapSizeDelta":74288}],"data":{"scriptLine":1,"scriptName":"miscellaneous_bindings"},"endTime":1377574729905.4,"frameId":"1312.1","startTime":1377574729903.4,"type":"FunctionCall","usedHeapSize":3246436,"usedHeapSizeDelta":100600},{"children":[],"data":{"scriptLine":76,"scriptName":"miscellaneous_bindings"},"endTime":1377574729905.4,"frameId":"1312.1","startTime":1377574729905.4,"type":"FunctionCall","usedHeapSize":3246604}],"data":[],"endTime":1377574729905.4,"startTime":1377574729897.4,"type":"Program","usedHeapSizeDelta":118500},{"children":[{"children":[],"data":{"scriptLine":253,"scriptName":"miscellaneous_bindings"},"endTime":1377574729905.4,"frameId":"1312.1","startTime":1377574729905.4,"type":"FunctionCall","usedHeapSize":3246736},{"children":[],"data":{"scriptLine":253,"scriptName":"miscellaneous_bindings"},"endTime":1377574729905.4,"frameId":"1312.1","startTime":1377574729905.4,"type":"FunctionCall","usedHeapSize":3247032,"usedHeapSizeDelta":164}],"data":[],"endTime":1377574729905.4,"startTime":1377574729905.4,"type":"Program","usedHeapSizeDelta":428},{"children":[],"data":[],"endTime":1377574729908.4,"startTime":1377574729908.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574730293.4,"startTime":1377574730291.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574730306.4,"startTime":1377574730306.4,"type":"Program"},{"children":[],"data":[],"endTime":1377574730306.4,"startTime":1377574730306.4,"type":"Program"},{"children":[{"data":{"usedHeapSizeDelta":1130268},"endTime":1377574730329.4,"startTime":1377574730307.4,"type":"GCEvent","usedHeapSize":2116764}],"data":[],"endTime":1377574730330.4,"startTime":1377574730306.4,"type":"Program","usedHeapSizeDelta":4293837028},{"children":[],"data":[],"endTime":1377574730565.4,"startTime":1377574730565.4,"type":"Program"}]
\ No newline at end of file
diff --git a/tools/perf/metrics/unittest_data/sample_timeline.json b/tools/perf/metrics/unittest_data/sample_timeline.json
deleted file mode 100644
index 7fbfefd..0000000
--- a/tools/perf/metrics/unittest_data/sample_timeline.json
+++ /dev/null
@@ -1,81 +0,0 @@
-[
-  {
-    "type": "Paint",
-    "comment": "This paint event occurs before Layout, it will be filtered out.",
-    "startTime": 0,
-    "endTime": 0,
-    "data": {
-      "clip": [ 0, 0, 1000, 0, 1000, 800, 0, 800 ]
-    },
-    "frameId": "1.1",
-    "children": []
-  },
-  {
-    "type": "ResourceReceiveResponse",
-    "startTime": 0,
-    "endTime": 10,
-    "children": []
-  },
-  {
-    "type": "Layout",
-    "startTime": 10,
-    "endTime": 20,
-    "children": []
-  },
-  {
-    "type": "Paint",
-    "comment": "Full-screen paint event; area 1,000,000",
-    "startTime": 100,
-    "endTime": 100,
-    "data": {
-      "clip": [ 0, 0, 1000, 0, 1000, 1000, 0, 1000 ]
-    },
-    "frameId": "1.1",
-    "children": []
-  },
-  {
-    "type": "Paint",
-    "comment": "This paint event is not a leaf event. Area 300,000, time 400",
-    "startTime": 200,
-    "endTime": 400,
-    "data": {
-      "clip": [ 0, 0, 1000, 0, 1000, 300, 0, 300 ]
-    },
-    "frameId": "1.1",
-    "children": [
-      {
-        "type": "Paint",
-        "comment": "Area 300,000, time 300",
-        "startTime": 200,
-        "endTime": 300,
-        "data": {
-          "clip": [ 0, 0, 1000, 0, 1000, 300, 0, 300 ]
-        },
-        "frameId": "1.1",
-        "children": []
-      },
-      {
-        "type": "Paint",
-        "comment": "Area 300,000, time 400",
-        "startTime": 300,
-        "endTime": 400,
-        "data": {
-          "clip": [ 0, 0, 1000, 0, 1000, 300, 0, 300 ]
-        },
-        "frameId": "1.1",
-        "children": []
-      }
-    ]
-  },
-  {
-    "type": "Paint",
-    "comment": "Final paint event, with an area of 200,000 ",
-    "startTime": 550,
-    "endTime": 800,
-    "data": {
-      "clip": [ 0, 0, 1000, 0, 1000, 200, 0, 200 ]
-    },
-    "frameId": "1.1",
-    "children": []
-  }
-]
diff --git a/tools/perf/page_sets/data/typical_10_mobile.json b/tools/perf/page_sets/data/typical_10_mobile.json
deleted file mode 100644
index 304a37b..0000000
--- a/tools/perf/page_sets/data/typical_10_mobile.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-    "archives": {
-        "http://de.m.wikipedia.org/wiki/K%C3%B6lner_Dom": {
-            "DEFAULT": "typical_10_mobile_001.wprgo"
-        },
-        "http://m.chiebukuro.yahoo.co.jp/detail/q10136829180": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "http://m.ebay.com/itm/351157205404": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "http://m.facebook.com/barackobama": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "http://m.huffpost.com/us/entry/6004486": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "http://m.ynet.co.il": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "http://siriuslymeg.tumblr.com/": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "http://wapbaike.baidu.com/": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "http://www.cnn.com/2014/03/31/showbiz/tv/himym-finale/index.html": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "http://www.rg.ru/2014/10/21/cska-site.html": {
-            "DEFAULT": "typical_10_mobile_000.wprgo"
-        },
-        "https://en.wikipedia.org/wiki/File:Rotating_earth_(large).gif": {
-            "DEFAULT": "typical_10_mobile_002.wprgo"
-        }
-    },
-    "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
-    "platform_specific": true
-}
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/typical_10_mobile_000.wprgo.sha1 b/tools/perf/page_sets/data/typical_10_mobile_000.wprgo.sha1
deleted file mode 100644
index 3c43c49..0000000
--- a/tools/perf/page_sets/data/typical_10_mobile_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-675bce83c65987b425305731ea4dcc346cf31735
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/typical_10_mobile_001.wprgo.sha1 b/tools/perf/page_sets/data/typical_10_mobile_001.wprgo.sha1
deleted file mode 100644
index 54060189..0000000
--- a/tools/perf/page_sets/data/typical_10_mobile_001.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4e370dd68389ac7a48acc76ed12599794010e565
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/typical_10_mobile_002.wprgo.sha1 b/tools/perf/page_sets/data/typical_10_mobile_002.wprgo.sha1
deleted file mode 100644
index de16b03..0000000
--- a/tools/perf/page_sets/data/typical_10_mobile_002.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-dede6ce924ce925f46e07abeb2eba2e0156031b9
\ No newline at end of file
diff --git a/tools/perf/page_sets/typical_10_mobile.py b/tools/perf/page_sets/typical_10_mobile.py
deleted file mode 100644
index c6d43fc..0000000
--- a/tools/perf/page_sets/typical_10_mobile.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from telemetry.page import page as page_module
-from telemetry.page import shared_page_state
-from telemetry import story
-
-
-class Typical10MobilePage(page_module.Page):
-
-  def __init__(self, url, page_set, name=''):
-    super(Typical10MobilePage, self).__init__(
-        url=url, page_set=page_set, name=name,
-        shared_page_state_class=shared_page_state.SharedMobilePageState)
-
-  def RunPageInteractions(self, action_runner):
-    action_runner.Wait(20)
-    action_runner.ScrollPage()
-    action_runner.Wait(20)
-
-
-urls_list = [
-    # Why: Top site
-    'http://m.facebook.com/barackobama',
-    # Why: Wikipedia article with lots of pictures, German language
-    'http://de.m.wikipedia.org/wiki/K%C3%B6lner_Dom',
-    # Why: current top Q&A on popular Japanese site
-    'http://m.chiebukuro.yahoo.co.jp/detail/q10136829180',
-    # Why: news article on popular site
-    'http://m.huffpost.com/us/entry/6004486',
-    # Why: news article on popular site
-    'http://www.cnn.com/2014/03/31/showbiz/tv/himym-finale/index.html',
-    # Why: Popular RTL language site
-    'http://m.ynet.co.il',
-    # Why: Popular Russian language site
-    'http://www.rg.ru/2014/10/21/cska-site.html',
-    # Why: Popular shopping site
-    'http://m.ebay.com/itm/351157205404',
-    # Why: Popular viral site, lots of images
-    'http://siriuslymeg.tumblr.com/',
-    # Why: Popular Chinese language site.
-    'http://wapbaike.baidu.com/',
-    # Why: Simple page with an animated GIF
-    'https://en.wikipedia.org/wiki/File:Rotating_earth_(large).gif',
-]
-
-
-class Typical10MobilePageSet(story.StorySet):
-  """10 typical mobile pages, used for power testing."""
-
-  def __init__(self):
-    super(Typical10MobilePageSet, self).__init__(
-        archive_data_file='data/typical_10_mobile.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
-
-    for url in urls_list:
-      self.AddStory(Typical10MobilePage(url, self, name=url))
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 69864509..6ab62f2 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -296,4 +296,5 @@
  <item id="webui_content_scripts_download" hash_code="100545943" type="0" content_hash_code="119898059" os_list="linux,windows" file_path="extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc"/>
  <item id="worker_script_load" hash_code="72087791" type="0" content_hash_code="24889169" os_list="linux,windows" file_path="content/browser/worker_host/worker_script_fetcher.cc"/>
  <item id="xmpp_signal_strategy" hash_code="88906454" type="0" content_hash_code="88958321" os_list="linux,windows" file_path="remoting/signaling/xmpp_signal_strategy.cc"/>
+ <item id="web_push_message" hash_code="39886742" type="0" content_hash_code="110064650" os_list="linux,windows" file_path="components/gcm_driver/web_push_sender.cc"/>
 </annotations>
diff --git a/ui/accessibility/ax_range.h b/ui/accessibility/ax_range.h
index 49b6387..1398579 100644
--- a/ui/accessibility/ax_range.h
+++ b/ui/accessibility/ax_range.h
@@ -139,8 +139,7 @@
         forward_range.anchor()->AsLeafTextPosition();
     AXPositionInstance range_end = forward_range.focus()->AsLeafTextPosition();
 
-    while (!current_anchor_start->IsNullPosition() &&
-           *current_anchor_start <= *range_end) {
+    while (!current_anchor_start->IsNullPosition()) {
       // When the current start reaches the same anchor as this AXRange's end,
       // simply append this last anchor (trimmed at range_end) and exit.
       if (current_anchor_start->GetAnchor() == range_end->GetAnchor()) {
@@ -166,58 +165,66 @@
   // Appends rects in screen coordinates of all anchor nodes that span between
   // anchor_ and focus_. Rects outside of the viewport are skipped.
   std::vector<gfx::Rect> GetScreenRects() const {
-    AXRange forward_range = GetForwardRange();
-    std::vector<gfx::Rect> rectangles;
-    auto current_line_start = forward_range.anchor()->Clone();
-    auto range_end = forward_range.focus()->Clone();
+    std::vector<gfx::Rect> rects;
 
-    while (*current_line_start <= *range_end) {
-      auto current_line_end = current_line_start->CreateNextLineEndPosition(
-          ui::AXBoundaryBehavior::CrossBoundary);
+    for (const AXRange& anchor_range : GetAnchors()) {
+      DCHECK(!anchor_range.IsNull());
+      AXPositionInstance anchor_end =
+          anchor_range.focus()->AsLeafTextPosition();
+      AXPositionInstance current_line_start =
+          anchor_range.anchor()->AsLeafTextPosition();
 
-      if (current_line_end->IsNullPosition() || *current_line_end > *range_end)
-        current_line_end = range_end->Clone();
+      while (current_line_start->GetAnchor() == anchor_end->GetAnchor() &&
+             *current_line_start <= *anchor_end) {
+        AXPositionInstance current_line_end =
+            current_line_start->CreateNextLineEndPosition(
+                ui::AXBoundaryBehavior::CrossBoundary);
 
-      DCHECK_EQ(current_line_end->GetAnchor(), current_line_start->GetAnchor());
+        if (current_line_end->GetAnchor() != anchor_end->GetAnchor() ||
+            *current_line_end > *anchor_end)
+          current_line_end = anchor_end->Clone();
 
-      if (current_line_start->GetAnchor()->data().role ==
-          ax::mojom::Role::kInlineTextBox) {
-        current_line_start = current_line_start->CreateParentPosition();
-        current_line_end = current_line_end->CreateParentPosition();
+        DCHECK_LE(*current_line_start, *current_line_end);
+        DCHECK_LE(*current_line_end, *anchor_end);
+
+        if (current_line_start->GetAnchor()->data().role ==
+            ax::mojom::Role::kInlineTextBox) {
+          current_line_start = current_line_start->CreateParentPosition();
+          current_line_end = current_line_end->CreateParentPosition();
+        }
+
+        AXTreeID current_tree_id = current_line_start->tree_id();
+        AXTreeManager* manager =
+            AXTreeManagerMap::GetInstance().GetManager(current_tree_id);
+        AXNode* current_anchor = current_line_start->GetAnchor();
+        AXPlatformNodeDelegate* current_anchor_delegate =
+            manager->GetDelegate(current_tree_id, current_anchor->id());
+
+        // For text anchors, we retrieve the bounding rectangles of its text
+        // content. For non-text anchors (such as checkboxes, images, etc.), we
+        // want to directly retrieve their bounding rectangles.
+        gfx::Rect current_rect;
+        if (IsTextOrLineBreak(current_anchor->data().role))
+          current_rect = current_anchor_delegate->GetInnerTextRangeBoundsRect(
+              current_line_start->text_offset(),
+              current_line_end->text_offset(), AXCoordinateSystem::kScreen,
+              AXClippingBehavior::kClipped);
+        else
+          current_rect = current_anchor_delegate->GetBoundsRect(
+              AXCoordinateSystem::kScreen, AXClippingBehavior::kClipped);
+
+        // We only add rects that are visible within the current viewport.
+        // If the bounding rectangle is outside the viewport, the `kClipped`
+        // parameter from the bounds APIs will result in returning an empty
+        // rect, which we should omit from the final result.
+        if (!current_rect.IsEmpty())
+          rects.push_back(current_rect);
+
+        current_line_start = current_line_end->CreateNextLineStartPosition(
+            ui::AXBoundaryBehavior::CrossBoundary);
       }
-
-      AXTreeID current_tree_id = current_line_start->tree_id();
-      AXTreeManager* manager =
-          AXTreeManagerMap::GetInstance().GetManager(current_tree_id);
-      AXNode* current_anchor = current_line_start->GetAnchor();
-      AXPlatformNodeDelegate* current_anchor_delegate =
-          manager->GetDelegate(current_tree_id, current_anchor->id());
-
-      gfx::Rect current_rect;
-      // For text anchors, we retrieve the bounding rectangles of its text
-      // content. For non-text anchors (such as checkboxes, images, etc.), we
-      // want to directly retrieve their bounding rectangles.
-      if (IsTextOrLineBreak(current_anchor->data().role)) {
-        current_rect = current_anchor_delegate->GetInnerTextRangeBoundsRect(
-            current_line_start->text_offset(), current_line_end->text_offset(),
-            AXCoordinateSystem::kScreen, AXClippingBehavior::kClipped);
-      } else {
-        current_rect = current_anchor_delegate->GetBoundsRect(
-            AXCoordinateSystem::kScreen, AXClippingBehavior::kClipped);
-      }
-
-      // We only add rects that are within the current viewport. If the bounding
-      // rect is outside the viewport, the 'clipped' parameter in the bounds
-      // API's above will result in returning an empty rect and we omit it in
-      // the final result.
-      if (!current_rect.IsEmpty())
-        rectangles.emplace_back(current_rect);
-
-      current_line_start = current_line_end->CreateNextLineStartPosition(
-          ui::AXBoundaryBehavior::CrossBoundary);
     }
-
-    return rectangles;
+    return rects;
   }
 
  private:
diff --git a/ui/accessibility/ax_range_unittest.cc b/ui/accessibility/ax_range_unittest.cc
index fc5498e..383c203 100644
--- a/ui/accessibility/ax_range_unittest.cc
+++ b/ui/accessibility/ax_range_unittest.cc
@@ -144,7 +144,7 @@
   inline_box1_.role = ax::mojom::Role::kInlineTextBox;
   inline_box1_.AddState(ax::mojom::State::kEditable);
   inline_box1_.SetName("Line 1");
-  inline_box1_.relative_bounds.bounds = gfx::RectF(220, 20, 100, 30);
+  inline_box1_.relative_bounds.bounds = gfx::RectF(220, 20, 30, 30);
   std::vector<int32_t> character_offsets1;
   // The width of each character is 5px.
   character_offsets1.push_back(225);  // "L" {220, 20, 5x30}
@@ -165,6 +165,7 @@
   line_break_.role = ax::mojom::Role::kLineBreak;
   line_break_.AddState(ax::mojom::State::kEditable);
   line_break_.SetName("\n");
+  line_break_.relative_bounds.bounds = gfx::RectF(250, 20, 0, 30);
   line_break_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
                               inline_box1_.id);
 
@@ -176,7 +177,7 @@
   inline_box2_.role = ax::mojom::Role::kInlineTextBox;
   inline_box2_.AddState(ax::mojom::State::kEditable);
   inline_box2_.SetName("Line 2");
-  inline_box2_.relative_bounds.bounds = gfx::RectF(220, 50, 100, 30);
+  inline_box2_.relative_bounds.bounds = gfx::RectF(220, 50, 42, 30);
   std::vector<int32_t> character_offsets2;
   // The width of each character is 7 px.
   character_offsets2.push_back(227);  // "L" {220, 50, 7x30}
@@ -565,17 +566,23 @@
   // Since a button is not visible to the text representation, it spans an
   // empty anchor whose start and end positions are the same.
   TestPositionRange button_range(button->Clone(), button->Clone());
-  std::vector<gfx::Rect> expected_screen_rects;
-  expected_screen_rects = {gfx::Rect(20, 20, 200, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(button_range.GetScreenRects()));
+  std::vector<gfx::Rect> expected_screen_rects = {gfx::Rect(20, 20, 200, 30)};
+  EXPECT_THAT(button_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Since a check box is not visible to the text representation, it spans an
   // empty anchor whose start and end positions are the same.
   TestPositionRange check_box_range(check_box->Clone(), check_box->Clone());
   expected_screen_rects = {gfx::Rect(20, 50, 200, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(check_box_range.GetScreenRects()));
+  EXPECT_THAT(check_box_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
+
+  // Retrieving bounding boxes of both, button and check box.
+  TestPositionRange button_check_box_range(button->Clone(), check_box->Clone());
+  expected_screen_rects = {gfx::Rect(20, 20, 200, 30),
+                           gfx::Rect(20, 50, 200, 30)};
+  EXPECT_THAT(button_check_box_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding box of text line 1, its whole range.
   //  0 1 2 3 4 5
@@ -583,8 +590,8 @@
   // |-----------|
   TestPositionRange line1_whole_range(line1_start->Clone(), line1_end->Clone());
   expected_screen_rects = {gfx::Rect(220, 20, 30, 30)};  //
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line1_whole_range.GetScreenRects()));
+  EXPECT_THAT(line1_whole_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding box of text line 1, its first half range.
   //  0 1 2 3 4 5
@@ -593,8 +600,8 @@
   TestPositionRange line1_first_half_range(line1_start->Clone(),
                                            line1_middle->Clone());
   expected_screen_rects = {gfx::Rect(220, 20, 15, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line1_first_half_range.GetScreenRects()));
+  EXPECT_THAT(line1_first_half_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding box of text line 1, its second half range.
   //  0 1 2 3 4 5
@@ -603,8 +610,8 @@
   TestPositionRange line1_second_half_range(line1_middle->Clone(),
                                             line1_end->Clone());
   expected_screen_rects = {gfx::Rect(235, 20, 15, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line1_second_half_range.GetScreenRects()));
+  EXPECT_THAT(line1_second_half_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding box of text line 1, its mid range.
   //  0 1 2 3 4 5
@@ -613,8 +620,8 @@
   TestPositionRange line1_mid_range(line1_second_char->Clone(),
                                     line1_second_to_last_char->Clone());
   expected_screen_rects = {gfx::Rect(225, 20, 20, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line1_mid_range.GetScreenRects()));
+  EXPECT_THAT(line1_mid_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding box of text line 2, its whole range.
   //  0 1 2 3 4 5
@@ -622,8 +629,8 @@
   // |-----------|
   TestPositionRange line2_whole_range(line2_start->Clone(), line2_end->Clone());
   expected_screen_rects = {gfx::Rect(220, 50, 42, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line2_whole_range.GetScreenRects()));
+  EXPECT_THAT(line2_whole_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding box of text line 2, its first half range.
   //  0 1 2 3 4 5
@@ -632,8 +639,8 @@
   TestPositionRange line2_first_half_range(line2_start->Clone(),
                                            line2_middle->Clone());
   expected_screen_rects = {gfx::Rect(220, 50, 21, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line2_first_half_range.GetScreenRects()));
+  EXPECT_THAT(line2_first_half_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding box of text line 2, its second half range.
   //  0 1 2 3 4 5
@@ -642,8 +649,8 @@
   TestPositionRange line2_second_half_range(line2_middle->Clone(),
                                             line2_end->Clone());
   expected_screen_rects = {gfx::Rect(241, 50, 21, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line2_second_half_range.GetScreenRects()));
+  EXPECT_THAT(line2_second_half_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding box of text line 2, its mid range.
   //  0 1 2 3 4 5
@@ -652,8 +659,8 @@
   TestPositionRange line2_mid_range(line2_second_char->Clone(),
                                     line2_second_to_last_char->Clone());
   expected_screen_rects = {gfx::Rect(227, 50, 28, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line2_mid_range.GetScreenRects()));
+  EXPECT_THAT(line2_mid_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding boxes of text line 1 and line 2, the entire range.
   // |L|i|n|e| |1|\n|L|i|n|e| |2|
@@ -662,8 +669,8 @@
                                             line2_end->Clone());
   expected_screen_rects = {gfx::Rect(220, 20, 30, 30),
                            gfx::Rect(220, 50, 42, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line1_line2_whole_range.GetScreenRects()));
+  EXPECT_THAT(line1_line2_whole_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 
   // Retrieving bounding boxes of the range that spans from the middle of text
   // line 1 to the middle of text line 2.
@@ -673,8 +680,30 @@
                                           line2_middle->Clone());
   expected_screen_rects = {gfx::Rect(235, 20, 15, 30),
                            gfx::Rect(220, 50, 21, 30)};
-  EXPECT_THAT(expected_screen_rects,
-              testing::ContainerEq(line1_line2_mid_range.GetScreenRects()));
+  EXPECT_THAT(line1_line2_mid_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
+
+  // Retrieving bounding boxes of the range that spans from the check box
+  // ("invisible" in the text representation) to the middle of text line 2.
+  // |[Button][Check box]L|i|n|e| |1|\n|L|i|n|e| |2|
+  //          |------------------------------|
+  TestPositionRange check_box_line2_mid_range(check_box->Clone(),
+                                              line2_middle->Clone());
+  expected_screen_rects = {gfx::Rect(20, 50, 200, 30),
+                           gfx::Rect(220, 20, 30, 30),
+                           gfx::Rect(220, 50, 21, 30)};
+  EXPECT_THAT(check_box_line2_mid_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
+
+  // Retrieving bounding boxes of the range spanning the entire document.
+  // |[Button][Check box]L|i|n|e| |1|\n|L|i|n|e| |2|
+  // |---------------------------------------------|
+  TestPositionRange entire_document_range(button->Clone(), line2_end->Clone());
+  expected_screen_rects = {
+      gfx::Rect(20, 20, 200, 30), gfx::Rect(20, 50, 200, 30),
+      gfx::Rect(220, 20, 30, 30), gfx::Rect(220, 50, 42, 30)};
+  EXPECT_THAT(entire_document_range.GetScreenRects(),
+              testing::ContainerEq(expected_screen_rects));
 }
 
 TEST_F(AXRangeTest, GetAnchors) {
diff --git a/ui/accessibility/ax_tree_update.h b/ui/accessibility/ax_tree_update.h
index 215e50a..dfdf892 100644
--- a/ui/accessibility/ax_tree_update.h
+++ b/ui/accessibility/ax_tree_update.h
@@ -32,9 +32,11 @@
 //
 // Suppose that the next AXNodeData to be applied is |node|. The following
 // invariants must hold:
-// 1. Either |node.id| is already in the tree, or else the tree is empty,
-//        |node| is the new root of the tree, and
-//        |node.role| == WebAXRoleRootWebArea.
+// 1. Either
+//   a) |node.id| is already in the tree, or
+//   b) the tree is empty, and
+//      |node| is the new root of the tree, and
+//      |node.role| == WebAXRoleRootWebArea.
 // 2. Every child id in |node.child_ids| must either be already a child
 //        of this node, or a new id not previously in the tree. It is not
 //        allowed to "reparent" a child to this node without first removing
diff --git a/ui/accessibility/platform/atk_util_auralinux.cc b/ui/accessibility/platform/atk_util_auralinux.cc
index bc83396..60d24858 100644
--- a/ui/accessibility/platform/atk_util_auralinux.cc
+++ b/ui/accessibility/platform/atk_util_auralinux.cc
@@ -16,7 +16,11 @@
 
 namespace {
 
-const char kAccessibilityEnabled[] = "ACCESSIBILITY_ENABLED";
+const char* kAccessibilityEnabledVariables[] = {
+    "ACCESSIBILITY_ENABLED",
+    "GNOME_ACCESSIBILITY",
+    "QT_ACCESSIBILITY",
+};
 
 }  // namespace
 
@@ -127,10 +131,13 @@
 
 bool AtkUtilAuraLinux::ShouldEnableAccessibility() {
   std::unique_ptr<base::Environment> env(base::Environment::Create());
-  std::string enable_accessibility;
-  env->GetVar(kAccessibilityEnabled, &enable_accessibility);
-  if (enable_accessibility == "1" || PlatformShouldEnableAccessibility())
-    return true;
+  for (const auto* variable : kAccessibilityEnabledVariables) {
+    std::string enable_accessibility;
+    env->GetVar(variable, &enable_accessibility);
+    if (enable_accessibility == "1")
+      return true;
+  }
+
   return false;
 }
 
@@ -150,7 +157,7 @@
 
 void AtkUtilAuraLinux::InitializeForTesting() {
   std::unique_ptr<base::Environment> env(base::Environment::Create());
-  env->SetVar(kAccessibilityEnabled, "1");
+  env->SetVar(kAccessibilityEnabledVariables[0], "1");
 
   InitializeAsync();
 }
diff --git a/ui/accessibility/platform/atk_util_auralinux.h b/ui/accessibility/platform/atk_util_auralinux.h
index 9f6ad91..aab42bd 100644
--- a/ui/accessibility/platform/atk_util_auralinux.h
+++ b/ui/accessibility/platform/atk_util_auralinux.h
@@ -57,7 +57,6 @@
 
   bool ShouldEnableAccessibility();
 
-  bool PlatformShouldEnableAccessibility();
   void PlatformInitializeAsync();
 
   DISALLOW_COPY_AND_ASSIGN(AtkUtilAuraLinux);
diff --git a/ui/accessibility/platform/atk_util_auralinux_gtk.cc b/ui/accessibility/platform/atk_util_auralinux_gtk.cc
index 196cf6a..fde59b27 100644
--- a/ui/accessibility/platform/atk_util_auralinux_gtk.cc
+++ b/ui/accessibility/platform/atk_util_auralinux_gtk.cc
@@ -9,10 +9,6 @@
 
 namespace ui {
 
-bool AtkUtilAuraLinux::PlatformShouldEnableAccessibility() {
-  return false;
-}
-
 void AtkUtilAuraLinux::PlatformInitializeAsync() {
   // AT bridge enabling was disabled before loading GTK to avoid
   // getting GTK implementation ATK root.
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index ec70d6e..2435f7c 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -403,20 +403,8 @@
   UIA_VALIDATE_TEXTRANGEPROVIDER_CALL();
 
   *rectangles = nullptr;
-
   AXNodeRange range(start_->Clone(), end_->Clone());
-  std::vector<AXNodeRange> anchors = range.GetAnchors();
-
-  std::vector<gfx::Rect> rects;
-  for (auto&& current_range : anchors) {
-    std::vector<gfx::Rect> current_anchor_rects =
-        current_range.GetScreenRects();
-    // std::vector does not have a built-in way of appending another
-    // std::vector. Using insert with iterators is the safest and most
-    // performant way to accomplish this.
-    rects.insert(rects.end(), current_anchor_rects.begin(),
-                 current_anchor_rects.end());
-  }
+  std::vector<gfx::Rect> rects = range.GetScreenRects();
 
   // 4 array items per rect: left, top, width, height
   SAFEARRAY* safe_array = SafeArrayCreateVector(
@@ -799,7 +787,6 @@
 
 base::string16 AXPlatformNodeTextRangeProviderWin::GetString() {
   AXNodeRange range(start_->Clone(), end_->Clone());
-
   return range.GetText();
 }
 
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 8c2b712..1c4ffbd 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -405,7 +405,6 @@
 
 test("ui_android_unittests") {
   sources = [
-    "delegated_frame_host_android_unittest.cc",
     "overscroll_refresh_unittest.cc",
     "resources/resource_manager_impl_unittest.cc",
     "run_all_unittests.cc",
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 99d6b7cf..0a271d5 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -121,14 +121,6 @@
   }
   content_layer_->SetContentsOpaque(!has_transparent_background_);
 
-  compositor_attach_until_frame_lock_.reset();
-
-  // If surface synchronization is disabled, SubmitCompositorFrame immediately
-  // activates the CompositorFrame and issues OnFirstSurfaceActivation if the
-  // |local_surface_id| has changed since the last submission.
-  if (content_layer_->bounds() == expected_pixel_size_)
-    compositor_pending_resize_lock_.reset();
-
   if (id_changed)
     frame_evictor_->OnNewSurfaceEmbedded();
 }
@@ -242,20 +234,6 @@
     WindowAndroidCompositor* compositor) {
   if (registered_parent_compositor_)
     DetachFromCompositor();
-  // If this is the first frame after the compositor became visible, we want to
-  // take the compositor lock, preventing compositor frames from being produced
-  // until all delegated frames are ready. This improves the resume transition,
-  // preventing flashes. Set a 5 second timeout to prevent locking up the
-  // browser in cases where the renderer hangs or another factor prevents a
-  // frame from being produced. If we already have delegated content, no need
-  // to take the lock.
-  // If surface synchronization is enabled, then it will block browser UI until
-  // a renderer frame is available instead.
-  if (!enable_surface_synchronization_ &&
-      compositor->IsDrawingFirstVisibleFrame() && !HasDelegatedContent()) {
-    compositor_attach_until_frame_lock_ =
-        compositor->GetCompositorLock(this, FirstFrameTimeout());
-  }
   compositor->AddChildFrameSink(frame_sink_id_);
   if (!enable_viz_)
     client_->SetBeginFrameSource(&begin_frame_source_);
@@ -265,8 +243,6 @@
 void DelegatedFrameHostAndroid::DetachFromCompositor() {
   if (!registered_parent_compositor_)
     return;
-  compositor_attach_until_frame_lock_.reset();
-  compositor_pending_resize_lock_.reset();
   if (!enable_viz_) {
     client_->SetBeginFrameSource(nullptr);
     support_->SetNeedsBeginFrame(false);
@@ -357,29 +333,6 @@
   }
 }
 
-void DelegatedFrameHostAndroid::PixelSizeWillChange(
-    const gfx::Size& pixel_size) {
-  if (enable_surface_synchronization_)
-    return;
-
-  // We never take the resize lock unless we're on O+, as previous versions of
-  // Android won't wait for us to produce the correct sized frame and will end
-  // up looking worse.
-  if (base::android::BuildInfo::GetInstance()->sdk_int() <
-      base::android::SDK_VERSION_OREO) {
-    return;
-  }
-
-  expected_pixel_size_ = pixel_size;
-  if (content_layer_ && registered_parent_compositor_) {
-    if (content_layer_->bounds() != expected_pixel_size_) {
-      compositor_pending_resize_lock_ =
-          registered_parent_compositor_->GetCompositorLock(this,
-                                                           ResizeTimeout());
-    }
-  }
-}
-
 void DelegatedFrameHostAndroid::DidReceiveCompositorFrameAck(
     const std::vector<viz::ReturnedResource>& resources) {
   client_->DidReceiveCompositorFrameAck(resources);
@@ -419,8 +372,6 @@
   client_->OnFrameTokenChanged(frame_token);
 }
 
-void DelegatedFrameHostAndroid::CompositorLockTimedOut() {}
-
 void DelegatedFrameHostAndroid::CreateCompositorFrameSinkSupport() {
   if (enable_viz_)
     return;
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index 9de56e6d..cea3be3 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -17,7 +17,6 @@
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
 #include "ui/android/ui_android_export.h"
-#include "ui/compositor/compositor_lock.h"
 
 namespace cc {
 class SurfaceLayer;
@@ -37,7 +36,6 @@
     : public viz::mojom::CompositorFrameSinkClient,
       public viz::ExternalBeginFrameSourceClient,
       public viz::HostFrameSinkClient,
-      public ui::CompositorLockClient,
       public viz::FrameEvictorClient {
  public:
   class Client {
@@ -128,10 +126,6 @@
                     const gfx::Size& new_size_in_pixels,
                     cc::DeadlinePolicy deadline_policy);
 
-  // Called when we begin a resize operation. Takes the compositor lock until we
-  // receive a frame of the expected size.
-  void PixelSizeWillChange(const gfx::Size& pixel_size);
-
   // Returns the ID for the current Surface. Returns an invalid ID if no
   // surface exists (!HasDelegatedContent()).
   viz::SurfaceId SurfaceId() const;
@@ -160,9 +154,6 @@
   void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
   void OnFrameTokenChanged(uint32_t frame_token) override;
 
-  // ui::CompositorLockClient implementation.
-  void CompositorLockTimedOut() override;
-
   void CreateCompositorFrameSinkSupport();
 
   void ProcessCopyOutputRequest(
@@ -186,19 +177,6 @@
   const bool enable_surface_synchronization_;
   const bool enable_viz_;
 
-  // The size we are resizing to. Once we receive a frame of this size we can
-  // release any resize compositor lock.
-  gfx::Size expected_pixel_size_;
-
-  // A lock that is held from the point at which we attach to the compositor to
-  // the point at which we submit our first frame to the compositor. This
-  // ensures that the compositor doesn't swap without a frame available.
-  std::unique_ptr<ui::CompositorLock> compositor_attach_until_frame_lock_;
-
-  // A lock that is held from the point we begin resizing this frame to the
-  // point at which we receive a frame of the correct size.
-  std::unique_ptr<ui::CompositorLock> compositor_pending_resize_lock_;
-
   // Whether we've received a frame from the renderer since navigating.
   // Only used when surface synchronization is on.
   viz::LocalSurfaceId first_local_surface_id_after_navigation_;
diff --git a/ui/android/delegated_frame_host_android_unittest.cc b/ui/android/delegated_frame_host_android_unittest.cc
deleted file mode 100644
index 0b747d04..0000000
--- a/ui/android/delegated_frame_host_android_unittest.cc
+++ /dev/null
@@ -1,438 +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 "ui/android/delegated_frame_host_android.h"
-
-#include "base/android/build_info.h"
-#include "base/bind_helpers.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "cc/layers/layer.h"
-#include "cc/layers/solid_color_layer.h"
-#include "cc/layers/surface_layer.h"
-#include "cc/trees/layer_tree_host.h"
-#include "components/viz/common/features.h"
-#include "components/viz/common/frame_timing_details_map.h"
-#include "components/viz/common/hit_test/hit_test_region_list.h"
-#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
-#include "components/viz/host/host_frame_sink_manager.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
-#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
-#include "components/viz/test/compositor_frame_helpers.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/android/resources/resource_manager.h"
-#include "ui/android/view_android.h"
-#include "ui/android/window_android_compositor.h"
-
-namespace ui {
-namespace {
-
-using ::testing::Return;
-using ::testing::_;
-using ::testing::NiceMock;
-using ::testing::Invoke;
-
-class MockDelegatedFrameHostAndroidClient
-    : public DelegatedFrameHostAndroid::Client {
- public:
-  MOCK_METHOD1(SetBeginFrameSource, void(viz::BeginFrameSource*));
-  MOCK_METHOD1(DidReceiveCompositorFrameAck,
-               void(const std::vector<viz::ReturnedResource>&));
-  MOCK_METHOD1(ReclaimResources,
-               void(const std::vector<viz::ReturnedResource>&));
-  MOCK_METHOD1(DidPresentCompositorFrames,
-               void(const viz::FrameTimingDetailsMap&));
-  MOCK_METHOD1(OnFrameTokenChanged, void(uint32_t));
-  MOCK_METHOD0(WasEvicted, void());
-};
-
-class MockWindowAndroidCompositor : public WindowAndroidCompositor {
- public:
-  MOCK_METHOD1(AttachLayerForReadback, void(scoped_refptr<cc::Layer>));
-  MOCK_METHOD1(DoRequestCopyOfOutputOnRootLayer, void(viz::CopyOutputRequest*));
-  MOCK_METHOD0(SetNeedsAnimate, void());
-  MOCK_METHOD0(GetResourceManager, ResourceManager&());
-  MOCK_METHOD0(GetFrameSinkId, viz::FrameSinkId());
-  MOCK_METHOD1(AddChildFrameSink, void(const viz::FrameSinkId&));
-  MOCK_METHOD1(RemoveChildFrameSink, void(const viz::FrameSinkId&));
-  MOCK_METHOD2(DoGetCompositorLock,
-               CompositorLock*(CompositorLockClient*, base::TimeDelta));
-  MOCK_CONST_METHOD0(IsDrawingFirstVisibleFrame, bool());
-  MOCK_METHOD1(SetVSyncPaused, void(bool));
-  MOCK_METHOD1(OnUpdateRefreshRate, void(float));
-
-  void OnUpdateSupportedRefreshRates(
-      const std::vector<float>& supported_refresh_rates) override {}
-
-  // Helpers for move-only types:
-  void RequestCopyOfOutputOnRootLayer(
-      std::unique_ptr<viz::CopyOutputRequest> request) override {
-    return DoRequestCopyOfOutputOnRootLayer(request.get());
-  }
-
-  std::unique_ptr<CompositorLock> GetCompositorLock(
-      CompositorLockClient* client,
-      base::TimeDelta time_delta) override {
-    return std::unique_ptr<CompositorLock>(
-        DoGetCompositorLock(client, time_delta));
-  }
-};
-
-class DelegatedFrameHostAndroidTest : public testing::Test {
- public:
-  DelegatedFrameHostAndroidTest()
-      : frame_sink_manager_impl_(&shared_bitmap_manager_),
-        frame_sink_id_(1, 1),
-        task_runner_(new base::TestMockTimeTaskRunner()),
-        lock_manager_(task_runner_) {
-    host_frame_sink_manager_.SetLocalManager(&frame_sink_manager_impl_);
-    frame_sink_manager_impl_.SetLocalClient(&host_frame_sink_manager_);
-  }
-
-  void SetUp() override {
-    view_.SetLayer(cc::SolidColorLayer::Create());
-    frame_host_ = std::make_unique<DelegatedFrameHostAndroid>(
-        &view_, &host_frame_sink_manager_, &client_, frame_sink_id_,
-        features::IsSurfaceSynchronizationEnabled());
-  }
-
-  void TearDown() override { frame_host_.reset(); }
-
-  ui::CompositorLock* GetLock(CompositorLockClient* client,
-                              base::TimeDelta time_delta) {
-    return lock_manager_.GetCompositorLock(client, time_delta, nullptr)
-        .release();
-  }
-
-  bool IsLocked() const { return lock_manager_.IsLocked(); }
-
-  void SubmitCompositorFrame(const gfx::Size& frame_size = gfx::Size(10, 10)) {
-    viz::CompositorFrame frame =
-        viz::CompositorFrameBuilder()
-            .AddRenderPass(gfx::Rect(frame_size), gfx::Rect(frame_size))
-            .Build();
-    allocator_.GenerateId();
-    frame_host_->SubmitCompositorFrame(
-        allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(),
-        std::move(frame), base::nullopt);
-  }
-
-  void SetUpValidFrame(const gfx::Size& frame_size) {
-    EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame())
-        .WillOnce(Return(true));
-    EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
-        .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
-    frame_host_->AttachToCompositor(&compositor_);
-    EXPECT_TRUE(IsLocked());
-
-    SubmitCompositorFrame(frame_size);
-    EXPECT_FALSE(IsLocked());
-  }
-
- protected:
-  MockWindowAndroidCompositor compositor_;
-  ui::ViewAndroid view_;
-  viz::ServerSharedBitmapManager shared_bitmap_manager_;
-  viz::FrameSinkManagerImpl frame_sink_manager_impl_;
-  viz::HostFrameSinkManager host_frame_sink_manager_;
-  MockDelegatedFrameHostAndroidClient client_;
-  viz::FrameSinkId frame_sink_id_;
-  viz::ParentLocalSurfaceIdAllocator allocator_;
-  std::unique_ptr<DelegatedFrameHostAndroid> frame_host_;
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  CompositorLockManager lock_manager_;
-};
-
-class DelegatedFrameHostAndroidVizTest : public DelegatedFrameHostAndroidTest {
- public:
-  DelegatedFrameHostAndroidVizTest() = default;
-  ~DelegatedFrameHostAndroidVizTest() override = default;
-
-  void SetUp() override {
-    // Enable both Viz and SurfaceSync.
-    scoped_feature_list_.InitWithFeatures(
-        {features::kVizDisplayCompositor,
-         features::kEnableSurfaceSynchronization},
-        {});
-
-    DelegatedFrameHostAndroidTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// TODO(ericrk): Remove these tests once Viz OOP-D has launched.
-class DelegatedFrameHostAndroidSurfaceSynchronizationOnlyTest
-    : public DelegatedFrameHostAndroidTest {
- public:
-  DelegatedFrameHostAndroidSurfaceSynchronizationOnlyTest() = default;
-  ~DelegatedFrameHostAndroidSurfaceSynchronizationOnlyTest() override = default;
-
-  void SetUp() override {
-    // Enable SurfaceSync without Viz.
-    scoped_feature_list_.InitWithFeatures(
-        {features::kEnableSurfaceSynchronization},
-        {features::kVizDisplayCompositor});
-
-    DelegatedFrameHostAndroidTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// TODO(ericrk): Remove these tests once Viz OOP-D has launched.
-class DelegatedFrameHostAndroidLegacyNonVizTest
-    : public DelegatedFrameHostAndroidTest {
- public:
-  DelegatedFrameHostAndroidLegacyNonVizTest() = default;
-  ~DelegatedFrameHostAndroidLegacyNonVizTest() override = default;
-
-  void SetUp() override {
-    // Disable both Viz and SurfaceSync.
-    scoped_feature_list_.InitWithFeatures(
-        {}, {features::kVizDisplayCompositor,
-             features::kEnableSurfaceSynchronization});
-
-    DelegatedFrameHostAndroidTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// Resize lock is only enabled on O+.
-static bool IsResizeLockEnabled() {
-  return base::android::BuildInfo::GetInstance()->sdk_int() >=
-         base::android::SDK_VERSION_OREO;
-}
-
-// If OOP-D is enabled then we should not be acquiring a compositor lock on
-// attach.
-TEST_F(DelegatedFrameHostAndroidVizTest, NoCompositorLockOnAttach) {
-  EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).Times(0);
-  EXPECT_CALL(compositor_, DoGetCompositorLock(_, _)).Times(0);
-  frame_host_->AttachToCompositor(&compositor_);
-}
-
-// If surface synchronization is off, and we are doing a cross-process
-// navigation, then both the primary and fallback surface IDs need to be
-// updated together.
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest,
-       TakeFallbackContentFromUpdatesPrimary) {
-  EXPECT_FALSE(frame_host_->SurfaceId().is_valid());
-  // Submit a compositor frame to ensure we have delegated content.
-  SubmitCompositorFrame();
-
-  EXPECT_TRUE(frame_host_->SurfaceId().is_valid());
-  std::unique_ptr<DelegatedFrameHostAndroid> other_frame_host =
-      std::make_unique<DelegatedFrameHostAndroid>(
-          &view_, &host_frame_sink_manager_, &client_, viz::FrameSinkId(2, 2),
-          features::IsSurfaceSynchronizationEnabled());
-
-  EXPECT_FALSE(other_frame_host->SurfaceId().is_valid());
-
-  other_frame_host->TakeFallbackContentFrom(frame_host_.get());
-
-  EXPECT_TRUE(other_frame_host->SurfaceId().is_valid());
-  EXPECT_EQ(other_frame_host->content_layer_for_testing()->surface_id(),
-            other_frame_host->content_layer_for_testing()
-                ->oldest_acceptable_fallback());
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest,
-       CompositorLockDuringFirstFrame) {
-  // Attach during the first frame, lock will be taken.
-  EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true));
-  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
-      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
-  frame_host_->AttachToCompositor(&compositor_);
-  EXPECT_TRUE(IsLocked());
-
-  // Lock should be released when we submit a compositor frame.
-  SubmitCompositorFrame();
-  EXPECT_FALSE(IsLocked());
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest,
-       CompositorLockDuringLaterFrame) {
-  // Attach after the first frame, lock will not be taken.
-  EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame())
-      .WillOnce(Return(false));
-  EXPECT_CALL(compositor_, DoGetCompositorLock(_, _)).Times(0);
-  frame_host_->AttachToCompositor(&compositor_);
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest,
-       CompositorLockWithDelegatedContent) {
-  // Submit a compositor frame to ensure we have delegated content.
-  SubmitCompositorFrame();
-
-  // Even though it's the first frame, we won't take the lock as we already have
-  // delegated content.
-  EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true));
-  EXPECT_CALL(compositor_, DoGetCompositorLock(_, _)).Times(0);
-  frame_host_->AttachToCompositor(&compositor_);
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest,
-       CompositorLockReleasedWithDetach) {
-  // Attach during the first frame, lock will be taken.
-  EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true));
-  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
-      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
-  frame_host_->AttachToCompositor(&compositor_);
-  EXPECT_TRUE(IsLocked());
-
-  // Lock should be released when we detach.
-  frame_host_->DetachFromCompositor();
-  EXPECT_FALSE(IsLocked());
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest, ResizeLockBasic) {
-  // Resize lock is only enabled on O+.
-  if (!IsResizeLockEnabled())
-    return;
-
-  SetUpValidFrame(gfx::Size(10, 10));
-
-  // Tell the frame host to resize, it should take a lock.
-  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
-      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
-  frame_host_->PixelSizeWillChange(gfx::Size(50, 50));
-  EXPECT_TRUE(IsLocked());
-
-  // Submit a frame of the wrong size, nothing should change.
-  SubmitCompositorFrame(gfx::Size(20, 20));
-  EXPECT_TRUE(IsLocked());
-
-  // Submit a frame with the right size, the lock should release.
-  SubmitCompositorFrame(gfx::Size(50, 50));
-  EXPECT_FALSE(IsLocked());
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest,
-       ResizeLockNotTakenIfNoSizeChange) {
-  // Resize lock is only enabled on O+.
-  if (!IsResizeLockEnabled())
-    return;
-
-  SetUpValidFrame(gfx::Size(10, 10));
-
-  // Tell the frame host to resize to the existing size, nothing should happen.
-  frame_host_->PixelSizeWillChange(gfx::Size(10, 10));
-  EXPECT_FALSE(IsLocked());
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest,
-       ResizeLockReleasedWithDetach) {
-  // Resize lock is only enabled on O+.
-  if (!IsResizeLockEnabled())
-    return;
-
-  SetUpValidFrame(gfx::Size(10, 10));
-
-  // Tell the frame host to resize, it should take a lock.
-  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
-      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
-  frame_host_->PixelSizeWillChange(gfx::Size(50, 50));
-  EXPECT_TRUE(IsLocked());
-
-  // Lock should be released when we detach.
-  frame_host_->DetachFromCompositor();
-  EXPECT_FALSE(IsLocked());
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest, TestBothCompositorLocks) {
-  // Resize lock is only enabled on O+.
-  if (!IsResizeLockEnabled())
-    return;
-
-  // Attach during the first frame, first lock will be taken.
-  EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true));
-  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
-      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
-  frame_host_->AttachToCompositor(&compositor_);
-  EXPECT_TRUE(IsLocked());
-
-  // Tell the frame host to resize, it should take a second lock.
-  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
-      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
-  frame_host_->PixelSizeWillChange(gfx::Size(50, 50));
-  EXPECT_TRUE(IsLocked());
-
-  // Submit a compositor frame of the right size, both locks should release.
-  SubmitCompositorFrame(gfx::Size(50, 50));
-  EXPECT_FALSE(IsLocked());
-}
-
-// Make sure frame evictor is notified of the newly embedded surface after
-// WasShown.
-TEST_F(DelegatedFrameHostAndroidVizTest, EmbedWhileHidden) {
-  // Ensure there is currently no frame.
-  frame_host_->WasHidden();
-  frame_host_->EvictDelegatedFrame();
-  EXPECT_FALSE(frame_host_->HasSavedFrame());
-
-  allocator_.GenerateId();
-  viz::LocalSurfaceId id =
-      allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id();
-  gfx::Size size(100, 100);
-  frame_host_->EmbedSurface(id, size, cc::DeadlinePolicy::UseDefaultDeadline());
-  EXPECT_FALSE(frame_host_->HasSavedFrame());
-  frame_host_->WasShown(id, size);
-  EXPECT_TRUE(frame_host_->HasSavedFrame());
-}
-
-// Verify that when a source rect or output size is not provided to
-// CopyFromCompositingSurface, the corresponding values in CopyOutputRequest
-// are also not initialized.
-TEST_F(DelegatedFrameHostAndroidSurfaceSynchronizationOnlyTest,
-       FullSurfaceCapture) {
-  // First embed a surface to make sure we have something to copy from.
-  allocator_.GenerateId();
-  viz::LocalSurfaceId id =
-      allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id();
-  gfx::Size size(100, 100);
-  frame_host_->EmbedSurface(id, size, cc::DeadlinePolicy::UseDefaultDeadline());
-
-  // Request readback without source rect or output size specified.
-  frame_host_->CopyFromCompositingSurface(gfx::Rect(), gfx::Size(),
-                                          base::DoNothing());
-
-  // Make sure the resulting CopyOutputRequest does not have its area or result
-  // selection set.
-  const std::vector<
-      std::pair<viz::LocalSurfaceId, std::unique_ptr<viz::CopyOutputRequest>>>&
-      requests = frame_sink_manager_impl_.GetFrameSinkForId(frame_sink_id_)
-                     ->copy_output_requests_for_testing();
-  ASSERT_EQ(1u, requests.size());
-  viz::CopyOutputRequest* request = requests[0].second.get();
-  EXPECT_FALSE(request->has_area());
-  EXPECT_FALSE(request->has_result_selection());
-}
-
-TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest, EvictWhileVisible) {
-  // Create a frame and mark it visible.
-  gfx::Size size(10, 10);
-  SetUpValidFrame(size);
-  auto id = allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id();
-  frame_host_->WasShown(id, size);
-  EXPECT_TRUE(frame_host_->HasSavedFrame());
-
-  // Evict, which should do nothing as the frame is still visible.
-  frame_host_->EvictDelegatedFrame();
-  EXPECT_TRUE(frame_host_->HasSavedFrame());
-
-  // Hide frame and evict again, now it should release the frame.
-  EXPECT_CALL(client_, WasEvicted());
-  frame_host_->WasHidden();
-  frame_host_->EvictDelegatedFrame();
-  EXPECT_FALSE(frame_host_->HasSavedFrame());
-}
-
-}  // namespace
-}  // namespace ui
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml
index fcfdd0b..d3899ac 100644
--- a/ui/android/java/res/values-v17/styles.xml
+++ b/ui/android/java/res/values-v17/styles.xml
@@ -55,6 +55,7 @@
     </style>
     <style name="TextButton.Inverse" tools:ignore="UnusedResources">
         <item name="android:textAppearance">@style/TextAppearance.ButtonText.Inverse</item>
+        <item name="rippleColor">@color/filled_button_ripple_color</item>
     </style>
 
     <!-- Chips -->
diff --git a/ui/android/window_android_compositor.h b/ui/android/window_android_compositor.h
index 6f4103e3..d299b65 100644
--- a/ui/android/window_android_compositor.h
+++ b/ui/android/window_android_compositor.h
@@ -33,9 +33,6 @@
   virtual viz::FrameSinkId GetFrameSinkId() = 0;
   virtual void AddChildFrameSink(const viz::FrameSinkId& frame_sink_id) = 0;
   virtual void RemoveChildFrameSink(const viz::FrameSinkId& frame_sink_id) = 0;
-  virtual std::unique_ptr<CompositorLock> GetCompositorLock(
-      CompositorLockClient* client,
-      base::TimeDelta timeout) = 0;
   virtual bool IsDrawingFirstVisibleFrame() const = 0;
   virtual void SetVSyncPaused(bool paused) = 0;
   virtual void OnUpdateRefreshRate(float refresh_rate) = 0;
diff --git a/ui/aura/window_tree_host_unittest.cc b/ui/aura/window_tree_host_unittest.cc
index 46e15ec5..726f3a1 100644
--- a/ui/aura/window_tree_host_unittest.cc
+++ b/ui/aura/window_tree_host_unittest.cc
@@ -40,8 +40,6 @@
 
   EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
   EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds());
-  EXPECT_EQ(gfx::Vector2dF(0, 0),
-            host()->compositor()->root_layer()->subpixel_position_offset());
 }
 
 #if defined(OS_CHROMEOS)
diff --git a/ui/base/ui_features.gni b/ui/base/ui_features.gni
index 88b0226..213fdef 100644
--- a/ui/base/ui_features.gni
+++ b/ui/base/ui_features.gni
@@ -12,7 +12,7 @@
   has_native_accessibility = use_atk || is_win || is_mac
 
   # Whether the message center should be included for displaying notifications.
-  enable_message_center = is_win || is_mac || is_linux || is_chromeos
+  enable_message_center = is_win || is_mac || is_linux
 }
 
-enable_hidpi = is_mac || is_win || is_linux
+enable_hidpi = is_mac || is_win || is_linux || is_ios
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index e2702b1..15d2568 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -815,9 +815,18 @@
   <message name="IDS_FILE_BROWSER_CANCEL_LABEL" desc="Cancel label.">
     Cancel
   </message>
+  <message name="IDS_FILE_BROWSER_CLOSE_LABEL" desc="Close label.">
+    Close
+  </message>
+  <message name="IDS_FILE_BROWSER_EXPAND_LABEL" desc="Expand label.">
+    Expand
+  </message>
   <message name="IDS_FILE_BROWSER_OPEN_LABEL" desc="Open label.">
     Open
   </message>
+  <message name="IDS_FILE_BROWSER_PAUSE_LABEL" desc="Pause label.">
+    Pause
+  </message>
   <message name="IDS_FILE_BROWSER_SAVE_LABEL" desc="Save label.">
     Save
   </message>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_bn.xtb b/ui/chromeos/translations/ui_chromeos_strings_bn.xtb
index 7557ba31..c2b932a 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_bn.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_bn.xtb
@@ -627,7 +627,7 @@
 <translation id="8160015581537295331">স্প্যানীয় কীবোর্ড</translation>
 <translation id="8179976553408161302">Enter</translation>
 <translation id="8193175696669055101">ডিভাইস মডেল</translation>
-<translation id="8208580316430297579">শিল্পকর্ম</translation>
+<translation id="8208580316430297579">আর্টওয়ার্ক</translation>
 <translation id="8223479393428528563">অফলাইনে ব্যবহারের জন্য এই ফাইলগুলি সেভ করতে অনলাইনে ফিরে আসুন, ফাইলগুলিতে ডান-ক্লিক করুন এবং <ph name="OFFLINE_CHECKBOX_NAME" /> বিকল্পটি নির্বাচন করুন৷</translation>
 <translation id="8249296373107784235">বাতিল</translation>
 <translation id="8261506727792406068">মুছুন</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_ca.xtb b/ui/chromeos/translations/ui_chromeos_strings_ca.xtb
index 18374f8c..8152fb5 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_ca.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_ca.xtb
@@ -628,7 +628,7 @@
 <translation id="8160015581537295331">Teclat espanyol</translation>
 <translation id="8179976553408161302">Retorn</translation>
 <translation id="8193175696669055101">Model de dispositiu</translation>
-<translation id="8208580316430297579">Art</translation>
+<translation id="8208580316430297579">Obra d'art</translation>
 <translation id="8223479393428528563">Per desar aquests fitxers per utilitzar-los fora de línia, connecteu-vos a una xarxa, feu clic amb el botó dret als fitxers i seleccioneu l'opció <ph name="OFFLINE_CHECKBOX_NAME" />.</translation>
 <translation id="8249296373107784235">Cancel·la</translation>
 <translation id="8261506727792406068">Suprimeix</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_it.xtb b/ui/chromeos/translations/ui_chromeos_strings_it.xtb
index 2fd93698..1472a796 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_it.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_it.xtb
@@ -627,7 +627,7 @@
 <translation id="8160015581537295331">Tastiera spagnola</translation>
 <translation id="8179976553408161302">Invio</translation>
 <translation id="8193175696669055101">Modello dispositivo</translation>
-<translation id="8208580316430297579">Opere d'arte</translation>
+<translation id="8208580316430297579">Copertina</translation>
 <translation id="8223479393428528563">Per salvare i file da utilizzare offline, torna online, fai clic con il pulsante destro del mouse sui file e seleziona l'opzione <ph name="OFFLINE_CHECKBOX_NAME" />.</translation>
 <translation id="8249296373107784235">Interrompi</translation>
 <translation id="8261506727792406068">Elimina</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_iw.xtb b/ui/chromeos/translations/ui_chromeos_strings_iw.xtb
index 04bd023..8db92ef 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_iw.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_iw.xtb
@@ -629,7 +629,7 @@
 <translation id="8160015581537295331">מקלדת ספרדית</translation>
 <translation id="8179976553408161302">Enter</translation>
 <translation id="8193175696669055101">דגם מכשיר</translation>
-<translation id="8208580316430297579">אמנות</translation>
+<translation id="8208580316430297579">עטיפת האלבום</translation>
 <translation id="8223479393428528563">כדי לשמור קבצים אלה לשימוש לא מקוון, חזור למצב מקוון, לחץ לחיצה ימנית על הקבצים ובחר את האפשרות <ph name="OFFLINE_CHECKBOX_NAME" />.</translation>
 <translation id="8249296373107784235">בטל</translation>
 <translation id="8261506727792406068">מחיקה</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_ko.xtb b/ui/chromeos/translations/ui_chromeos_strings_ko.xtb
index fdd963c..67d1416 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_ko.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_ko.xtb
@@ -628,7 +628,7 @@
 <translation id="8160015581537295331">스페인어 키보드</translation>
 <translation id="8179976553408161302">Enter</translation>
 <translation id="8193175696669055101">기기 모델</translation>
-<translation id="8208580316430297579">포스터</translation>
+<translation id="8208580316430297579">아트워크</translation>
 <translation id="8223479393428528563">오프라인 사용 목적으로 이 파일을 저장하려면 온라인으로 돌아가서 마우스 오른쪽 버튼으로 파일을 클릭한 다음 <ph name="OFFLINE_CHECKBOX_NAME" /> 옵션을 선택하세요.</translation>
 <translation id="8249296373107784235">중단</translation>
 <translation id="8261506727792406068">삭제</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_nl.xtb b/ui/chromeos/translations/ui_chromeos_strings_nl.xtb
index 015c516f..d1f6e46 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_nl.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_nl.xtb
@@ -561,7 +561,7 @@
 <translation id="7589661784326793847">Een ogenblik geduld</translation>
 <translation id="7603724359189955920">Rasters</translation>
 <translation id="7627790789328695202"><ph name="FILE_NAME" /> bestaat al. Geef een nieuwe naam op en probeer het opnieuw.</translation>
-<translation id="7649070708921625228">Help</translation>
+<translation id="7649070708921625228">Hulp</translation>
 <translation id="7654209398114106148"><ph name="NUMBER_OF_ITEMS" /> items verplaatsen...</translation>
 <translation id="7658239707568436148">Annuleren</translation>
 <translation id="770015031906360009">Grieks</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_no.xtb b/ui/chromeos/translations/ui_chromeos_strings_no.xtb
index 7d81a04..0ca90eb 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_no.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_no.xtb
@@ -628,7 +628,7 @@
 <translation id="8160015581537295331">Spansk tastatur</translation>
 <translation id="8179976553408161302">Enter</translation>
 <translation id="8193175696669055101">Enhetsmodell</translation>
-<translation id="8208580316430297579">Kunst</translation>
+<translation id="8208580316430297579">Albumforside</translation>
 <translation id="8223479393428528563">For å lagre disse filene for bruk utenfor Internett, går du på nettet, høyreklikker på filene og velger <ph name="OFFLINE_CHECKBOX_NAME" />-alternativet.</translation>
 <translation id="8249296373107784235">Avbryt</translation>
 <translation id="8261506727792406068">Slett</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_pt-BR.xtb b/ui/chromeos/translations/ui_chromeos_strings_pt-BR.xtb
index 69cec552..f0d51d34 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_pt-BR.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_pt-BR.xtb
@@ -628,7 +628,7 @@
 <translation id="8160015581537295331">Teclado espanhol</translation>
 <translation id="8179976553408161302">Entrar</translation>
 <translation id="8193175696669055101">Modelo do dispositivo</translation>
-<translation id="8208580316430297579">Obras de arte</translation>
+<translation id="8208580316430297579">Capa do álbum</translation>
 <translation id="8223479393428528563">Para salvar esses arquivos para uso off-line, fique on-line novamente, clique com o botão direito nos arquivos e selecione a opção <ph name="OFFLINE_CHECKBOX_NAME" />.</translation>
 <translation id="8249296373107784235">Cancelar</translation>
 <translation id="8261506727792406068">Excluir</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_ro.xtb b/ui/chromeos/translations/ui_chromeos_strings_ro.xtb
index f5c813b..cec9d0f 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_ro.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_ro.xtb
@@ -628,7 +628,7 @@
 <translation id="8160015581537295331">Tastatură spaniolă</translation>
 <translation id="8179976553408161302">Enter</translation>
 <translation id="8193175696669055101">Modelul dispozitivului</translation>
-<translation id="8208580316430297579">Opere de artă</translation>
+<translation id="8208580316430297579">Grafică</translation>
 <translation id="8223479393428528563">Pentru a salva aceste fișiere pentru utilizare offline, revino online, dă clic dreapta pe fișiere și selectează opțiunea <ph name="OFFLINE_CHECKBOX_NAME" />.</translation>
 <translation id="8249296373107784235">Abandonați</translation>
 <translation id="8261506727792406068">Șterge</translation>
diff --git a/ui/compositor/debug_utils.cc b/ui/compositor/debug_utils.cc
index d1d998b..a2b385dc 100644
--- a/ui/compositor/debug_utils.cc
+++ b/ui/compositor/debug_utils.cc
@@ -64,16 +64,15 @@
   *out << '\n' << property_indent_str;
   *out << "bounds: " << layer->bounds().x() << ',' << layer->bounds().y();
   *out << ' ' << layer->bounds().width() << 'x' << layer->bounds().height();
-  if (!layer->subpixel_position_offset().IsZero())
-    *out << " " << layer->subpixel_position_offset().ToString();
+  if (!layer->GetSubpixelOffset().IsZero())
+    *out << " " << layer->GetSubpixelOffset().ToString();
 
   const ui::Layer* mask = const_cast<ui::Layer*>(layer)->layer_mask_layer();
 
   if (mask) {
     *out << '\n' << property_indent_str;
-    *out << "mask layer: " << std::setprecision(2)
-         << mask->bounds().ToString()
-         << mask->subpixel_position_offset().ToString();
+    *out << "mask layer: " << std::setprecision(2) << mask->bounds().ToString()
+         << mask->GetSubpixelOffset().ToString();
   }
 
   if (layer->opacity() != 1.0f) {
diff --git a/ui/compositor/dip_util.cc b/ui/compositor/dip_util.cc
index 1dd9145..ee949f2 100644
--- a/ui/compositor/dip_util.cc
+++ b/ui/compositor/dip_util.cc
@@ -62,61 +62,4 @@
   return gfx::ConvertRectToPixel(GetDeviceScaleFactor(layer), rect_in_dip);
 }
 
-#if DCHECK_IS_ON()
-namespace {
-
-void CheckSnapped(float snapped_position) {
-  // The acceptable error epsilon should be small enough to detect visible
-  // artifacts as well as large enough to not cause false crashes when an
-  // uncommon device scale factor is applied.
-  const float kEplison = 0.003f;
-  float diff = std::abs(snapped_position - gfx::ToRoundedInt(snapped_position));
-  DCHECK_LT(diff, kEplison);
-}
-
-}  // namespace
-#endif
-
-void SnapLayerToPhysicalPixelBoundary(ui::Layer* snapped_layer,
-                                      ui::Layer* layer_to_snap) {
-  DCHECK_NE(snapped_layer, layer_to_snap);
-  DCHECK(snapped_layer);
-  DCHECK(snapped_layer->Contains(layer_to_snap));
-
-  gfx::PointF view_offset(layer_to_snap->GetTargetBounds().origin());
-  ui::Layer::ConvertPointToLayer(layer_to_snap->parent(), snapped_layer,
-                                 &view_offset);
-
-  float scale_factor = GetDeviceScaleFactor(layer_to_snap);
-  view_offset.Scale(scale_factor);
-  gfx::PointF view_offset_snapped(gfx::ToRoundedPoint(view_offset));
-
-  gfx::Vector2dF fudge = view_offset_snapped - view_offset;
-  fudge.Scale(1.0 / scale_factor);
-
-  // Apply any scale originating from transforms to the fudge.
-  gfx::Transform transform;
-  layer_to_snap->parent()->GetTargetTransformRelativeTo(snapped_layer,
-                                                        &transform);
-  gfx::Vector2dF transform_scale = transform.Scale2d();
-  fudge.Scale(1.0 / transform_scale.x(), 1.0 / transform_scale.y());
-
-  layer_to_snap->SetSubpixelPositionOffset(fudge);
-#if DCHECK_IS_ON()
-  gfx::PointF layer_offset;
-  gfx::PointF origin;
-  Layer::ConvertPointToLayer(
-      layer_to_snap->parent(), snapped_layer, &layer_offset);
-  if (layer_to_snap->GetAnimator()->is_animating()) {
-    origin = gfx::PointF(layer_to_snap->GetTargetBounds().origin()) +
-             layer_to_snap->subpixel_position_offset();
-  } else {
-    origin = layer_to_snap->position();
-  }
-  origin.Scale(transform_scale.x(), transform_scale.y());
-  CheckSnapped((layer_offset.x() + origin.x()) * scale_factor);
-  CheckSnapped((layer_offset.y() + origin.y()) * scale_factor);
-#endif
-}
-
 }  // namespace ui
diff --git a/ui/compositor/dip_util.h b/ui/compositor/dip_util.h
index 0a78361f6..810b719c 100644
--- a/ui/compositor/dip_util.h
+++ b/ui/compositor/dip_util.h
@@ -42,14 +42,6 @@
 COMPOSITOR_EXPORT gfx::Rect ConvertRectToPixel(
     const Layer* layer,
     const gfx::Rect& rect_in_dip);
-
-// Snaps the |layer_to_snap| to the physical pixel boundary.
-// |snapped_layer| is a reference layer that should also be
-// snapped at the pysical pixel boundary.
-COMPOSITOR_EXPORT void SnapLayerToPhysicalPixelBoundary(
-    ui::Layer* snapped_layer,
-    ui::Layer* layer_to_snap);
-
 }  // namespace ui
 
 #endif  // UI_COMPOSITOR_DIP_UTIL_H_
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 3b95dd1a..0a1dcf6 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/numerics/ranges.h"
 #include "base/trace_event/trace_event.h"
+#include "cc/layers/mirror_layer.h"
 #include "cc/layers/nine_patch_layer.h"
 #include "cc/layers/picture_layer.h"
 #include "cc/layers/solid_color_layer.h"
@@ -51,6 +52,17 @@
   return layer;
 }
 
+#if DCHECK_IS_ON()
+void CheckSnapped(float snapped_position) {
+  // The acceptable error epsilon should be small enough to detect visible
+  // artifacts as well as large enough to not cause false crashes when an
+  // uncommon device scale factor is applied.
+  const float kEplison = 0.003f;
+  float diff = std::abs(snapped_position - gfx::ToRoundedInt(snapped_position));
+  DCHECK_LT(diff, kEplison);
+}
+#endif
+
 }  // namespace
 
 namespace ui {
@@ -91,39 +103,86 @@
   DISALLOW_COPY_AND_ASSIGN(LayerMirror);
 };
 
-Layer::Layer()
-    : type_(LAYER_TEXTURED),
-      compositor_(nullptr),
-      parent_(nullptr),
-      visible_(true),
-      fills_bounds_opaquely_(true),
-      fills_bounds_completely_(false),
-      background_blur_sigma_(0.0f),
-      layer_saturation_(0.0f),
-      layer_brightness_(0.0f),
-      layer_grayscale_(0.0f),
-      layer_inverted_(false),
-      layer_blur_sigma_(0.0f),
-      layer_mask_(nullptr),
-      layer_mask_back_link_(nullptr),
-      zoom_(1),
-      zoom_inset_(0),
-      delegate_(nullptr),
-      owner_(nullptr),
-      cc_layer_(nullptr),
-      device_scale_factor_(1.0f),
-      cache_render_surface_requests_(0),
-      deferred_paint_requests_(0),
-      backdrop_filter_quality_(1.0f),
-      trilinear_filtering_request_(0),
-      weak_ptr_factory_(this) {
-  CreateCcLayer();
-}
+// Manages the subpixel offset data for a given set of parameters (device
+// scale factor and DIP offset from parent layer).
+class Layer::SubpixelPositionOffsetCache {
+ public:
+  SubpixelPositionOffsetCache() = default;
+  ~SubpixelPositionOffsetCache() = default;
+
+  gfx::Vector2dF GetSubpixelOffset(float device_scale_factor,
+                                   const gfx::Point& origin,
+                                   const gfx::Transform& tm) const {
+    if (has_explicit_subpixel_offset_)
+      return offset_;
+
+    if (device_scale_factor <= 0)
+      return gfx::Vector2dF();
+
+    // Compute the effective offset (position + transform) from the parent.
+    gfx::PointF offset_from_parent(origin);
+    if (!tm.IsIdentity() && tm.Preserves2dAxisAlignment())
+      offset_from_parent += tm.To2dTranslation();
+
+    if (device_scale_factor == device_scale_factor_ &&
+        offset_from_parent == offset_from_parent_) {
+      return offset_;
+    }
+
+    // Compute subpixel offset for the given parameters.
+    gfx::PointF scaled_offset_from_parent(offset_from_parent);
+    scaled_offset_from_parent.Scale(device_scale_factor, device_scale_factor);
+    gfx::PointF snapped_offset_from_parent(
+        gfx::ToRoundedPoint(scaled_offset_from_parent));
+
+    gfx::Vector2dF offset =
+        snapped_offset_from_parent - scaled_offset_from_parent;
+    offset.Scale(1.f / device_scale_factor);
+
+    // Store key and value information for the cache.
+    offset_ = offset;
+    device_scale_factor_ = device_scale_factor;
+    offset_from_parent_ = offset_from_parent;
+
+#if DCHECK_IS_ON()
+    const gfx::PointF snapped_position = offset_from_parent_ + offset_;
+    CheckSnapped(snapped_position.x() * device_scale_factor);
+    CheckSnapped(snapped_position.y() * device_scale_factor);
+#endif
+    return offset_;
+  }
+
+  void SetExplicitSubpixelPositionOffset(const gfx::Vector2dF& offset) {
+    has_explicit_subpixel_offset_ = true;
+    offset_ = offset;
+  }
+
+  bool has_explicit_subpixel_offset() const {
+    return has_explicit_subpixel_offset_;
+  }
+
+ private:
+  // The subpixel offset value.
+  mutable gfx::Vector2dF offset_;
+
+  // The device scale factor for which the |offset_| was computed.
+  mutable float device_scale_factor_ = 1.f;
+
+  // The offset of the layer from its parent for which |offset_| was computed.
+  mutable gfx::PointF offset_from_parent_;
+
+  // True if the subpixel offset was computed and set by an external source.
+  bool has_explicit_subpixel_offset_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(SubpixelPositionOffsetCache);
+};
 
 Layer::Layer(LayerType type)
     : type_(type),
       compositor_(nullptr),
       parent_(nullptr),
+      subpixel_position_offset_(
+          std::make_unique<SubpixelPositionOffsetCache>()),
       visible_(true),
       fills_bounds_opaquely_(true),
       fills_bounds_completely_(false),
@@ -209,7 +268,8 @@
 
   clone->SetTransform(GetTargetTransform());
   clone->SetBounds(bounds_);
-  clone->SetSubpixelPositionOffset(subpixel_position_offset_);
+  if (subpixel_position_offset_->has_explicit_subpixel_offset())
+    clone->SetSubpixelPositionOffset(GetSubpixelOffset());
   clone->SetMasksToBounds(GetMasksToBounds());
   clone->SetOpacity(GetTargetOpacity());
   clone->SetVisible(GetTargetVisibility());
@@ -240,6 +300,24 @@
   return mirror;
 }
 
+void Layer::MirrorLayer(Layer* mirrored_layer) {
+  DCHECK_EQ(type_, LAYER_SOLID_COLOR);
+
+  if (!mirrored_layer) {
+    SetShowSolidColorContent();
+    return;
+  }
+
+  auto new_layer = cc::MirrorLayer::Create(mirrored_layer->cc_layer_);
+  SwitchToLayer(new_layer);
+  mirror_layer_ = new_layer;
+  gfx::Rect bounds = bounds_;
+  bounds.set_size(mirrored_layer->bounds().size());
+  SetBounds(bounds);
+
+  RecomputeDrawsContentAndUVRect();
+}
+
 const Compositor* Layer::GetCompositor() const {
   return GetRoot(this)->compositor_;
 }
@@ -380,10 +458,15 @@
 }
 
 void Layer::SetSubpixelPositionOffset(const gfx::Vector2dF& offset) {
-  subpixel_position_offset_ = offset;
+  subpixel_position_offset_->SetExplicitSubpixelPositionOffset(offset);
   RecomputePosition();
 }
 
+const gfx::Vector2dF Layer::GetSubpixelOffset() const {
+  return subpixel_position_offset_->GetSubpixelOffset(
+      device_scale_factor_, GetTargetBounds().origin(), GetTargetTransform());
+}
+
 gfx::Rect Layer::GetTargetBounds() const {
   if (animator_ && animator_->IsAnimatingProperty(
       LayerAnimationElement::BOUNDS)) {
@@ -684,6 +767,7 @@
   solid_color_layer_ = nullptr;
   texture_layer_ = nullptr;
   surface_layer_ = nullptr;
+  mirror_layer_ = nullptr;
 
   for (auto* child : children_) {
     DCHECK(child->cc_layer_);
@@ -1255,7 +1339,8 @@
   bounds_ = bounds;
 
   RecomputeDrawsContentAndUVRect();
-  RecomputePosition();
+  if (old_bounds.origin() != bounds_.origin())
+    RecomputePosition();
 
   if (delegate_)
     delegate_->OnLayerBoundsChanged(old_bounds, reason);
@@ -1281,6 +1366,11 @@
                                       PropertyChangeReason reason) {
   const gfx::Transform old_transform = this->transform();
   cc_layer_->SetTransform(transform);
+
+  // Skip recomputing position if the subpixel offset does not need updating
+  // which is the case if an explicit offset is set.
+  if (!subpixel_position_offset_->has_explicit_subpixel_offset())
+    RecomputePosition();
   if (delegate_)
     delegate_->OnLayerTransformed(old_transform, reason);
 }
@@ -1433,8 +1523,7 @@
 }
 
 void Layer::RecomputePosition() {
-  cc_layer_->SetPosition(gfx::PointF(bounds_.origin()) +
-                         subpixel_position_offset_);
+  cc_layer_->SetPosition(gfx::PointF(bounds_.origin()) + GetSubpixelOffset());
 }
 
 void Layer::SetCompositorForAnimatorsInTree(Compositor* compositor) {
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 9ffad84..25421525 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -35,6 +35,7 @@
 
 namespace cc {
 class Layer;
+class MirrorLayer;
 class NinePatchLayer;
 class SolidColorLayer;
 class SurfaceLayer;
@@ -70,8 +71,7 @@
                                 public cc::LayerClient {
  public:
   using ShapeRects = std::vector<gfx::Rect>;
-  Layer();
-  explicit Layer(LayerType type);
+  explicit Layer(LayerType type = LAYER_TEXTURED);
   ~Layer() override;
 
   // Note that only solid color and surface content is copied.
@@ -82,6 +82,11 @@
   // content is only mirrored if painted by a delegate or backed by a surface.
   std::unique_ptr<Layer> Mirror();
 
+  // Sets up this layer to mirror contents of |mirrored_layer|. This layer
+  // should be of type |LAYER_SOLID_COLOR| and should not be a descendant of the
+  // |mirrored_layer|.
+  void MirrorLayer(Layer* mirrored_layer);
+
   // This method is relevant only if this layer is a mirror destination layer.
   // Sets whether this mirror layer's bounds are synchronized with the source
   // layer's bounds.
@@ -169,11 +174,11 @@
   const gfx::Size& size() const { return bounds_.size(); }
 
   // The offset from our parent (stored in bounds.origin()) is an integer but we
-  // may need to be at a fractional pixel offset to align properly on screen.
+  // may need to be at a fractional pixel offset to align properly on screen. If
+  // this is not set, the layer will auto compute its sub pixel offset
+  // information with respect to its parent layer.
   void SetSubpixelPositionOffset(const gfx::Vector2dF& offset);
-  const gfx::Vector2dF& subpixel_position_offset() const {
-    return subpixel_position_offset_;
-  }
+  const gfx::Vector2dF GetSubpixelOffset() const;
 
   // Return the target bounds if animator is running, or the current bounds
   // otherwise.
@@ -437,6 +442,7 @@
   bool FillsBoundsCompletely() const override;
   size_t GetApproximateUnsharedMemoryUsage() const override;
 
+  cc::MirrorLayer* mirror_layer_for_testing() { return mirror_layer_.get(); }
   cc::Layer* cc_layer_for_testing() { return cc_layer_; }
   const cc::Layer* cc_layer_for_testing() const { return cc_layer_; }
 
@@ -508,6 +514,7 @@
  private:
   friend class LayerOwner;
   class LayerMirror;
+  class SubpixelPositionOffsetCache;
 
   void CollectAnimators(std::vector<scoped_refptr<LayerAnimator> >* animators);
 
@@ -589,7 +596,8 @@
   bool sync_bounds_with_source_ = false;
 
   gfx::Rect bounds_;
-  gfx::Vector2dF subpixel_position_offset_;
+
+  std::unique_ptr<SubpixelPositionOffsetCache> subpixel_position_offset_;
 
   // Visibility of this layer. See SetVisible/IsDrawn for more details.
   bool visible_;
@@ -652,6 +660,7 @@
   // Ownership of the layer is held through one of the strongly typed layer
   // pointers, depending on which sort of layer this is.
   scoped_refptr<cc::PictureLayer> content_layer_;
+  scoped_refptr<cc::MirrorLayer> mirror_layer_;
   scoped_refptr<cc::NinePatchLayer> nine_patch_layer_;
   scoped_refptr<cc::TextureLayer> texture_layer_;
   scoped_refptr<cc::SolidColorLayer> solid_color_layer_;
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index 252f92a..5ec002c 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -30,6 +30,7 @@
 #include "cc/animation/keyframe_effect.h"
 #include "cc/animation/single_keyframe_effect_animation.h"
 #include "cc/layers/layer.h"
+#include "cc/layers/mirror_layer.h"
 #include "cc/test/pixel_test_utils.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
@@ -1511,6 +1512,69 @@
   EXPECT_EQ(mask->damaged_region_for_testing().bounds(), gfx::Rect());
 }
 
+// Verifies that when a layer is mirroring other layers, mirror count
+// of mirrored layers is updated properly.
+TEST_F(LayerWithNullDelegateTest, MirrorLayer) {
+  std::unique_ptr<Layer> mirrored_layer_1(CreateLayer(LAYER_SOLID_COLOR));
+  auto* mirrored_layer_1_cc = mirrored_layer_1->cc_layer_for_testing();
+
+  std::unique_ptr<Layer> mirrored_layer_2(CreateLayer(LAYER_SOLID_COLOR));
+  auto* mirrored_layer_2_cc = mirrored_layer_2->cc_layer_for_testing();
+
+  std::unique_ptr<Layer> mirror_layer(CreateLayer(LAYER_SOLID_COLOR));
+
+  // Originally, mirror counts should be zero.
+  auto* mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+  EXPECT_EQ(nullptr, mirror_layer_cc);
+  EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+  EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+
+  // Mirror the first layer. Its mirror count should be increased.
+  mirror_layer->MirrorLayer(mirrored_layer_1.get());
+  mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+  ASSERT_NE(nullptr, mirror_layer_cc);
+  EXPECT_EQ(mirror_layer->cc_layer_for_testing(), mirror_layer_cc);
+  EXPECT_EQ(mirrored_layer_1_cc, mirror_layer_cc->mirrored_layer());
+  EXPECT_EQ(1, mirrored_layer_1_cc->mirror_count());
+  EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+
+  // Mirror the second layer. Its mirror count should be increased, but mirror
+  // count for the first mirrored layer should be set back to zero.
+  mirror_layer->MirrorLayer(mirrored_layer_2.get());
+  mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+  ASSERT_NE(nullptr, mirror_layer_cc);
+  EXPECT_EQ(mirror_layer->cc_layer_for_testing(), mirror_layer_cc);
+  EXPECT_EQ(mirrored_layer_2_cc, mirror_layer_cc->mirrored_layer());
+  EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+  EXPECT_EQ(1, mirrored_layer_2_cc->mirror_count());
+
+  // Un-mirror the layer. All mirror counts should be set to zero.
+  mirror_layer->MirrorLayer(nullptr);
+  mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+  EXPECT_EQ(nullptr, mirror_layer_cc);
+  EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+  EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+}
+
+// Verifies that when a layer is mirroring another layer, its size matches the
+// size of the mirrored layer.
+TEST_F(LayerWithNullDelegateTest, MirrorLayerBounds) {
+  const gfx::Rect mirrored_bounds(0, 0, 50, 50);
+  const gfx::Rect mirror_bounds(0, 50, 10, 10);
+
+  std::unique_ptr<Layer> mirrored_layer(CreateLayer(LAYER_SOLID_COLOR));
+  mirrored_layer->SetBounds(mirrored_bounds);
+
+  std::unique_ptr<Layer> mirror_layer(CreateLayer(LAYER_SOLID_COLOR));
+  mirror_layer->SetBounds(mirror_bounds);
+
+  EXPECT_EQ(mirror_bounds, mirror_layer->bounds());
+
+  mirror_layer->MirrorLayer(mirrored_layer.get());
+  EXPECT_EQ(mirror_bounds.origin(), mirror_layer->bounds().origin());
+  EXPECT_EQ(mirrored_bounds.size(), mirror_layer->bounds().size());
+}
+
 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 << "; "
@@ -2681,76 +2745,22 @@
   root->SetBounds(gfx::Rect(0, 0, 100, 100));
   c1->SetBounds(gfx::Rect(1, 1, 10, 10));
   c11->SetBounds(gfx::Rect(1, 1, 10, 10));
-  SnapLayerToPhysicalPixelBoundary(root.get(), c11.get());
-  // 0.5 at 1.25 scale : (1 - 0.25 + 0.25) / 1.25 = 0.4
-  EXPECT_EQ("0.40 0.40",
-            Vector2dFTo100thPrecisionString(c11->subpixel_position_offset()));
+  // 1 at 1.25 scale = 1.25 : (-0.25) / 1.25 = -0.20
+  EXPECT_EQ("-0.20 -0.20",
+            Vector2dFTo100thPrecisionString(c11->GetSubpixelOffset()));
 
   allocator.GenerateId();
   GetCompositor()->SetScaleAndSize(
       1.5f, gfx::Size(100, 100),
       allocator.GetCurrentLocalSurfaceIdAllocation());
-  SnapLayerToPhysicalPixelBoundary(root.get(), c11.get());
-  // c11 must already be aligned at 1.5 scale.
-  EXPECT_EQ("0.00 0.00",
-            Vector2dFTo100thPrecisionString(c11->subpixel_position_offset()));
+  // 1 at 1.5 scale = 1.5 : (round(1.5) - 1.5) / 1.5 = 0.33
+  EXPECT_EQ("0.33 0.33",
+            Vector2dFTo100thPrecisionString(c11->GetSubpixelOffset()));
 
   c11->SetBounds(gfx::Rect(2, 2, 10, 10));
-  SnapLayerToPhysicalPixelBoundary(root.get(), c11.get());
-  // c11 is now off the pixel.
-  // 0.5 / 1.5 = 0.333...
-  EXPECT_EQ("0.33 0.33",
-            Vector2dFTo100thPrecisionString(c11->subpixel_position_offset()));
-}
-
-TEST_F(LayerWithRealCompositorTest, SnapLayerToPixelsWithScaleTransform) {
-  std::unique_ptr<Layer> root(CreateLayer(LAYER_TEXTURED));
-  std::unique_ptr<Layer> c1(CreateLayer(LAYER_TEXTURED));
-  std::unique_ptr<Layer> c11(CreateLayer(LAYER_TEXTURED));
-  std::unique_ptr<Layer> c111(CreateLayer(LAYER_TEXTURED));
-
-  viz::ParentLocalSurfaceIdAllocator allocator;
-  allocator.GenerateId();
-  GetCompositor()->SetScaleAndSize(
-      1.0f, gfx::Size(100, 100),
-      allocator.GetCurrentLocalSurfaceIdAllocation());
-  GetCompositor()->SetRootLayer(root.get());
-  root->Add(c1.get());
-  c1->Add(c11.get());
-  c11->Add(c111.get());
-
-  root->SetBounds(gfx::Rect(0, 0, 100, 100));
-  c1->SetBounds(gfx::Rect(0, 0, 10, 10));
-  c11->SetBounds(gfx::Rect(0, 0, 10, 10));
-  c111->SetBounds(gfx::Rect(2, 2, 5, 5));
-
-  gfx::Transform transform;
-  transform.Scale(1.25f, 1.25f);
-
-  c1->SetTransform(transform);
-  SnapLayerToPhysicalPixelBoundary(root.get(), c111.get());
-
-  // c111 ends up at 2.5, and is supposed to be snapped to 3.0. So subpixel
-  // offset is expected to be:
-  // 0.5 / 1.25 = 0.40
-  EXPECT_EQ("0.40 0.40",
-            Vector2dFTo100thPrecisionString(c111->subpixel_position_offset()));
-
-  c11->SetTransform(transform);
-  SnapLayerToPhysicalPixelBoundary(root.get(), c111.get());
-
-  // c111 ends up at 3.125, and is supposed to be snapped to 3.0. So subpixel
-  // offset is expected to be:
-  // -0.125 / (1.25 * 1.25) = -0.08
-  EXPECT_EQ("-0.08 -0.08",
-            Vector2dFTo100thPrecisionString(c111->subpixel_position_offset()));
-
-  // A transform on c111 should not affect the subpixel offset so expect it to
-  // be the same as before.
-  c111->SetTransform(transform);
-  SnapLayerToPhysicalPixelBoundary(root.get(), c111.get());
-  EXPECT_EQ("-0.08 -0.08",
-            Vector2dFTo100thPrecisionString(c111->subpixel_position_offset()));
+  // 2 at 1.5 scale = 3 : (round(3) - 3) / 1.5 = 0
+  EXPECT_EQ("0.00 0.00",
+            Vector2dFTo100thPrecisionString(c11->GetSubpixelOffset()));
 }
 
 // Verify that LayerDelegate::OnLayerBoundsChanged() is called when the bounds
diff --git a/ui/display/manager/BUILD.gn b/ui/display/manager/BUILD.gn
index 38dcba7..c1ccba7 100644
--- a/ui/display/manager/BUILD.gn
+++ b/ui/display/manager/BUILD.gn
@@ -15,6 +15,8 @@
     "display_manager_export.h",
     "display_manager_utilities.cc",
     "display_manager_utilities.h",
+    "display_util.cc",
+    "display_util.h",
     "json_converter.cc",
     "json_converter.h",
     "managed_display_info.cc",
@@ -36,8 +38,6 @@
       "display_configurator.cc",
       "display_configurator.h",
       "display_layout_manager.h",
-      "display_util.cc",
-      "display_util.h",
       "query_content_protection_task.cc",
       "query_content_protection_task.h",
       "touch_device_manager.cc",
diff --git a/ui/display/manager/content_protection_manager.cc b/ui/display/manager/content_protection_manager.cc
index 0fa2da6..a23c5d3 100644
--- a/ui/display/manager/content_protection_manager.cc
+++ b/ui/display/manager/content_protection_manager.cc
@@ -4,9 +4,11 @@
 
 #include "ui/display/manager/content_protection_manager.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "base/logging.h"
+#include "base/observer_list.h"
 #include "base/stl_util.h"
 #include "ui/display/manager/apply_content_protection_task.h"
 #include "ui/display/manager/display_layout_manager.h"
@@ -17,6 +19,13 @@
 
 namespace display {
 
+namespace {
+
+// HDCP requires suppressing content within 2 seconds when authentication drops.
+constexpr auto kDisplaySecurityPollingPeriod = base::TimeDelta::FromSeconds(2);
+
+}  // namespace
+
 ContentProtectionManager::ContentProtectionManager(
     DisplayLayoutManager* layout_manager,
     ConfigurationDisabledCallback config_disabled_callback)
@@ -51,6 +60,17 @@
       base::BindOnce(&ContentProtectionManager::OnContentProtectionApplied,
                      weak_ptr_factory_.GetWeakPtr(),
                      ApplyContentProtectionCallback(), base::nullopt)));
+
+  ToggleDisplaySecurityPolling();
+}
+
+void ContentProtectionManager::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+  QueueDisplaySecurityQueries();
+}
+
+void ContentProtectionManager::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
 }
 
 void ContentProtectionManager::QueryContentProtection(
@@ -95,6 +115,8 @@
       base::BindOnce(&ContentProtectionManager::OnContentProtectionApplied,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      client_id)));
+
+  ToggleDisplaySecurityPolling();
 }
 
 const DisplaySnapshot* ContentProtectionManager::GetDisplay(
@@ -150,6 +172,8 @@
 
   // Fire failure callbacks.
   tasks_ = {};
+
+  ToggleDisplaySecurityPolling();
 }
 
 void ContentProtectionManager::OnContentProtectionQueried(
@@ -204,4 +228,74 @@
   KillTasks();
 }
 
+void ContentProtectionManager::ToggleDisplaySecurityPolling() {
+  const bool polling = security_timer_.IsRunning();
+
+  const auto protections = AggregateContentProtections();
+  const bool should_poll =
+      std::any_of(protections.begin(), protections.end(), [](const auto& pair) {
+        return pair.second != CONTENT_PROTECTION_METHOD_NONE;
+      });
+
+  if (polling && !should_poll) {
+    security_timer_.Stop();
+    QueueDisplaySecurityQueries();
+  } else if (!polling && should_poll) {
+    security_timer_.Start(
+        FROM_HERE, kDisplaySecurityPollingPeriod,
+        base::BindRepeating(
+            &ContentProtectionManager::QueueDisplaySecurityQueries,
+            weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
+bool ContentProtectionManager::TriggerDisplaySecurityTimeoutForTesting() {
+  if (!security_timer_.IsRunning())
+    return false;
+
+  security_timer_.user_task().Run();
+  return true;
+}
+
+void ContentProtectionManager::QueueDisplaySecurityQueries() {
+  for (DisplaySnapshot* display : layout_manager_->GetDisplayStates()) {
+    int64_t display_id = display->display_id();
+
+    if (!IsPhysicalDisplayType(display->type())) {
+      NotifyDisplaySecurityObservers(display_id, /*secure=*/false);
+      continue;
+    }
+
+    QueueTask(std::make_unique<QueryContentProtectionTask>(
+        layout_manager_, native_display_delegate_, display_id,
+        base::BindOnce(&ContentProtectionManager::OnDisplaySecurityQueried,
+                       weak_ptr_factory_.GetWeakPtr(), display_id)));
+  }
+}
+
+void ContentProtectionManager::OnDisplaySecurityQueried(
+    int64_t display_id,
+    Task::Status status,
+    uint32_t connection_mask,
+    uint32_t protection_mask) {
+  if (GetDisplay(display_id)) {
+    // Internal display is secure if not mirrored on unsecure external display.
+    const bool secure = status == Task::Status::SUCCESS &&
+                        (protection_mask != CONTENT_PROTECTION_METHOD_NONE ||
+                         connection_mask == DISPLAY_CONNECTION_TYPE_INTERNAL);
+
+    NotifyDisplaySecurityObservers(display_id, secure);
+  }
+
+  if (status != Task::Status::KILLED)
+    DequeueTask();
+}
+
+void ContentProtectionManager::NotifyDisplaySecurityObservers(
+    int64_t display_id,
+    bool secure) {
+  for (Observer& observer : observers_)
+    observer.OnDisplaySecurityChanged(display_id, secure);
+}
+
 }  // namespace display
diff --git a/ui/display/manager/content_protection_manager.h b/ui/display/manager/content_protection_manager.h
index b95005c..cd7d704f 100644
--- a/ui/display/manager/content_protection_manager.h
+++ b/ui/display/manager/content_protection_manager.h
@@ -13,7 +13,9 @@
 #include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "base/optional.h"
+#include "base/timer/timer.h"
 #include "ui/display/manager/display_configurator.h"
 #include "ui/display/manager/display_manager_export.h"
 
@@ -27,7 +29,11 @@
 class ContentProtectionManagerTest;
 }  // namespace test
 
-// Fulfills client requests to query and apply per-display content protection.
+// Fulfills client requests to query and apply per-display content protection,
+// and notifies observers of display security changes. Changes are detected by
+// polling as required by the kernel API, since authentication latency depends
+// on hardware topology, and the hardware may temporarily drop authentication,
+// in which case the kernel automatically tries to re-establish protection.
 class DISPLAY_MANAGER_EXPORT ContentProtectionManager
     : public DisplayConfigurator::Observer {
  public:
@@ -49,6 +55,14 @@
     virtual void Run() = 0;
   };
 
+  class Observer : public base::CheckedObserver {
+   public:
+    ~Observer() override = default;
+
+    // Called after the secure state of a display has been changed.
+    virtual void OnDisplaySecurityChanged(int64_t display_id, bool secure) = 0;
+  };
+
   // Returns whether display configuration is disabled, in which case API calls
   // are no-ops resulting in failure callbacks.
   using ConfigurationDisabledCallback = base::RepeatingCallback<bool()>;
@@ -69,6 +83,9 @@
   ClientId RegisterClient();
   void UnregisterClient(ClientId client_id);
 
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   // Queries protection against the client's latest request on the same display,
   // i.e. the result is CONTENT_PROTECTION_METHOD_NONE unless the client has
   // previously applied protection on that display, such that requests from
@@ -126,6 +143,21 @@
   void OnDisplayModeChangeFailed(const DisplayConfigurator::DisplayStateList&,
                                  MultipleDisplayState) override;
 
+  // Toggles timer for periodic security queries given latest client requests.
+  void ToggleDisplaySecurityPolling();
+
+  // Forces timer to fire if running, and returns whether it was running.
+  bool TriggerDisplaySecurityTimeoutForTesting();
+
+  // Queries protection status for all displays, and notifies observers whether
+  // each display is secure. Called periodically while protection is requested.
+  void QueueDisplaySecurityQueries();
+  void OnDisplaySecurityQueried(int64_t display_id,
+                                Task::Status status,
+                                uint32_t connection_mask,
+                                uint32_t protection_mask);
+  void NotifyDisplaySecurityObservers(int64_t display_id, bool secure);
+
   DisplayLayoutManager* const layout_manager_;                // Not owned.
   NativeDisplayDelegate* native_display_delegate_ = nullptr;  // Not owned.
 
@@ -139,6 +171,11 @@
   // Pending tasks to query or apply content protection.
   base::queue<std::unique_ptr<Task>> tasks_;
 
+  base::ObserverList<Observer> observers_;
+
+  // Used for periodic queries to notify observers of display security changes.
+  base::RepeatingTimer security_timer_;
+
   base::WeakPtrFactory<ContentProtectionManager> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ContentProtectionManager);
diff --git a/ui/display/manager/content_protection_manager_unittest.cc b/ui/display/manager/content_protection_manager_unittest.cc
index 2943560..df288150 100644
--- a/ui/display/manager/content_protection_manager_unittest.cc
+++ b/ui/display/manager/content_protection_manager_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ui/display/manager/content_protection_manager.h"
 
+#include "base/containers/flat_map.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,6 +23,30 @@
 
 }  // namespace
 
+using SecurityChanges = base::flat_map<int64_t, bool>;
+
+class TestObserver : public ContentProtectionManager::Observer {
+ public:
+  explicit TestObserver(ContentProtectionManager* manager) : manager_(manager) {
+    manager_->AddObserver(this);
+  }
+  ~TestObserver() override { manager_->RemoveObserver(this); }
+
+  const SecurityChanges& security_changes() const { return security_changes_; }
+
+  void Reset() { security_changes_.clear(); }
+
+ private:
+  void OnDisplaySecurityChanged(int64_t display_id, bool secure) override {
+    security_changes_.emplace(display_id, secure);
+  }
+
+  ContentProtectionManager* const manager_;
+  SecurityChanges security_changes_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
 class ContentProtectionManagerTest : public testing::Test {
  public:
   ContentProtectionManagerTest() = default;
@@ -76,6 +101,10 @@
     manager_.OnDisplayModeChanged(layout_manager_.GetDisplayStates());
   }
 
+  bool TriggerDisplaySecurityTimeout() {
+    return manager_.TriggerDisplaySecurityTimeoutForTesting();
+  }
+
   base::MessageLoop message_loop_;
   TestDisplayLayoutManager layout_manager_{{}, MULTIPLE_DISPLAY_STATE_INVALID};
 
@@ -487,5 +516,124 @@
   EXPECT_EQ(HDCP_STATE_UNDESIRED, native_display_delegate_.hdcp_state());
 }
 
+TEST_F(ContentProtectionManagerTest, DisplaySecurityObserver) {
+  TestObserver observer(&manager_);
+
+  // Internal display is secure if not mirroring.
+  EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true}, {kDisplayIds[1], false}}),
+            observer.security_changes());
+  observer.Reset();
+
+  auto id = manager_.RegisterClient();
+  EXPECT_TRUE(id);
+
+  native_display_delegate_.set_run_async(true);
+
+  manager_.ApplyContentProtection(
+      id, kDisplayIds[1], CONTENT_PROTECTION_METHOD_HDCP,
+      base::BindOnce(
+          &ContentProtectionManagerTest::ApplyContentProtectionCallback,
+          base::Unretained(this)));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(observer.security_changes().empty());
+
+  EXPECT_TRUE(TriggerDisplaySecurityTimeout());
+  base::RunLoop().RunUntilIdle();
+
+  // Observer should be notified when client applies protection.
+  EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true}, {kDisplayIds[1], true}}),
+            observer.security_changes());
+  observer.Reset();
+
+  layout_manager_.set_display_state(MULTIPLE_DISPLAY_STATE_MULTI_MIRROR);
+  TriggerDisplayConfiguration();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(TriggerDisplaySecurityTimeout());
+  base::RunLoop().RunUntilIdle();
+
+  // Observer should be notified on configuration change.
+  EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true}, {kDisplayIds[1], true}}),
+            observer.security_changes());
+  observer.Reset();
+
+  manager_.ApplyContentProtection(
+      id, kDisplayIds[1], CONTENT_PROTECTION_METHOD_NONE,
+      base::BindOnce(
+          &ContentProtectionManagerTest::ApplyContentProtectionCallback,
+          base::Unretained(this)));
+
+  // Timer should be stopped when no client requests protection.
+  EXPECT_FALSE(TriggerDisplaySecurityTimeout());
+  base::RunLoop().RunUntilIdle();
+
+  // Internal display is not secure if mirrored to an unprotected display.
+  EXPECT_EQ(SecurityChanges({{kDisplayIds[0], false}, {kDisplayIds[1], false}}),
+            observer.security_changes());
+  observer.Reset();
+
+  native_display_delegate_.set_set_hdcp_state_expectation(false);
+
+  manager_.ApplyContentProtection(
+      id, kDisplayIds[1], CONTENT_PROTECTION_METHOD_HDCP,
+      base::BindOnce(
+          &ContentProtectionManagerTest::ApplyContentProtectionCallback,
+          base::Unretained(this)));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(observer.security_changes().empty());
+
+  EXPECT_TRUE(TriggerDisplaySecurityTimeout());
+  base::RunLoop().RunUntilIdle();
+
+  // Internal display is not secure if mirrored to an unprotected display.
+  EXPECT_EQ(SecurityChanges({{kDisplayIds[0], false}, {kDisplayIds[1], false}}),
+            observer.security_changes());
+  observer.Reset();
+
+  native_display_delegate_.set_hdcp_state(HDCP_STATE_UNDESIRED);
+  native_display_delegate_.set_set_hdcp_state_expectation(true);
+
+  manager_.ApplyContentProtection(
+      id, kDisplayIds[1], CONTENT_PROTECTION_METHOD_HDCP,
+      base::BindOnce(
+          &ContentProtectionManagerTest::ApplyContentProtectionCallback,
+          base::Unretained(this)));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(observer.security_changes().empty());
+
+  EXPECT_TRUE(TriggerDisplaySecurityTimeout());
+  base::RunLoop().RunUntilIdle();
+
+  // Internal display is secure if mirrored to a protected display.
+  EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true}, {kDisplayIds[1], true}}),
+            observer.security_changes());
+  observer.Reset();
+
+  layout_manager_.set_display_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED);
+  TriggerDisplayConfiguration();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(TriggerDisplaySecurityTimeout());
+  base::RunLoop().RunUntilIdle();
+
+  // Observer should be notified on configuration change.
+  EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true}, {kDisplayIds[1], true}}),
+            observer.security_changes());
+  observer.Reset();
+
+  manager_.UnregisterClient(id);
+
+  // Timer should be stopped when no client requests protection.
+  EXPECT_FALSE(TriggerDisplaySecurityTimeout());
+  base::RunLoop().RunUntilIdle();
+
+  // Observer should be notified when client unregisters.
+  EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true}, {kDisplayIds[1], false}}),
+            observer.security_changes());
+}
+
 }  // namespace test
 }  // namespace display
diff --git a/ui/display/manager/display_util.cc b/ui/display/manager/display_util.cc
index 592b131..5f299ae 100644
--- a/ui/display/manager/display_util.cc
+++ b/ui/display/manager/display_util.cc
@@ -62,6 +62,7 @@
 
 }  // namespace
 
+#if defined(OS_CHROMEOS)
 std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) {
   switch (state) {
     case chromeos::DISPLAY_POWER_ALL_ON:
@@ -77,23 +78,6 @@
   }
 }
 
-std::string MultipleDisplayStateToString(MultipleDisplayState state) {
-  switch (state) {
-    case MULTIPLE_DISPLAY_STATE_INVALID:
-      return "INVALID";
-    case MULTIPLE_DISPLAY_STATE_HEADLESS:
-      return "HEADLESS";
-    case MULTIPLE_DISPLAY_STATE_SINGLE:
-      return "SINGLE";
-    case MULTIPLE_DISPLAY_STATE_MULTI_MIRROR:
-      return "DUAL_MIRROR";
-    case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED:
-      return "MULTI_EXTENDED";
-  }
-  NOTREACHED() << "Unknown state " << state;
-  return "INVALID";
-}
-
 int GetDisplayPower(const std::vector<DisplaySnapshot*>& displays,
                     chromeos::DisplayPowerState state,
                     std::vector<bool>* display_power) {
@@ -116,6 +100,25 @@
   return num_on_displays;
 }
 
+#endif  // defined(OS_CHROMEOS)
+
+std::string MultipleDisplayStateToString(MultipleDisplayState state) {
+  switch (state) {
+    case MULTIPLE_DISPLAY_STATE_INVALID:
+      return "INVALID";
+    case MULTIPLE_DISPLAY_STATE_HEADLESS:
+      return "HEADLESS";
+    case MULTIPLE_DISPLAY_STATE_SINGLE:
+      return "SINGLE";
+    case MULTIPLE_DISPLAY_STATE_MULTI_MIRROR:
+      return "DUAL_MIRROR";
+    case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED:
+      return "MULTI_EXTENDED";
+  }
+  NOTREACHED() << "Unknown state " << state;
+  return "INVALID";
+}
+
 bool IsPhysicalDisplayType(DisplayConnectionType type) {
   return !(type & DISPLAY_CONNECTION_TYPE_NETWORK);
 }
diff --git a/ui/display/manager/display_util.h b/ui/display/manager/display_util.h
index 786a6db..2f2a3a7 100644
--- a/ui/display/manager/display_util.h
+++ b/ui/display/manager/display_util.h
@@ -8,21 +8,22 @@
 #include <string>
 #include <vector>
 
-#include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/display/manager/display_manager_export.h"
 #include "ui/display/types/display_constants.h"
 
+#if defined(OS_CHROMEOS)
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#endif  // defined(OS_CHROMEOS)
+
 namespace display {
 
 class DisplaySnapshot;
 class ManagedDisplayMode;
 
+#if defined(OS_CHROMEOS)
 // Returns a string describing |state|.
 std::string DisplayPowerStateToString(chromeos::DisplayPowerState state);
 
-// Returns a string describing |state|.
-std::string MultipleDisplayStateToString(MultipleDisplayState state);
-
 // Returns the number of displays in |displays| that should be turned on, per
 // |state|.  If |display_power| is non-NULL, it is updated to contain the
 // on/off state of each corresponding entry in |displays|.
@@ -31,6 +32,11 @@
                 chromeos::DisplayPowerState state,
                 std::vector<bool>* display_power);
 
+#endif  // defined(OS_CHROMEOS)
+
+// Returns a string describing |state|.
+std::string MultipleDisplayStateToString(MultipleDisplayState state);
+
 // Returns whether the DisplayConnectionType |type| is a physically connected
 // display. Currently only DISPLAY_CONNECTION_TYPE_NETWORK return false.
 // All other types return true.
diff --git a/ui/display/manager/test/test_display_layout_manager.h b/ui/display/manager/test/test_display_layout_manager.h
index c27b401..421a45f 100644
--- a/ui/display/manager/test/test_display_layout_manager.h
+++ b/ui/display/manager/test/test_display_layout_manager.h
@@ -26,6 +26,10 @@
     displays_ = std::move(displays);
   }
 
+  void set_display_state(MultipleDisplayState display_state) {
+    display_state_ = display_state;
+  }
+
   // DisplayLayoutManager:
   DisplayConfigurator::StateController* GetStateController() const override;
   DisplayConfigurator::SoftwareMirroringController*
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index a8d405c..9c55f3a1 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -508,6 +508,7 @@
       "blink/prediction/input_predictor_unittest_helpers.h",
       "blink/prediction/kalman_predictor_unittest.cc",
       "blink/prediction/least_squares_predictor_unittest.cc",
+      "blink/prediction/linear_predictor_unittest.cc",
       "blink/scroll_predictor_unittest.cc",
       "blink/web_input_event_traits_unittest.cc",
       "blink/web_input_event_unittest.cc",
diff --git a/ui/events/blink/BUILD.gn b/ui/events/blink/BUILD.gn
index c392567..8693184 100644
--- a/ui/events/blink/BUILD.gn
+++ b/ui/events/blink/BUILD.gn
@@ -46,6 +46,8 @@
     "prediction/kalman_predictor.h",
     "prediction/least_squares_predictor.cc",
     "prediction/least_squares_predictor.h",
+    "prediction/linear_predictor.cc",
+    "prediction/linear_predictor.h",
     "scroll_predictor.cc",
     "scroll_predictor.h",
     "synchronous_input_handler_proxy.h",
diff --git a/ui/events/blink/prediction/input_predictor_unittest_helpers.cc b/ui/events/blink/prediction/input_predictor_unittest_helpers.cc
index c78447a..6fc028b 100644
--- a/ui/events/blink/prediction/input_predictor_unittest_helpers.cc
+++ b/ui/events/blink/prediction/input_predictor_unittest_helpers.cc
@@ -29,4 +29,40 @@
   }
 }
 
+void InputPredictorTest::ValidatePredictor(
+    const std::vector<double>& events_x,
+    const std::vector<double>& events_y,
+    const std::vector<double>& events_ts_ms,
+    const std::vector<double>& prediction_ts_ms,
+    const std::vector<double>& predicted_x,
+    const std::vector<double>& predicted_y) {
+  predictor_->Reset();
+  std::vector<double> computed_x;
+  std::vector<double> computed_y;
+  size_t current_prediction_ts = 0;
+  for (size_t i = 0; i < events_ts_ms.size(); i++) {
+    InputPredictor::InputData data = {gfx::PointF(events_x[i], events_y[i]),
+                                      FromMilliseconds(events_ts_ms[i])};
+    predictor_->Update(data);
+
+    if (predictor_->HasPrediction()) {
+      InputPredictor::InputData result;
+      EXPECT_TRUE(predictor_->GeneratePrediction(
+          FromMilliseconds(prediction_ts_ms[current_prediction_ts]),
+          false /* is_resampling */, &result));
+      computed_x.push_back(result.pos.x());
+      computed_y.push_back(result.pos.y());
+      current_prediction_ts++;
+    }
+  }
+
+  EXPECT_TRUE(computed_x.size() == predicted_x.size());
+  if (computed_x.size() == predicted_x.size()) {
+    for (size_t i = 0; i < predicted_x.size(); i++) {
+      EXPECT_NEAR(computed_x[i], predicted_x[i], kEpsilon);
+      EXPECT_NEAR(computed_y[i], predicted_y[i], kEpsilon);
+    }
+  }
+}
+
 }  // namespace ui
diff --git a/ui/events/blink/prediction/input_predictor_unittest_helpers.h b/ui/events/blink/prediction/input_predictor_unittest_helpers.h
index 569d7a2e..1cda1ec 100644
--- a/ui/events/blink/prediction/input_predictor_unittest_helpers.h
+++ b/ui/events/blink/prediction/input_predictor_unittest_helpers.h
@@ -27,6 +27,13 @@
                          const std::vector<double>& y,
                          const std::vector<double>& timestamp_ms);
 
+  void ValidatePredictor(const std::vector<double>& events_x,
+                         const std::vector<double>& events_y,
+                         const std::vector<double>& events_ts_ms,
+                         const std::vector<double>& prediction_ts_ms,
+                         const std::vector<double>& predicted_x,
+                         const std::vector<double>& predicted_y);
+
  protected:
   static constexpr double kEpsilon = 0.1;
 
diff --git a/ui/events/blink/prediction/least_squares_predictor.cc b/ui/events/blink/prediction/least_squares_predictor.cc
index e6d98c25..55d13a9 100644
--- a/ui/events/blink/prediction/least_squares_predictor.cc
+++ b/ui/events/blink/prediction/least_squares_predictor.cc
@@ -4,8 +4,6 @@
 
 #include "ui/events/blink/prediction/least_squares_predictor.h"
 
-#include "base/trace_event/trace_event.h"
-
 namespace ui {
 
 namespace {
diff --git a/ui/events/blink/prediction/linear_predictor.cc b/ui/events/blink/prediction/linear_predictor.cc
new file mode 100644
index 0000000..07faff3
--- /dev/null
+++ b/ui/events/blink/prediction/linear_predictor.cc
@@ -0,0 +1,119 @@
+// Copyright 2019 The Chromium 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/events/blink/prediction/linear_predictor.h"
+
+namespace ui {
+
+LinearPredictor::LinearPredictor(EquationOrder order) {
+  equation_order_ = order;
+}
+
+LinearPredictor::~LinearPredictor() {}
+
+const char* LinearPredictor::GetName() const {
+  if (equation_order_ == EquationOrder::kFirstOrder)
+    return "LinearFirst";
+  else
+    return "LinearSecond";
+}
+
+void LinearPredictor::Reset() {
+  events_queue_.clear();
+}
+
+size_t LinearPredictor::NumberOfEventsNeeded() {
+  return static_cast<size_t>(equation_order_);
+}
+
+void LinearPredictor::Update(const InputData& new_input) {
+  // The last input received is at least kMaxDeltaTime away, we consider it
+  // is a new trajectory
+  if (!events_queue_.empty() &&
+      new_input.time_stamp - events_queue_.back().time_stamp > kMaxTimeDelta) {
+    Reset();
+  }
+
+  // Queue the new event and keep only the number of last events needed
+  events_queue_.push_back(new_input);
+  if (events_queue_.size() > static_cast<size_t>(equation_order_)) {
+    events_queue_.pop_front();
+  }
+
+  // Compute the current velocity
+  if (events_queue_.size() >= static_cast<size_t>(EquationOrder::kFirstOrder)) {
+    // Even if cur_velocity is empty the first time, last_velocity is only
+    // used when 3 events are in the queue, so it will be initialized
+    last_velocity_.set_x(cur_velocity_.x());
+    last_velocity_.set_y(cur_velocity_.y());
+
+    // We have at least 2 events to compute the current velocity
+    // Get delta time between the last 2 events
+    // Note: this delta time is also used to compute the acceleration term
+    events_dt_ = (events_queue_.at(events_queue_.size() - 1).time_stamp -
+                  events_queue_.at(events_queue_.size() - 2).time_stamp)
+                     .InMillisecondsF();
+
+    // Get delta pos between the last 2 events
+    gfx::Vector2dF delta_pos = events_queue_.at(events_queue_.size() - 1).pos -
+                               events_queue_.at(events_queue_.size() - 2).pos;
+
+    // Get the velocity
+    cur_velocity_.set_x(ScaleVector2d(delta_pos, 1.0 / events_dt_).x());
+    cur_velocity_.set_y(ScaleVector2d(delta_pos, 1.0 / events_dt_).y());
+  }
+}
+
+bool LinearPredictor::HasPrediction() const {
+  // Even if the current equation is second order, we still can predict a
+  // first order
+  return events_queue_.size() >=
+         static_cast<size_t>(EquationOrder::kFirstOrder);
+}
+
+bool LinearPredictor::GeneratePrediction(base::TimeTicks predict_time,
+                                         bool is_resampling,
+                                         InputData* result) const {
+  if (!HasPrediction())
+    return false;
+
+  float pred_dt =
+      (predict_time - events_queue_.back().time_stamp).InMillisecondsF();
+
+  // For resampling, we don't want to predict too far away,
+  // We then take the maximum of prediction time
+  if (is_resampling)
+    pred_dt = std::fmax(pred_dt, kMaxResampleTime.InMillisecondsF());
+
+  // Compute the prediction
+  // Note : a first order prediction is computed when only 2 events are
+  // available in the second order predictor
+  GeneratePredictionFirstOrder(pred_dt, result);
+  if (equation_order_ == EquationOrder::kSecondOrder &&
+      events_queue_.size() == static_cast<size_t>(EquationOrder::kSecondOrder))
+    // Add the acceleration term to the current result
+    GeneratePredictionSecondOrder(pred_dt, result);
+
+  return true;
+}
+
+void LinearPredictor::GeneratePredictionFirstOrder(float pred_dt,
+                                                   InputData* result) const {
+  result->pos =
+      events_queue_.back().pos + ScaleVector2d(cur_velocity_, pred_dt);
+}
+
+void LinearPredictor::GeneratePredictionSecondOrder(float pred_dt,
+                                                    InputData* result) const {
+  DCHECK(equation_order_ == EquationOrder::kSecondOrder);
+
+  // Compute the acceleration between the last two velocities
+  gfx::Vector2dF acceleration =
+      ScaleVector2d(cur_velocity_ - last_velocity_, 1.0 / events_dt_);
+  // Update the prediction
+  result->pos =
+      result->pos + ScaleVector2d(acceleration, 0.5 * pred_dt * pred_dt);
+}
+
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/events/blink/prediction/linear_predictor.h b/ui/events/blink/prediction/linear_predictor.h
new file mode 100644
index 0000000..7ec49fd
--- /dev/null
+++ b/ui/events/blink/prediction/linear_predictor.h
@@ -0,0 +1,78 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_BLINK_PREDICTION_LINEAR_PREDICTOR_H_
+#define UI_EVENTS_BLINK_PREDICTION_LINEAR_PREDICTOR_H_
+
+#include <deque>
+
+#include "ui/events/blink/prediction/input_predictor.h"
+
+namespace ui {
+
+// This class use a linear model for prediction
+// You can choose between a first order equation:
+// pred_p = last_p + velocity*pred_time
+// and a second order equation:
+// pred_p = last_p + velocity*pred_dt + 0.5*acceleration*pred_dt^2
+
+class LinearPredictor : public InputPredictor {
+ public:
+  // Used to dissociate the order of the equation used but also used to
+  // define the number of events needed by each model
+  enum class EquationOrder : size_t { kFirstOrder = 2, kSecondOrder = 3 };
+
+  explicit LinearPredictor(EquationOrder order);
+  ~LinearPredictor() override;
+
+  const char* GetName() const override;
+
+  // Reset the predictor to initial state.
+  void Reset() override;
+
+  // Store current input in queue.
+  void Update(const InputData& new_input) override;
+
+  // Return if there is enough data in the queue to generate prediction.
+  bool HasPrediction() const override;
+
+  // Generate the prediction based on stored points and given time_stamp.
+  // Return false if no prediction available.
+  bool GeneratePrediction(base::TimeTicks predict_time,
+                          bool is_resampling,
+                          InputData* result) const override;
+
+  // Return the number of events needed to compute a prediction
+  size_t NumberOfEventsNeeded();
+
+ private:
+  // Add the velocity term to the current prediction
+  void GeneratePredictionFirstOrder(float pred_dt, InputData* result) const;
+
+  // Add the acceleration term to the current prediction
+  void GeneratePredictionSecondOrder(float pred_dt, InputData* result) const;
+
+  // Store the last events received
+  std::deque<InputData> events_queue_;
+
+  // Store the equation order of the predictor
+  // The enum value also represents the number of events needed to compute the
+  // prediction
+  EquationOrder equation_order_;
+
+  // Store the current velocity of the 2 last events
+  gfx::Vector2dF cur_velocity_;
+
+  // Store the last velocity of the 2 last past events
+  gfx::Vector2dF last_velocity_;
+
+  // Store the current delta time between the last 2 events
+  float events_dt_;
+
+  DISALLOW_COPY_AND_ASSIGN(LinearPredictor);
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_BLINK_PREDICTION_LINEAR_PREDICTOR_H_
diff --git a/ui/events/blink/prediction/linear_predictor_unittest.cc b/ui/events/blink/prediction/linear_predictor_unittest.cc
new file mode 100644
index 0000000..57a4ff6
--- /dev/null
+++ b/ui/events/blink/prediction/linear_predictor_unittest.cc
@@ -0,0 +1,119 @@
+// Copyright 2019 The Chromium 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/events/blink/prediction/linear_predictor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/blink/prediction/input_predictor_unittest_helpers.h"
+
+namespace ui {
+namespace test {
+
+class LinearPredictorFirstOrderTest : public InputPredictorTest {
+ public:
+  explicit LinearPredictorFirstOrderTest() {}
+
+  void SetUp() override {
+    predictor_ = std::make_unique<ui::LinearPredictor>(
+        LinearPredictor::EquationOrder::kFirstOrder);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(LinearPredictorFirstOrderTest);
+};
+
+class LinearPredictorSecondOrderTest : public InputPredictorTest {
+ public:
+  explicit LinearPredictorSecondOrderTest() {}
+
+  void SetUp() override {
+    predictor_ = std::make_unique<ui::LinearPredictor>(
+        LinearPredictor::EquationOrder::kSecondOrder);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(LinearPredictorSecondOrderTest);
+};
+
+// Test if the output name of the predictor is taking account of the
+// equation order
+TEST_F(LinearPredictorFirstOrderTest, GetName) {
+  ASSERT_STREQ(predictor_->GetName(), "LinearFirst");
+}
+
+// Test if the output name of the predictor is taking account of the
+// equation order
+TEST_F(LinearPredictorSecondOrderTest, GetName) {
+  ASSERT_STREQ(predictor_->GetName(), "LinearSecond");
+}
+
+// Test that the number of events required to compute a prediction is correct
+TEST_F(LinearPredictorFirstOrderTest, ShouldHavePrediction) {
+  LinearPredictor predictor(LinearPredictor::EquationOrder::kFirstOrder);
+  size_t n = static_cast<size_t>(LinearPredictor::EquationOrder::kFirstOrder);
+  for (size_t i = 0; i < n; i++) {
+    EXPECT_FALSE(predictor.HasPrediction());
+    predictor.Update(InputPredictor::InputData());
+  }
+  EXPECT_TRUE(predictor.HasPrediction());
+  predictor.Reset();
+  EXPECT_FALSE(predictor.HasPrediction());
+}
+
+// Test that the number of events required to compute a prediction is correct
+TEST_F(LinearPredictorSecondOrderTest, ShouldHavePrediction) {
+  LinearPredictor predictor(LinearPredictor::EquationOrder::kSecondOrder);
+  size_t n1 = static_cast<size_t>(LinearPredictor::EquationOrder::kFirstOrder);
+  size_t n2 = static_cast<size_t>(LinearPredictor::EquationOrder::kSecondOrder);
+  for (size_t i = 0; i < n2; i++) {
+    if (i < n1)
+      EXPECT_FALSE(predictor.HasPrediction());
+    else
+      EXPECT_TRUE(predictor.HasPrediction());
+    predictor.Update(InputPredictor::InputData());
+  }
+  EXPECT_TRUE(predictor.HasPrediction());
+  predictor.Reset();
+  EXPECT_FALSE(predictor.HasPrediction());
+}
+
+TEST_F(LinearPredictorFirstOrderTest, PredictedValue) {
+  std::vector<double> x = {10, 20};
+  std::vector<double> y = {5, 25};
+  std::vector<double> t = {17, 33};
+  // Compensating 23 ms
+  // 1st order prediction at 33 + 23 = 56 ms
+
+  std::vector<double> pred_ts = {56};
+  std::vector<double> pred_x = {34.37};
+  std::vector<double> pred_y = {53.75};
+  ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+TEST_F(LinearPredictorSecondOrderTest, PredictedValue) {
+  std::vector<double> x = {0, 10, 20};
+  std::vector<double> y = {0, 5, 25};
+  std::vector<double> t = {0, 17, 33};
+  // Compensating 23 ms in both results
+  // 1st order prediction at 17 + 23 = 40 ms
+  // 2nd order prediction at 33 + 23 = 56 ms
+  std::vector<double> pred_ts = {40, 56};
+  std::vector<double> pred_x = {23.52, 34.98};
+  std::vector<double> pred_y = {11.76, 69.55};
+  ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+// Test constant position and constant velocity
+TEST_F(LinearPredictorSecondOrderTest, ConstantPositionAndVelocity) {
+  std::vector<double> x = {10, 10, 10, 10, 10};  // constant position
+  std::vector<double> y = {0, 5, 10, 15, 20};    // constant velocity
+  std::vector<double> t = {0, 7, 14, 21, 28};    // regular interval
+  // since velocity is constant, acceleration should be 0 which simplifies
+  // computations
+  // Compensating 10 ms in all results
+  std::vector<double> pred_ts = {17, 24, 31, 38};
+  std::vector<double> pred_x = {10, 10, 10, 10};
+  std::vector<double> pred_y = {12.14, 17.14, 22.14, 27.14};
+  ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+}  // namespace test
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/events/blink/scroll_predictor.cc b/ui/events/blink/scroll_predictor.cc
index fad9e0a..13735fc 100644
--- a/ui/events/blink/scroll_predictor.cc
+++ b/ui/events/blink/scroll_predictor.cc
@@ -11,6 +11,7 @@
 #include "ui/events/blink/prediction/empty_predictor.h"
 #include "ui/events/blink/prediction/kalman_predictor.h"
 #include "ui/events/blink/prediction/least_squares_predictor.h"
+#include "ui/events/blink/prediction/linear_predictor.h"
 
 using blink::WebInputEvent;
 using blink::WebGestureEvent;
@@ -24,6 +25,8 @@
 constexpr char kScrollPredictorTypeKalman[] = "kalman";
 constexpr char kScrollPredictorTypeKalmanTimeFiltered[] =
     "kalman_time_filtered";
+constexpr char kScrollPredictorLinearFirst[] = "linearFirst";
+constexpr char kScrollPredictorLinearSecond[] = "linearSecond";
 
 }  // namespace
 
@@ -46,6 +49,12 @@
   else if (predictor_type == kScrollPredictorTypeKalmanTimeFiltered)
     predictor_ =
         std::make_unique<KalmanPredictor>(true /* enable_time_filtering */);
+  else if (predictor_type == kScrollPredictorLinearFirst)
+    predictor_ = std::make_unique<LinearPredictor>(
+        LinearPredictor::EquationOrder::kFirstOrder);
+  else if (predictor_type == kScrollPredictorLinearSecond)
+    predictor_ = std::make_unique<LinearPredictor>(
+        LinearPredictor::EquationOrder::kSecondOrder);
   else
     predictor_ = std::make_unique<EmptyPredictor>();
 }
diff --git a/ui/file_manager/file_manager/background/js/test_util.js b/ui/file_manager/file_manager/background/js/test_util.js
index 686418d..00045a8 100644
--- a/ui/file_manager/file_manager/background/js/test_util.js
+++ b/ui/file_manager/file_manager/background/js/test_util.js
@@ -516,5 +516,15 @@
   chrome.fileManagerPrivate.getPreferences(callback);
 };
 
+/**
+ * Stubs out the formatVolume() function in fileManagerPrivate.
+ *
+ * @param {Window} contentWindow Window to be affected.
+ */
+test.util.sync.overrideFormat = contentWindow => {
+  contentWindow.chrome.fileManagerPrivate.formatVolume =
+      (volumeId, filesystem, volumeLabel) => {};
+};
+
 // Register the test utils.
 test.util.registerRemoteTestUtils();
diff --git a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
index 961f91b4..4a92d57 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
@@ -9,9 +9,9 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
 <dom-module id="files-format-dialog">
@@ -44,7 +44,8 @@
       }
     </style>
 
-    <cr-dialog id="dialog" show-close-button>
+    <cr-dialog id="dialog" show-close-button
+        close-text="[[i18n('CLOSE_LABEL')]]">
       <div slot="title">
         [[i18n('FORMAT_DIALOG_TITLE', volumeInfo_.label)]]
       </div>
diff --git a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js
index 0ea1c9ed..10cd378 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js
@@ -13,9 +13,10 @@
       value: '',
     },
 
+    /** @type {chrome.fileManagerPrivate.FormatFileSystemType} */
     formatType_: {
       type: String,
-      value: 'vfat',
+      value: chrome.fileManagerPrivate.FormatFileSystemType.VFAT,
     },
 
     space_used_: {
@@ -31,7 +32,8 @@
 
   /** @private */
   format_: function() {
-    // TODO(austinct): add file manager private call
+    chrome.fileManagerPrivate.formatVolume(
+        this.volumeInfo_.volumeId, this.formatType_, this.label_);
     this.$.dialog.close();
   },
 
@@ -41,7 +43,7 @@
    */
   showModal: function(volumeInfo) {
     this.label_ = '';
-    this.formatType_ = 'vfat';
+    this.formatType_ = chrome.fileManagerPrivate.FormatFileSystemType.VFAT;
     this.space_used_ = '';
 
     this.volumeInfo_ = volumeInfo;
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
index cdd0828a..35b362c9 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
@@ -143,12 +143,8 @@
 }
 
 #metadata-button {
-  --files-toggle-ripple: {
-    background-color: white;
-  };
-  --files-toggle-ripple-activated: {
-    opacity: 0.3;
-  };
+  --files-toggle-ripple-bg-color: white;
+  --files-toggle-ripple-activated-opacity: 0.3;
 }
 
 cr-button,
diff --git a/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.html b/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.html
index 27e4fa9..2e755c0 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.html
@@ -32,7 +32,6 @@
         margin: 0 auto;
         opacity: 0;
         width: 0;
-        @apply(--files-toggle-ripple);
       }
 
       .ripple.activated {
@@ -41,7 +40,6 @@
         height: 50%;
         opacity: var(--files-toggle-ripple-activated-opacity, 0.2);
         width: 50%;
-        @apply(--files-toggle-ripple-activated);
       }
     </style>
     <div class="ripple-container">
diff --git a/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js b/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js
index cbfde41..f14ea62 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js
@@ -8,9 +8,7 @@
  * You can change ripple color by the following CSS variable.
  *
  * files-toggle-ripple#my-button {
- *   --files-toggle-ripple: {
- *     background-color: black;
- *   }
+ *   --files-toggle-ripple-color: black;
  * }
  *
  * Ripple size of the activated state is same with the size of this element.
diff --git a/ui/file_manager/file_manager/foreground/elements/files_xf_elements.css b/ui/file_manager/file_manager/foreground/elements/files_xf_elements.css
index f392a20..e78e099 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_xf_elements.css
+++ b/ui/file_manager/file_manager/foreground/elements/files_xf_elements.css
@@ -19,7 +19,6 @@
     display: flex;
     flex-direction: row;
     max-width: 400px;
-    min-height: 68px;
 }
 
 .xf-button {
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_button.js b/ui/file_manager/file_manager/foreground/elements/xf_button.js
index 841dc260..191306f7 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_button.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_button.js
@@ -29,12 +29,8 @@
    */
   static html_() {
     return `<style>
-              button {
-                background: none;
-                border: none;
-                height: 36px;
-                width: 36px;
-                position: relative;
+              cr-icon-button {
+                margin-inline-start: 0px;
               }
 
               :host([data-category='pause']) {
@@ -69,16 +65,18 @@
               }
 
               :host([data-category='expand']) {
-                  animation: setexpand 150ms forwards;
+                  animation: setexpand 200ms forwards;
               }
 
               :host([data-category='collapse']) {
-                  animation: setcollapse 150ms forwards;
+                  animation: setcollapse 200ms forwards;
+              }
+              :host {
+                  width: 36px;
+                  position: relative;
               }
             </style>
-            <button>
-                <paper-ripple class="circle" center></paper-ripple>
-            </button>`;
+            <cr-icon-button></cr-icon-button>`;
   }
 }
 
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_circular_progress.js b/ui/file_manager/file_manager/foreground/elements/xf_circular_progress.js
index 405346d..9bd73d3 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_circular_progress.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_circular_progress.js
@@ -67,7 +67,7 @@
                         stroke-width: 3px;
                     }
                     text {
-                        font: Roboto Bold 14pt;
+                        font: bold 14px Roboto;
                         fill: rgb(26, 115, 232);
                     }
                 </style>
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_display_panel.js b/ui/file_manager/file_manager/foreground/elements/xf_display_panel.js
index 580beff..d4fcf3f2 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_display_panel.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_display_panel.js
@@ -79,10 +79,19 @@
               }
               /* Limit to 3 visible progress panels before scroll */
               #panels {
-                  max-height: calc(92px * 3);
+                  max-height: calc(192px + 28px);
               }
               xf-panel-item:not(:only-child) {
-                --multi-progress-height: 92px;
+                --progress-height: 64px;
+              }
+              xf-panel-item:not(:only-child):first-child {
+                --progress-padding-top: 14px;
+              }
+              xf-panel-item:not(:only-child):last-child {
+                --progress-padding-bottom: 14px;
+              }
+              xf-panel-item:only-child {
+                --progress-height: 68px;
               }
               @keyframes setcollapse {
                 0% {
@@ -91,12 +100,12 @@
                   opacity: 0;
                 }
                 75% {
-                  max-height: calc(92px * 3);;
+                  max-height: calc(192px + 28px);
                   max-width: 400px;
                   opacity: 0;
                 }
                 100% {
-                  max-height: calc(92px * 3);;
+                  max-height: calc(192px + 28px);
                   max-width: 400px;
                   opacity: 1;
                 }
@@ -104,12 +113,12 @@
 
               @keyframes setexpand {
                 0% {
-                  max-height: calc(92px * 3);;
+                  max-height: calc(192px + 28px);
                   max-width: 400px;
                   opacity: 1;
                 }
                 25% {
-                  max-height: calc(92px * 3);;
+                  max-height: calc(192px + 28px);
                   max-width: 400px;
                   opacity: 0;
                 }
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
index 5ab026c..94922ac6 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
@@ -43,7 +43,12 @@
               href='foreground/elements/files_xf_elements.css'>
             <style>
               :host([panel-type='0']) .xf-panel-item {
-                height: var(--multi-progress-height);
+                height: var(--progress-height);
+                padding-top: var(--progress-padding-top);
+                padding-bottom: var(--progress-padding-bottom);
+              }
+              :not(:host([panel-type='0'])) .xf-panel-item {
+                height: 68px;
               }
             </style>
             <div class='xf-panel-item'>
@@ -109,11 +114,13 @@
         primaryButton = document.createElement('xf-button');
         primaryButton.id = 'primary-action';
         primaryButton.dataset.category = 'pause';
+        primaryButton.setAttribute('aria-label', '$i18n{PAUSE_LABEL}');
         buttonSpacer.insertAdjacentElement('beforebegin', primaryButton);
 
         secondaryButton = document.createElement('xf-button');
         secondaryButton.id = 'secondary-action';
         secondaryButton.dataset.category = 'cancel';
+        secondaryButton.setAttribute('aria-label', '$i18n{CANCEL_LABEL}');
         buttonSpacer.insertAdjacentElement('afterend', secondaryButton);
         break;
       case this.panelTypeSummary:
@@ -121,6 +128,7 @@
         primaryButton = document.createElement('xf-button');
         primaryButton.id = 'primary-action';
         primaryButton.dataset.category = 'expand';
+        primaryButton.setAttribute('aria-label', '$i18n{EXPAND_LABEL}');
         buttonSpacer.insertAdjacentElement('afterend', primaryButton);
         break;
       case this.panelTypeDone:
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index ec2a06e..bacb7be 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -922,9 +922,7 @@
 
     this.ui_.initAdditionalUI(
         assertInstanceof(table, FileTable), assertInstanceof(grid, FileGrid),
-        new LocationLine(
-            queryRequiredElement('#location-breadcrumbs', dom),
-            this.volumeManager_));
+        this.volumeManager_);
 
     // Handle UI events.
     this.fileBrowserBackground_.progressCenter.addPanel(
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 1dfc73b7..e0ef527f 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
@@ -647,7 +647,9 @@
         fileManager.ui.confirmDialog.show(
             loadTimeData.getString('FORMATTING_WARNING'),
             chrome.fileManagerPrivate.formatVolume.bind(
-                null, volumeInfo.volumeId),
+                null, volumeInfo.volumeId,
+                chrome.fileManagerPrivate.FormatFileSystemType.VFAT,
+                'UNTITLED'),
             null, null);
       }
     }
diff --git a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
index 980a146..f786ee6 100644
--- a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
@@ -154,11 +154,7 @@
    * @private
    */
   updateCurrentDirectoryButtons_() {
-    const volumeInfo = this.directoryModel_.getCurrentVolumeInfo();
-    this.refreshCommand_.disabled = !!volumeInfo && volumeInfo.watchable;
-    this.refreshCommand_.setHidden(
-        volumeInfo && volumeInfo.watchable ||
-        this.directoryModel_.getFileListSelection().getCheckSelectMode());
+    this.updateRefreshCommand_();
 
     const currentDirectory = this.directoryModel_.getCurrentDirEntry();
     const locationInfo = currentDirectory &&
@@ -166,12 +162,22 @@
     this.readOnlyIndicator_.hidden = !(locationInfo && locationInfo.isReadOnly);
   }
 
+  /** @private */
+  updateRefreshCommand_() {
+    const volumeInfo = this.directoryModel_.getCurrentVolumeInfo();
+    this.refreshCommand_.disabled = !!volumeInfo && volumeInfo.watchable;
+    this.refreshCommand_.setHidden(
+        volumeInfo && volumeInfo.watchable ||
+        this.directoryModel_.getFileListSelection().getCheckSelectMode());
+  }
+
   /**
    * Handles selection's change event to update the UI.
    * @private
    */
   onSelectionChanged_() {
     const selection = this.selectionHandler_.selection;
+    this.updateRefreshCommand_();
 
     // Update the label "x files selected." on the header.
     let text;
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
index a44a209..f71aecf 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -411,20 +411,22 @@
    *
    * @param {!FileTable} table
    * @param {!FileGrid} grid
-   * @param {!LocationLine} locationLine
+   * @param {!VolumeManager} volumeManager
    */
-  initAdditionalUI(table, grid, locationLine) {
+  initAdditionalUI(table, grid, volumeManager) {
     // List container.
     this.listContainer = new ListContainer(
         queryRequiredElement('#list-container', this.element), table, grid);
 
+    // Location line.
+    this.locationLine = new LocationLine(
+        queryRequiredElement('#location-breadcrumbs', this.element),
+        volumeManager, this.listContainer);
+
     // Splitter.
     this.decorateSplitter_(
         queryRequiredElement('#navigation-list-splitter', this.element));
 
-    // Location line.
-    this.locationLine = locationLine;
-
     // Init context menus.
     cr.ui.contextMenuHandler.setContextMenu(grid, this.fileContextMenu);
     cr.ui.contextMenuHandler.setContextMenu(table.list, this.fileContextMenu);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/location_line.js b/ui/file_manager/file_manager/foreground/js/ui/location_line.js
index 15fc8c9..77abad9 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/location_line.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/location_line.js
@@ -9,12 +9,15 @@
   /**
    * @param {!Element} breadcrumbs Container element for breadcrumbs.
    * @param {!VolumeManager} volumeManager Volume manager.
+   * @param {!ListContainer} listContainer List Container.
    */
-  constructor(breadcrumbs, volumeManager) {
+  constructor(breadcrumbs, volumeManager, listContainer) {
     super();
 
     this.breadcrumbs_ = breadcrumbs;
     this.volumeManager_ = volumeManager;
+    /** @private {!ListContainer} */
+    this.listContainer_ = listContainer;
     this.entry_ = null;
     this.components_ = [];
   }
@@ -360,12 +363,9 @@
    * @private
    */
   onClick_(index, event) {
-    if (index >= this.components_.length - 1) {
-      return;
-    }
+    let button = event.target;
 
     // Remove 'focused' state from the clicked button.
-    let button = event.target;
     while (button && !button.classList.contains('breadcrumb-path')) {
       button = button.parentElement;
     }
@@ -373,6 +373,13 @@
       button.blur();
     }
 
+    // Last breadcrumb component is the currently selected folder, skip
+    // navigation and just move the focus to file list.
+    if (index >= this.components_.length - 1) {
+      this.listContainer_.focus();
+      return;
+    }
+
     const pathComponent = this.components_[index];
     pathComponent.resolveEntry().then(entry => {
       const pathClickEvent = new Event('pathclick');
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index 0aaa307..ac02e998 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -36,15 +36,11 @@
       <style>
         #search-box cr-input {
           --cr-input-background-color: transparent;
+          --cr-input-border-bottom: 1px solid rgba(255, 255, 255, 0.5);
+          --cr-input-border-radius: 0;
           --cr-input-color: white;
           --cr-input-focus-color: white;
-          --cr-input-container: {
-            border-radius: 0;
-          }
           --cr-input-error-display: none;
-          --cr-input-input: {
-            border-bottom: 1px solid rgba(255, 255, 255, 0.5);
-          }
           --cr-input-padding-end: 20px;
           --cr-input-padding-start: 0;
           --cr-input-placeholder-color: rgba(255, 255, 255, 0.5);
@@ -55,14 +51,9 @@
         }
 
         .dialog-footer cr-input {
-          --cr-input-container: {
-            border-radius: 0;
-          }
+          --cr-input-border-bottom: 1px solid var(--paper-grey-800);
+          --cr-input-border-radius: 0;
           --cr-input-error-display: none;
-          --cr-input-input: {
-            background-color: transparent;
-            border-bottom: 1px solid var(--paper-grey-800);
-          }
           --cr-input-padding-end: 0;
           --cr-input-padding-start: 0;
           --cr-input-padding-bottom: 2px;
@@ -74,19 +65,13 @@
         }
 
         body.check-select .dialog-header files-toggle-ripple {
-          --files-toggle-ripple: {
-            background-color: black;
-          };
-          --files-toggle-ripple-activated: {
-            background-color: black;
-            opacity: 0.08;
-          };
+          --files-toggle-ripple-bg-color: black;
+          --files-toggle-ripple-activated-bg-color: black;
+          --files-toggle-ripple-activated-opacity: 0.08;
         }
 
         body.check-select .dialog-header files-ripple {
-          --files-ripple: {
-            background-color: black;
-          }
+          --files-ripple-bg-color: black;
         }
 
         files-format-dialog {
diff --git a/ui/file_manager/gallery/gallery.html b/ui/file_manager/gallery/gallery.html
index 6d8f2a6a..60e1f05 100644
--- a/ui/file_manager/gallery/gallery.html
+++ b/ui/file_manager/gallery/gallery.html
@@ -60,28 +60,14 @@
         box-shadow: none;
       }
       files-toggle-ripple {
-        --files-toggle-ripple-activated: {
-          opacity: 0.4;
-        };
+        --files-toggle-ripple-activated-opacity: 0.4;
       }
 
       cr-input {
-        --cr-form-field-label: {
-          color: rgba(255, 255, 255, 0.54);
-          display: block;
-          font-size: var(--cr-form-field-label-font-size);
-          letter-spacing: 0.4px;
-          line-height: var(--cr-form-field-label-font-size);
-          margin-bottom: 0;
-        }
         --cr-input-background-color: transparent;
+        --cr-input-border-bottom: 1px solid var(--paper-grey-800);
+        --cr-input-border-radius: 0;
         --cr-input-color: white;
-        --cr-input-container: {
-          border-radius: 0;
-        }
-        --cr-input-input: {
-          border-bottom: 1px solid var(--paper-grey-800);
-        }
         --cr-input-padding-end: 0;
         --cr-input-padding-start: 0;
         --cr-input-error-display: none;
diff --git a/ui/file_manager/integration_tests/file_manager/breadcrumbs.js b/ui/file_manager/integration_tests/file_manager/breadcrumbs.js
index a66721b..8116235 100644
--- a/ui/file_manager/integration_tests/file_manager/breadcrumbs.js
+++ b/ui/file_manager/integration_tests/file_manager/breadcrumbs.js
@@ -27,4 +27,31 @@
     chrome.test.assertEq(
         1, await getUserActionCount('FileBrowser.ClickBreadcrumbs'));
   };
+
+  /**
+   * Test that clicking on the current directory in the Breadcrumbs doesn't
+   * leave the focus in the breadcrumbs. crbug.com/944022
+   */
+  testcase.breadcrumbsLeafNoFocus = async () => {
+    const appId =
+        await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.photos], []);
+
+    // Navigate to Downloads/photos.
+    await remoteCall.navigateWithDirectoryTree(
+        appId, RootPath.DOWNLOADS_PATH + '/photos', 'My files/Downloads');
+
+    // Focus and click on "photos" in the breadcrumbs.
+    const leafBreadCrumb =
+        '#location-breadcrumbs .breadcrumb-path.breadcrumb-last';
+    chrome.test.assertTrue(
+        !!await remoteCall.callRemoteTestUtil('focus', appId, [leafBreadCrumb]),
+        'focus failed: ' + leafBreadCrumb);
+    await remoteCall.waitAndClickElement(appId, leafBreadCrumb);
+
+    // Wait focus to not be on breadcrumb clicked.
+    await remoteCall.waitForElementLost(appId, leafBreadCrumb + ':focus');
+
+    // Focus should be on file list.
+    await remoteCall.waitForElement(appId, '#file-list:focus');
+  };
 })();
diff --git a/ui/file_manager/integration_tests/file_manager/context_menu.js b/ui/file_manager/integration_tests/file_manager/context_menu.js
index 09ae5d21..fa07d20 100644
--- a/ui/file_manager/integration_tests/file_manager/context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/context_menu.js
@@ -632,6 +632,10 @@
   // Wait for the context menu to appear.
   await remoteCall.waitForElement(appId, '#file-context-menu:not([hidden])');
 
+  // Wait for the menu item to get focus.
+  await remoteCall.waitForElement(
+      appId, '#file-context-menu cr-menu-item:focus');
+
   // Check currently focused element.
   const focusedElement =
       await remoteCall.callRemoteTestUtil('getActiveElement', appId, []);
diff --git a/ui/file_manager/integration_tests/file_manager/format_dialog.js b/ui/file_manager/integration_tests/file_manager/format_dialog.js
index 8daeac0d..40c72d5 100644
--- a/ui/file_manager/integration_tests/file_manager/format_dialog.js
+++ b/ui/file_manager/integration_tests/file_manager/format_dialog.js
@@ -9,6 +9,7 @@
  */
 async function openFormatDialog(usbLabel) {
   const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+  await remoteCall.callRemoteTestUtil('overrideFormat', appId, []);
 
   // Focus the directory tree.
   chrome.test.assertTrue(
diff --git a/ui/file_manager/integration_tests/file_manager/delete.js b/ui/file_manager/integration_tests/file_manager/toolbar.js
similarity index 62%
rename from ui/file_manager/integration_tests/file_manager/delete.js
rename to ui/file_manager/integration_tests/file_manager/toolbar.js
index e9adfdd..09682cf 100644
--- a/ui/file_manager/integration_tests/file_manager/delete.js
+++ b/ui/file_manager/integration_tests/file_manager/toolbar.js
@@ -1,10 +1,11 @@
 // 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.
+
 /**
  * Tests that the Delete menu item is disabled if no entry is selected.
  */
-testcase.deleteMenuItemNoEntrySelected = async () => {
+testcase.toolbarDeleteWithMenuItemNoEntrySelected = async () => {
   const contextMenu = '#file-context-menu:not([hidden])';
 
   const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
@@ -26,7 +27,7 @@
 /**
  * Tests deleting an entry using the toolbar.
  */
-testcase.deleteEntryWithToolbar = async () => {
+testcase.toolbarDeleteEntry = async () => {
   const beforeDeletion = TestEntryInfo.getExpectedRows([
     ENTRIES.photos,
     ENTRIES.hello,
@@ -72,3 +73,38 @@
   await remoteCall.waitForFiles(
       appId, afterDeletion, {ignoreLastModifiedTime: true});
 };
+
+/**
+ * Tests that refresh button hides in selection mode.
+ *
+ * Non-watchable volumes display refresh button so users can refresh the file
+ * list content. However this button should be hidden when entering the
+ * selection mode. crbug.com/978383
+ *
+ */
+testcase.toolbarRefreshButtonWithSelection = async () => {
+  // Enable media views which are non-watchable.
+  await sendTestMessage({name: 'mountMediaView'});
+
+  // Add some content to media view "Images".
+  await addEntries(['media_view_images'], [ENTRIES.desktop]);
+
+  // Open files app.
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
+
+  // Navigate to Images media view.
+  await remoteCall.waitAndClickElement(
+      appId, '#directory-tree [entry-label="Images"]');
+  await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Images');
+
+  // Check that refresh button is visible.
+  await remoteCall.waitForElement(appId, '#refresh-button:not([hidden])');
+
+  // Ctrl+A to enter selection mode.
+  const ctrlA = ['#file-list', 'a', true, false, false];
+  await remoteCall.fakeKeyDown(appId, ...ctrlA);
+
+  // Check that the button should be hidden.
+  await remoteCall.waitForElement(appId, '#refresh-button[hidden]');
+};
diff --git a/ui/file_manager/integration_tests/file_manager_test_manifest.json b/ui/file_manager/integration_tests/file_manager_test_manifest.json
index 451cee9..924332c 100644
--- a/ui/file_manager/integration_tests/file_manager_test_manifest.json
+++ b/ui/file_manager/integration_tests/file_manager_test_manifest.json
@@ -18,7 +18,6 @@
       "file_manager/copy_between_windows.js",
       "file_manager/create_new_folder.js",
       "file_manager/crostini.js",
-      "file_manager/delete.js",
       "file_manager/directory_tree_context_menu.js",
       "file_manager/drive_specific.js",
       "file_manager/file_dialog.js",
@@ -51,6 +50,7 @@
       "file_manager/suggest_app_dialog.js",
       "file_manager/tab_index.js",
       "file_manager/tasks.js",
+      "file_manager/toolbar.js",
       "file_manager/transfer.js",
       "file_manager/traverse.js",
       "file_manager/zip_files.js"
diff --git a/ui/file_manager/video_player/video_player.html b/ui/file_manager/video_player/video_player.html
index a22b079..b56bc0df 100644
--- a/ui/file_manager/video_player/video_player.html
+++ b/ui/file_manager/video_player/video_player.html
@@ -25,15 +25,9 @@
   <custom-style>
     <style>
       files-icon-button {
-        --files-ripple: {
-          background-color: black;
-        };
-        --files-toggle-ripple: {
-          background-color: black;
-        };
-        --files-toggle-ripple-activated: {
-          background-color: black;
-        };
+        --files-ripple-bg-color: black;
+        --files-toggle-ripple-bg-color: black;
+        --files-toggle-ripple-activated-bg-color: black;
       }
 
       cr-slider {
diff --git a/ui/gfx/geometry/vector3d_f.h b/ui/gfx/geometry/vector3d_f.h
index 32b7265..8238a16 100644
--- a/ui/gfx/geometry/vector3d_f.h
+++ b/ui/gfx/geometry/vector3d_f.h
@@ -87,6 +87,10 @@
   return lhs.x() == rhs.x() && lhs.y() == rhs.y() && lhs.z() == rhs.z();
 }
 
+inline bool operator!=(const Vector3dF& lhs, const Vector3dF& rhs) {
+  return !(lhs == rhs);
+}
+
 inline Vector3dF operator-(const Vector3dF& v) {
   return Vector3dF(-v.x(), -v.y(), -v.z());
 }
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 86f9f71..342df0e4 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -1913,6 +1913,33 @@
     std::vector<internal::TextRunHarfBuzz*> runs) {
   TRACE_EVENT1("ui", "RenderTextHarfBuzz::ShapeRuns", "run_count", runs.size());
 
+  // Runs with a single newline character should be skipped since they can't be
+  // rendered (see http://crbug/680430). The following code sets the runs
+  // shaping output to report report the missing glyph and removes the runs from
+  // the vector of runs to shape. The newline character doesn't have a
+  // glyph, which otherwise forces this function to go through the expensive
+  // font fallbacks before reporting a missing glyph (see http://crbug/972090).
+  std::vector<internal::TextRunHarfBuzz*> need_shaping_runs;
+  for (internal::TextRunHarfBuzz*& run : runs) {
+    if (run->range.length() == 1 && text[run->range.start()] == '\n') {
+      // Newline runs can't be shaped. Shape this run as if the glyph is
+      // missing.
+      run->font_params = font_params;
+      run->shape.missing_glyph_count = 1;
+      run->shape.glyph_count = 1;
+      run->shape.glyphs.resize(run->shape.glyph_count);
+      run->shape.glyph_to_char.resize(run->shape.glyph_count);
+      run->shape.positions.resize(run->shape.glyph_count);
+      run->shape.width = glyph_width_for_test_;
+    } else {
+      // This run needs shaping.
+      need_shaping_runs.push_back(run);
+    }
+  }
+  runs.swap(need_shaping_runs);
+  if (runs.empty())
+    return;
+
   const Font& primary_font = font_list().GetPrimaryFont();
 
   for (const Font& font : font_list().GetFonts()) {
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 743a275f..4c8c6a0 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -4102,6 +4102,24 @@
   }
 }
 
+TEST_F(RenderTextTest, Multiline_ZeroWidthNewline) {
+  RenderTextHarfBuzz* render_text = GetRenderText();
+  render_text->SetMultiline(true);
+
+  const base::string16 text(UTF8ToUTF16("\n\n"));
+  render_text->SetText(text);
+  test_api()->EnsureLayout();
+  EXPECT_EQ(3u, test_api()->lines().size());
+  for (const auto& line : test_api()->lines()) {
+    EXPECT_EQ(0, line.size.width());
+    EXPECT_LT(0, line.size.height());
+  }
+
+  const internal::TextRunList* run_list = GetHarfBuzzRunList();
+  EXPECT_EQ(2U, run_list->size());
+  EXPECT_EQ(0, run_list->width());
+}
+
 TEST_F(RenderTextTest, Multiline_GetLineContainingCaret) {
   struct {
     const SelectionModel caret;
@@ -4438,6 +4456,36 @@
   EXPECT_EQ("[0][1->2][3->4]", GetRunListStructureString());
 }
 
+TEST_F(RenderTextTest, HarfBuzz_BreakRunsByNewline) {
+  RenderText* render_text = GetRenderText();
+  render_text->SetMultiline(true);
+  render_text->SetText(WideToUTF16(L"x\ny"));
+  test_api()->EnsureLayout();
+  EXPECT_EQ(ToString16Vec({"x", "\n", "y"}), GetRunListStrings());
+  EXPECT_EQ("[0][1][2]", GetRunListStructureString());
+
+  // Validate that the character newline is an unknown glyph
+  // (see http://crbug/972090 and http://crbug/680430).
+  const internal::TextRunList* run_list = GetHarfBuzzRunList();
+  ASSERT_EQ(3U, run_list->size());
+  EXPECT_EQ(0U, run_list->runs()[0]->CountMissingGlyphs());
+  EXPECT_EQ(1U, run_list->runs()[1]->CountMissingGlyphs());
+  EXPECT_EQ(0U, run_list->runs()[2]->CountMissingGlyphs());
+
+  SkScalar x_width =
+      run_list->runs()[0]->GetGlyphWidthForCharRange(Range(0, 1));
+  EXPECT_GT(x_width, 0);
+
+  // Newline character must have a width of zero.
+  SkScalar newline_width =
+      run_list->runs()[1]->GetGlyphWidthForCharRange(Range(1, 2));
+  EXPECT_EQ(newline_width, 0);
+
+  SkScalar y_width =
+      run_list->runs()[2]->GetGlyphWidthForCharRange(Range(2, 3));
+  EXPECT_GT(y_width, 0);
+}
+
 TEST_F(RenderTextTest, HarfBuzz_BreakRunsByEmojiVariationSelectors) {
   constexpr int kGlyphWidth = 30;
   SetGlyphWidth(30);
diff --git a/ui/gl/direct_composition_child_surface_win.cc b/ui/gl/direct_composition_child_surface_win.cc
index 27fa93848..15cfd1fa 100644
--- a/ui/gl/direct_composition_child_surface_win.cc
+++ b/ui/gl/direct_composition_child_surface_win.cc
@@ -143,14 +143,22 @@
 }
 
 bool DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
-  // At the end we'll MakeCurrent the same surface but its handle will be
-  // |default_surface_|.
-  ui::ScopedReleaseCurrent release_current;
+  EGLSurface egl_surface = real_surface_;
+  real_surface_ = nullptr;
 
-  if (real_surface_) {
-    eglDestroySurface(GetDisplay(), real_surface_);
-    real_surface_ = nullptr;
-  }
+  // We make current with the same surface (could be the parent), but its
+  // handle has changed to |default_surface_|.
+  gl::GLContext* context = gl::GLContext::GetCurrent();
+  DCHECK(context);
+  gl::GLSurface* surface = gl::GLSurface::GetCurrent();
+  DCHECK(surface);
+  bool result = context->MakeCurrent(surface);
+  // If MakeCurrent fails (probably lost device), we'll want to return failure,
+  // but we still want to reset the rest of the state for consistency.
+  DLOG_IF(ERROR, !result) << "Failed to make current in ReleaseDrawTexture";
+
+  if (egl_surface)
+    eglDestroySurface(GetDisplay(), egl_surface);
 
   if (dcomp_surface_.Get() == g_current_surface)
     g_current_surface = nullptr;
@@ -198,7 +206,7 @@
       }
     }
   }
-  return true;
+  return result;
 }
 
 void DirectCompositionChildSurfaceWin::Destroy() {
@@ -316,10 +324,6 @@
     return false;
   }
 
-  // At the end we'll MakeCurrent the same surface but its handle will be
-  // |real_surface_|.
-  ui::ScopedReleaseCurrent release_current;
-
   DXGI_FORMAT dxgi_format = ColorSpaceUtils::GetDXGIFormat(color_space_);
 
   bool force_swap_chain = UseSwapChainFrameStatistics();
@@ -426,6 +430,17 @@
     return false;
   }
 
+  // We make current with the same surface (could be the parent), but its
+  // handle has changed to |real_surface_|.
+  gl::GLContext* context = gl::GLContext::GetCurrent();
+  DCHECK(context);
+  gl::GLSurface* surface = gl::GLSurface::GetCurrent();
+  DCHECK(surface);
+  if (!context->MakeCurrent(surface)) {
+    DLOG(ERROR) << "Failed to make current in SetDrawRectangle";
+    return false;
+  }
+
   return true;
 }
 
diff --git a/ui/gl/gl_context.h b/ui/gl/gl_context.h
index 8170446..2ea0a9a 100644
--- a/ui/gl/gl_context.h
+++ b/ui/gl/gl_context.h
@@ -12,7 +12,7 @@
 #include "base/cancelable_callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "build/build_config.h"
 #include "ui/gfx/extension_set.h"
 #include "ui/gl/gl_export.h"
diff --git a/ui/gl/gl_image.cc b/ui/gl/gl_image.cc
index 0ab4af7..c54f118 100644
--- a/ui/gl/gl_image.cc
+++ b/ui/gl/gl_image.cc
@@ -34,4 +34,8 @@
 }
 #endif
 
+bool GLImage::HasMutableState() const {
+  return true;
+}
+
 }  // namespace gl
diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h
index a6b1e47..3949a09 100644
--- a/ui/gl/gl_image.h
+++ b/ui/gl/gl_image.h
@@ -138,6 +138,10 @@
   enum class Type { NONE, MEMORY, IOSURFACE, DXGI_IMAGE, DXGI_SWAP_CHAIN };
   virtual Type GetType() const;
 
+  // Workaround for StreamTexture which must be re-copied on each access.
+  // TODO(ericrk): Remove this once SharedImage transition is complete.
+  virtual bool HasMutableState() const;
+
  protected:
   virtual ~GLImage() {}
 
diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h
index 77f4f53..f77ddc9 100644
--- a/ui/gl/gl_surface.h
+++ b/ui/gl/gl_surface.h
@@ -76,12 +76,15 @@
     HDR10,
   };
 
+  // Resizes the surface, returning success. If failed, it is possible that the
+  // context is no longer current.
   virtual bool Resize(const gfx::Size& size,
                       float scale_factor,
                       ColorSpace color_space,
                       bool has_alpha);
 
-  // Recreate the surface without changing the size.
+  // Recreate the surface without changing the size, returning success. If
+  // failed, it is possible that the context is no longer current.
   virtual bool Recreate();
 
   // Unschedule the CommandExecutor and return true to abort the processing of
@@ -99,7 +102,8 @@
       base::OnceCallback<void(const gfx::PresentationFeedback& feedback)>;
 
   // Swaps front and back buffers. This has no effect for off-screen
-  // contexts.
+  // contexts. If it returns SWAP_FAILED, it is possible that the context is no
+  // longer current.
   virtual gfx::SwapResult SwapBuffers(PresentationCallback callback) = 0;
 
   // Get the size of the surface.
@@ -141,12 +145,14 @@
   virtual void SwapBuffersAsync(SwapCompletionCallback completion_callback,
                                 PresentationCallback presentation_callback);
 
-  // Swap buffers with content bounds.
+  // Swap buffers with content bounds. If it returns SWAP_FAILED, it is possible
+  // that the context is no longer current.
   virtual gfx::SwapResult SwapBuffersWithBounds(
       const std::vector<gfx::Rect>& rects,
       PresentationCallback callback);
 
-  // Copy part of the backbuffer to the frontbuffer.
+  // Copy part of the backbuffer to the frontbuffer. If it returns SWAP_FAILED,
+  // it is possible that the context is no longer current.
   virtual gfx::SwapResult PostSubBuffer(int x,
                                         int y,
                                         int width,
@@ -167,7 +173,8 @@
 
   // Show overlay planes but don't swap the front and back buffers. This acts
   // like SwapBuffers from the point of view of the client, but is cheaper when
-  // overlays account for all the damage.
+  // overlays account for all the damage. If it returns SWAP_FAILED,
+  // it is possible that the context is no longer current.
   virtual gfx::SwapResult CommitOverlayPlanes(PresentationCallback callback);
 
   // Show overlay planes but don't swap the front and back buffers. On some
@@ -252,6 +259,8 @@
 
   virtual bool ScheduleDCLayer(const ui::DCRendererLayerParams& params);
 
+  // Enables or disables DC layers, returning success. If failed, it is possible
+  // that the context is no longer current.
   virtual bool SetEnableDCLayers(bool enable);
 
   virtual bool IsSurfaceless() const;
@@ -268,7 +277,8 @@
 
   virtual bool SupportsProtectedVideo() const;
 
-  // Set the rectangle that will be drawn into on the surface.
+  // Set the rectangle that will be drawn into on the surface, returning
+  // success. If failed, it is possible that the context is no longer current.
   virtual bool SetDrawRectangle(const gfx::Rect& rect);
 
   // This is the amount by which the scissor and viewport rectangles should be
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 79a0a35b..19c747b 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -1480,26 +1480,44 @@
   if (size == GetSize())
     return true;
   size_ = size;
-  {
-    ui::ScopedReleaseCurrent release_current;
-    Destroy();
-    if (!Initialize(format_)) {
-      LOG(ERROR) << "Failed to resize window.";
-      return false;
-    }
+  GLContext* context = GLContext::GetCurrent();
+  DCHECK(context);
+  GLSurface* surface = GLSurface::GetCurrent();
+  DCHECK(surface);
+  // Current surface may not be |this| if it is wrapped, but it should point to
+  // the same handle.
+  DCHECK_EQ(surface->GetHandle(), GetHandle());
+  context->ReleaseCurrent(surface);
+  Destroy();
+  if (!Initialize(format_)) {
+    LOG(ERROR) << "Failed to resize window.";
+    return false;
+  }
+  if (!context->MakeCurrent(surface)) {
+    LOG(ERROR) << "Failed to make current in NativeViewGLSurfaceEGL::Resize";
+    return false;
   }
   SetVSyncEnabled(vsync_enabled_);
   return true;
 }
 
 bool NativeViewGLSurfaceEGL::Recreate() {
-  {
-    ui::ScopedReleaseCurrent release_current;
-    Destroy();
-    if (!Initialize(format_)) {
-      LOG(ERROR) << "Failed to create surface.";
-      return false;
-    }
+  GLContext* context = GLContext::GetCurrent();
+  DCHECK(context);
+  GLSurface* surface = GLSurface::GetCurrent();
+  DCHECK(surface);
+  // Current surface may not be |this| if it is wrapped, but it should point to
+  // the same handle.
+  DCHECK_EQ(surface->GetHandle(), GetHandle());
+  context->ReleaseCurrent(surface);
+  Destroy();
+  if (!Initialize(format_)) {
+    LOG(ERROR) << "Failed to create surface.";
+    return false;
+  }
+  if (!context->MakeCurrent(surface)) {
+    LOG(ERROR) << "Failed to make current in NativeViewGLSurfaceEGL::Recreate";
+    return false;
   }
   SetVSyncEnabled(vsync_enabled_);
   return true;
@@ -1835,13 +1853,25 @@
 
   size_ = size;
 
-  ui::ScopedReleaseCurrent release_current;
+  GLContext* context = GLContext::GetCurrent();
+  DCHECK(context);
+  GLSurface* surface = GLSurface::GetCurrent();
+  DCHECK(surface);
+  // Current surface may not be |this| if it is wrapped, but it should point to
+  // the same handle.
+  DCHECK_EQ(surface->GetHandle(), GetHandle());
+  context->ReleaseCurrent(surface);
 
   if (!Initialize(format_)) {
     LOG(ERROR) << "Failed to resize pbuffer.";
     return false;
   }
 
+  if (!context->MakeCurrent(surface)) {
+    LOG(ERROR) << "Failed to make current in PbufferGLSurfaceEGL::Resize";
+    return false;
+  }
+
   return true;
 }
 
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc
index cc92f623..b6028631 100644
--- a/ui/gl/gl_surface_glx.cc
+++ b/ui/gl/gl_surface_glx.cc
@@ -13,7 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
@@ -279,7 +279,7 @@
       XDestroyWindow(vsync_thread_->GetDisplay(), window_);
   }
 
-  base::CancellationFlag* cancel_vsync_flag() { return &cancel_vsync_flag_; }
+  base::AtomicFlag* cancel_vsync_flag() { return &cancel_vsync_flag_; }
 
   base::Lock* vsync_lock() { return &vsync_lock_; }
 
@@ -348,7 +348,7 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
-  base::CancellationFlag cancel_vsync_flag_;
+  base::AtomicFlag cancel_vsync_flag_;
   base::Lock vsync_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
@@ -420,7 +420,7 @@
   // Raw pointers to sync primitives owned by the shim_.
   // These will only be referenced before we post a task to destroy
   // the shim_, so they are safe to access.
-  base::CancellationFlag* cancel_vsync_flag_;
+  base::AtomicFlag* cancel_vsync_flag_;
   base::Lock* vsync_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
diff --git a/ui/gl/scoped_make_current.cc b/ui/gl/scoped_make_current.cc
index 72eaec367..c6bc9f3 100644
--- a/ui/gl/scoped_make_current.cc
+++ b/ui/gl/scoped_make_current.cc
@@ -30,18 +30,4 @@
   }
 }
 
-ScopedReleaseCurrent::ScopedReleaseCurrent()
-    : previous_context_(gl::GLContext::GetCurrent()),
-      previous_surface_(gl::GLSurface::GetCurrent()) {
-  if (previous_context_) {
-    DCHECK(previous_surface_);
-    previous_context_->ReleaseCurrent(previous_surface_.get());
-  }
-}
-
-ScopedReleaseCurrent::~ScopedReleaseCurrent() {
-  if (previous_context_)
-    previous_context_->MakeCurrent(previous_surface_.get());
-}
-
 }  // namespace ui
diff --git a/ui/gl/scoped_make_current.h b/ui/gl/scoped_make_current.h
index d850f86..4ff16e3 100644
--- a/ui/gl/scoped_make_current.h
+++ b/ui/gl/scoped_make_current.h
@@ -37,20 +37,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedMakeCurrent);
 };
 
-// This behaves similarly to ScopedMakeCurrent, except that it releases the
-// current context on creation and restores it on destruction.
-class GL_EXPORT ScopedReleaseCurrent {
- public:
-  ScopedReleaseCurrent();
-  ~ScopedReleaseCurrent();
-
- private:
-  scoped_refptr<gl::GLContext> previous_context_;
-  scoped_refptr<gl::GLSurface> previous_surface_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedReleaseCurrent);
-};
-
 }  // namespace ui
 
 #endif  // UI_GL_SCOPED_MAKE_CURRENT_H_
diff --git a/ui/message_center/views/notification_header_view.cc b/ui/message_center/views/notification_header_view.cc
index a64de04e..dc04686e 100644
--- a/ui/message_center/views/notification_header_view.cc
+++ b/ui/message_center/views/notification_header_view.cc
@@ -26,6 +26,7 @@
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/painter.h"
+#include "ui/views/view_class_properties.h"
 
 namespace message_center {
 
@@ -162,7 +163,7 @@
           .WithOrder(2);
 
   auto* layout = SetLayoutManager(std::make_unique<views::FlexLayout>());
-  layout->SetDefaultChildMargins(kHeaderSpacing);
+  layout->SetDefault(views::kMarginsKey, kHeaderSpacing);
   layout->SetInteriorMargin(kHeaderOuterPadding);
   layout->SetCollapseMargins(true);
 
@@ -194,15 +195,15 @@
   app_name_view_ = create_label();
   // Explicitly disable multiline to support proper text elision for URLs.
   app_name_view_->SetMultiLine(false);
+  app_name_view_->SetProperty(views::kFlexBehaviorKey, kAppNameFlex);
   AddChildView(app_name_view_);
-  layout->SetFlexForView(app_name_view_, kAppNameFlex);
 
   // Detail views which will be hidden in settings mode.
   detail_views_ = new views::View();
   auto* detail_layout =
       detail_views_->SetLayoutManager(std::make_unique<views::FlexLayout>());
   detail_layout->SetCollapseMargins(true);
-  detail_layout->SetDefaultChildMargins(kHeaderSpacing);
+  detail_layout->SetDefault(views::kMarginsKey, kHeaderSpacing);
   AddChildView(detail_views_);
 
   // Summary text divider
@@ -240,8 +241,8 @@
   views::View* spacer = new views::View;
   spacer->SetPreferredSize(
       gfx::Size(kControlButtonSpacing, kInnerHeaderHeight));
+  spacer->SetProperty(views::kFlexBehaviorKey, kSpacerFlex);
   AddChildView(spacer);
-  layout->SetFlexForView(spacer, kSpacerFlex);
 
   SetAccentColor(accent_color_);
   SetPreferredSize(gfx::Size(kNotificationWidth, kHeaderHeight));
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index 18d581a..7a32271 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -351,6 +351,7 @@
   set_ink_drop_base_color(SK_ColorBLACK);
   set_ink_drop_visible_opacity(kActionButtonInkDropRippleVisibleOpacity);
   SetEnabledTextColors(kActionButtonTextColor);
+  SetElideBehavior(gfx::NO_ELIDE);
   SetBorder(views::CreateEmptyBorder(kActionButtonPadding));
   SetMinSize(kActionButtonMinSize);
   SetFocusForPlatform();
diff --git a/ui/native_theme/caption_style_mac.mm b/ui/native_theme/caption_style_mac.mm
index f4c39b1..c39d09d 100644
--- a/ui/native_theme/caption_style_mac.mm
+++ b/ui/native_theme/caption_style_mac.mm
@@ -12,6 +12,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/native_theme/caption_style.h"
 
@@ -124,6 +125,9 @@
 
 // static
 base::Optional<CaptionStyle> CaptionStyle::FromSystemSettings() {
+  if (!base::FeatureList::IsEnabled(features::kSystemCaptionStyle))
+    return base::nullopt;
+
   CaptionStyle style;
 
   style.text_color = GetMAForegroundColorAndOpacityAsCSSColor();
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 0770d56..55e26c2 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -177,6 +177,7 @@
     "common/drm_overlay_manager_unittest.cc",
     "common/drm_util_unittest.cc",
     "gpu/drm_overlay_validator_unittest.cc",
+    "gpu/drm_thread_unittest.cc",
     "gpu/drm_window_unittest.cc",
     "gpu/hardware_display_controller_unittest.cc",
     "gpu/hardware_display_plane_manager_unittest.cc",
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index 8f8cda3..38928f3 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -19,7 +19,6 @@
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
 #include "ui/ozone/common/linux/gbm_device.h"
-#include "ui/ozone/common/linux/gbm_wrapper.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
@@ -84,43 +83,26 @@
   *out_framebuffer = std::move(framebuffer);
 }
 
-class GbmDeviceGenerator : public DrmDeviceGenerator {
- public:
-  GbmDeviceGenerator() {}
-  ~GbmDeviceGenerator() override {}
-
-  // DrmDeviceGenerator:
-  scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
-                                        base::File file,
-                                        bool is_primary_device) override {
-    auto gbm = CreateGbmDevice(file.GetPlatformFile());
-    if (!gbm) {
-      PLOG(ERROR) << "Unable to initialize GBM for " << path.value();
-      return nullptr;
-    }
-
-    auto drm = base::MakeRefCounted<DrmDevice>(
-        path, std::move(file), is_primary_device, std::move(gbm));
-    if (!drm->Initialize())
-      return nullptr;
-    return drm;
-  }
-
- private:
-
-  DISALLOW_COPY_AND_ASSIGN(GbmDeviceGenerator);
-};
-
 }  // namespace
 
+DrmThread::TaskInfo::TaskInfo(base::OnceClosure task, base::WaitableEvent* done)
+    : task(std::move(task)), done(done) {}
+
+DrmThread::TaskInfo::TaskInfo(TaskInfo&& other) = default;
+
+DrmThread::TaskInfo::~TaskInfo() = default;
+
 DrmThread::DrmThread() : base::Thread("DrmThread"), weak_ptr_factory_(this) {}
 
 DrmThread::~DrmThread() {
   Stop();
 }
 
-void DrmThread::Start(base::OnceClosure binding_completer) {
+void DrmThread::Start(base::OnceClosure binding_completer,
+                      std::unique_ptr<DrmDeviceGenerator> device_generator) {
   complete_early_binding_requests_ = std::move(binding_completer);
+  device_generator_ = std::move(device_generator);
+
   base::Thread::Options thread_options;
   thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
   thread_options.priority = base::ThreadPriority::DISPLAY;
@@ -129,11 +111,22 @@
     LOG(FATAL) << "Failed to create DRM thread";
 }
 
-void DrmThread::Init() {
-  device_manager_.reset(
-      new DrmDeviceManager(std::make_unique<GbmDeviceGenerator>()));
-  screen_manager_.reset(new ScreenManager());
+void DrmThread::RunTaskAfterWindowReady(gfx::AcceleratedWidget window,
+                                        base::OnceClosure task,
+                                        base::WaitableEvent* done) {
+  if (!device_manager_->GetDrmDevices().empty() &&
+      window <= last_created_window_) {
+    std::move(task).Run();
+    if (done)
+      done->Signal();
+    return;
+  }
+  pending_tasks_[window].emplace_back(std::move(task), done);
+}
 
+void DrmThread::Init() {
+  device_manager_.reset(new DrmDeviceManager(std::move(device_generator_)));
+  screen_manager_.reset(new ScreenManager());
   display_manager_.reset(
       new DrmGpuDisplayManager(screen_manager_.get(), device_manager_.get()));
 
@@ -249,10 +242,16 @@
 }
 
 void DrmThread::CreateWindow(gfx::AcceleratedWidget widget) {
+  DCHECK_GT(widget, last_created_window_);
+  last_created_window_ = widget;
+
   std::unique_ptr<DrmWindow> window(
       new DrmWindow(widget, device_manager_.get(), screen_manager_.get()));
   window->Initialize();
   screen_manager_->AddWindow(widget, std::move(window));
+
+  // There might be tasks that were waiting for |widget| to become available.
+  ProcessPendingTasks();
 }
 
 void DrmThread::DestroyWindow(gfx::AcceleratedWidget widget) {
@@ -293,6 +292,11 @@
           screen_manager_->GetWindow(widget)->TestPageFlip(params)));
 }
 
+void DrmThread::GetDeviceCursor(
+    ozone::mojom::DeviceCursorAssociatedRequest request) {
+  cursor_bindings_.AddBinding(this, std::move(request));
+}
+
 void DrmThread::RefreshNativeDisplays(
     base::OnceCallback<void(MovableDisplaySnapshots)> callback) {
   std::move(callback).Run(display_manager_->GetDisplays());
@@ -325,6 +329,9 @@
 
 void DrmThread::AddGraphicsDevice(const base::FilePath& path, base::File file) {
   device_manager_->AddDrmDevice(path, std::move(file));
+
+  // There might be tasks that were blocked on a DrmDevice becoming available.
+  ProcessPendingTasks();
 }
 
 void DrmThread::RemoveGraphicsDevice(const base::FilePath& path) {
@@ -358,24 +365,25 @@
   display_manager_->SetGammaCorrection(display_id, degamma_lut, gamma_lut);
 }
 
-void DrmThread::StartDrmDevice(StartDrmDeviceCallback callback) {
-  // We currently assume that |Init| always succeeds so return true to indicate
-  // when the DRM thread has completed launching.  In particular, the invocation
-  // of the callback in the client triggers the invocation of DRM thread
-  // readiness observers.
-  std::move(callback).Run(true);
-}
-
-// DrmThread requires a BindingSet instead of a simple Binding because it will
-// be used from multiple threads in multiple processes.
-void DrmThread::AddBindingCursorDevice(
-    ozone::mojom::DeviceCursorRequest request) {
-  cursor_bindings_.AddBinding(this, std::move(request));
-}
-
 void DrmThread::AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request) {
   TRACE_EVENT0("drm", "DrmThread::AddBindingDrmDevice");
   drm_bindings_.AddBinding(this, std::move(request));
 }
 
+void DrmThread::ProcessPendingTasks() {
+  DCHECK(!device_manager_->GetDrmDevices().empty());
+
+  auto it = pending_tasks_.begin();
+  for (; it != pending_tasks_.end() && it->first <= last_created_window_;
+       ++it) {
+    for (auto& task_info : it->second) {
+      std::move(task_info.task).Run();
+      if (task_info.done)
+        task_info.done->Signal();
+    }
+  }
+
+  pending_tasks_.erase(pending_tasks_.begin(), it);
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.h b/ui/ozone/platform/drm/gpu/drm_thread.h
index 211ec46e..b874b2b 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread.h
@@ -13,12 +13,13 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/associated_binding_set.h"
 #include "ui/gfx/native_pixmap_handle.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/vsync_provider.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 #include "ui/ozone/platform/drm/common/display_types.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/public/interfaces/device_cursor.mojom.h"
 #include "ui/ozone/public/interfaces/drm_device.mojom.h"
 #include "ui/ozone/public/swap_completion_callback.h"
@@ -61,7 +62,14 @@
   DrmThread();
   ~DrmThread() override;
 
-  void Start(base::OnceClosure binding_completer);
+  void Start(base::OnceClosure binding_completer,
+             std::unique_ptr<DrmDeviceGenerator> device_generator);
+
+  // Runs |task| once a DrmDevice is registered and |window| was created via
+  // CreateWindow(). |done| will be signaled if it's not null.
+  void RunTaskAfterWindowReady(gfx::AcceleratedWidget window,
+                               base::OnceClosure task,
+                               base::WaitableEvent* done);
 
   // Must be called on the DRM thread. All methods for use from the GPU thread.
   // DrmThreadProxy (on GPU)thread) is the client for these methods.
@@ -79,7 +87,6 @@
                               std::unique_ptr<GbmBuffer>* buffer,
                               scoped_refptr<DrmFramebuffer>* framebuffer);
   void SetClearOverlayCacheCallback(base::RepeatingClosure callback);
-  void AddBindingCursorDevice(ozone::mojom::DeviceCursorRequest request);
   void AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request);
 
   // DrmWindowProxy (on GPU thread) is the client for these methods.
@@ -91,7 +98,6 @@
   void IsDeviceAtomic(gfx::AcceleratedWidget widget, bool* is_atomic);
 
   // ozone::mojom::DrmDevice
-  void StartDrmDevice(StartDrmDeviceCallback callback) override;
   void CreateWindow(gfx::AcceleratedWidget widget) override;
   void DestroyWindow(gfx::AcceleratedWidget widget) override;
   void SetWindowBounds(gfx::AcceleratedWidget widget,
@@ -129,6 +135,8 @@
       base::OnceCallback<void(gfx::AcceleratedWidget,
                               const OverlaySurfaceCandidateList&,
                               const OverlayStatusList&)> callback) override;
+  void GetDeviceCursor(
+      ozone::mojom::DeviceCursorAssociatedRequest cursor) override;
 
   // ozone::mojom::DeviceCursor
   void SetCursor(gfx::AcceleratedWidget widget,
@@ -142,26 +150,50 @@
   void Init() override;
 
  private:
+  struct TaskInfo {
+    base::OnceClosure task;
+    base::WaitableEvent* done;
+
+    TaskInfo(base::OnceClosure task, base::WaitableEvent* done);
+    TaskInfo(TaskInfo&& other);
+    ~TaskInfo();
+  };
+
   void OnPlanesReadyForPageFlip(gfx::AcceleratedWidget widget,
                                 SwapCompletionOnceCallback submission_callback,
                                 PresentationOnceCallback presentation_callback,
                                 std::vector<DrmOverlayPlane> planes);
 
+  // Called when a DrmDevice or DrmWindow is created. Runs tasks that are now
+  // unblocked.
+  void ProcessPendingTasks();
+
   std::unique_ptr<DrmDeviceManager> device_manager_;
   std::unique_ptr<ScreenManager> screen_manager_;
   std::unique_ptr<DrmGpuDisplayManager> display_manager_;
 
   base::OnceClosure complete_early_binding_requests_;
 
-  // The mojo implementation requires a BindingSet because the DrmThread serves
-  // requests from two different client threads.
-  mojo::BindingSet<ozone::mojom::DeviceCursor> cursor_bindings_;
+  // The mojo implementation requires an AssociatedBindingSet because the
+  // DrmThread serves requests from two different client threads.
+  mojo::AssociatedBindingSet<ozone::mojom::DeviceCursor> cursor_bindings_;
 
   // The mojo implementation of DrmDevice requires a BindingSet because the
   // DrmThread services requests from different client threads when operating in
   // mus mode
   mojo::BindingSet<ozone::mojom::DrmDevice> drm_bindings_;
 
+  // The AcceleratedWidget from the last call to CreateWindow.
+  gfx::AcceleratedWidget last_created_window_ = gfx::kNullAcceleratedWidget;
+
+  // The tasks that are blocked on a DrmDevice and a certain AcceleratedWidget
+  // becoming available.
+  base::flat_map<gfx::AcceleratedWidget, std::vector<TaskInfo>> pending_tasks_;
+
+  // Holds the DrmDeviceGenerator that DrmDeviceManager will use. Will be passed
+  // on to DrmDeviceManager after the thread starts.
+  std::unique_ptr<DrmDeviceGenerator> device_generator_;
+
   base::WeakPtrFactory<DrmThread> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DrmThread);
diff --git a/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc b/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
index 34d4412..789b8907a 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
@@ -8,6 +8,9 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "ui/ozone/common/linux/gbm_wrapper.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h"
 #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h"
 #include "ui/ozone/platform/drm/gpu/gbm_pixmap.h"
@@ -15,6 +18,36 @@
 
 namespace ui {
 
+namespace {
+
+class GbmDeviceGenerator : public DrmDeviceGenerator {
+ public:
+  GbmDeviceGenerator() {}
+  ~GbmDeviceGenerator() override {}
+
+  // DrmDeviceGenerator:
+  scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
+                                        base::File file,
+                                        bool is_primary_device) override {
+    auto gbm = CreateGbmDevice(file.GetPlatformFile());
+    if (!gbm) {
+      PLOG(ERROR) << "Unable to initialize GBM for " << path.value();
+      return nullptr;
+    }
+
+    auto drm = base::MakeRefCounted<DrmDevice>(
+        path, std::move(file), is_primary_device, std::move(gbm));
+    if (!drm->Initialize())
+      return nullptr;
+    return drm;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GbmDeviceGenerator);
+};
+
+}  // namespace
+
 DrmThreadProxy::DrmThreadProxy() = default;
 
 DrmThreadProxy::~DrmThreadProxy() = default;
@@ -26,7 +59,8 @@
 }
 
 void DrmThreadProxy::StartDrmThread(base::OnceClosure binding_drainer) {
-  drm_thread_.Start(std::move(binding_drainer));
+  drm_thread_.Start(std::move(binding_drainer),
+                    std::make_unique<GbmDeviceGenerator>());
 }
 
 std::unique_ptr<DrmWindowProxy> DrmThreadProxy::CreateDrmWindowProxy(
@@ -43,10 +77,13 @@
                                   scoped_refptr<DrmFramebuffer>* framebuffer) {
   DCHECK(drm_thread_.task_runner())
       << "no task runner! in DrmThreadProxy::CreateBuffer";
+  base::OnceClosure task =
+      base::BindOnce(&DrmThread::CreateBuffer, base::Unretained(&drm_thread_),
+                     widget, size, format, usage, flags, buffer, framebuffer);
   PostSyncTask(
       drm_thread_.task_runner(),
-      base::BindOnce(&DrmThread::CreateBuffer, base::Unretained(&drm_thread_),
-                     widget, size, format, usage, flags, buffer, framebuffer));
+      base::BindOnce(&DrmThread::RunTaskAfterWindowReady,
+                     base::Unretained(&drm_thread_), widget, std::move(task)));
 }
 
 void DrmThreadProxy::CreateBufferFromHandle(
@@ -56,10 +93,13 @@
     gfx::NativePixmapHandle handle,
     std::unique_ptr<GbmBuffer>* buffer,
     scoped_refptr<DrmFramebuffer>* framebuffer) {
-  PostSyncTask(drm_thread_.task_runner(),
-               base::BindOnce(&DrmThread::CreateBufferFromHandle,
-                              base::Unretained(&drm_thread_), widget, size,
-                              format, std::move(handle), buffer, framebuffer));
+  base::OnceClosure task = base::BindOnce(
+      &DrmThread::CreateBufferFromHandle, base::Unretained(&drm_thread_),
+      widget, size, format, std::move(handle), buffer, framebuffer);
+  PostSyncTask(
+      drm_thread_.task_runner(),
+      base::BindOnce(&DrmThread::RunTaskAfterWindowReady,
+                     base::Unretained(&drm_thread_), widget, std::move(task)));
 }
 
 void DrmThreadProxy::SetClearOverlayCacheCallback(
@@ -78,20 +118,14 @@
     const std::vector<OverlaySurfaceCandidate>& candidates,
     OverlayCapabilitiesCallback callback) {
   DCHECK(drm_thread_.task_runner());
+  base::OnceClosure task = base::BindOnce(
+      &DrmThread::CheckOverlayCapabilities, base::Unretained(&drm_thread_),
+      widget, candidates, CreateSafeOnceCallback(std::move(callback)));
 
   drm_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&DrmThread::CheckOverlayCapabilities,
-                     base::Unretained(&drm_thread_), widget, candidates,
-                     CreateSafeOnceCallback(std::move(callback))));
-}
-
-void DrmThreadProxy::AddBindingCursorDevice(
-    ozone::mojom::DeviceCursorRequest request) {
-  drm_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&DrmThread::AddBindingCursorDevice,
-                     base::Unretained(&drm_thread_), std::move(request)));
+      FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady,
+                                base::Unretained(&drm_thread_), widget,
+                                std::move(task), nullptr));
 }
 
 void DrmThreadProxy::AddBindingDrmDevice(
diff --git a/ui/ozone/platform/drm/gpu/drm_thread_proxy.h b/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
index a32b70dd..f3c54595 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
@@ -68,7 +68,6 @@
       const std::vector<OverlaySurfaceCandidate>& candidates,
       OverlayCapabilitiesCallback callback);
 
-  void AddBindingCursorDevice(ozone::mojom::DeviceCursorRequest request);
   void AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request);
 
  private:
diff --git a/ui/ozone/platform/drm/gpu/drm_thread_unittest.cc b/ui/ozone/platform/drm/gpu/drm_thread_unittest.cc
new file mode 100644
index 0000000..bc08d78
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_thread_unittest.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 "ui/ozone/platform/drm/gpu/drm_thread.h"
+#include "base/bind_helpers.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
+#include "ui/ozone/platform/drm/gpu/mock_gbm_device.h"
+
+namespace ui {
+
+namespace {
+
+class FakeDrmDeviceGenerator : public DrmDeviceGenerator {
+  // DrmDeviceGenerator:
+  scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
+                                        base::File file,
+                                        bool is_primary_device) override {
+    auto gbm_device = std::make_unique<MockGbmDevice>();
+    return base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device));
+  }
+};
+
+void StubTask() {}
+
+void StubTaskWithDoneFeedback(bool* done) {
+  *done = true;
+}
+
+}  // namespace
+
+class DrmThreadTest : public testing::Test {
+ protected:
+  // Overridden from testing::Test
+  void SetUp() override {
+    drm_thread_.Start(base::DoNothing(),
+                      std::make_unique<FakeDrmDeviceGenerator>());
+    drm_thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&DrmThread::AddBindingDrmDevice,
+                                  base::Unretained(&drm_thread_),
+                                  mojo::MakeRequest(&drm_device_ptr_)));
+    drm_thread_.FlushForTesting();
+  }
+
+  std::unique_ptr<base::WaitableEvent> PostStubTaskWithWaitableEvent(
+      gfx::AcceleratedWidget window) {
+    base::OnceClosure task = base::BindOnce(StubTask);
+    auto event = std::make_unique<base::WaitableEvent>(
+        base::WaitableEvent::ResetPolicy::AUTOMATIC,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    drm_thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady,
+                                  base::Unretained(&drm_thread_), window,
+                                  std::move(task), event.get()));
+    return event;
+  }
+
+  void PostStubTask(gfx::AcceleratedWidget window, bool* done) {
+    *done = false;
+    base::OnceClosure task = base::BindOnce(StubTaskWithDoneFeedback, done);
+    drm_thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady,
+                                  base::Unretained(&drm_thread_), window,
+                                  std::move(task), nullptr));
+  }
+
+  void AddGraphicsDevice() {
+    base::FilePath file_path("/dev/null");
+    base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_WRITE |
+                                   base::File::FLAG_READ);
+    drm_device_ptr_->AddGraphicsDevice(file_path, std::move(file));
+  }
+
+  base::test::ScopedTaskEnvironment env_;
+  DrmThread drm_thread_;
+  ozone::mojom::DrmDevicePtr drm_device_ptr_;
+};
+
+TEST_F(DrmThreadTest, RunTaskAfterWindowReady) {
+  bool called1 = false, called2 = false;
+  gfx::AcceleratedWidget widget1 = 1, widget2 = 2;
+
+  // Post a task not blocked on any window. It should still block on a graphics
+  // device becoming available.
+  PostStubTask(gfx::kNullAcceleratedWidget, &called1);
+  drm_thread_.FlushForTesting();
+  EXPECT_FALSE(called1);
+
+  // Add the graphics device. The task should run.
+  AddGraphicsDevice();
+  drm_thread_.FlushForTesting();
+  ASSERT_TRUE(called1);
+
+  // Now that a graphics device is available, further tasks that don't block on
+  // any window should execute immediately.
+  PostStubTask(gfx::kNullAcceleratedWidget, &called1);
+  drm_thread_.FlushForTesting();
+  ASSERT_TRUE(called1);
+
+  // Post a task blocked on |widget1|. It shouldn't run.
+  PostStubTask(widget1, &called1);
+  drm_thread_.FlushForTesting();
+  ASSERT_FALSE(called1);
+
+  // Post two tasks blocked on |widget2|, one with a WaitableEvent and one
+  // without. They shouldn't run.
+  std::unique_ptr<base::WaitableEvent> event =
+      PostStubTaskWithWaitableEvent(widget2);
+  PostStubTask(widget2, &called2);
+  drm_thread_.FlushForTesting();
+  ASSERT_FALSE(event->IsSignaled());
+  ASSERT_FALSE(called2);
+
+  // Now create |widget1|. The first task should run.
+  drm_device_ptr_->CreateWindow(widget1);
+  drm_thread_.FlushForTesting();
+  ASSERT_TRUE(called1);
+  ASSERT_FALSE(event->IsSignaled());
+  ASSERT_FALSE(called2);
+
+  // Now that |widget1| is created. any further task depending on it should run
+  // immediately.
+  PostStubTask(widget1, &called1);
+  drm_thread_.FlushForTesting();
+  ASSERT_TRUE(called1);
+  ASSERT_FALSE(event->IsSignaled());
+  ASSERT_FALSE(called2);
+
+  // Destroy |widget1| and post a task blocked on it. The task should still run
+  // immediately even though the window is destroyed.
+  drm_device_ptr_->DestroyWindow(widget1);
+  PostStubTask(widget1, &called1);
+  drm_thread_.FlushForTesting();
+  ASSERT_TRUE(called1);
+  ASSERT_FALSE(event->IsSignaled());
+  ASSERT_FALSE(called2);
+
+  // Create |widget2|. The two blocked tasks should run.
+  drm_device_ptr_->CreateWindow(widget2);
+  drm_thread_.FlushForTesting();
+  ASSERT_TRUE(event->IsSignaled());
+  ASSERT_TRUE(called2);
+
+  // Post another task blocked on |widget1| with a WaitableEvent. It should run
+  // immediately.
+  event = PostStubTaskWithWaitableEvent(widget1);
+  drm_thread_.FlushForTesting();
+  ASSERT_TRUE(event->IsSignaled());
+
+  // Post another task blocked on |widget2| with a WaitableEvent. It should run
+  // immediately.
+  event = PostStubTaskWithWaitableEvent(widget2);
+  drm_thread_.FlushForTesting();
+  ASSERT_TRUE(event->IsSignaled());
+
+  // Destroy |widget2| to avoid failures during tear down.
+  drm_device_ptr_->DestroyWindow(widget2);
+  drm_thread_.FlushForTesting();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_window_proxy.cc b/ui/ozone/platform/drm/gpu/drm_window_proxy.cc
index 45ed5b6..517fee2 100644
--- a/ui/ozone/platform/drm/gpu/drm_window_proxy.cc
+++ b/ui/ozone/platform/drm/gpu/drm_window_proxy.cc
@@ -30,21 +30,26 @@
     std::vector<DrmOverlayPlane> planes,
     SwapCompletionOnceCallback submission_callback,
     PresentationOnceCallback presentation_callback) {
+  base::OnceClosure task = base::BindOnce(
+      &DrmThread::SchedulePageFlip, base::Unretained(drm_thread_), widget_,
+      base::Passed(&planes),
+      CreateSafeOnceCallback(std::move(submission_callback)),
+      CreateSafeOnceCallback(std::move(presentation_callback)));
   drm_thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&DrmThread::SchedulePageFlip,
-                     base::Unretained(drm_thread_), widget_,
-                     base::Passed(&planes),
-                     CreateSafeOnceCallback(std::move(submission_callback)),
-                     CreateSafeOnceCallback(std::move(presentation_callback))));
+      FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady,
+                                base::Unretained(drm_thread_), widget_,
+                                std::move(task), nullptr));
 }
 
 bool DrmWindowProxy::SupportsGpuFences() const {
   bool is_atomic = false;
+  base::OnceClosure task =
+      base::BindOnce(&DrmThread::IsDeviceAtomic, base::Unretained(drm_thread_),
+                     widget_, &is_atomic);
   PostSyncTask(
       drm_thread_->task_runner(),
-      base::BindOnce(&DrmThread::IsDeviceAtomic, base::Unretained(drm_thread_),
-                     widget_, &is_atomic));
+      base::BindOnce(&DrmThread::RunTaskAfterWindowReady,
+                     base::Unretained(drm_thread_), widget_, std::move(task)));
   return is_atomic && !base::CommandLine::ForCurrentProcess()->HasSwitch(
                           switches::kDisableExplicitDmaFences);
 }
diff --git a/ui/ozone/platform/drm/gpu/proxy_helpers.cc b/ui/ozone/platform/drm/gpu/proxy_helpers.cc
index 603b3bc..c65c5f6 100644
--- a/ui/ozone/platform/drm/gpu/proxy_helpers.cc
+++ b/ui/ozone/platform/drm/gpu/proxy_helpers.cc
@@ -6,28 +6,15 @@
 
 #include <utility>
 
-#include "base/synchronization/waitable_event.h"
-
 namespace ui {
 
-namespace {
-
-void OnRunPostedTaskAndSignal(base::OnceClosure callback,
-                              base::WaitableEvent* wait) {
-  std::move(callback).Run();
-  wait->Signal();
-}
-
-}  // namespace
-
 void PostSyncTask(
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    base::OnceClosure callback) {
+    base::OnceCallback<void(base::WaitableEvent*)> callback) {
   base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                            base::WaitableEvent::InitialState::NOT_SIGNALED);
   bool success = task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(OnRunPostedTaskAndSignal, std::move(callback), &wait));
+      FROM_HERE, base::BindOnce(std::move(callback), &wait));
   if (success)
     wait.Wait();
 }
diff --git a/ui/ozone/platform/drm/gpu/proxy_helpers.h b/ui/ozone/platform/drm/gpu/proxy_helpers.h
index b037713..d5be9bd 100644
--- a/ui/ozone/platform/drm/gpu/proxy_helpers.h
+++ b/ui/ozone/platform/drm/gpu/proxy_helpers.h
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
 
 namespace ui {
@@ -31,7 +32,7 @@
 // executing.
 void PostSyncTask(
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    base::OnceClosure callback);
+    base::OnceCallback<void(base::WaitableEvent*)> callback);
 
 // Creates a RepeatingCallback that will run |callback| on the calling thread.
 // Useful when posting a task on a different thread and expecting a callback
diff --git a/ui/ozone/platform/drm/host/drm_device_connector.cc b/ui/ozone/platform/drm/host/drm_device_connector.cc
index 3ab9667..f107e9f 100644
--- a/ui/ozone/platform/drm/host/drm_device_connector.cc
+++ b/ui/ozone/platform/drm/host/drm_device_connector.cc
@@ -69,18 +69,14 @@
   binder_callback_ = std::move(binder);
   if (am_running_in_ws_mode()) {
     ui::ozone::mojom::DrmDevicePtr drm_device_ptr_ui, drm_device_ptr_ws;
-    ui::ozone::mojom::DeviceCursorPtr cursor_ptr_ws, cursor_ptr_io;
 
     BindInterfaceDrmDevice(&drm_device_ptr_ui);
     BindInterfaceDrmDevice(&drm_device_ptr_ws);
-    BindInterfaceDeviceCursor(&cursor_ptr_ws);
-    BindInterfaceDeviceCursor(&cursor_ptr_io);
 
     ws_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&HostDrmDevice::OnGpuServiceLaunched, host_drm_device_,
-                       std::move(drm_device_ptr_ws), std::move(cursor_ptr_ws),
-                       std::move(cursor_ptr_io)));
+                       std::move(drm_device_ptr_ws)));
 
     ui_runner->PostTask(
         FROM_HERE,
@@ -89,17 +85,13 @@
 
   } else {
     ui::ozone::mojom::DrmDevicePtr drm_device_ptr_ui;
-    ui::ozone::mojom::DeviceCursorPtr cursor_ptr_ui, cursor_ptr_io;
 
     BindInterfaceDrmDevice(&drm_device_ptr_ui);
-    BindInterfaceDeviceCursor(&cursor_ptr_ui);
-    BindInterfaceDeviceCursor(&cursor_ptr_io);
 
     ui_runner->PostTask(
         FROM_HERE,
         base::BindOnce(&HostDrmDevice::OnGpuServiceLaunched, host_drm_device_,
-                       std::move(drm_device_ptr_ui), std::move(cursor_ptr_ui),
-                       std::move(cursor_ptr_io)));
+                       std::move(drm_device_ptr_ui)));
 
     ui_runner->PostTask(
         FROM_HERE,
@@ -123,14 +115,4 @@
   }
 }
 
-void DrmDeviceConnector::BindInterfaceDeviceCursor(
-    ui::ozone::mojom::DeviceCursorPtr* cursor_ptr) const {
-  if (connector_) {
-    connector_->BindInterface(service_name_, cursor_ptr);
-  } else {
-    auto request = mojo::MakeRequest(cursor_ptr);
-    BindInterfaceInGpuProcess(std::move(request), binder_callback_);
-  }
-}
-
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_device_connector.h b/ui/ozone/platform/drm/host/drm_device_connector.h
index 851208b4..5532c8a 100644
--- a/ui/ozone/platform/drm/host/drm_device_connector.h
+++ b/ui/ozone/platform/drm/host/drm_device_connector.h
@@ -48,10 +48,6 @@
   void BindInterfaceDrmDevice(
       ui::ozone::mojom::DrmDevicePtr* drm_device_ptr) const;
 
-  // BindInterface arranges for the cursor_ptr to be wired up.
-  void BindInterfaceDeviceCursor(
-      ui::ozone::mojom::DeviceCursorPtr* cursor_ptr) const;
-
   // BindableNow returns true if this DrmDeviceConnector is capable of binding a
   // mojo endpoint for the DrmDevice service.
   bool BindableNow() const { return !!connector_; }
diff --git a/ui/ozone/platform/drm/host/host_cursor_proxy.cc b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
index d2950b9..73ffc64 100644
--- a/ui/ozone/platform/drm/host/host_cursor_proxy.cc
+++ b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
@@ -13,8 +13,8 @@
 
 // We assume that this is invoked only on the Mus/UI thread.
 HostCursorProxy::HostCursorProxy(
-    ui::ozone::mojom::DeviceCursorPtr main_cursor_ptr,
-    ui::ozone::mojom::DeviceCursorPtr evdev_cursor_ptr)
+    ui::ozone::mojom::DeviceCursorAssociatedPtr main_cursor_ptr,
+    ui::ozone::mojom::DeviceCursorAssociatedPtr evdev_cursor_ptr)
     : main_cursor_ptr_(std::move(main_cursor_ptr)),
       evdev_cursor_ptr_(std::move(evdev_cursor_ptr)),
       ui_thread_ref_(base::PlatformThread::CurrentRef()) {}
diff --git a/ui/ozone/platform/drm/host/host_cursor_proxy.h b/ui/ozone/platform/drm/host/host_cursor_proxy.h
index 87115dd..b13c68e 100644
--- a/ui/ozone/platform/drm/host/host_cursor_proxy.h
+++ b/ui/ozone/platform/drm/host/host_cursor_proxy.h
@@ -18,8 +18,8 @@
 // priviledged process.
 class HostCursorProxy : public DrmCursorProxy {
  public:
-  HostCursorProxy(ui::ozone::mojom::DeviceCursorPtr main_cursor_ptr,
-                  ui::ozone::mojom::DeviceCursorPtr evdev_cursor_ptr);
+  HostCursorProxy(ui::ozone::mojom::DeviceCursorAssociatedPtr main_cursor_ptr,
+                  ui::ozone::mojom::DeviceCursorAssociatedPtr evdev_cursor_ptr);
   ~HostCursorProxy() override;
 
  private:
@@ -32,8 +32,8 @@
   void InitializeOnEvdevIfNecessary() override;
 
   // Mojo implementation of the DrmCursorProxy.
-  ui::ozone::mojom::DeviceCursorPtr main_cursor_ptr_ = nullptr;
-  ui::ozone::mojom::DeviceCursorPtr evdev_cursor_ptr_ = nullptr;
+  ui::ozone::mojom::DeviceCursorAssociatedPtr main_cursor_ptr_ = nullptr;
+  ui::ozone::mojom::DeviceCursorAssociatedPtr evdev_cursor_ptr_ = nullptr;
 
   base::PlatformThreadRef ui_thread_ref_;
   bool evdev_bound_ = false;
diff --git a/ui/ozone/platform/drm/host/host_drm_device.cc b/ui/ozone/platform/drm/host/host_drm_device.cc
index e4bcc07..840db40 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.cc
+++ b/ui/ozone/platform/drm/host/host_drm_device.cc
@@ -42,23 +42,20 @@
 
   connector.BindInterfaceDrmDevice(&drm_device_ptr_);
 
-  // Launch the DRM thread.
-  auto callback =
-      base::BindOnce(&HostDrmDevice::OnDrmServiceStartedCallback, this);
-  drm_device_ptr_->StartDrmDevice(std::move(callback));
-
   // Bind the cursor interface pointers.
-  ui::ozone::mojom::DeviceCursorPtr cursor_ptr_ui, cursor_ptr_io;
-  connector.BindInterfaceDeviceCursor(&cursor_ptr_ui);
+  ui::ozone::mojom::DeviceCursorAssociatedPtr cursor_ptr_ui, cursor_ptr_io;
+  drm_device_ptr_->GetDeviceCursor(mojo::MakeRequest(&cursor_ptr_ui));
 
   // This interface pointer is bound on the wrong thread. But that's OK because
   // we'll re-bind it the first time that it's used from the I/O thread in
   // HostCursorProxy.
-  connector.BindInterfaceDeviceCursor(&cursor_ptr_io);
+  drm_device_ptr_->GetDeviceCursor(mojo::MakeRequest(&cursor_ptr_io));
 
   // Stash the cursor_proxy so that we can install it in the callback.
   cursor_proxy_ = std::make_unique<HostCursorProxy>(std::move(cursor_ptr_ui),
                                                     std::move(cursor_ptr_io));
+
+  OnDrmServiceStarted();
 }
 
 // TODO(rjkroege): Remove the need for this entry point.
@@ -67,27 +64,18 @@
   // blocking.
   base::RunLoop().RunUntilIdle();
 
-  bool success;
-  drm_device_ptr_->StartDrmDevice(&success);
-  CHECK(success)
-      << "drm thread failed to successfully start in single process mode.";
-  if (!connected_)
-    OnDrmServiceStartedCallback(true);
-  return;
+  OnDrmServiceStarted();
 }
 
-// The callback is executed in response to getting back a message from a
-// DrmDevice service that we launched earlier via ServiceManager.
-void HostDrmDevice::OnDrmServiceStartedCallback(bool success) {
+void HostDrmDevice::OnDrmServiceStarted() {
   // This can be called multiple times in the course of single-threaded startup.
   // Ignore invocations after we've started.
   if (connected_)
     return;
 
-  if (success == true) {
-    connected_ = true;
-    RunObservers();
-  }
+  connected_ = true;
+  RunObservers();
+
   // TODO(rjkroege): Handle failure of launching a viz process with the
   // ServiceManager.
 }
@@ -392,21 +380,17 @@
 
 // Invoked in response to the successful launching of the GPU service.
 void HostDrmDevice::OnGpuServiceLaunched(
-    ui::ozone::mojom::DrmDevicePtr drm_device_ptr,
-    ui::ozone::mojom::DeviceCursorPtr cursor_ptr_ui,
-    ui::ozone::mojom::DeviceCursorPtr cursor_ptr_io) {
+    ui::ozone::mojom::DrmDevicePtr drm_device_ptr) {
   DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
 
   // Rebind InterfacePtrs to the window server thread.
-  cursor_ptr_ui.Bind(cursor_ptr_ui.PassInterface());
-  drm_device_ptr.Bind(drm_device_ptr.PassInterface());
+  drm_device_ptr_.Bind(drm_device_ptr.PassInterface());
 
-  drm_device_ptr_ = std::move(drm_device_ptr);
-
-  // Make sure that we've launched the DRM device service in the Viz process.
-  auto callback =
-      base::BindOnce(&HostDrmDevice::OnDrmServiceStartedCallback, this);
-  drm_device_ptr_->StartDrmDevice(std::move(callback));
+  // Create two DeviceCursor connections: one for the UI thread and one for the
+  // IO thread.
+  ui::ozone::mojom::DeviceCursorAssociatedPtr cursor_ptr_ui, cursor_ptr_io;
+  drm_device_ptr_->GetDeviceCursor(mojo::MakeRequest(&cursor_ptr_ui));
+  drm_device_ptr_->GetDeviceCursor(mojo::MakeRequest(&cursor_ptr_io));
 
   // The cursor is special since it will process input events on the IO thread
   // and can by-pass the UI thread. As a result, it has an InterfacePtr for both
@@ -414,6 +398,8 @@
   // to an I/O thread by GpuProcessHost.
   cursor_proxy_ = std::make_unique<HostCursorProxy>(std::move(cursor_ptr_ui),
                                                     std::move(cursor_ptr_io));
+
+  OnDrmServiceStarted();
 }
 
 void HostDrmDevice::OnGpuServiceLaunchedCompositor(
diff --git a/ui/ozone/platform/drm/host/host_drm_device.h b/ui/ozone/platform/drm/host/host_drm_device.h
index 7c518779..6159b5d 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.h
+++ b/ui/ozone/platform/drm/host/host_drm_device.h
@@ -50,9 +50,7 @@
   void ProvideManagers(DrmDisplayHostManager* display_manager,
                        DrmOverlayManagerHost* overlay_manager);
 
-  void OnGpuServiceLaunched(ui::ozone::mojom::DrmDevicePtr drm_device_ptr,
-                            ui::ozone::mojom::DeviceCursorPtr cursor_ptr_ui,
-                            ui::ozone::mojom::DeviceCursorPtr cursor_ptr_io);
+  void OnGpuServiceLaunched(ui::ozone::mojom::DrmDevicePtr drm_device_ptr);
 
   void OnGpuServiceLaunchedCompositor(
       ui::ozone::mojom::DrmDevicePtr drm_device_ptr_compositor);
@@ -119,7 +117,7 @@
   void BindInterfaceDeviceCursor(
       ui::ozone::mojom::DeviceCursorPtr* cursor_ptr) const;
 
-  void OnDrmServiceStartedCallback(bool success);
+  void OnDrmServiceStarted();
 
   // TODO(rjkroege): Get rid of the need for this method in a subsequent CL.
   void PollForSingleThreadReady(int previous_delay);
diff --git a/ui/ozone/platform/drm/ozone_platform_gbm.cc b/ui/ozone/platform/drm/ozone_platform_gbm.cc
index b451e778..d762d447 100644
--- a/ui/ozone/platform/drm/ozone_platform_gbm.cc
+++ b/ui/ozone/platform/drm/ozone_platform_gbm.cc
@@ -110,11 +110,6 @@
     if (!using_mojo_)
       return;
 
-    registry->AddInterface<ozone::mojom::DeviceCursor>(
-        base::BindRepeating(&OzonePlatformGbm::CreateDeviceCursorBinding,
-                            weak_factory_.GetWeakPtr()),
-        base::ThreadTaskRunnerHandle::Get());
-
     registry->AddInterface<ozone::mojom::DrmDevice>(
         base::BindRepeating(&OzonePlatformGbm::CreateDrmDeviceBinding,
                             weak_factory_.GetWeakPtr()),
@@ -123,15 +118,6 @@
 
   // Runs on the thread where AddInterfaces was invoked. But the endpoint is
   // always bound on the DRM thread.
-  void CreateDeviceCursorBinding(ozone::mojom::DeviceCursorRequest request) {
-    if (drm_thread_started_)
-      drm_thread_proxy_->AddBindingCursorDevice(std::move(request));
-    else
-      pending_cursor_requests_.push_back(std::move(request));
-  }
-
-  // Runs on the thread where AddInterfaces was invoked. But the endpoint is
-  // always bound on the DRM thread.
   void CreateDrmDeviceBinding(ozone::mojom::DrmDeviceRequest request) {
     if (drm_thread_started_)
       drm_thread_proxy_->AddBindingDrmDevice(std::move(request));
@@ -143,9 +129,6 @@
   // binding requests that could not be satisfied until the DRM thread is
   // available (i.e. if waiting until the sandbox has been entered.)
   void DrainBindingRequests() {
-    for (auto& request : pending_cursor_requests_)
-      drm_thread_proxy_->AddBindingCursorDevice(std::move(request));
-    pending_cursor_requests_.clear();
     for (auto& request : pending_gpu_adapter_requests_)
       drm_thread_proxy_->AddBindingDrmDevice(std::move(request));
     pending_gpu_adapter_requests_.clear();
@@ -338,7 +321,6 @@
 
   // TODO(rjkroege,sadrul): Provide a more elegant solution for this issue when
   // running in single process mode.
-  std::vector<ozone::mojom::DeviceCursorRequest> pending_cursor_requests_;
   std::vector<ozone::mojom::DrmDeviceRequest> pending_gpu_adapter_requests_;
   bool drm_thread_started_ = false;
 
diff --git a/ui/ozone/public/interfaces/drm_device.mojom b/ui/ozone/public/interfaces/drm_device.mojom
index 1a21e59..d6ae55e 100644
--- a/ui/ozone/public/interfaces/drm_device.mojom
+++ b/ui/ozone/public/interfaces/drm_device.mojom
@@ -12,6 +12,7 @@
 import "ui/display/mojo/gamma_ramp_rgb_entry.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "ui/gfx/mojo/accelerated_widget.mojom";
+import "ui/ozone/public/interfaces/device_cursor.mojom";
 import "ui/ozone/public/interfaces/overlay_surface_candidate.mojom";
 
 
@@ -20,10 +21,6 @@
 // All functions in DrmDevice are implemented by the lower privilege viz
 // process.
 interface DrmDevice {
-  // Starts the DRM service and returns true on success.
-  [Sync]
-  StartDrmDevice() => (bool success);
-
   // Creates scanout capable DRM buffers to back |widget|.
   CreateWindow(gfx.mojom.AcceleratedWidget widget);
 
@@ -86,5 +83,10 @@
           (gfx.mojom.AcceleratedWidget widget,
           array<ui.ozone.mojom.OverlaySurfaceCandidate> candidates,
           array<ui.ozone.mojom.OverlayStatus> status);
+
+  // Provides a DeviceCursor interface. The provided interface needs to be
+  // associated because the AcceleratedWidgets referenced by its methods are
+  // registered via CreateWindow() in this interface.
+  GetDeviceCursor(associated DeviceCursor& cursor);
 };
 
diff --git a/ui/views/controls/message_box_view.cc b/ui/views/controls/message_box_view.cc
index 984f4dc..aa9ce75 100644
--- a/ui/views/controls/message_box_view.cc
+++ b/ui/views/controls/message_box_view.cc
@@ -82,7 +82,8 @@
 MessageBoxView::InitParams::~InitParams() = default;
 
 MessageBoxView::MessageBoxView(const InitParams& params)
-    : message_width_(params.message_width) {
+    : inter_row_vertical_spacing_(params.inter_row_vertical_spacing),
+      message_width_(params.message_width) {
   Init(params);
 }
 
@@ -227,8 +228,6 @@
     prompt_field_ = AddChildView(std::move(prompt_field));
   }
 
-  inter_row_vertical_spacing_ = params.inter_row_vertical_spacing;
-
   ResetLayoutManager();
 }
 
@@ -256,27 +255,27 @@
   }
 
   layout->StartRow(0, kMessageViewColumnSetId);
-  layout->AddView(scroll_view_);
+  layout->AddExistingView(scroll_view_);
 
   views::DialogContentType trailing_content_type = views::TEXT;
   if (prompt_field_) {
     layout->AddPaddingRow(0, inter_row_vertical_spacing_);
     layout->StartRow(0, kExtraViewColumnSetId);
-    layout->AddView(prompt_field_);
+    layout->AddExistingView(prompt_field_);
     trailing_content_type = views::CONTROL;
   }
 
   if (checkbox_) {
     layout->AddPaddingRow(0, inter_row_vertical_spacing_);
     layout->StartRow(0, kExtraViewColumnSetId);
-    layout->AddView(checkbox_);
+    layout->AddExistingView(checkbox_);
     trailing_content_type = views::TEXT;
   }
 
   if (link_) {
     layout->AddPaddingRow(0, inter_row_vertical_spacing_);
     layout->StartRow(0, kExtraViewColumnSetId);
-    layout->AddView(link_);
+    layout->AddExistingView(link_);
     trailing_content_type = views::TEXT;
   }
 
diff --git a/ui/views/controls/message_box_view.h b/ui/views/controls/message_box_view.h
index 214bf73f..9660518c 100644
--- a/ui/views/controls/message_box_view.h
+++ b/ui/views/controls/message_box_view.h
@@ -122,10 +122,10 @@
   Link* link_ = nullptr;
 
   // Spacing between rows in the grid layout.
-  int inter_row_vertical_spacing_ = 0;
+  const int inter_row_vertical_spacing_ = 0;
 
   // Maximum width of the message label.
-  int message_width_;
+  int message_width_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(MessageBoxView);
 };
diff --git a/ui/views/controls/native/native_view_host_aura.cc b/ui/views/controls/native/native_view_host_aura.cc
index 6e97d8e2..b8837382 100644
--- a/ui/views/controls/native/native_view_host_aura.cc
+++ b/ui/views/controls/native/native_view_host_aura.cc
@@ -24,7 +24,6 @@
 #include "ui/views/view_class_properties.h"
 #include "ui/views/view_constants_aura.h"
 #include "ui/views/widget/widget.h"
-#include "ui/wm/core/window_util.h"
 
 namespace views {
 
@@ -271,15 +270,8 @@
     const gfx::Rect& old_bounds,
     const gfx::Rect& new_bounds,
     ui::PropertyChangeReason reason) {
-  if (mask_) {
-    // Having a mask means this layer has a render surface of its own. This
-    // means we want this layer snapped as the render surface uses this layer
-    // (its primary layer) to snap to the physical pixel grid.
-    // See https://crbug.com/843250 for more details.
-    wm::SnapWindowToPixelBoundary(window);
-
+  if (mask_)
     mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size()));
-  }
 }
 
 void NativeViewHostAura::OnWindowDestroying(aura::Window* window) {
@@ -346,13 +338,6 @@
   if (!mask_)
     return;
   if (host_->native_view()) {
-    // Setting a mask triggers this layer to have a render surface of its own.
-    // This means we cannot skip computing its subpixel offset positioning as
-    // the render surface uses this layer (its primary layer) to snap to the
-    // physical pixel grid.
-    // See https://crbug.com/843250 for more details.
-    wm::SnapWindowToPixelBoundary(host_->native_view());
-
     mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size()));
     host_->native_view()->layer()->SetMaskLayer(mask_->layer());
   }
diff --git a/ui/views/controls/separator.cc b/ui/views/controls/separator.cc
index d3f8e8b3..4a9d558 100644
--- a/ui/views/controls/separator.cc
+++ b/ui/views/controls/separator.cc
@@ -17,14 +17,30 @@
 
 Separator::~Separator() = default;
 
+SkColor Separator::GetColor() const {
+  if (overridden_color_ == true)
+    return overridden_color_.value();
+  return 0;
+}
+
 void Separator::SetColor(SkColor color) {
+  if (overridden_color_ == color)
+    return;
+
   overridden_color_ = color;
-  SchedulePaint();
+  OnPropertyChanged(&overridden_color_, kPropertyEffectsPaint);
+}
+
+int Separator::GetPreferredHeight() const {
+  return preferred_height_;
 }
 
 void Separator::SetPreferredHeight(int height) {
+  if (preferred_height_ == height)
+    return;
+
   preferred_height_ = height;
-  PreferredSizeChanged();
+  OnPropertyChanged(&preferred_height_, kPropertyEffectsPreferredSizeChanged);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -62,6 +78,8 @@
 
 BEGIN_METADATA(Separator)
 METADATA_PARENT_CLASS(View)
+ADD_PROPERTY_METADATA(Separator, SkColor, Color)
+ADD_PROPERTY_METADATA(Separator, int, PreferredHeight)
 END_METADATA()
 
 }  // namespace views
diff --git a/ui/views/controls/separator.h b/ui/views/controls/separator.h
index ae03677..f547479 100644
--- a/ui/views/controls/separator.h
+++ b/ui/views/controls/separator.h
@@ -25,8 +25,10 @@
   Separator();
   ~Separator() override;
 
+  SkColor GetColor() const;
   void SetColor(SkColor color);
 
+  int GetPreferredHeight() const;
   void SetPreferredHeight(int height);
 
   // Overridden from View:
diff --git a/ui/views/controls/slider.cc b/ui/views/controls/slider.cc
index f16860ad8..1838685 100644
--- a/ui/views/controls/slider.cc
+++ b/ui/views/controls/slider.cc
@@ -76,13 +76,34 @@
 
 Slider::~Slider() = default;
 
+float Slider::GetValue() const {
+  return value_;
+}
+
 void Slider::SetValue(float value) {
   SetValueInternal(value, VALUE_CHANGED_BY_API);
 }
 
-void Slider::UpdateState(bool control_on) {
-  is_active_ = control_on;
-  SchedulePaint();
+bool Slider::GetEnableAccessibilityEvents() const {
+  return accessibility_events_enabled_;
+}
+
+void Slider::SetEnableAccessibilityEvents(bool enabled) {
+  if (accessibility_events_enabled_ == enabled)
+    return;
+  accessibility_events_enabled_ = enabled;
+  OnPropertyChanged(&accessibility_events_enabled_, kPropertyEffectsNone);
+}
+
+bool Slider::GetIsActive() const {
+  return is_active_;
+}
+
+void Slider::SetIsActive(bool is_active) {
+  if (is_active == is_active_)
+    return;
+  is_active_ = is_active;
+  OnPropertyChanged(&is_active_, kPropertyEffectsPaint);
 }
 
 float Slider::GetAnimatingValue() const{
@@ -140,8 +161,9 @@
       move_animation_->SetSlideDuration(kSlideValueChangeDurationMs);
       move_animation_->Show();
     }
+    OnPropertyChanged(&value_, kPropertyEffectsNone);
   } else {
-    SchedulePaint();
+    OnPropertyChanged(&value_, kPropertyEffectsPaint);
   }
 
   if (accessibility_events_enabled_) {
@@ -355,6 +377,9 @@
 
 BEGIN_METADATA(Slider)
 METADATA_PARENT_CLASS(View)
+ADD_PROPERTY_METADATA(Slider, float, Value)
+ADD_PROPERTY_METADATA(Slider, bool, EnableAccessibilityEvents)
+ADD_PROPERTY_METADATA(Slider, bool, IsActive)
 END_METADATA()
 
 }  // namespace views
diff --git a/ui/views/controls/slider.h b/ui/views/controls/slider.h
index ea68bac4..7ecf9eb 100644
--- a/ui/views/controls/slider.h
+++ b/ui/views/controls/slider.h
@@ -49,15 +49,15 @@
   explicit Slider(SliderListener* listener);
   ~Slider() override;
 
-  float value() const { return value_; }
+  float GetValue() const;
   void SetValue(float value);
 
-  void set_enable_accessibility_events(bool enabled) {
-    accessibility_events_enabled_ = enabled;
-  }
+  bool GetEnableAccessibilityEvents() const;
+  void SetEnableAccessibilityEvents(bool enabled);
 
-  // Update UI based on control on/off state.
-  void UpdateState(bool control_on);
+  // Gets/Sets IsActive state
+  bool GetIsActive() const;
+  void SetIsActive(bool is_active);
 
  protected:
   // Returns the current position of the thumb on the slider.
diff --git a/ui/views/controls/slider_unittest.cc b/ui/views/controls/slider_unittest.cc
index f5775d90..b2844dc 100644
--- a/ui/views/controls/slider_unittest.cc
+++ b/ui/views/controls/slider_unittest.cc
@@ -233,20 +233,20 @@
 
 TEST_F(SliderTest, UpdateFromClickHorizontal) {
   ClickAt(0, 0);
-  EXPECT_EQ(0.0f, slider()->value());
+  EXPECT_EQ(0.0f, slider()->GetValue());
 
   ClickAt(max_x(), 0);
-  EXPECT_EQ(1.0f, slider()->value());
+  EXPECT_EQ(1.0f, slider()->GetValue());
 }
 
 TEST_F(SliderTest, UpdateFromClickRTLHorizontal) {
   base::i18n::SetICUDefaultLocale("he");
 
   ClickAt(0, 0);
-  EXPECT_EQ(1.0f, slider()->value());
+  EXPECT_EQ(1.0f, slider()->GetValue());
 
   ClickAt(max_x(), 0);
-  EXPECT_EQ(0.0f, slider()->value());
+  EXPECT_EQ(0.0f, slider()->GetValue());
 }
 
 // No touch on desktop Mac. Tracked in http://crbug.com/445520.
@@ -257,17 +257,17 @@
   // Tap below the minimum.
   slider()->SetValue(0.5);
   event_generator()->GestureTapAt(gfx::Point(0, 0));
-  EXPECT_FLOAT_EQ(0, slider()->value());
+  EXPECT_FLOAT_EQ(0, slider()->GetValue());
 
   // Tap above the maximum.
   slider()->SetValue(0.5);
   event_generator()->GestureTapAt(gfx::Point(max_x(), max_y()));
-  EXPECT_FLOAT_EQ(1, slider()->value());
+  EXPECT_FLOAT_EQ(1, slider()->GetValue());
 
   // Tap somwhere in the middle.
   slider()->SetValue(0.5);
   event_generator()->GestureTapAt(gfx::Point(0.75 * max_x(), 0.75 * max_y()));
-  EXPECT_NEAR(0.75, slider()->value(), 0.03);
+  EXPECT_NEAR(0.75, slider()->GetValue(), 0.03);
 }
 
 // Test the slider location after a scroll gesture.
@@ -277,14 +277,14 @@
   event_generator()->GestureScrollSequence(
       gfx::Point(0.5 * max_x(), 0.5 * max_y()), gfx::Point(0, 0),
       base::TimeDelta::FromMilliseconds(10), 5 /* steps */);
-  EXPECT_EQ(0, slider()->value());
+  EXPECT_EQ(0, slider()->GetValue());
 
   // Scroll above the maximum.
   slider()->SetValue(0.5);
   event_generator()->GestureScrollSequence(
       gfx::Point(0.5 * max_x(), 0.5 * max_y()), gfx::Point(max_x(), max_y()),
       base::TimeDelta::FromMilliseconds(10), 5 /* steps */);
-  EXPECT_EQ(1, slider()->value());
+  EXPECT_EQ(1, slider()->GetValue());
 
   // Scroll somewhere in the middle.
   slider()->SetValue(0.25);
@@ -292,7 +292,7 @@
       gfx::Point(0.25 * max_x(), 0.25 * max_y()),
       gfx::Point(0.75 * max_x(), 0.75 * max_y()),
       base::TimeDelta::FromMilliseconds(10), 5 /* steps */);
-  EXPECT_NEAR(0.75, slider()->value(), 0.03);
+  EXPECT_NEAR(0.75, slider()->GetValue(), 0.03);
 }
 
 // Test the slider location by adjusting it using keyboard.
@@ -301,38 +301,38 @@
   slider()->SetValue(value);
   slider()->RequestFocus();
   event_generator()->PressKey(ui::VKEY_RIGHT, 0);
-  EXPECT_GT(slider()->value(), value);
+  EXPECT_GT(slider()->GetValue(), value);
 
   slider()->SetValue(value);
   event_generator()->PressKey(ui::VKEY_LEFT, 0);
-  EXPECT_LT(slider()->value(), value);
+  EXPECT_LT(slider()->GetValue(), value);
 
   slider()->SetValue(value);
   event_generator()->PressKey(ui::VKEY_UP, 0);
-  EXPECT_GT(slider()->value(), value);
+  EXPECT_GT(slider()->GetValue(), value);
 
   slider()->SetValue(value);
   event_generator()->PressKey(ui::VKEY_DOWN, 0);
-  EXPECT_LT(slider()->value(), value);
+  EXPECT_LT(slider()->GetValue(), value);
 
   // RTL reverse left/right but not up/down.
   base::i18n::SetICUDefaultLocale("he");
   EXPECT_TRUE(base::i18n::IsRTL());
 
   event_generator()->PressKey(ui::VKEY_RIGHT, 0);
-  EXPECT_LT(slider()->value(), value);
+  EXPECT_LT(slider()->GetValue(), value);
 
   slider()->SetValue(value);
   event_generator()->PressKey(ui::VKEY_LEFT, 0);
-  EXPECT_GT(slider()->value(), value);
+  EXPECT_GT(slider()->GetValue(), value);
 
   slider()->SetValue(value);
   event_generator()->PressKey(ui::VKEY_UP, 0);
-  EXPECT_GT(slider()->value(), value);
+  EXPECT_GT(slider()->GetValue(), value);
 
   slider()->SetValue(value);
   event_generator()->PressKey(ui::VKEY_DOWN, 0);
-  EXPECT_LT(slider()->value(), value);
+  EXPECT_LT(slider()->GetValue(), value);
 }
 
 // Verifies the correct SliderListener events are raised for a tap gesture.
diff --git a/ui/views/controls/throbber.cc b/ui/views/controls/throbber.cc
index 47d384c..44bd36d 100644
--- a/ui/views/controls/throbber.cc
+++ b/ui/views/controls/throbber.cc
@@ -52,12 +52,16 @@
   SchedulePaint();
 }
 
+bool Throbber::GetChecked() const {
+  return checked_;
+}
+
 void Throbber::SetChecked(bool checked) {
   if (checked == checked_)
     return;
 
   checked_ = checked;
-  SchedulePaint();
+  OnPropertyChanged(&checked_, kPropertyEffectsPaint);
 }
 
 gfx::Size Throbber::CalculatePreferredSize() const {
@@ -88,6 +92,7 @@
 
 BEGIN_METADATA(Throbber)
 METADATA_PARENT_CLASS(View)
+ADD_PROPERTY_METADATA(Throbber, bool, Checked)
 END_METADATA()
 
 // Smoothed throbber ---------------------------------------------------------
@@ -128,12 +133,36 @@
                     &SmoothedThrobber::StopDelayOver);
 }
 
+int SmoothedThrobber::GetStartDelayMs() const {
+  return start_delay_ms_;
+}
+
+void SmoothedThrobber::SetStartDelayMs(int start_delay_ms) {
+  if (start_delay_ms == start_delay_ms_)
+    return;
+  start_delay_ms_ = start_delay_ms;
+  OnPropertyChanged(&start_delay_ms_, kPropertyEffectsNone);
+}
+
+int SmoothedThrobber::GetStopDelayMs() const {
+  return stop_delay_ms_;
+}
+
+void SmoothedThrobber::SetStopDelayMs(int stop_delay_ms) {
+  if (stop_delay_ms == stop_delay_ms_)
+    return;
+  stop_delay_ms_ = stop_delay_ms;
+  OnPropertyChanged(&stop_delay_ms_, kPropertyEffectsNone);
+}
+
 void SmoothedThrobber::StopDelayOver() {
   Throbber::Stop();
 }
 
 BEGIN_METADATA(SmoothedThrobber)
 METADATA_PARENT_CLASS(Throbber)
+ADD_PROPERTY_METADATA(SmoothedThrobber, int, StartDelayMs)
+ADD_PROPERTY_METADATA(SmoothedThrobber, int, StopDelayMs)
 END_METADATA()
 
 }  // namespace views
diff --git a/ui/views/controls/throbber.h b/ui/views/controls/throbber.h
index c9be504..2b19d996 100644
--- a/ui/views/controls/throbber.h
+++ b/ui/views/controls/throbber.h
@@ -25,7 +25,9 @@
   virtual void Start();
   virtual void Stop();
 
-  // Stop spinning and, if checked is true, display a checkmark.
+  // Gets/Sets checked. For SetChecked, stop spinning and, if
+  // checked is true, display a checkmark.
+  bool GetChecked() const;
   void SetChecked(bool checked);
 
   // Overridden from View:
@@ -59,8 +61,11 @@
   void Start() override;
   void Stop() override;
 
-  void set_start_delay_ms(int value) { start_delay_ms_ = value; }
-  void set_stop_delay_ms(int value) { stop_delay_ms_ = value; }
+  int GetStartDelayMs() const;
+  void SetStartDelayMs(int start_delay_ms);
+
+  int GetStopDelayMs() const;
+  void SetStopDelayMs(int stop_delay_ms);
 
  private:
   // Called when the startup-delay timer fires
diff --git a/ui/views/controls/webview/web_dialog_view.cc b/ui/views/controls/webview/web_dialog_view.cc
index fc3d487..dccc4e3d 100644
--- a/ui/views/controls/webview/web_dialog_view.cc
+++ b/ui/views/controls/webview/web_dialog_view.cc
@@ -124,6 +124,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 // WebDialogView, views::WidgetDelegate implementation:
 
+bool WebDialogView::OnCloseRequested(Widget::ClosedReason close_reason) {
+  return !delegate_ || delegate_->OnDialogCloseRequested();
+}
+
 bool WebDialogView::CanResize() const {
   if (delegate_)
     return delegate_->CanResizeDialog();
diff --git a/ui/views/controls/webview/web_dialog_view.h b/ui/views/controls/webview/web_dialog_view.h
index 3e397a5..f77676f 100644
--- a/ui/views/controls/webview/web_dialog_view.h
+++ b/ui/views/controls/webview/web_dialog_view.h
@@ -64,6 +64,7 @@
   bool CanClose() override;
 
   // Overridden from views::WidgetDelegate:
+  bool OnCloseRequested(Widget::ClosedReason close_reason) override;
   bool CanResize() const override;
   ui::ModalType GetModalType() const override;
   base::string16 GetWindowTitle() const override;
diff --git a/ui/views/examples/flex_layout_example.cc b/ui/views/examples/flex_layout_example.cc
index 0983e30..7027dc1 100644
--- a/ui/views/examples/flex_layout_example.cc
+++ b/ui/views/examples/flex_layout_example.cc
@@ -23,6 +23,7 @@
 #include "ui/views/examples/example_combobox_model.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/view.h"
+#include "ui/views/view_class_properties.h"
 
 namespace views {
 namespace examples {
@@ -81,8 +82,8 @@
                                         const base::string16& new_contents) {
   layout_->SetInteriorMargin(
       LayoutExampleBase::TextfieldsToInsets(interior_margin_));
-  layout_->SetDefaultChildMargins(
-      LayoutExampleBase::TextfieldsToInsets(default_child_margins_));
+  layout_->SetDefault(views::kMarginsKey, LayoutExampleBase::TextfieldsToInsets(
+                                              default_child_margins_));
   RefreshLayoutPanel(false);
 }
 
@@ -97,9 +98,9 @@
     ChildPanel* panel = static_cast<ChildPanel*>(child);
     int flex = panel->GetFlex();
     if (flex < 0)
-      layout_->ClearFlexForView(panel);
+      panel->ClearProperty(views::kFlexBehaviorKey);
     else
-      layout_->SetFlexForView(panel, GetFlexSpecification(flex));
+      panel->SetProperty(views::kFlexBehaviorKey, GetFlexSpecification(flex));
   }
 }
 
diff --git a/ui/views/layout/flex_layout.cc b/ui/views/layout/flex_layout.cc
index b8e2d102..b82cd59 100644
--- a/ui/views/layout/flex_layout.cc
+++ b/ui/views/layout/flex_layout.cc
@@ -151,6 +151,10 @@
   CacheEntry current_;
 };
 
+}  // anonymous namespace
+
+// Private implementation ------------------------------------------------------
+
 // Calculates and maintains 1D spacing between a sequence of child views.
 class ChildViewSpacing {
  public:
@@ -286,18 +290,6 @@
   return it->first;
 }
 
-// Utility functions -----------------------------------------------------------
-
-gfx::Insets GetInternalPadding(const View* view) {
-  const gfx::Insets* const margins =
-      view->GetProperty(views::kInternalPaddingKey);
-  return margins ? *margins : gfx::Insets();
-}
-
-}  // anonymous namespace
-
-// Private implementation ------------------------------------------------------
-
 // Holds child-view-specific layout parameters that are not stored in the
 // properties system.
 //
@@ -305,7 +297,6 @@
 struct ChildLayoutParams {
   bool excluded = false;
   bool hidden_by_owner = false;
-  base::Optional<FlexSpecification> flex_specification;
 };
 
 // Internal data structure and functionality for FlexLayout so we don't have to
@@ -385,7 +376,11 @@
                             base::Optional<size_t> child1_index,
                             base::Optional<size_t> child2_index) const;
 
-  const gfx::Insets& GetMargins(const View* view) const;
+  // Fetches the layout-specific value for a child view, then the layout-default
+  // value, then the global default value if none of the others are present.
+  template <typename T>
+  T GetViewProperty(const View* view,
+                    const ui::ClassProperty<T*>* property) const;
 
   FlexLayout& layout_;
 
@@ -511,11 +506,6 @@
                          left_padding + right_padding);
 }
 
-const gfx::Insets& FlexLayoutInternal::GetMargins(const View* view) const {
-  const gfx::Insets* const margins = view->GetProperty(views::kMarginsKey);
-  return margins ? *margins : layout_.default_child_margins();
-}
-
 void FlexLayoutInternal::UpdateLayoutFromChildren(
     Layout* layout,
     ChildViewSpacing* child_spacing,
@@ -751,16 +741,18 @@
   View* const view = layout_.host();
   for (size_t i = 0; i < view->children().size(); ++i) {
     View* child = view->children()[i];
-    layout->child_layouts.emplace_back(child, layout_.GetFlexForView(child));
+    layout->child_layouts.emplace_back(
+        child, GetViewProperty(child, views::kFlexBehaviorKey));
     ChildLayout& child_layout = layout->child_layouts.back();
 
     child_layout.excluded = layout_.IsViewExcluded(child);
     if (child_layout.excluded)
       continue;
 
-    child_layout.margins = Normalize(orientation(), GetMargins(child));
-    child_layout.internal_padding =
-        Normalize(orientation(), GetInternalPadding(child));
+    child_layout.margins =
+        Normalize(orientation(), GetViewProperty(child, views::kMarginsKey));
+    child_layout.internal_padding = Normalize(
+        orientation(), GetViewProperty(child, views::kInternalPaddingKey));
     child_layout.preferred_size =
         Normalize(orientation(), child->GetPreferredSize());
 
@@ -864,12 +856,12 @@
 
     if (proposed_view_layout.visible) {
       // Check that view margins haven't changed for visible controls.
-      if (GetMargins(child) !=
+      if (GetViewProperty(child, views::kMarginsKey) !=
           Denormalize(orientation(), proposed_view_layout.margins))
         return false;
 
       // Same for internal padding.
-      if (GetInternalPadding(child) !=
+      if (GetViewProperty(child, views::kInternalPaddingKey) !=
           Denormalize(orientation(), proposed_view_layout.internal_padding))
         return false;
     }
@@ -880,7 +872,9 @@
         Denormalize(orientation(), proposed_view_layout.preferred_size))
       return false;
 
-    const gfx::Size preferred = proposed_view_layout.flex.rule().Run(
+    const FlexSpecification child_flex =
+        GetViewProperty(child, views::kFlexBehaviorKey);
+    const gfx::Size preferred = child_flex.rule().Run(
         child, Denormalize(orientation(), proposed_view_layout.available_size));
     if (preferred !=
         Denormalize(orientation(), proposed_view_layout.current_size))
@@ -896,13 +890,36 @@
   return true;
 }
 
+template <typename T>
+T FlexLayoutInternal::GetViewProperty(
+    const View* view,
+    const ui::ClassProperty<T*>* property) const {
+  T* found_value = view->GetProperty(property);
+  if (found_value)
+    return *found_value;
+  found_value = layout_.layout_defaults_.GetProperty(property);
+  if (found_value)
+    return *found_value;
+  return T();
+}
+
 }  // namespace internal
 
 // FlexLayout
 // -------------------------------------------------------------------
 
+FlexLayout::PropertyHandler::PropertyHandler(
+    internal::FlexLayoutInternal* layout)
+    : layout_(layout) {}
+
+void FlexLayout::PropertyHandler::AfterPropertyChange(const void* key,
+                                                      int64_t old_value) {
+  layout_->InvalidateLayout(false);
+}
+
 FlexLayout::FlexLayout()
-    : internal_(std::make_unique<internal::FlexLayoutInternal>(this)) {}
+    : internal_(std::make_unique<internal::FlexLayoutInternal>(this)),
+      layout_defaults_(internal_.get()) {}
 
 FlexLayout::~FlexLayout() = default;
 
@@ -958,32 +975,6 @@
   return *this;
 }
 
-FlexLayout& FlexLayout::SetDefaultChildMargins(const gfx::Insets& margins) {
-  if (default_child_margins_ != margins) {
-    default_child_margins_ = margins;
-    internal_->InvalidateLayout(true);
-  }
-  return *this;
-}
-
-FlexLayout& FlexLayout::SetFlexForView(
-    const View* view,
-    const FlexSpecification& flex_specification) {
-  auto it = child_params_.find(view);
-  DCHECK(it != child_params_.end());
-  it->second.flex_specification = flex_specification;
-  internal_->InvalidateLayout(true);
-  return *this;
-}
-
-FlexLayout& FlexLayout::ClearFlexForView(const View* view) {
-  auto it = child_params_.find(view);
-  DCHECK(it != child_params_.end());
-  it->second.flex_specification.reset();
-  internal_->InvalidateLayout(true);
-  return *this;
-}
-
 FlexLayout& FlexLayout::SetViewExcluded(const View* view, bool excluded) {
   auto it = child_params_.find(view);
   DCHECK(it != child_params_.end());
@@ -994,20 +985,6 @@
   return *this;
 }
 
-FlexLayout& FlexLayout::SetDefaultFlex(
-    const FlexSpecification& flex_specification) {
-  default_flex_ = flex_specification;
-  internal_->InvalidateLayout(true);
-  return *this;
-}
-
-const FlexSpecification& FlexLayout::GetFlexForView(const View* view) const {
-  auto it = child_params_.find(view);
-  DCHECK(it != child_params_.end());
-  const base::Optional<FlexSpecification>& spec = it->second.flex_specification;
-  return spec ? *spec : default_flex_;
-}
-
 bool FlexLayout::IsViewExcluded(const View* view) const {
   auto it = child_params_.find(view);
   DCHECK(it != child_params_.end());
diff --git a/ui/views/layout/flex_layout.h b/ui/views/layout/flex_layout.h
index 45c51813..16649b2 100644
--- a/ui/views/layout/flex_layout.h
+++ b/ui/views/layout/flex_layout.h
@@ -13,6 +13,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "ui/base/class_property.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/layout/layout_manager.h"
@@ -23,6 +24,8 @@
 
 namespace views {
 
+class View;
+
 namespace internal {
 struct ChildLayoutParams;
 class FlexLayoutInternal;
@@ -88,11 +91,6 @@
   FlexLayout& SetCrossAxisAlignment(LayoutAlignment cross_axis_alignment);
   FlexLayout& SetInteriorMargin(const gfx::Insets& interior_margin);
   FlexLayout& SetMinimumCrossAxisSize(int size);
-  FlexLayout& SetDefaultChildMargins(const gfx::Insets& margins);
-  FlexLayout& SetFlexForView(const View* view,
-                             const FlexSpecification& flex_specification);
-  FlexLayout& ClearFlexForView(const View* view);
-  FlexLayout& SetDefaultFlex(const FlexSpecification& flex_specification);
 
   // Set whether a view should be excluded from the layout. Excluded views will
   // be completely ignored and must be explicitly placed by the host view.
@@ -106,12 +104,21 @@
   LayoutAlignment cross_axis_alignment() const { return cross_axis_alignment_; }
   const gfx::Insets& interior_margin() const { return interior_margin_; }
   int minimum_cross_axis_size() const { return minimum_cross_axis_size_; }
-  const gfx::Insets& default_child_margins() const {
-    return default_child_margins_;
-  }
-  const FlexSpecification& default_flex() const { return default_flex_; }
 
-  const FlexSpecification& GetFlexForView(const View* view) const;
+  // Moves and uses |value| as the default value for layout property |key|.
+  template <class T, class U>
+  FlexLayout& SetDefault(const ui::ClassProperty<T>* key, U&& value) {
+    layout_defaults_.SetProperty(key, std::forward<U>(value));
+    return *this;
+  }
+
+  // Copies and uses |value| as the default value for layout property |key|.
+  template <class T, class U>
+  FlexLayout& SetDefault(const ui::ClassProperty<T>* key, const U& value) {
+    layout_defaults_.SetProperty(key, value);
+    return *this;
+  }
+
   bool IsViewExcluded(const View* view) const;
   bool IsHiddenByOwner(const View* view) const;
 
@@ -133,6 +140,35 @@
  private:
   friend class internal::FlexLayoutInternal;
 
+  class PropertyHandler : public ui::PropertyHandler {
+   public:
+    explicit PropertyHandler(internal::FlexLayoutInternal* layout);
+
+   protected:
+    // ui::PropertyHandler:
+    void AfterPropertyChange(const void* key, int64_t old_value) override;
+
+   private:
+    internal::FlexLayoutInternal* const layout_;
+  };
+
+  // Gets the default value for a particular layout property, which will be used
+  // if the property is not set on a child view being laid out (e.g.
+  // kMarginsKey).
+  template <class T>
+  T* GetDefault(const ui::ClassProperty<T>* key) const {
+    return layout_defaults_.GetProperty(key);
+  }
+
+  // Clears the default value for a particular layout property, which will be
+  // used if the property is not set on a child view being laid out (e.g.
+  // kMarginsKey).
+  template <class T>
+  FlexLayout& ClearDefault(const ui::ClassProperty<T>* key) {
+    layout_defaults_.ClearProperty(key);
+    return *this;
+  }
+
   LayoutOrientation orientation_ = LayoutOrientation::kHorizontal;
 
   // Adjacent view margins should be collapsed.
@@ -141,12 +177,6 @@
   // Spacing between child views and host view border.
   gfx::Insets interior_margin_;
 
-  // Default spacing to put around child views.
-  gfx::Insets default_child_margins_;
-
-  // Child-view-specific details (e.g. hidden status, flex rules, etc.)
-  std::map<const View*, internal::ChildLayoutParams> child_params_;
-
   // The alignment of children in the main axis. This is start by default.
   LayoutAlignment main_axis_alignment_ = LayoutAlignment::kStart;
 
@@ -156,8 +186,8 @@
   // The minimum cross axis size for the layout.
   int minimum_cross_axis_size_ = 0;
 
-  // Flex specification for components with no flex set.
-  FlexSpecification default_flex_;
+  // Tracks layout-specific information about child views.
+  std::map<const View*, internal::ChildLayoutParams> child_params_;
 
   // The view that this FlexLayout is managing the layout for.
   views::View* host_ = nullptr;
@@ -166,6 +196,10 @@
   // data are module-private.
   std::unique_ptr<internal::FlexLayoutInternal> internal_;
 
+  // Default properties for any views that don't have them explicitly set for
+  // this layout.
+  PropertyHandler layout_defaults_;
+
   DISALLOW_COPY_AND_ASSIGN(FlexLayout);
 };
 
diff --git a/ui/views/layout/flex_layout_unittest.cc b/ui/views/layout/flex_layout_unittest.cc
index 53d6c2d2..9b10e13 100644
--- a/ui/views/layout/flex_layout_unittest.cc
+++ b/ui/views/layout/flex_layout_unittest.cc
@@ -235,7 +235,7 @@
   layout_->SetOrientation(LayoutOrientation::kHorizontal);
   layout_->SetCollapseMargins(false);
   layout_->SetInteriorMargin(kLayoutInsets);
-  layout_->SetDefaultChildMargins(gfx::Insets(11, 11));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(11, 11));
   EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
 }
 
@@ -376,7 +376,7 @@
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
-  layout_->SetFlexForView(child2, kDropOut);
+  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
 
   // Layout makes child view invisible due to flex rule.
   host_->SetSize(Size(40, 25));
@@ -595,7 +595,7 @@
 
   child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1));
   host_->InvalidateLayout();
-  layout_->SetDefaultChildMargins(gfx::Insets(0, 3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(0, 3));
   host_->Layout();
   expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(63, 6, 13, 11),
                                Rect(80, 5, 17, 13)};
@@ -617,7 +617,7 @@
   layout_->SetCollapseMargins(false);
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
@@ -638,7 +638,7 @@
   layout_->SetCollapseMargins(true);
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
@@ -658,7 +658,7 @@
   layout_->SetCollapseMargins(true);
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
@@ -678,7 +678,7 @@
   layout_->SetCollapseMargins(true);
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(10));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(10));
   View* child = AddChild(Size(13, 15));
   AddChild(kChild3Size);
   child->SetProperty(views::kInternalPaddingKey, Insets(1, 2, 4, 8));
@@ -697,7 +697,7 @@
   layout_->SetCollapseMargins(true);
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(2));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(2));
   View* child = AddChild(Size(13, 15));
   View* child2 = AddChild(kChild3Size);
   child->SetProperty(views::kInternalPaddingKey, Insets(1, 2, 4, 8));
@@ -717,7 +717,7 @@
   layout_->SetCollapseMargins(true);
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(20));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(20));
   View* child = AddChild(Size(13, 15));
   View* child2 = AddChild(kChild3Size);
   child->SetProperty(views::kInternalPaddingKey, Insets(1, 2, 4, 8));
@@ -819,7 +819,7 @@
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
@@ -839,7 +839,7 @@
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
@@ -859,7 +859,7 @@
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetMainAxisAlignment(LayoutAlignment::kEnd);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
@@ -883,7 +883,7 @@
   View* child2 = AddChild(Size(10, 10));
   View* child3 = AddChild(Size(10, 10));
   child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1));
-  layout_->SetFlexForView(child2, kDropOut);
+  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
   EXPECT_EQ(Size(30, 20), host_->GetMinimumSize());
 
   host_->SetSize(Size(100, 50));
@@ -907,7 +907,7 @@
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
@@ -920,7 +920,8 @@
                              Rect(51, 5, 17, 13)};
   EXPECT_EQ(expected, GetChildBounds());
 
-  layout_->SetFlexForView(child1, kDropOut);
+  child1->SetProperty(views::kFlexBehaviorKey, kDropOut);
+  host_->InvalidateLayout();
   EXPECT_EQ(Size(77, 32), host_->GetPreferredSize());
   EXPECT_EQ(Size(47, 25), host_->GetMinimumSize());
   host_->Layout();
@@ -930,8 +931,9 @@
   EXPECT_EQ(Rect(6, 5, 13, 11), child2->bounds());
   EXPECT_EQ(Rect(21, 5, 17, 13), child3->bounds());
 
-  layout_->ClearFlexForView(child1);
-  layout_->SetFlexForView(child2, kDropOut);
+  child1->ClearProperty(views::kFlexBehaviorKey);
+  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
+  host_->InvalidateLayout();
   EXPECT_EQ(Size(77, 32), host_->GetPreferredSize());
   EXPECT_EQ(Size(62, 32), host_->GetMinimumSize());
   host_->Layout();
@@ -941,8 +943,9 @@
   EXPECT_EQ(Rect(11, 10, 12, 10), child1->bounds());
   EXPECT_EQ(Rect(36, 5, 17, 13), child3->bounds());
 
-  layout_->ClearFlexForView(child2);
-  layout_->SetFlexForView(child3, kDropOut);
+  child2->ClearProperty(views::kFlexBehaviorKey);
+  child3->SetProperty(views::kFlexBehaviorKey, kDropOut);
+  host_->InvalidateLayout();
   EXPECT_EQ(Size(77, 32), host_->GetPreferredSize());
   EXPECT_EQ(Size(58, 32), host_->GetMinimumSize());
   host_->Layout();
@@ -959,7 +962,7 @@
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
@@ -967,9 +970,9 @@
   child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1));
   child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2));
   // Set flex separately; we'll test default flex later.
-  layout_->SetFlexForView(child1, kDropOut);
-  layout_->SetFlexForView(child2, kDropOut);
-  layout_->SetFlexForView(child3, kDropOut);
+  child1->SetProperty(views::kFlexBehaviorKey, kDropOut);
+  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
+  child3->SetProperty(views::kFlexBehaviorKey, kDropOut);
   EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
 
   host_->SetSize(Size(100, 50));
@@ -1011,14 +1014,14 @@
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
   child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
   child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1));
   child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2));
-  layout_->SetDefaultFlex(kDropOut);
+  layout_->SetDefault(views::kFlexBehaviorKey, kDropOut);
   EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
 
   host_->SetSize(Size(100, 50));
@@ -1059,15 +1062,15 @@
   layout_->SetInteriorMargin(kLayoutInsets);
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(3));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
   View* child1 = AddChild(kChild1Size);
   View* child2 = AddChild(kChild2Size);
   View* child3 = AddChild(kChild3Size);
   child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
   child2->SetProperty(views::kMarginsKey, Insets(1, 1, 1, 1));
   child3->SetProperty(views::kMarginsKey, Insets(2, 2, 2, 2));
-  layout_->SetDefaultFlex(kDropOut);
-  layout_->SetFlexForView(child3, kDropOutHighPriority);
+  layout_->SetDefault(views::kFlexBehaviorKey, kDropOut);
+  child3->SetProperty(views::kFlexBehaviorKey, kDropOutHighPriority);
   EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
 
   host_->SetSize(Size(100, 50));
@@ -1101,10 +1104,10 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(10, 20), Size(5, 5));
   View* child2 = AddChild(Size(10, 10));
-  layout_->SetFlexForView(child1, kFlex1ScaleToMinimum);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
 
   host_->SetSize(Size(20, 50));
   host_->Layout();
@@ -1128,10 +1131,10 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(10, 20), Size(5, 5));
   View* child2 = AddChild(Size(10, 10));
-  layout_->SetFlexForView(child1, kFlex1ScaleToMinimum);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
 
   host_->SetSize(Size(20, 20));
   host_->Layout();
@@ -1146,11 +1149,11 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(10, 20), Size(5, 5));
   View* child2 = AddChild(Size(10, 10), Size(5, 5));
-  layout_->SetFlexForView(child1, kFlex1ScaleToMinimum);
-  layout_->SetFlexForView(child2, kDropOut);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
+  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
 
   host_->SetSize(Size(20, 20));
   host_->Layout();
@@ -1165,11 +1168,11 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(10, 20), Size(5, 5));
   View* child2 = AddChild(Size(10, 10), Size(5, 5));
-  layout_->SetFlexForView(child1, kFlex1ScaleToMinimum);
-  layout_->SetFlexForView(child2, kFlex1ScaleToZero);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
+  child2->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
 
   host_->SetSize(Size(20, 19));
   host_->Layout();
@@ -1185,8 +1188,8 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
-  layout_->SetDefaultFlex(kFlex1ScaleToMinimum);
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
+  layout_->SetDefault(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
   View* child1 = AddChild(Size(20, 10), Size(5, 5));
   View* child2 = AddChild(Size(20, 10), Size(5, 5));
 
@@ -1207,8 +1210,8 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
-  layout_->SetDefaultFlex(kFlex1ScaleToMinimum);
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
+  layout_->SetDefault(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
   View* child1 = AddChild(Size(20, 10), Size(5, 5));
   View* child2 = AddChild(Size(20, 10), Size(5, 5));
 
@@ -1229,11 +1232,11 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(20, 10), Size(5, 5));
   View* child2 = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child1, kFlex1ScaleToMinimum);
-  layout_->SetFlexForView(child2, kFlex2ScaleToMinimum);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
+  child2->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);
 
   host_->SetSize(Size(45, 20));
   host_->Layout();
@@ -1247,11 +1250,11 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(20, 10), Size(5, 5));
   View* child2 = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child1, kFlex2ScaleToMinimum);
-  layout_->SetFlexForView(child2, kFlex1ScaleToMinimum);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);
+  child2->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
 
   host_->SetSize(Size(50, 20));
   host_->Layout();
@@ -1265,11 +1268,11 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(20, 10), Size(5, 5));
   View* child2 = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child1, kFlex1ScaleToMinimum);
-  layout_->SetFlexForView(child2, kFlex2ScaleToMinimum);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
+  child2->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);
 
   host_->SetSize(Size(50, 20));
   host_->Layout();
@@ -1286,11 +1289,12 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(20, 10), Size(5, 5));
   View* child2 = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child1, kFlex1ScaleToMinimum);
-  layout_->SetFlexForView(child2, kFlex1ScaleToMinimumHighPriority);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
+  child2->SetProperty(views::kFlexBehaviorKey,
+                      kFlex1ScaleToMinimumHighPriority);
 
   host_->SetSize(Size(50, 20));
   host_->Layout();
@@ -1310,11 +1314,12 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child1 = AddChild(Size(20, 10), Size(5, 5));
   View* child2 = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child1, kFlex1ScaleToZero);
-  layout_->SetFlexForView(child2, kFlex1ScaleToMinimumHighPriority);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
+  child2->SetProperty(views::kFlexBehaviorKey,
+                      kFlex1ScaleToMinimumHighPriority);
 
   host_->SetSize(Size(35, 20));
   host_->Layout();
@@ -1328,9 +1333,9 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child, kUnboundedSnapToMinimum);
+  child->SetProperty(views::kFlexBehaviorKey, kUnboundedSnapToMinimum);
 
   host_->SetSize(Size(35, 25));
   host_->Layout();
@@ -1361,9 +1366,10 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child, kUnboundedScaleToMinimumSnapToZero);
+  child->SetProperty(views::kFlexBehaviorKey,
+                     kUnboundedScaleToMinimumSnapToZero);
 
   host_->SetSize(Size(35, 25));
   host_->Layout();
@@ -1403,11 +1409,11 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   // Because we are using a flex rule that scales all the way to zero, ensure
   // that the child view's minimum size is *not* respected.
   View* child = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child, kUnboundedScaleToZero);
+  child->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);
 
   host_->SetSize(Size(35, 25));
   host_->Layout();
@@ -1456,9 +1462,10 @@
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
   View* child1 = AddChild(kLargeSize, kSmallSize);
-  layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority);
+  child1->SetProperty(views::kFlexBehaviorKey,
+                      kUnboundedScaleToMinimumHighPriority);
   View* child2 = AddChild(kSmallSize);
-  layout_->SetFlexForView(child2, kDropOut);
+  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
 
   // When there is no room for the second view, it drops out.
   host_->SetSize(Size(4, 5));
@@ -1494,9 +1501,10 @@
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
   View* child1 = AddChild(kLargeSize, kSmallSize);
-  layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority);
+  child1->SetProperty(views::kFlexBehaviorKey,
+                      kUnboundedScaleToMinimumHighPriority);
   View* child2 = AddChild(kSmallSize);
-  layout_->SetFlexForView(child2, kDropOut);
+  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
 
   constexpr Size kEnoughSpace(kSmallSize.width() + kLargeSize.width(),
                               kLargeSize.height());
@@ -1517,9 +1525,10 @@
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
   View* child1 = AddChild(kLargeSize, kSmallSize);
-  layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority);
+  child1->SetProperty(views::kFlexBehaviorKey,
+                      kUnboundedScaleToMinimumHighPriority);
   View* child2 = AddChild(kSmallSize);
-  layout_->SetFlexForView(child2, kDropOut);
+  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
 
   constexpr int kExtra = 7;
   constexpr Size kExtraSpace(kSmallSize.width() + kLargeSize.width() + kExtra,
@@ -1544,9 +1553,11 @@
   // Because we are using a flex rule that scales all the way to zero, ensure
   // that the child view's minimum size is *not* respected.
   View* child1 = AddChild(kLargeSize, kSmallSize);
-  layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority);
+  child1->SetProperty(views::kFlexBehaviorKey,
+                      kUnboundedScaleToMinimumHighPriority);
   View* child2 = AddChild(kLargeSize, kSmallSize);
-  layout_->SetFlexForView(child2, kUnboundedScaleToMinimumHighPriority);
+  child2->SetProperty(views::kFlexBehaviorKey,
+                      kUnboundedScaleToMinimumHighPriority);
 
   constexpr int kExtra = 8;
   constexpr Size kExtraSpace(2 * kLargeSize.width() + kExtra,
@@ -1572,9 +1583,10 @@
   // Because we are using a flex rule that scales all the way to zero, ensure
   // that the child view's minimum size is *not* respected.
   View* child1 = AddChild(kLargeSize, kSmallSize);
-  layout_->SetFlexForView(child1, kUnboundedScaleToMinimumHighPriority);
+  child1->SetProperty(views::kFlexBehaviorKey,
+                      kUnboundedScaleToMinimumHighPriority);
   View* child2 = AddChild(kLargeSize, kSmallSize);
-  layout_->SetFlexForView(child2, kUnboundedScaleToMinimum);
+  child2->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToMinimum);
 
   constexpr int kExtra = 8;
   constexpr Size kExtraSpace(2 * kLargeSize.width() + kExtra,
@@ -1594,9 +1606,9 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child = AddChild(Size(kFullSize, kFullSize));
-  layout_->SetFlexForView(child, kCustomFlex);
+  child->SetProperty(views::kFlexBehaviorKey, kCustomFlex);
 
   host_->SetSize(Size(100, 100));
   host_->Layout();
@@ -1629,10 +1641,10 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child = AddChild(Size(kFullSize, kFullSize));
   AddChild(Size(10, 10));
-  layout_->SetFlexForView(child, kCustomFlex);
+  child->SetProperty(views::kFlexBehaviorKey, kCustomFlex);
 
   host_->SetSize(Size(100, 100));
   host_->Layout();
@@ -1660,9 +1672,9 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   View* child = AddChild(Size(kFullSize, kFullSize));
-  layout_->SetFlexForView(child, kCustomFlexSnapToZero);
+  child->SetProperty(views::kFlexBehaviorKey, kCustomFlexSnapToZero);
 
   host_->SetSize(Size(100, 100));
   host_->Layout();
@@ -1691,11 +1703,12 @@
   layout_->SetInteriorMargin(Insets(5));
   layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
   layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
-  layout_->SetDefaultChildMargins(gfx::Insets(5));
+  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
   MockView* child1 = AddChild(Size(20, 10), Size(5, 5));
   MockView* child2 = AddChild(Size(20, 10), Size(5, 5));
-  layout_->SetFlexForView(child1, kFlex1ScaleToZero);
-  layout_->SetFlexForView(child2, kFlex1ScaleToMinimumHighPriority);
+  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
+  child2->SetProperty(views::kFlexBehaviorKey,
+                      kFlex1ScaleToMinimumHighPriority);
 
   child1->ResetCounts();
   child2->ResetCounts();
@@ -1889,19 +1902,19 @@
   layout_->SetOrientation(LayoutOrientation::kHorizontal)
       .SetCollapseMargins(false)
       .SetCrossAxisAlignment(LayoutAlignment::kStart)
-      .SetDefaultChildMargins(gfx::Insets(2, 3, 4, 5))
+      .SetDefault(views::kMarginsKey, gfx::Insets(2, 3, 4, 5))
       .SetInteriorMargin(gfx::Insets(4, 3, 2, 1));
 
   layout(1)
       ->SetOrientation(LayoutOrientation::kVertical)
       .SetCollapseMargins(true)
-      .SetDefaultChildMargins(gfx::Insets(2))
+      .SetDefault(views::kMarginsKey, gfx::Insets(2))
       .SetInteriorMargin(gfx::Insets(1));
 
   layout(2)
       ->SetOrientation(LayoutOrientation::kVertical)
       .SetCollapseMargins(true)
-      .SetDefaultChildMargins(gfx::Insets(1))
+      .SetDefault(views::kMarginsKey, gfx::Insets(1))
       .SetInteriorMargin(gfx::Insets(2));
 
   EXPECT_EQ(gfx::Size(39, 29), host_->GetPreferredSize());
@@ -1925,19 +1938,19 @@
   layout_->SetOrientation(LayoutOrientation::kHorizontal)
       .SetCollapseMargins(false)
       .SetCrossAxisAlignment(LayoutAlignment::kStart)
-      .SetDefaultChildMargins(gfx::Insets(2, 3, 4, 5))
+      .SetDefault(views::kMarginsKey, gfx::Insets(2, 3, 4, 5))
       .SetInteriorMargin(gfx::Insets(4, 3, 2, 1));
 
   layout(1)
       ->SetOrientation(LayoutOrientation::kHorizontal)
       .SetCollapseMargins(true)
-      .SetDefaultChildMargins(gfx::Insets(2))
+      .SetDefault(views::kMarginsKey, gfx::Insets(2))
       .SetInteriorMargin(gfx::Insets(1));
 
   layout(2)
       ->SetOrientation(LayoutOrientation::kHorizontal)
       .SetCollapseMargins(true)
-      .SetDefaultChildMargins(gfx::Insets(1))
+      .SetDefault(views::kMarginsKey, gfx::Insets(1))
       .SetInteriorMargin(gfx::Insets(2));
 
   EXPECT_EQ(gfx::Size(53, 22), host_->GetPreferredSize());
@@ -1964,26 +1977,26 @@
   layout_->SetOrientation(LayoutOrientation::kHorizontal)
       .SetCollapseMargins(true)
       .SetCrossAxisAlignment(LayoutAlignment::kStart)
-      .SetDefaultChildMargins(gfx::Insets(2))
-      .SetInteriorMargin(gfx::Insets(2))
-      .SetFlexForView(child(1), flex_specification)
-      .SetFlexForView(child(2), flex_specification);
+      .SetDefault(views::kMarginsKey, gfx::Insets(2))
+      .SetInteriorMargin(gfx::Insets(2));
+  child(1)->SetProperty(views::kFlexBehaviorKey, flex_specification);
+  child(2)->SetProperty(views::kFlexBehaviorKey, flex_specification);
 
   layout(1)
       ->SetOrientation(LayoutOrientation::kHorizontal)
       .SetCollapseMargins(true)
-      .SetDefaultChildMargins(gfx::Insets(2))
-      .SetInteriorMargin(gfx::Insets(2))
-      .SetFlexForView(grandchild(1, 1), flex_specification)
-      .SetFlexForView(grandchild(1, 2), flex_specification);
+      .SetDefault(views::kMarginsKey, gfx::Insets(2))
+      .SetInteriorMargin(gfx::Insets(2));
+  grandchild(1, 1)->SetProperty(views::kFlexBehaviorKey, flex_specification);
+  grandchild(1, 2)->SetProperty(views::kFlexBehaviorKey, flex_specification);
 
   layout(2)
       ->SetOrientation(LayoutOrientation::kHorizontal)
       .SetCollapseMargins(true)
-      .SetDefaultChildMargins(gfx::Insets(2))
-      .SetInteriorMargin(gfx::Insets(2))
-      .SetFlexForView(grandchild(2, 1), flex_specification)
-      .SetFlexForView(grandchild(2, 2), flex_specification);
+      .SetDefault(views::kMarginsKey, gfx::Insets(2))
+      .SetInteriorMargin(gfx::Insets(2));
+  grandchild(2, 1)->SetProperty(views::kFlexBehaviorKey, flex_specification);
+  grandchild(2, 2)->SetProperty(views::kFlexBehaviorKey, flex_specification);
 
   EXPECT_EQ(gfx::Size(40, 14), host_->GetPreferredSize());
   host_->SetSize(gfx::Size(20, 15));
diff --git a/ui/views/layout/grid_layout.cc b/ui/views/layout/grid_layout.cc
index 3bbb5f25..9056d52 100644
--- a/ui/views/layout/grid_layout.cc
+++ b/ui/views/layout/grid_layout.cc
@@ -816,37 +816,67 @@
   SkipPaddingColumns();
 }
 
-void GridLayout::AddView(View* view, int col_span, int row_span) {
-  DCHECK(host_);
+void GridLayout::AddExistingView(View* view, int col_span, int row_span) {
+  DCHECK(view->parent() && view->parent() == host_)
+      << "Use AddView() to add a new View that isn't already parented to "
+         "|host_|.";
   DCHECK(current_row_col_set_ &&
          next_column_ < current_row_col_set_->num_columns());
   Column* column = current_row_col_set_->columns_[next_column_].get();
-  AddView(view, col_span, row_span, column->h_align(), column->v_align());
+  AddExistingView(view, col_span, row_span, column->h_align(),
+                  column->v_align());
 }
 
-void GridLayout::AddView(View* view,
-                         int col_span,
-                         int row_span,
-                         Alignment h_align,
-                         Alignment v_align,
-                         int pref_width,
-                         int pref_height) {
+void GridLayout::AddViewImpl(std::unique_ptr<View> view,
+                             int col_span,
+                             int row_span) {
+  DCHECK(current_row_col_set_ &&
+         next_column_ < current_row_col_set_->num_columns());
+  Column* column = current_row_col_set_->columns_[next_column_].get();
+  AddViewImpl(std::move(view), col_span, row_span, column->h_align(),
+              column->v_align(), 0, 0);
+}
+
+void GridLayout::AddExistingView(View* view,
+                                 int col_span,
+                                 int row_span,
+                                 Alignment h_align,
+                                 Alignment v_align,
+                                 int pref_width,
+                                 int pref_height) {
+  DCHECK(view->parent() && view->parent() == host_)
+      << "Use AddView() to add a new View that isn't already parented to "
+         "|host_|.";
   DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 &&
          (next_column_ + col_span) <= current_row_col_set_->num_columns());
   // We don't support baseline alignment of views spanning rows. Please add if
   // you need it.
   DCHECK(v_align != BASELINE || row_span == 1);
-  DCHECK(view && (view->parent() == nullptr || view->parent() == host_));
-  if (!view->parent()) {
-    adding_view_ = true;
-    host_->AddChildView(view);
-    adding_view_ = false;
-  }
   AddViewState(std::make_unique<ViewState>(
       current_row_col_set_, view, next_column_, current_row_, col_span,
       row_span, h_align, v_align, pref_width, pref_height));
 }
 
+void GridLayout::AddViewImpl(std::unique_ptr<View> view,
+                             int col_span,
+                             int row_span,
+                             Alignment h_align,
+                             Alignment v_align,
+                             int pref_width,
+                             int pref_height) {
+  DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 &&
+         (next_column_ + col_span) <= current_row_col_set_->num_columns());
+  // We don't support baseline alignment of views spanning rows. Please add if
+  // you need it.
+  DCHECK(v_align != BASELINE || row_span == 1);
+  adding_view_ = true;
+  View* view_ptr = host_->AddChildView(std::move(view));
+  adding_view_ = false;
+  AddViewState(std::make_unique<ViewState>(
+      current_row_col_set_, view_ptr, next_column_, current_row_, col_span,
+      row_span, h_align, v_align, pref_width, pref_height));
+}
+
 static void CalculateSize(int pref_size, GridLayout::Alignment alignment,
                           int* location, int* size) {
   if (alignment != GridLayout::FILL) {
@@ -1046,37 +1076,6 @@
   }
 }
 
-void GridLayout::AddViewImpl(std::unique_ptr<View> view,
-                             int col_span,
-                             int row_span) {
-  DCHECK(current_row_col_set_ &&
-         next_column_ < current_row_col_set_->num_columns());
-  Column* column = current_row_col_set_->columns_[next_column_].get();
-  AddViewImpl(std::move(view), col_span, row_span, column->h_align(),
-              column->v_align(), 0, 0);
-}
-
-void GridLayout::AddViewImpl(std::unique_ptr<View> view,
-                             int col_span,
-                             int row_span,
-                             Alignment h_align,
-                             Alignment v_align,
-                             int pref_width,
-                             int pref_height) {
-  DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 &&
-         (next_column_ + col_span) <= current_row_col_set_->num_columns());
-  // We don't support baseline alignment of views spanning rows. Please add if
-  // you need it.
-  DCHECK(v_align != BASELINE || row_span == 1);
-  DCHECK(view && view->parent() == nullptr);
-  adding_view_ = true;
-  View* view_ptr = host_->AddChildView(std::move(view));
-  adding_view_ = false;
-  AddViewState(std::make_unique<ViewState>(
-      current_row_col_set_, view_ptr, next_column_, current_row_, col_span,
-      row_span, h_align, v_align, pref_width, pref_height));
-}
-
 void GridLayout::AddViewState(std::unique_ptr<ViewState> view_state) {
   DCHECK(view_state->view && view_state->view->parent() == host_);
   remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span);
diff --git a/ui/views/layout/grid_layout.h b/ui/views/layout/grid_layout.h
index 2b783f8..3d591436 100644
--- a/ui/views/layout/grid_layout.h
+++ b/ui/views/layout/grid_layout.h
@@ -156,7 +156,8 @@
   }
 
   // Adds a view to the layout using the default alignment from the column.
-  void AddView(View* view, int col_span = 1, int row_span = 1);
+  // NOTE: The |view| must already be present and owned by the host.
+  void AddExistingView(View* view, int col_span = 1, int row_span = 1);
 
   // Adds a view with the specified alignment and spans. If
   // pref_width/pref_height is > 0 then the preferred width/height of the view
@@ -180,15 +181,14 @@
   // Adds a view to the layout with the specified alignment and spans. If
   // pref_width/pref_height is > 0 then the preferred width/height of the view
   // is fixed to the specified value.
-  // TODO: Rename to AddViewToLayout() once call sites are refactored to prefer
-  //       the AddView<T> version above.
-  void AddView(View* view,
-               int col_span,
-               int row_span,
-               Alignment h_align,
-               Alignment v_align,
-               int pref_width = 0,
-               int pref_height = 0);
+  // NOTE: The |view| must already be present and owned by the host;
+  void AddExistingView(View* view,
+                       int col_span,
+                       int row_span,
+                       Alignment h_align,
+                       Alignment v_align,
+                       int pref_width = 0,
+                       int pref_height = 0);
 
   // Notification we've been installed on a particular host. Checks that host
   // is the same as the View supplied in the constructor.
diff --git a/ui/views/view.cc b/ui/views/view.cc
index bb9a41a..29bf95bf 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -25,7 +25,6 @@
 #include "ui/base/ime/input_method.h"
 #include "ui/compositor/clip_recorder.h"
 #include "ui/compositor/compositor.h"
-#include "ui/compositor/dip_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/paint_context.h"
@@ -2376,10 +2375,6 @@
       for (ui::Layer* layer_beneath : layers_beneath_)
         layer_beneath->SetSubpixelPositionOffset(
             offset_data.GetSubpixelOffset());
-    } else {
-      ui::SnapLayerToPhysicalPixelBoundary(layer()->parent(), layer());
-      for (ui::Layer* layer_beneath : layers_beneath_)
-        ui::SnapLayerToPhysicalPixelBoundary(layer()->parent(), layer_beneath);
     }
   } else {
     // Reset the offset.
diff --git a/ui/views/view_class_properties.cc b/ui/views/view_class_properties.cc
index 3a84000..f226564c 100644
--- a/ui/views/view_class_properties.cc
+++ b/ui/views/view_class_properties.cc
@@ -7,6 +7,7 @@
 #include "ui/base/hit_test.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/layout/flex_layout_types.h"
 
 #if !defined(USE_AURA)
 // aura_constants.cc also declared the bool and int[32_t]
@@ -21,6 +22,7 @@
                                        views::BubbleDialogDelegateView*)
 
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, SkPath*)
+DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::FlexSpecification*)
 
 namespace views {
 
@@ -31,5 +33,6 @@
                              kAnchoredDialogKey,
                              nullptr)
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(SkPath, kHighlightPathKey, nullptr)
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(FlexSpecification, kFlexBehaviorKey, nullptr)
 
 }  // namespace views
diff --git a/ui/views/view_class_properties.h b/ui/views/view_class_properties.h
index e6f6782..a92096e 100644
--- a/ui/views/view_class_properties.h
+++ b/ui/views/view_class_properties.h
@@ -17,6 +17,7 @@
 namespace views {
 
 class BubbleDialogDelegateView;
+class FlexSpecification;
 
 // The hit test component (e.g. HTCLIENT) for a View in a window frame. Defaults
 // to HTNOWHERE.
@@ -49,6 +50,11 @@
 // the view in different ways.
 VIEWS_EXPORT extern const ui::ClassProperty<SkPath*>* const kHighlightPathKey;
 
+// A property to store how a view should flex when placed in a layout.
+// Currently only supported by FlexLayout.
+VIEWS_EXPORT extern const ui::ClassProperty<FlexSpecification*>* const
+    kFlexBehaviorKey;
+
 }  // namespace views
 
 // Declaring the template specialization here to make sure that the
@@ -60,4 +66,5 @@
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT,
                                         views::BubbleDialogDelegateView*)
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, SkPath*)
+DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::FlexSpecification*)
 #endif  // UI_VIEWS_VIEW_CLASS_PROPERTIES_H_
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index e321103..2487b61 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -4523,31 +4523,31 @@
   v1->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
   v11->SetPaintToLayer();
 
-  EXPECT_EQ("0.40 0.40", ToString(v11->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.40 0.40", ToString(v11->layer()->GetSubpixelOffset()));
 
   // Creating a layer in parent should update the child view's layer offset.
   v1->SetPaintToLayer();
-  EXPECT_EQ("-0.20 -0.20", ToString(v1->layer()->subpixel_position_offset()));
-  EXPECT_EQ("-0.20 -0.20", ToString(v11->layer()->subpixel_position_offset()));
+  EXPECT_EQ("-0.20 -0.20", ToString(v1->layer()->GetSubpixelOffset()));
+  EXPECT_EQ("-0.20 -0.20", ToString(v11->layer()->GetSubpixelOffset()));
 
   // DSF change should get propagated and update offsets.
   GetRootLayer()->GetCompositor()->SetScaleAndSize(
       1.5f, size, allocator.GetCurrentLocalSurfaceIdAllocation());
-  EXPECT_EQ("0.33 0.33", ToString(v1->layer()->subpixel_position_offset()));
-  EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.33 0.33", ToString(v1->layer()->GetSubpixelOffset()));
+  EXPECT_EQ("0.33 0.33", ToString(v11->layer()->GetSubpixelOffset()));
 
   // Deleting parent's layer should update the child view's layer's offset.
   v1->DestroyLayer();
-  EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.00 0.00", ToString(v11->layer()->GetSubpixelOffset()));
 
   // Setting parent view should update the child view's layer's offset.
   v1->SetBoundsRect(gfx::Rect(2, 2, 10, 10));
-  EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.33 0.33", ToString(v11->layer()->GetSubpixelOffset()));
 
   // Setting integral DSF should reset the offset.
   GetRootLayer()->GetCompositor()->SetScaleAndSize(
       2.0f, size, allocator.GetCurrentLocalSurfaceIdAllocation());
-  EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.00 0.00", ToString(v11->layer()->GetSubpixelOffset()));
 }
 
 TEST_F(ViewLayerTest, LayerBeneathTriggersPaintToLayer) {
@@ -4601,9 +4601,8 @@
   view->AddLayerBeneathView(&layer);
 
   view->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
-  EXPECT_NE(gfx::Vector2dF(), view->layer()->subpixel_position_offset());
-  EXPECT_EQ(view->layer()->subpixel_position_offset(),
-            layer.subpixel_position_offset());
+  EXPECT_NE(gfx::Vector2dF(), view->layer()->GetSubpixelOffset());
+  EXPECT_EQ(view->layer()->GetSubpixelOffset(), layer.GetSubpixelOffset());
 
   view->RemoveLayerBeneathView(&layer);
 }
@@ -4873,19 +4872,19 @@
   v1->SetBoundsRect(gfx::Rect(9, 9, 100, 100));
 
   PaintRecordingSizeTest(v3, gfx::Size(21, 8));  // Enclosing Rect = (21, 8)
-  EXPECT_EQ("-0.63 -0.25", ToString(v3->layer()->subpixel_position_offset()));
+  EXPECT_EQ("-0.63 -0.25", ToString(v3->layer()->GetSubpixelOffset()));
 
   // Creating a layer in parent should update the child view's layer offset.
   v1->SetPaintToLayer();
-  EXPECT_EQ("-0.25 -0.25", ToString(v1->layer()->subpixel_position_offset()));
-  EXPECT_EQ("-0.37 -0.00", ToString(v3->layer()->subpixel_position_offset()));
+  EXPECT_EQ("-0.25 -0.25", ToString(v1->layer()->GetSubpixelOffset()));
+  EXPECT_EQ("-0.37 -0.00", ToString(v3->layer()->GetSubpixelOffset()));
 
   // DSF change should get propagated and update offsets.
   GetRootLayer()->GetCompositor()->SetScaleAndSize(
       1.5f, size, allocator.GetCurrentLocalSurfaceIdAllocation());
 
-  EXPECT_EQ("0.33 0.33", ToString(v1->layer()->subpixel_position_offset()));
-  EXPECT_EQ("0.33 0.67", ToString(v3->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.33 0.33", ToString(v1->layer()->GetSubpixelOffset()));
+  EXPECT_EQ("0.33 0.67", ToString(v3->layer()->GetSubpixelOffset()));
 
   v1->DestroyLayer();
   PaintRecordingSizeTest(v3, gfx::Size(20, 7));  // Enclosing Rect = (20, 8)
@@ -4894,23 +4893,23 @@
   GetRootLayer()->GetCompositor()->SetScaleAndSize(
       1.33f, size, allocator.GetCurrentLocalSurfaceIdAllocation());
 
-  EXPECT_EQ("0.02 0.02", ToString(v1->layer()->subpixel_position_offset()));
-  EXPECT_EQ("0.05 -0.45", ToString(v3->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.02 0.02", ToString(v1->layer()->GetSubpixelOffset()));
+  EXPECT_EQ("0.05 -0.45", ToString(v3->layer()->GetSubpixelOffset()));
 
   v1->DestroyLayer();
   PaintRecordingSizeTest(v3, gfx::Size(17, 7));  // Enclosing Rect = (18, 7)
 
   // Deleting parent's layer should update the child view's layer's offset.
-  EXPECT_EQ("0.08 -0.43", ToString(v3->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.08 -0.43", ToString(v3->layer()->GetSubpixelOffset()));
 
   // Setting parent view should update the child view's layer's offset.
   v1->SetBoundsRect(gfx::Rect(3, 3, 10, 10));
-  EXPECT_EQ("0.06 -0.44", ToString(v3->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.06 -0.44", ToString(v3->layer()->GetSubpixelOffset()));
 
   // Setting integral DSF should reset the offset.
   GetRootLayer()->GetCompositor()->SetScaleAndSize(
       2.0f, size, allocator.GetCurrentLocalSurfaceIdAllocation());
-  EXPECT_EQ("0.00 0.00", ToString(v3->layer()->subpixel_position_offset()));
+  EXPECT_EQ("0.00 0.00", ToString(v3->layer()->GetSubpixelOffset()));
 }
 
 TEST_F(ViewLayerPixelCanvasTest, LayerBeneathOnPixelCanvas) {
@@ -4929,9 +4928,8 @@
   view->AddLayerBeneathView(&layer);
 
   view->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
-  EXPECT_NE(gfx::Vector2dF(), view->layer()->subpixel_position_offset());
-  EXPECT_EQ(view->layer()->subpixel_position_offset(),
-            layer.subpixel_position_offset());
+  EXPECT_NE(gfx::Vector2dF(), view->layer()->GetSubpixelOffset());
+  EXPECT_EQ(view->layer()->GetSubpixelOffset(), layer.GetSubpixelOffset());
 
   view->RemoveLayerBeneathView(&layer);
 }
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc
index 85d907cc..17ca9c0 100644
--- a/ui/views/widget/root_view.cc
+++ b/ui/views/widget/root_view.cc
@@ -172,8 +172,7 @@
 RootView::~RootView() {
   // If we have children remove them explicitly so to make sure a remove
   // notification is sent for each one of them.
-  if (!children().empty())
-    RemoveAllChildViews(true);
+  RemoveAllChildViews(true);
 }
 
 // Tree operations -------------------------------------------------------------
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 3541c2d..aa80b44 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -1514,6 +1514,10 @@
     focus_manager_->SetFocusedView(nullptr);
   NotifyWillRemoveView(root_view_.get());
   non_client_view_ = nullptr;
+  // Remove all children before the unique_ptr reset so that
+  // GetWidget()->GetRootView() doesn't return nullptr while the views hierarchy
+  // is being torn down.
+  root_view_->RemoveAllChildViews(true);
   root_view_.reset();
 }
 
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 4f7b6667..ef9275f 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -2977,15 +2977,28 @@
         base::TimeDelta::FromMilliseconds(kTouchDownContextResetTimeout));
   }
 
-  size_t mapped_pointer_id = id_generator_.GetGeneratedID(pointer_id);
   POINTER_INFO pointer_info = pointer_touch_info.pointerInfo;
+  POINTER_FLAGS pointer_flags = pointer_info.pointerFlags;
+
+  // When there are touch move events but no finger is pressing on the screen,
+  // which most likely happens for smart board, we should ignore these events
+  // for now. POINTER_FLAG_INCONTACT indicates this pointer is in contact with
+  // the digitizer surface, which means pressing the screen.
+  // POINTER_FLAG_INRANGE indicates the pointer is in the detection range of
+  // the screen.
+  if ((message == WM_POINTERUPDATE) &&
+      !(pointer_flags & POINTER_FLAG_INCONTACT) &&
+      (pointer_flags & POINTER_FLAG_INRANGE)) {
+    SetMsgHandled(TRUE);
+    return 0;
+  }
+
   POINT client_point = pointer_info.ptPixelLocationRaw;
   ScreenToClient(hwnd(), &client_point);
   gfx::Point touch_point = gfx::Point(client_point.x, client_point.y);
-
-  POINTER_FLAGS pointer_flags = pointer_info.pointerFlags;
   ui::EventType event_type = GetTouchEventType(pointer_flags);
   const base::TimeTicks event_time = ui::EventTimeForNow();
+  size_t mapped_pointer_id = id_generator_.GetGeneratedID(pointer_id);
 
   // The pressure from POINTER_TOUCH_INFO is normalized to a range between 0
   // and 1024, but we define the pressure of the range of [0,1].
@@ -3011,17 +3024,16 @@
   event.latency()->AddLatencyNumberWithTimestamp(
       ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time, 1);
 
+  // Release the pointer id for touch release events.
+  if (event_type == ui::ET_TOUCH_RELEASED)
+    id_generator_.ReleaseNumber(pointer_id);
+
   // There are cases where the code handling the message destroys the
   // window, so use the weak ptr to check if destruction occurred or not.
   base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
   delegate_->HandleTouchEvent(&event);
 
   if (ref) {
-    // Release the pointer id only when |HWNDMessageHandler| and |id_generator_|
-    // are not destroyed.
-    if (event_type == ui::ET_TOUCH_RELEASED)
-      id_generator_.ReleaseNumber(pointer_id);
-
     // Mark touch released events handled. These will usually turn into tap
     // gestures, and doing this avoids propagating the event to other windows.
     if (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) {
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index 3bcb2e1..f4d25f2f 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -77,8 +77,8 @@
   // Doing this now ensures this accelerator will have lower priority than
   // one set by the contents view.
   AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
-  button_row_container_ = new ButtonRowContainer(this);
-  AddChildView(button_row_container_);
+  button_row_container_ =
+      AddChildView(std::make_unique<ButtonRowContainer>(this));
 }
 
 DialogClientView::~DialogClientView() {
@@ -296,7 +296,7 @@
     // MdTextButton, make it so. Note that some overrides may not always update
     // the title (they should). See http://crbug.com/697303 .
     const base::string16 title = delegate->GetDialogButtonLabel(type);
-    std::unique_ptr<LabelButton> button = nullptr;
+    std::unique_ptr<LabelButton> button;
 
     const bool is_default = delegate->GetDefaultDialogButton() == type &&
                             (type != ui::DIALOG_BUTTON_CANCEL ||
@@ -311,7 +311,7 @@
 
     button->SetGroup(kButtonGroup);
 
-    *member = button.release();
+    *member = button_row_container_->AddChildView(std::move(button));
   }
 
   delegate->UpdateButton(*member, type);
@@ -346,19 +346,26 @@
 
   // Clobber any existing LayoutManager since it has weak references to child
   // Views which may be removed by SetupViews().
-  GridLayout* layout = button_row_container_->SetLayoutManager(
-      std::make_unique<views::GridLayout>());
-  layout->set_minimum_size(minimum_size_);
+  button_row_container_->SetLayoutManager(nullptr);
 
   SetupViews();
+
   const std::array<View*, kNumButtons> views = GetButtonRowViews();
 
   // Visibility changes on |extra_view_| must be observed to re-Layout. However,
   // when hidden it's not included in the button row (it can't influence layout)
   // and it can't be added to |button_row_container_| (GridLayout complains).
   // So add it, hidden, to |this| so it can be observed.
-  if (extra_view_ && !views[0])
-    AddChildView(extra_view_);
+  if (extra_view_) {
+    if (!views[0])
+      AddChildView(extra_view_);
+    else
+      button_row_container_->AddChildViewAt(extra_view_, 0);
+  }
+
+  GridLayout* layout = button_row_container_->SetLayoutManager(
+      std::make_unique<views::GridLayout>());
+  layout->set_minimum_size(minimum_size_);
 
   if (std::count(views.begin(), views.end(), nullptr) == kNumButtons)
     return;
@@ -402,7 +409,7 @@
                               button_row_insets_.top());
   for (size_t view_index = 0; view_index < kNumButtons; ++view_index) {
     if (views[view_index]) {
-      layout->AddView(views[view_index]);
+      layout->AddExistingView(views[view_index]);
       link[link_index++] = kViewToColumnIndex[view_index];
     } else {
       layout->SkipColumns(1);
@@ -437,14 +444,13 @@
 }
 
 void DialogClientView::SetupViews() {
-  button_row_container_->RemoveAllChildViews(false /* delete children */);
-  // If SetupLayout() "stored" a hidden |extra_view_| in |this|, ensure it can
-  // be re-added to the layout when becoming visible.
-  if (extra_view_)
-    RemoveChildView(extra_view_);
-
-  UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK);
-  UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL);
+  if (PlatformStyle::kIsOkButtonLeading) {
+    UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK);
+    UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL);
+  } else {
+    UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL);
+    UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK);
+  }
 
   if (extra_view_)
     return;
diff --git a/ui/web_dialogs/web_dialog_delegate.cc b/ui/web_dialogs/web_dialog_delegate.cc
index 1ec8b495..37fc063 100644
--- a/ui/web_dialogs/web_dialog_delegate.cc
+++ b/ui/web_dialogs/web_dialog_delegate.cc
@@ -28,6 +28,10 @@
   return true;
 }
 
+bool WebDialogDelegate::OnDialogCloseRequested() {
+  return true;
+}
+
 void WebDialogDelegate::OnDialogCloseFromWebUI(
     const std::string& json_retval) {
   OnDialogClosed(json_retval);
diff --git a/ui/web_dialogs/web_dialog_delegate.h b/ui/web_dialogs/web_dialog_delegate.h
index be2c9d7..c83bedc 100644
--- a/ui/web_dialogs/web_dialog_delegate.h
+++ b/ui/web_dialogs/web_dialog_delegate.h
@@ -91,6 +91,11 @@
   virtual void OnDialogShown(content::WebUI* webui,
                              content::RenderViewHost* render_view_host) {}
 
+  // A callback to notify the delegate that the window is requesting to be
+  // closed.  If this returns true, the dialog is closed, otherwise the
+  // dialog remains open. Default implementation returns true.
+  virtual bool OnDialogCloseRequested();
+
   // A callback to notify the delegate that the dialog is about to close due to
   // the user pressing the ESC key.
   virtual void OnDialogClosingFromKeyEvent() {}
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html
index 7dd645e..37a72488 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html
@@ -2,7 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html">
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html b/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
index c57826eb..6c8fdd3 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="network_property_list.html">
 <link rel="import" href="network_shared_css.html">
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.html b/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.html
index 99b26fa..52edc82 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="network_shared_css.html">
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.html b/ui/webui/resources/cr_components/chromeos/network/network_config.html
index 783d1c3..b18cc73f 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.html
@@ -1,7 +1,11 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
+<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
@@ -14,7 +18,7 @@
 
 <dom-module id="network-config">
   <template>
-    <style include="network-shared iron-flex">
+    <style include="network-shared settings-shared action-link iron-flex">
       #spinner-container {
         height: 200px;
       }
@@ -185,6 +189,32 @@
           </cr-toggle>
         </div>
       </template>
+
+      <!-- AutoConnect (WiFi) -->
+      <template is="dom-if" if="[[configCanAutoConnect_(type)]]" restamp>
+        <div class="property-box">
+          <div id="autoConnectLabel" class="start">
+            [[i18n('networkAutoConnect')]]
+          </div>
+          <template is="dom-if"
+              if="[[isAutoConnectEnforcedByPolicy_(globalPolicy)]]" restamp>
+            <cr-policy-indicator indicator-type="devicePolicy">
+            </cr-policy-indicator>
+          </template>
+          <cr-toggle id="autoConnect" checked="{{autoConnect_}}"
+              disabled="[[autoConnectDisabled_(globalPolicy)]]"
+              aria-labeledby="autoConnectLabel">
+          </cr-toggle>
+        </div>
+      </template>
+
+      <!-- Hidden Network Warning -->
+      <template is="dom-if" if="{{hiddenNetworkWarning_}}" restamp>
+        <div>
+          <iron-icon icon="cr:warning"></iron-icon>
+          [[i18nAdvanced('hiddenNetworkWarning')]]
+        </div>
+      </template>
     </template>
   </template>
   <script src="network_config.js"></script>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.js b/ui/webui/resources/cr_components/chromeos/network/network_config.js
index 615cc938..58fb3bd7 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -191,6 +191,18 @@
     },
 
     /**
+     * Whether the device should automatically connect to the network.
+     * @private
+     */
+    autoConnect_: Boolean,
+
+    /**
+     * Whether or not to show the hidden network warning.
+     * @private
+     */
+    hiddenNetworkWarning_: Boolean,
+
+    /**
      * Security value, used for Ethernet and Wifi and to detect when Security
      * changes.
      * @private
@@ -337,6 +349,7 @@
   observers: [
     'setEnableConnect_(isConfigured_, propertiesSent_)',
     'setEnableSave_(isConfigured_, waitingForProperties_)',
+    'updateHiddenNetworkWarning_(autoConnect_)',
     'updateConfigProperties_(managedProperties)',
     'updateSecurity_(configProperties_, security_)',
     'updateEapOuter_(eapProperties_.Outer)',
@@ -395,6 +408,16 @@
     } else {
       this.focusFirstInput_();
     }
+
+    if (this.type == CrOnc.Type.VPN ||
+        (this.globalPolicy &&
+         this.globalPolicy.AllowOnlyPolicyNetworksToConnect)) {
+      this.autoConnect_ = false;
+    } else {
+      this.autoConnect_ = true;
+    }
+    this.hiddenNetworkWarning_ = this.showHiddenNetworkWarning_();
+
     this.onCertificateListsChanged_();
     this.updateIsConfigured_();
     this.setShareNetwork_();
@@ -423,12 +446,9 @@
     const propertiesToSet = this.getPropertiesToSet_();
     if (this.getSource_(this.guid, this.managedProperties) ==
         CrOnc.Source.NONE) {
-      // Set 'AutoConnect' to false for VPN or if prohibited by policy.
-      // Note: Do not set AutoConnect to true, the connection manager will do
-      // that on a successful connection (unless set to false here).
-      if (this.type == CrOnc.Type.VPN ||
-          (this.globalPolicy &&
-           this.globalPolicy.AllowOnlyPolicyNetworksToConnect)) {
+      if (!this.autoConnect_) {
+        // Note: Do not set AutoConnect to true, the connection manager will do
+        // that on a successful connection (unless set to false here).
         CrOnc.setTypeProperty(propertiesToSet, 'AutoConnect', false);
       }
       this.networkingPrivate.createNetwork(
@@ -1309,6 +1329,50 @@
    * @return {boolean}
    * @private
    */
+  configCanAutoConnect_: function() {
+    // Only WiFi can choose whether or not to autoConnect.
+    return loadTimeData.getBoolean('showHiddenNetworkWarning') &&
+        this.type == CrOnc.Type.WI_FI;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  autoConnectDisabled_: function() {
+    return this.isAutoConnectEnforcedByPolicy_();
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  isAutoConnectEnforcedByPolicy_: function() {
+    return !!this.globalPolicy &&
+        !!this.globalPolicy.AllowOnlyPolicyNetworksToAutoconnect;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  showHiddenNetworkWarning_: function() {
+    Polymer.dom.flush();
+    return loadTimeData.getBoolean('showHiddenNetworkWarning') &&
+        this.autoConnect_ && !this.hasGuid_();
+  },
+
+  /**
+   * @private
+   */
+  updateHiddenNetworkWarning_: function() {
+    this.hiddenNetworkWarning_ = this.showHiddenNetworkWarning_();
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
   selectedUserCertHashIsValid_: function() {
     return !!this.selectedUserCertHash_ &&
         this.selectedUserCertHash_ != NO_CERTS_HASH;
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config_select.html b/ui/webui/resources/cr_components/chromeos/network/network_config_select.html
index f9a1e3e..3600cff 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config_select.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config_select.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_indicator.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_tooltip_icon.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="network_config_element_behavior.html">
 <link rel="import" href="network_shared_css.html">
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html b/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
index 52faae4b..907971e 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
@@ -7,7 +7,7 @@
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="network_shared_css.html">
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_password_input.html b/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
index c43fcfa5..bcac7eb4 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
@@ -19,7 +19,7 @@
       }
 
       cr-input {
-        width: 100%;
+        flex: 1;
       }
 
       cr-policy-network-indicator {
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy.html b/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
index 8e5e97fff..eb3684a 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
@@ -8,7 +8,7 @@
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="network_proxy_exclusions.html">
 <link rel="import" href="network_proxy_input.html">
diff --git a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
index 3c6a8bd6..e298093 100644
--- a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
+++ b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
@@ -8,7 +8,7 @@
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="smb_browser_proxy.html">
diff --git a/ui/webui/resources/html/action_link_css.html b/ui/webui/resources/cr_elements/action_link_css.html
similarity index 92%
rename from ui/webui/resources/html/action_link_css.html
rename to ui/webui/resources/cr_elements/action_link_css.html
index 43984c19..06448aac 100644
--- a/ui/webui/resources/html/action_link_css.html
+++ b/ui/webui/resources/cr_elements/action_link_css.html
@@ -1,5 +1,5 @@
 <!-- "action_link_css.html" replaces "action_link.css" for MD pages. -->
-<link rel="import" href="../cr_elements/shared_vars_css.html">
+<link rel="import" href="shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
 <dom-module id="action-link">
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 7d115878..2a06b18 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
@@ -2,6 +2,7 @@
 
 <link rel="import" href="../../shared_style_css.html">
 <link rel="import" href="../../cr_icon_button/cr_icon_button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys/iron-a11y-keys.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="cr_png_behavior.html">
 
@@ -146,6 +147,7 @@
         <!-- Empty div for even 'space-between' justification -->
       </div>
       <div>
+        <iron-a11y-keys keys="up down left right"></iron-a11y-keys>
         <cr-icon-button id="takePhoto" tabindex="1"
             title="[[getTakePhotoLabel_(videomode, takePhotoLabel,
                 captureVideoLabel)]]" on-click="takePhoto"
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
index baa5b428..672ae8a 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
@@ -57,7 +57,7 @@
 
     <div id="container">
       <iron-a11y-keys keys="up down left right space enter"
-          on-keys-pressed="onKeysPressed_">
+          on-keys-pressed="onKeysPressed">
       </iron-a11y-keys>
       <iron-selector id="selector"
           on-iron-activate="onIronActivate_" on-iron-select="onIronSelect_"
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
index eedea168..03eddc2 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
@@ -168,30 +168,10 @@
   },
 
   /**
-   * @param {!CrPicture.ImageElement} image
-   */
-  setSelectedImage_(image) {
-    this.fallbackImage_ = image;
-    // If the user is currently taking a photo, do not change the focus.
-    if (!this.selectedItem ||
-        this.selectedItem.dataset.type != CrPicture.SelectionTypes.CAMERA) {
-      this.$.selector.select(this.$.selector.indexOf(image));
-      this.selectedItem = image;
-    }
-  },
-
-  /** @private */
-  onDefaultImagesChanged_: function() {
-    if (this.selectedImageUrl_) {
-      this.setSelectedImageUrl(this.selectedImageUrl_);
-    }
-  },
-
-  /**
    * Handler for when accessibility-specific keys are pressed.
    * @param {!CustomEvent<!{key: string, keyboardEvent: Object}>} e
    */
-  onKeysPressed_: function(e) {
+  onKeysPressed: function(e) {
     if (!this.selectedItem) {
       return;
     }
@@ -224,6 +204,26 @@
   },
 
   /**
+   * @param {!CrPicture.ImageElement} image
+   */
+  setSelectedImage_(image) {
+    this.fallbackImage_ = image;
+    // If the user is currently taking a photo, do not change the focus.
+    if (!this.selectedItem ||
+        this.selectedItem.dataset.type != CrPicture.SelectionTypes.CAMERA) {
+      this.$.selector.select(this.$.selector.indexOf(image));
+      this.selectedItem = image;
+    }
+  },
+
+  /** @private */
+  onDefaultImagesChanged_: function() {
+    if (this.selectedImageUrl_) {
+      this.setSelectedImageUrl(this.selectedImageUrl_);
+    }
+  },
+
+  /**
    * @param {!CrPicture.ImageElement} selected
    * @param {boolean} activate
    * @private
diff --git a/ui/webui/resources/cr_elements/chromeos/network/BUILD.gn b/ui/webui/resources/cr_elements/chromeos/network/BUILD.gn
index 04f8ef77..30fde56 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/BUILD.gn
+++ b/ui/webui/resources/cr_elements/chromeos/network/BUILD.gn
@@ -27,6 +27,7 @@
     deps = [
       ":cr_onc_types",
       "//ui/webui/resources/js:assert",
+      "//ui/webui/resources/js/chromeos:onc_mojo",
     ]
   }
 
@@ -66,7 +67,6 @@
     deps = [
       ":cr_network_list_types",
       ":cr_network_listener_behavior",
-      ":cr_onc_types",
       "//ui/webui/resources/js:util",
       "//ui/webui/resources/js/chromeos:onc_mojo",
     ]
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.html b/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.html
index 56013c2..c475be27 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.html
@@ -3,6 +3,7 @@
 <link rel="import" href="../../chromeos/network/cr_onc_types.html">
 <link rel="import" href="../../chromeos/network/network_icons.html">
 <link rel="import" href="../../hidden_style_css.html">
+<link rel="import" href="../../../html/chromeos/onc_mojo.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
 <dom-module id="cr-network-icon">
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js
index 5e47f525..814d60f 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js
@@ -15,14 +15,14 @@
      * If set, the ONC properties will be used to display the icon. This may
      * either be the complete set of NetworkProperties or the subset of
      * NetworkStateProperties.
-     * @type {!CrOnc.NetworkProperties|!CrOnc.NetworkStateProperties|undefined}
+     * @type {!OncMojo.NetworkStateProperties|undefined}
      */
     networkState: Object,
 
     /**
      * If set, the device state for the network type. Otherwise it defaults to
      * null rather than undefined so that it does not block computed bindings.
-     * @type {?CrOnc.DeviceStateProperties}
+     * @type {?OncMojo.DeviceStateProperties}
      */
     deviceState: {
       type: Object,
@@ -63,38 +63,37 @@
     if (!this.networkState) {
       return '';
     }
-    const type = this.networkState.Type;
-    if (type == CrOnc.Type.ETHERNET) {
+    const mojom = chromeos.networkConfig.mojom;
+    const type = this.networkState.type;
+    if (type == mojom.NetworkType.kEthernet) {
       return 'ethernet';
     }
-    if (type == CrOnc.Type.VPN) {
+    if (type == mojom.NetworkType.kVPN) {
       return 'vpn';
     }
 
-    const prefix = (type == CrOnc.Type.CELLULAR || type == CrOnc.Type.TETHER) ?
-        'cellular-' :
-        'wifi-';
-    if (!this.isListItem && !this.networkState.GUID) {
-      const deviceState = this.deviceState;
-      if (!deviceState || deviceState.State == 'Enabled' ||
-          deviceState.State == 'Enabling') {
+    const prefix = OncMojo.networkTypeIsMobile(type) ? 'cellular-' : 'wifi-';
+    if (!this.isListItem && !this.networkState.guid) {
+      const device = this.deviceState;
+      if (!device || device.deviceState == mojom.DeviceStateType.kEnabled ||
+          device.deviceState == mojom.DeviceStateType.kEnabling) {
         return prefix + 'no-network';
       }
       return prefix + 'off';
     }
 
-    const connectionState = this.networkState.ConnectionState;
-    if (connectionState == CrOnc.ConnectionState.CONNECTING) {
+    const connectionState = this.networkState.connectionState;
+    if (connectionState == mojom.ConnectionStateType.kConnecting) {
       return prefix + 'connecting';
     }
 
     if (!this.isListItem &&
         (!connectionState ||
-         connectionState == CrOnc.ConnectionState.NOT_CONNECTED)) {
+         connectionState == mojom.ConnectionStateType.kNotConnected)) {
       return prefix + 'not-connected';
     }
 
-    const strength = CrOnc.getSignalStrength(this.networkState);
+    const strength = OncMojo.getSignalStrength(this.networkState);
     return prefix + this.strengthToIndex_(strength).toString(10);
   },
 
@@ -131,17 +130,18 @@
    * @private
    */
   getTechnology_: function() {
-    const networkState = this.networkState;
-    if (!networkState) {
+    if (!this.networkState) {
       return '';
     }
-    const type = networkState.Type;
-    if (type == CrOnc.Type.WI_MAX) {
+    const mojom = chromeos.networkConfig.mojom;
+    const type = this.networkState.type;
+    if (type == mojom.NetworkType.kWiMAX) {
       return 'network:4g';
     }
-    if (type == CrOnc.Type.CELLULAR && networkState.Cellular) {
+    if (type == mojom.NetworkType.kCellular) {
+      assert(this.networkState.cellular);
       const technology =
-          this.getTechnologyId_(networkState.Cellular.NetworkTechnology);
+          this.getTechnologyId_(this.networkState.cellular.networkTechnology);
       if (technology != '') {
         return 'network:' + technology;
       }
@@ -184,18 +184,16 @@
    * @private
    */
   showSecure_: function() {
-    const networkState = this.networkState;
     if (!this.networkState) {
       return false;
     }
-    if (networkState.Type != CrOnc.Type.WI_FI || !networkState.WiFi) {
-      return false;
-    }
+    const mojom = chromeos.networkConfig.mojom;
     if (!this.isListItem &&
-        networkState.ConnectionState == CrOnc.ConnectionState.NOT_CONNECTED) {
+        this.networkState.connectionState ==
+            mojom.ConnectionStateType.kNotConnected) {
       return false;
     }
-    const security = CrOnc.getStateOrActiveString(networkState.WiFi.Security);
-    return !!security && security != 'None';
+    return this.networkState.type == mojom.NetworkType.kWiFi &&
+        this.networkState.wifi.security != mojom.SecurityType.kNone;
   },
 });
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.html b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.html
index b198f316..78f84b65 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.html
@@ -1,8 +1,6 @@
 <link rel="import" href="../../../html/polymer.html">
 
 <link rel="import" href="../../chromeos/network/cr_network_list_item.html">
-<link rel="import" href="../../chromeos/network/cr_onc_types.html">
-<link rel="import" href="../../chromeos/network/cr_onc_types.html">
 <link rel="import" href="../../cr_scrollable_behavior.html">
 <link rel="import" href="../../shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.js
index a8f4b93..724375a 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.js
@@ -15,7 +15,7 @@
   properties: {
     /**
      * The list of network state properties for the items to display.
-     * @type {!Array<!CrOnc.NetworkStateProperties>}
+     * @type {!Array<!OncMojo.NetworkStateProperties>}
      */
     networks: {
       type: Array,
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html
index 91dfb899..9d52d4d 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html
@@ -1,5 +1,6 @@
 <link rel="import" href="../../../html/polymer.html">
 
+<link rel="import" href="../../../html/chromeos/onc_mojo.html">
 <link rel="import" href="../../chromeos/network/cr_network_icon.html">
 <link rel="import" href="../../chromeos/network/cr_onc_types.html">
 <link rel="import" href="../../cr_icon_button/cr_icon_button.html">
@@ -58,9 +59,9 @@
     <div id="divOuter"
         class="layout horizontal center flex" actionable>
       <template is="dom-if" if="[[networkState]]">
-        <cr-network-icon
+        <cr-network-icon is-list-item
             show-technology-badge="[[showTechnologyBadge]]"
-            is-list-item network-state="[[networkState]]">
+            network-state="[[networkState]]">
         </cr-network-icon>
       </template>
       <template is="dom-if" if="[[item.polymerIcon]]">
@@ -74,9 +75,9 @@
           [[getNetworkStateText_(networkState)]]
         </div>
       </div>
-      <template is="dom-if" if="[[isPolicySource(networkState.Source)]]">
-        <cr-policy-indicator
-            indicator-type="[[getIndicatorTypeForSource(networkState.Source)]]">
+      <template is="dom-if" if="[[isPolicySourceMojo(networkState.source)]]">
+        <cr-policy-indicator indicator-type="[[getIndicatorTypeForSourceMojo(
+            networkState.source)]]">
         </cr-policy-indicator>
       </template>
       <template is="dom-if"
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
index 03c02da5..5eacf52 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
@@ -19,7 +19,7 @@
 
     /**
      * The ONC data properties used to display the list item.
-     * @type {!CrOnc.NetworkStateProperties|undefined}
+     * @type {!OncMojo.NetworkStateProperties|undefined}
      */
     networkState: {
       type: Object,
@@ -57,9 +57,9 @@
 
     /**
      * The cached ConnectionState for the network.
-     * @type {!CrOnc.ConnectionState|undefined}
+     * @type {!chromeos.networkConfig.mojom.ConnectionStateType|undefined}
      */
-    connectionState_: String,
+    connectionState_: Number,
 
     /** Whether to show technology badge on mobile network icon. */
     showTechnologyBadge: {type: Boolean, value: true},
@@ -81,7 +81,7 @@
   itemChanged_: function() {
     if (this.item && !this.item.hasOwnProperty('customItemName')) {
       this.networkState =
-          /** @type {!CrOnc.NetworkStateProperties} */ (this.item);
+          /** @type {!OncMojo.NetworkStateProperties} */ (this.item);
     } else if (this.networkState) {
       this.networkState = undefined;
     }
@@ -92,7 +92,7 @@
     if (!this.networkState) {
       return;
     }
-    const connectionState = this.networkState.ConnectionState;
+    const connectionState = this.networkState.connectionState;
     if (connectionState == this.connectionState_) {
       return;
     }
@@ -114,8 +114,8 @@
       }
       return name;
     }
-    const network = /** @type {!CrOnc.NetworkStateProperties} */ (this.item);
-    return CrOnc.getNetworkName(network);
+    return OncMojo.getNetworkDisplayName(
+        /** @type {!OncMojo.NetworkStateProperties} */ (this.item));
   },
 
   /**
@@ -132,31 +132,30 @@
    * @private
    */
   getNetworkStateText_: function() {
+    const mojom = chromeos.networkConfig.mojom;
     if (!this.networkState) {
       return '';
     }
-    const connectionState = this.networkState.ConnectionState;
-    if (this.networkState.Type == CrOnc.Type.CELLULAR) {
-      // For Cellular, an empty ConnectionState indicates that the device is
-      // still initializing.
-      if (!connectionState) {
-        return CrOncStrings.networkListItemInitializing;
-      }
-      if (this.networkState.Cellular && this.networkState.Cellular.Scanning) {
-        return CrOncStrings.networkListItemScanning;
-      }
+    const connectionState = this.networkState.connectionState;
+    if (this.networkState.type == mojom.NetworkType.kCellular &&
+        this.networkState.cellular.scanning) {
+      // TODO(khorimoto): Add and sim locked and possibly initializing states to
+      // CellularStateProperties.
+      return CrOncStrings.networkListItemScanning;
     }
-    if (connectionState == CrOnc.ConnectionState.CONNECTED) {
+    if (OncMojo.connectionStateIsConnected(connectionState)) {
+      // TODO(khorimoto): Consider differentiating between Portal, Connected,
+      // and Online.
       return CrOncStrings.networkListItemConnected;
     }
-    if (connectionState == CrOnc.ConnectionState.CONNECTING) {
+    if (connectionState == mojom.ConnectionStateType.kConnecting) {
       return CrOncStrings.networkListItemConnecting;
     }
     return '';
   },
 
   /**
-   * @param {!CrOnc.NetworkStateProperties|undefined} networkState
+   * @param {!OncMojo.NetworkStateProperties|undefined} networkState
    * @param {boolean} showButtons
    * @return {boolean}
    * @private
@@ -170,8 +169,11 @@
    * @private
    */
   isConnected_: function() {
-    return !!this.networkState &&
-        this.networkState.ConnectionState == CrOnc.ConnectionState.CONNECTED;
+    if (!this.networkState) {
+      return false;
+    }
+    return OncMojo.connectionStateIsConnected(
+        this.networkState.connectionState);
   },
 
   /**
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_types.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_types.js
index 93d258c6..7839d2c1 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_types.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_types.js
@@ -11,8 +11,7 @@
 const CrNetworkList = {};
 
 /**
- * Generic managed property type. This should match any of the basic managed
- * types in chrome.networkingPrivate, e.g. networkingPrivate.ManagedBoolean.
+ * Custom data for implementation specific network list items.
  * @typedef {{
  *   customItemName: string,
  *   polymerIcon: (string|undefined),
@@ -22,5 +21,5 @@
  */
 CrNetworkList.CustomItemState;
 
-/** @typedef {CrOnc.NetworkStateProperties|CrNetworkList.CustomItemState} */
+/** @typedef {OncMojo.NetworkStateProperties|CrNetworkList.CustomItemState} */
 CrNetworkList.CrNetworkListItemType;
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.html b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.html
index b60466f..12245798 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.html
@@ -1,7 +1,6 @@
 <link rel="import" href="../../../html/polymer.html">
 
 <link rel="import" href="../../chromeos/network/cr_network_list.html">
-<link rel="import" href="../../chromeos/network/cr_onc_types.html">
 <link rel="import" href="cr_network_listener_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/chromeos/onc_mojo.html">
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
index 6124a2b1..f79b03c5 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
@@ -4,9 +4,13 @@
 
 /**
  * @fileoverview Polymer element wrapping cr-network-list including the
- * networkingPrivate calls to populate it.
+ * networkConfig mojo API calls to populate it.
  */
 
+(function() {
+
+const mojom = chromeos.networkConfig.mojom;
+
 Polymer({
   is: 'cr-network-select',
 
@@ -45,7 +49,7 @@
 
     /**
      * List of all network state data for all visible networks.
-     * @private {!Array<!CrOnc.NetworkStateProperties>}
+     * @private {!Array<!OncMojo.NetworkStateProperties>}
      */
     networkStateList_: {
       type: Array,
@@ -62,12 +66,12 @@
 
     /**
      * Cached Cellular Device state or undefined if there is no Cellular device.
-     * @private {!CrOnc.DeviceStateProperties|undefined} deviceState
+     * @private {!OncMojo.DeviceStateProperties|undefined} deviceState
      */
     cellularDeviceState_: Object,
   },
 
-  /** @type {!CrOnc.NetworkStateProperties|undefined} */
+  /** @type {!OncMojo.NetworkStateProperties|undefined} */
   defaultNetworkState_: undefined,
 
 
@@ -129,28 +133,29 @@
    * refresh and list update (e.g. when the element is shown).
    */
   refreshNetworks: function() {
-    chrome.networkingPrivate.getDeviceStates(
-        this.getDeviceStatesCallback_.bind(this));
+    this.networkConfigProxy_.getDeviceStateList().then(response => {
+      this.onGetDeviceStates_(response.result);
+    });
   },
 
   /**
    * Returns default network if it is present.
-   * @return {!CrOnc.NetworkStateProperties|undefined}
+   * @return {!OncMojo.NetworkStateProperties|undefined}
    */
   getDefaultNetwork: function() {
     let defaultNetwork;
     for (let i = 0; i < this.networkStateList_.length; ++i) {
       const state = this.networkStateList_[i];
-      if (state.ConnectionState == CrOnc.ConnectionState.CONNECTED) {
+      if (OncMojo.connectionStateIsConnected(state.connectionState)) {
         defaultNetwork = state;
         break;
       }
-      if (state.ConnectionState == CrOnc.ConnectionState.CONNECTING &&
+      if (state.connectionState == mojom.ConnectionStateType.kConnecting &&
           !defaultNetwork) {
         defaultNetwork = state;
         // Do not break here in case a non WiFi network is connecting but a
         // WiFi network is connected.
-      } else if (state.Type == CrOnc.Type.WI_FI) {
+      } else if (state.type == mojom.NetworkType.kWiFi) {
         break;  // Non connecting or connected WiFI networks are always last.
       }
     }
@@ -160,40 +165,40 @@
   /**
    * Returns network with specified GUID if it is available.
    * @param {string} guid of network.
-   * @return {!CrOnc.NetworkStateProperties|undefined}
+   * @return {!OncMojo.NetworkStateProperties|undefined}
    */
   getNetwork: function(guid) {
     return this.networkStateList_.find(function(network) {
-      return network.GUID == guid;
+      return network.guid == guid;
     });
   },
 
   /**
-   * @param {!Array<!CrOnc.DeviceStateProperties>} deviceStates
+   * @param {!Array<!OncMojo.DeviceStateProperties>} deviceStates
    * @private
    */
-  getDeviceStatesCallback_: function(deviceStates) {
+  onGetDeviceStates_: function(deviceStates) {
     this.isScanOngoing_ =
-        deviceStates.some((deviceState) => !!deviceState.Scanning);
+        deviceStates.some((deviceState) => !!deviceState.scanning);
 
     const filter = {
-      networkType: chrome.networkingPrivate.NetworkType.ALL,
-      visible: true,
-      configured: false
+      filter: chromeos.networkConfig.mojom.FilterType.kVisible,
+      networkType: mojom.NetworkType.kAll,
+      limit: chromeos.networkConfig.mojom.kNoLimit,
     };
-    chrome.networkingPrivate.getNetworks(filter, function(networkStates) {
-      this.getNetworksCallback_(deviceStates, networkStates);
-    }.bind(this));
+    this.networkConfigProxy_.getNetworkStateList(filter).then(response => {
+      this.onGetNetworkStateList_(deviceStates, response.result);
+    });
   },
 
   /**
-   * @param {!Array<!CrOnc.DeviceStateProperties>} deviceStates
-   * @param {!Array<!CrOnc.NetworkStateProperties>} networkStates
+   * @param {!Array<!OncMojo.DeviceStateProperties>} deviceStates
+   * @param {!Array<!OncMojo.NetworkStateProperties>} networkStates
    * @private
    */
-  getNetworksCallback_: function(deviceStates, networkStates) {
+  onGetNetworkStateList_: function(deviceStates, networkStates) {
     this.cellularDeviceState_ = deviceStates.find(function(device) {
-      return device.Type == CrOnc.Type.CELLULAR;
+      return device.type == mojom.NetworkType.kCellular;
     });
     if (this.cellularDeviceState_) {
       this.ensureCellularNetwork_(networkStates);
@@ -205,62 +210,56 @@
 
     if ((!defaultNetwork && !this.defaultNetworkState_) ||
         (defaultNetwork && this.defaultNetworkState_ &&
-         defaultNetwork.GUID == this.defaultNetworkState_.GUID &&
-         defaultNetwork.ConnectionState ==
-             this.defaultNetworkState_.ConnectionState)) {
+         defaultNetwork.guid == this.defaultNetworkState_.guid &&
+         defaultNetwork.connectionState ==
+             this.defaultNetworkState_.connectionState)) {
       return;  // No change to network or ConnectionState
     }
     this.defaultNetworkState_ = defaultNetwork ?
-        /** @type {!CrOnc.NetworkStateProperties|undefined} */ (
+        /** @type {!OncMojo.NetworkStateProperties|undefined} */ (
             Object.assign({}, defaultNetwork)) :
         undefined;
+    // Note: event.detail will be {} if defaultNetwork is undefined.
     this.fire('default-network-changed', defaultNetwork);
   },
 
   /**
    * Modifies |networkStates| to include a cellular network if one is required
    * but does not exist.
-   * @param {!Array<!CrOnc.NetworkStateProperties>} networkStates
+   * @param {!Array<!OncMojo.NetworkStateProperties>} networkStates
    * @private
    */
   ensureCellularNetwork_: function(networkStates) {
     if (networkStates.find(function(network) {
-          return network.Type == CrOnc.Type.CELLULAR;
+          return network.type == mojom.NetworkType.kCellular;
         })) {
       return;
     }
-    let connectionState;
-    switch (this.cellularDeviceState_.State) {
-      case CrOnc.DeviceState.DISABLED:
-      case CrOnc.DeviceState.PROHIBITED:
-        return;  // No Cellular network
-      case CrOnc.DeviceState.UNINITIALIZED:
-      case CrOnc.DeviceState.ENABLING:
-        // Leave |connectionState| undefined to indicate "initializing".
-        break;
-      case CrOnc.DeviceState.ENABLED:
-        // The default network state is never connected. When a real network
-        // state is provided, it will have ConnectionState properly set.
-        connectionState = CrOnc.ConnectionState.NOT_CONNECTED;
-        break;
+    const deviceState = this.cellularDeviceState_.deviceState;
+    if (deviceState == mojom.DeviceStateType.kDisabled ||
+        deviceState == mojom.DeviceStateType.kProhibited) {
+      return;  // No Cellular network
     }
-    // Add a Cellular network after the Ethernet network if it exists.
-    const idx = networkStates.length > 0 &&
-            networkStates[0].Type == CrOnc.Type.ETHERNET ?
+
+    const cellular =
+        OncMojo.getDefaultNetworkState(mojom.NetworkType.kCellular);
+    cellular.cellular.scanning = this.cellularDeviceState_.scanning;
+
+    // Note: the default connectionState is kNotConnected.
+    // TODO(khorimoto): Maybe set an 'initializing' CellularState property if
+    // the device state is initializing, see TODO in cr_network_list_item.js.
+
+    // Insert the Cellular network after the Ethernet network if it exists.
+    const idx = (networkStates.length > 0 &&
+                 networkStates[0].type == mojom.NetworkType.kEthernet) ?
         1 :
         0;
-    const cellular = {
-      GUID: '',
-      Type: CrOnc.Type.CELLULAR,
-      Cellular: {Scanning: this.cellularDeviceState_.Scanning},
-      ConnectionState: connectionState,
-    };
     networkStates.splice(idx, 0, cellular);
   },
 
   /**
    * Event triggered when a cr-network-list-item is selected.
-   * @param {!{target: HTMLElement, detail: !CrOnc.NetworkStateProperties}} e
+   * @param {!{target: HTMLElement, detail: !OncMojo.NetworkStateProperties}} e
    * @private
    */
   onNetworkListItemSelected_: function(e) {
@@ -270,3 +269,4 @@
     this.fire('network-item-selected', state);
   },
 });
+})();
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js b/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js
index ac52512..2452bbc 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js
@@ -699,23 +699,6 @@
 };
 
 /**
- * @param {!CrOnc.NetworkProperties|!CrOnc.NetworkStateProperties|undefined}
- *     networkProperties The ONC network properties or state properties.
- * @return {boolean}
- */
-CrOnc.shouldShowTetherDialogBeforeConnection = function(networkProperties) {
-  // Only show for Tether networks.
-  if (networkProperties.Type != CrOnc.Type.TETHER) {
-    return false;
-  }
-
-  // Show if there are no Tether properties or if there are Tether properties
-  // and they indicate that a connection has not yet occurred to this host.
-  return !networkProperties.Tether ||
-      !networkProperties.Tether.HasConnectedToHost;
-};
-
-/**
  * Returns a valid CrOnc.Type, or undefined.
  * @param {string} typeStr
  * @return {!CrOnc.Type|undefined}
diff --git a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js
index 0d5d7b8..71b2649 100644
--- a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js
+++ b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js
@@ -265,11 +265,17 @@
       return;
     }
 
-    // Accept Enter keys from either the dialog, or a child input element.
-    if (e.target != this && e.target.tagName != 'CR-INPUT') {
+    // Accept Enter keys from either the dialog, or a child input or cr-input
+    // element (but consider that the event may have been retargeted).
+    const origTarget = e.composedPath()[0];
+    const accept = e.target == this || origTarget.tagName == 'CR-INPUT' ||
+        (origTarget.tagName == 'INPUT' &&
+         // <cr-input> can only be text-like; apply the same limit to <input> so
+         // that e.g. enter on a checkbox does not submit the dialog.
+         ['text', 'password', 'number', 'search'].includes(origTarget.type));
+    if (!accept) {
       return;
     }
-
     const actionButton =
         this.querySelector('.action-button:not([disabled]):not([hidden])');
     if (actionButton) {
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input.html b/ui/webui/resources/cr_elements/cr_input/cr_input.html
index e54fcdd..1f892d2 100644
--- a/ui/webui/resources/cr_elements/cr_input/cr_input.html
+++ b/ui/webui/resources/cr_elements/cr_input/cr_input.html
@@ -50,6 +50,10 @@
         color: var(--cr-input-error-color);
       }
 
+      #input {
+        border-bottom: var(--cr-input-border-bottom, none);
+      }
+
       #input::placeholder {
         color: var(--cr-input-placeholder-color);
       }
@@ -104,12 +108,12 @@
     </style>
     <div id="label" hidden="[[!label]]" aria-hidden="true">[[label]]</div>
     <div id="row-container" part="row-container">
-      <div id="input-container">
-        <!-- Only attributes that are named inconsistently between html and js
-             need to use attr$="", such as |tabindex| vs .tabIndex and
-             |readonly| vs .readOnly. -->
-        <div id="inner-input-container">
+      <div id="input-container" part="input-container">
+        <div id="inner-input-container" part="inner-input-container">
           <slot name="prefix"></slot>
+          <!-- Only attributes that are named inconsistently between html and js
+              need to use attr$="", such as |tabindex| vs .tabIndex and
+              |readonly| vs .readOnly. -->
           <input id="input" disabled="[[disabled]]" autofocus="[[autofocus]]"
               value="{{value::input}}" tabindex$="[[tabindex]]" type="[[type]]"
               readonly$="[[readonly]]" maxlength$="[[maxlength]]"
@@ -117,7 +121,7 @@
               minlength$="[[minlength]]"
               max="[[max]]" min="[[min]]" on-focus="onInputFocus_"
               on-blur="onInputBlur_" on-change="onInputChange_"
-              on-keydown="onInputKeydown_">
+              on-keydown="onInputKeydown_" part="input">
         </div>
         <div id="underline"></div>
       </div>
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html b/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html
index e695ac45..e862b7e 100644
--- a/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html
+++ b/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html
@@ -40,20 +40,16 @@
 
       /* Input styling below. */
       #input-container {
-        border-radius: 4px;
+        border-radius: var(--cr-input-border-radius, 4px);
         overflow: hidden;
         position: relative;
         width: var(--cr-input-width, 100%);
-
-        @apply --cr-input-container;
       }
 
       #inner-input-container {
         background-color: var(--cr-input-background-color);
         box-sizing: border-box;
         padding: 0;
-
-        @apply --cr-input-inner-container;
       }
 
       #input {
@@ -86,7 +82,10 @@
         text-overflow: ellipsis;
         width: 100%;
 
-        @apply --cr-input-input;
+        /* Avoid using the mixin. Use ::part(input) instead.
+         * Necessary for backward compatibility with Polymer1/SDv0 (only used by
+         * OOBE). TODO(crbug.com/955194): Remove this once fixed.*/
+         @apply --cr-input-input;
       }
 
       /* Underline styling below. */
diff --git a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html
index e96d555..b96d2ba 100644
--- a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html
+++ b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html
@@ -26,18 +26,9 @@
 
       cr-input {
         --cr-input-background-color: white;
-        --cr-input-container: {
-          border-radius: 0;
-          height: 100%;
-        }
-        --cr-input-inner-container: {
-          height: 100%;
-        }
+        --cr-input-border-bottom: 1px solid var(--cr-secondary-text-color);
+        --cr-input-border-radius: 0;
         --cr-input-error-display: none;
-        --cr-input-input: {
-          height: 100%;
-          border-bottom: 1px solid var(--cr-secondary-text-color);
-        }
         --cr-input-padding-end: 0;
         --cr-input-padding-start: 0;
         --cr-input-padding-bottom: 2px;
@@ -48,7 +39,12 @@
         width: 160px;  /* Special width for search input. */
       }
 
-      cr-input::part(row-container) {
+      /* TODO(johntlee): Figure out a way to align the input without
+       * accessing all these parts. */
+      cr-input::part(row-container),
+      cr-input::part(input-container),
+      cr-input::part(inner-input-container),
+      cr-input::part(input) {
         height: 100%;
       }
 
diff --git a/ui/webui/resources/html/md_select_css.html b/ui/webui/resources/cr_elements/md_select_css.html
similarity index 95%
rename from ui/webui/resources/html/md_select_css.html
rename to ui/webui/resources/cr_elements/md_select_css.html
index ba2f742..683de7c 100644
--- a/ui/webui/resources/html/md_select_css.html
+++ b/ui/webui/resources/cr_elements/md_select_css.html
@@ -1,6 +1,6 @@
-<link rel="import" href="polymer.html">
+<link rel="import" href="../html/polymer.html">
 
-<link rel="import" href="../cr_elements/shared_vars_css.html">
+<link rel="import" href="shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
 <dom-module id="md-select">
diff --git a/ui/webui/resources/cr_elements/policy/BUILD.gn b/ui/webui/resources/cr_elements/policy/BUILD.gn
index e758d2e..29780cb 100644
--- a/ui/webui/resources/cr_elements/policy/BUILD.gn
+++ b/ui/webui/resources/cr_elements/policy/BUILD.gn
@@ -51,6 +51,7 @@
   js_library("cr_policy_network_behavior") {
     deps = [
       ":cr_policy_indicator_behavior",
+      "../../js/chromeos:onc_mojo",
       "../chromeos/network:cr_onc_types",
     ]
   }
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.js b/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.js
index dd564c7..6c3af98 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.js
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.js
@@ -108,6 +108,17 @@
   },
 
   /**
+   * @param {chromeos.networkConfig.mojom.OncSource} source
+   * @return {boolean}
+   * @protected
+   */
+  isPolicySourceMojo: function(source) {
+    return !!source &&
+        (source == chromeos.networkConfig.mojom.OncSource.kDevicePolicy ||
+         source == chromeos.networkConfig.mojom.OncSource.kUserPolicy);
+  },
+
+  /**
    * @param {string} source
    * @return {!CrPolicyIndicatorType}
    * @private
@@ -123,6 +134,21 @@
   },
 
   /**
+   * @param {chromeos.networkConfig.mojom.OncSource} source
+   * @return {!CrPolicyIndicatorType}
+   * @private
+   */
+  getIndicatorTypeForSourceMojo: function(source) {
+    if (source == chromeos.networkConfig.mojom.OncSource.kDevicePolicy) {
+      return CrPolicyIndicatorType.DEVICE_POLICY;
+    }
+    if (source == chromeos.networkConfig.mojom.OncSource.kUserPolicy) {
+      return CrPolicyIndicatorType.USER_POLICY;
+    }
+    return CrPolicyIndicatorType.NONE;
+  },
+
+  /**
    * @param {Object} dict A managed ONC dictionary.
    * @param {string} path A path to a setting inside |dict|.
    * @return {!CrOnc.ManagedProperty|undefined} The value of the setting at
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp
index 2c012619..45ff4f2f 100644
--- a/ui/webui/resources/cr_elements_resources.grdp
+++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -1,6 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
   <!-- Polymer 1.0 Elements -->
+  <structure name="IDR_CR_ELEMENTS_ACTION_LINK_CSS_HTML"
+             file="cr_elements/action_link_css.html"
+             type="chrome_html"
+             compress="gzip" />
   <structure name="IDR_CR_ELEMENTS_CR_ACTION_MENU_HTML"
              file="cr_elements/cr_action_menu/cr_action_menu.html"
              type="chrome_html"
@@ -380,6 +384,10 @@
              flattenhtml="true"
              type="chrome_html"
              compress="gzip" />
+  <structure name="IDR_CR_ELEMENTS_MD_SELECT_CSS_HTML"
+             file="cr_elements/md_select_css.html"
+             type="chrome_html"
+             compress="gzip" />
   <structure name="IDR_CR_ELEMENTS_PAPER_BUTTON_STYLE_CSS_HTML"
              file="cr_elements/paper_button_style_css.html"
              type="chrome_html"
diff --git a/ui/webui/resources/cr_polymer_resources.grdp b/ui/webui/resources/cr_polymer_resources.grdp
index 2c1f69b..386af40 100644
--- a/ui/webui/resources/cr_polymer_resources.grdp
+++ b/ui/webui/resources/cr_polymer_resources.grdp
@@ -3,9 +3,6 @@
      Polymer or are Polymer related. -->
 <grit-part>
   <!-- HTML resources -->
-  <structure name="IDR_WEBUI_HTML_ACTION_LINK_CSS"
-             file="html/action_link_css.html" type="chrome_html"
-             compress="gzip" />
   <structure name="IDR_WEBUI_HTML_CR_UI_FOCUS_ROW_BEHAVIOR"
              file="html/cr/ui/focus_row_behavior.html" type="chrome_html"
              compress="gzip" />
@@ -21,9 +18,6 @@
   <structure name="IDR_WEBUI_HTML_LIST_PROPERTY_UPDATE_BEHAVIOR"
              file="html/list_property_update_behavior.html" type="chrome_html"
              compress="gzip" />
-  <structure name="IDR_WEBUI_HTML_MD_SELECT_CSS_HTML"
-             file="html/md_select_css.html" type="chrome_html"
-             compress="gzip" flattenhtml="true" />
   <structure name="IDR_WEBUI_HTML_POLYMER"
              file="html/polymer.html" type="chrome_html" compress="gzip" />
   <structure name="IDR_WEBUI_HTML_WEBUI_LISTENER_BEHAVIOR"
diff --git a/ui/webui/resources/css/tree.css b/ui/webui/resources/css/tree.css
index 4165b21..4cdac5f0 100644
--- a/ui/webui/resources/css/tree.css
+++ b/ui/webui/resources/css/tree.css
@@ -119,7 +119,6 @@
       url(../../../views/resources/default_200_percent/common/folder_open.png) 2x);
 }
 
-<if expr="not is_macosx and not is_ios">
 html[dir=rtl] .tree-label,
 html[dir=rtl] .tree-row[may-have-children] > .tree-label {
   background-image: -webkit-image-set(
@@ -132,7 +131,6 @@
      url(../../../views/resources/default_100_percent/common/folder_open_rtl.png) 1x,
      url(../../../views/resources/default_200_percent/common/folder_open_rtl.png) 2x);
 }
-</if>
 
 tree[icon-visibility=hidden] .tree-label {
   background-image: none !important;
diff --git a/ui/webui/resources/js/chromeos/BUILD.gn b/ui/webui/resources/js/chromeos/BUILD.gn
index 061d95ad..6b283b6c 100644
--- a/ui/webui/resources/js/chromeos/BUILD.gn
+++ b/ui/webui/resources/js/chromeos/BUILD.gn
@@ -4,6 +4,8 @@
 
 import("//third_party/closure_compiler/compile_js.gni")
 
+assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+
 group("closure_compile") {
   deps = [
     ":js_resources",
@@ -19,6 +21,7 @@
 js_library("onc_mojo") {
   deps = [
     "//chromeos/services/network_config/public/mojom:mojom_js_library_for_compile",
+    "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types",
     "//ui/webui/resources/js:assert",
   ]
 }
diff --git a/ui/webui/resources/js/chromeos/onc_mojo.js b/ui/webui/resources/js/chromeos/onc_mojo.js
index accbd8d..a566d7322 100644
--- a/ui/webui/resources/js/chromeos/onc_mojo.js
+++ b/ui/webui/resources/js/chromeos/onc_mojo.js
@@ -11,6 +11,17 @@
 
 class OncMojo {
   /**
+   * @param {number|undefined} value
+   * @return {string}
+   */
+  static getEnumString(value) {
+    if (value === undefined) {
+      return 'undefined';
+    }
+    return value.toString();
+  }
+
+  /**
    * @param {!chromeos.networkConfig.mojom.ActivationStateType} value
    * @return {string}
    */
@@ -31,11 +42,36 @@
       case ActivationStateType.kNoService:
         return 'NoService';
     }
-    assertNotReached();
+    assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
     return '';
   }
 
   /**
+   * @param {string} value
+   * @return {!chromeos.networkConfig.mojom.ActivationStateType}
+   */
+  static getActivationStateTypeFromString(value) {
+    const ActivationStateType =
+        chromeos.networkConfig.mojom.ActivationStateType;
+    switch (value) {
+      case 'Unknown':
+        return ActivationStateType.kUnknown;
+      case 'NotActivated':
+        return ActivationStateType.kNotActivated;
+      case 'Activating':
+        return ActivationStateType.kActivating;
+      case 'PartiallyActivated':
+        return ActivationStateType.kPartiallyActivated;
+      case 'Activated':
+        return ActivationStateType.kActivated;
+      case 'NoService':
+        return ActivationStateType.kNoService;
+    }
+    assertNotReached('Unexpected value: ' + value);
+    return ActivationStateType.kUnknown;
+  }
+
+  /**
    * @param {!chromeos.networkConfig.mojom.ConnectionStateType} value
    * @return {string}
    */
@@ -54,11 +90,54 @@
       case ConnectionStateType.kNotConnected:
         return 'NotConnected';
     }
-    assertNotReached();
+    assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
     return '';
   }
 
   /**
+   * @param {string} value
+   * @return {!chromeos.networkConfig.mojom.ConnectionStateType}
+   */
+  static getConnectionStateTypeFromString(value) {
+    const ConnectionStateType =
+        chromeos.networkConfig.mojom.ConnectionStateType;
+    switch (value) {
+      case 'Online':
+        return ConnectionStateType.kOnline;
+      case 'Connected':
+        return ConnectionStateType.kConnected;
+      case 'Portal':
+        return ConnectionStateType.kPortal;
+      case 'Connecting':
+        return ConnectionStateType.kConnecting;
+      case 'NotConnected':
+        return ConnectionStateType.kNotConnected;
+    }
+    assertNotReached('Unexpected value: ' + value);
+    return ConnectionStateType.kNotConnected;
+  }
+
+  /**
+   * @param {!chromeos.networkConfig.mojom.ConnectionStateType} value
+   * @return {boolean}
+   */
+  static connectionStateIsConnected(value) {
+    const ConnectionStateType =
+        chromeos.networkConfig.mojom.ConnectionStateType;
+    switch (value) {
+      case ConnectionStateType.kOnline:
+      case ConnectionStateType.kConnected:
+      case ConnectionStateType.kPortal:
+        return true;
+      case ConnectionStateType.kConnecting:
+      case ConnectionStateType.kNotConnected:
+        return false;
+    }
+    assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
+    return false;
+  }
+
+  /**
    * @param {!chromeos.networkConfig.mojom.DeviceStateType} value
    * @return {string}
    */
@@ -78,11 +157,35 @@
       case DeviceStateType.kUnavailable:
         return 'Unavailable';
     }
-    assertNotReached();
+    assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
     return '';
   }
 
   /**
+   * @param {string} value
+   * @return {!chromeos.networkConfig.mojom.DeviceStateType}
+   */
+  static getDeviceStateTypeFromString(value) {
+    const DeviceStateType = chromeos.networkConfig.mojom.DeviceStateType;
+    switch (value) {
+      case 'Uninitialized':
+        return DeviceStateType.kUninitialized;
+      case 'Disabled':
+        return DeviceStateType.kDisabled;
+      case 'Enabling':
+        return DeviceStateType.kEnabling;
+      case 'Enabled':
+        return DeviceStateType.kEnabled;
+      case 'Prohibited':
+        return DeviceStateType.kProhibited;
+      case 'Unavailable':
+        return DeviceStateType.kUnavailable;
+    }
+    assertNotReached('Unexpected value: ' + value);
+    return DeviceStateType.kUnavailable;
+  }
+
+  /**
    * @param {!chromeos.networkConfig.mojom.NetworkType} value
    * @return {string}
    */
@@ -108,11 +211,34 @@
       case NetworkType.kWiMAX:
         return 'WiMAX';
     }
-    assertNotReached();
+    assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
     return '';
   }
 
   /**
+   * @param {!chromeos.networkConfig.mojom.NetworkType} value
+   * @return {boolean}
+   */
+  static networkTypeIsMobile(value) {
+    const NetworkType = chromeos.networkConfig.mojom.NetworkType;
+    switch (value) {
+      case NetworkType.kCellular:
+      case NetworkType.kMobile:
+      case NetworkType.kTether:
+        return true;
+      case NetworkType.kAll:
+      case NetworkType.kEthernet:
+      case NetworkType.kVPN:
+      case NetworkType.kWireless:
+      case NetworkType.kWiFi:
+      case NetworkType.kWiMAX:
+        return false;
+    }
+    assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
+    return false;
+  }
+
+  /**
    * @param {string} value
    * @return {!chromeos.networkConfig.mojom.NetworkType}
    */
@@ -138,33 +264,55 @@
       case 'WiMAX':
         return NetworkType.kWiMAX;
     }
-    assertNotReached();
+    assertNotReached('Unexpected value: ' + value);
     return NetworkType.kAll;
   }
 
   /**
-   * @param {!chromeos.networkConfig.mojom.ONCSource} value
+   * @param {!chromeos.networkConfig.mojom.OncSource} value
    * @return {string}
    */
-  static getONCSourceString(value) {
-    const ONCSource = chromeos.networkConfig.mojom.ONCSource;
+  static getOncSourceString(value) {
+    const OncSource = chromeos.networkConfig.mojom.OncSource;
     switch (value) {
-      case ONCSource.kUnknown:
-        return 'Unknown';
-      case ONCSource.kNone:
+      case OncSource.kNone:
         return 'None';
-      case ONCSource.kUserImport:
-        return 'UserImport';
-      case ONCSource.kDevicePolicy:
+      case OncSource.kDevice:
+        return 'Device';
+      case OncSource.kDevicePolicy:
         return 'DevicePolicy';
-      case ONCSource.kUserPolicy:
+      case OncSource.kUser:
+        return 'User';
+      case OncSource.kUserPolicy:
         return 'UserPolicy';
     }
-    assertNotReached();
+    assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
     return '';
   }
 
   /**
+   * @param {string} value
+   * @return {!chromeos.networkConfig.mojom.OncSource} value
+   */
+  static getOncSourceFromString(value) {
+    const OncSource = chromeos.networkConfig.mojom.OncSource;
+    switch (value) {
+      case CrOnc.Source.NONE:
+        return OncSource.kNone;
+      case CrOnc.Source.DEVICE:
+        return OncSource.kDevice;
+      case CrOnc.Source.DEVICE_POLICY:
+        return OncSource.kDevicePolicy;
+      case CrOnc.Source.USER:
+        return OncSource.kUser;
+      case CrOnc.Source.USER_POLICY:
+        return OncSource.kUserPolicy;
+    }
+    assertNotReached('Unexpected value: ' + value);
+    return OncSource.kNone;
+  }
+
+  /**
    * @param {!chromeos.networkConfig.mojom.SecurityType} value
    * @return {string}
    */
@@ -174,19 +322,61 @@
       case SecurityType.kNone:
         return 'None';
       case SecurityType.kWep8021x:
-        return 'Wep8021x';
+        return 'WEP-8021X';
       case SecurityType.kWepPsk:
-        return 'WepPsk';
+        return 'WEP-PSK';
       case SecurityType.kWpaEap:
-        return 'WpaEap';
+        return 'WPA-EAP';
       case SecurityType.kWpaPsk:
-        return 'WpaPsk';
+        return 'WPA-PSK';
     }
-    assertNotReached();
+    assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
     return '';
   }
 
   /**
+   * @param {string} value
+   * @return {!chromeos.networkConfig.mojom.SecurityType}
+   */
+  static getSecurityTypeFromString(value) {
+    const SecurityType = chromeos.networkConfig.mojom.SecurityType;
+    switch (value) {
+      case 'None':
+        return SecurityType.kNone;
+      case 'WEP-8021X':
+        return SecurityType.kWep8021x;
+      case 'WEP-PSK':
+        return SecurityType.kWepPsk;
+      case 'WPA-EAP':
+        return SecurityType.kWpaEap;
+      case 'WPA-PSK':
+        return SecurityType.kWpaPsk;
+    }
+    assertNotReached('Unexpected value: ' + value);
+    return SecurityType.kNone;
+  }
+
+  /**
+   * @param {string} value
+   * @return {!chromeos.networkConfig.mojom.VPNType}
+   */
+  static getVPNTypeFromString(value) {
+    const VPNType = chromeos.networkConfig.mojom.VPNType;
+    switch (value) {
+      case 'L2TP-IPsec':
+        return VPNType.kL2TPIPsec;
+      case 'OpenVPN':
+        return VPNType.kOpenVPN;
+      case 'ThirdPartyVPN':
+        return VPNType.kThirdPartyVPN;
+      case 'ARCVPN':
+        return VPNType.kArcVPN;
+    }
+    assertNotReached('Unexpected value: ' + value);
+    return VPNType.kOpenVPN;
+  }
+
+  /**
    * This infers the type from |key|, casts |value| (which should be a number)
    * to the corresponding enum type, and converts it to a string. If |key| is
    * known, then |value| is expected to match an enum value. Otherwise |value|
@@ -197,33 +387,244 @@
    */
   static getTypeString(key, value) {
     if (key == 'activationState') {
-      return this.getActivationStateTypeString(
+      return OncMojo.getActivationStateTypeString(
           /** @type {!chromeos.networkConfig.mojom.ActivationStateType} */ (
               value));
     }
     if (key == 'connectionState') {
-      return this.getConnectionStateTypeString(
+      return OncMojo.getConnectionStateTypeString(
           /** @type {!chromeos.networkConfig.mojom.ConnectionStateType} */ (
               value));
     }
     if (key == 'deviceState') {
-      return this.getDeviceStateTypeString(
+      return OncMojo.getDeviceStateTypeString(
           /** @type {!chromeos.networkConfig.mojom.DeviceStateType} */ (value));
     }
     if (key == 'type') {
-      return this.getNetworkTypeString(
+      return OncMojo.getNetworkTypeString(
           /** @type {!chromeos.networkConfig.mojom.NetworkType} */ (value));
     }
     if (key == 'source') {
-      return this.getONCSourceString(
-          /** @type {!chromeos.networkConfig.mojom.ONCSource} */ (value));
+      return OncMojo.getOncSourceString(
+          /** @type {!chromeos.networkConfig.mojom.OncSource} */ (value));
     }
     if (key == 'security') {
-      return this.getSecurityTypeString(
+      return OncMojo.getSecurityTypeString(
           /** @type {!chromeos.networkConfig.mojom.SecurityType} */ (value));
     }
     return value;
   }
+
+  /**
+   * @param {!chromeos.networkConfig.mojom.NetworkStateProperties} network
+   * @return {string}
+   */
+  static getNetworkDisplayName(network) {
+    if (!network.name) {
+      assert(CrOncStrings);
+      const typestr = 'OncType' + OncMojo.getNetworkTypeString(network.type);
+      return CrOncStrings[typestr];
+    }
+    if (network.type == chromeos.networkConfig.mojom.NetworkType.kVPN) {
+      const vpnType = network.vpn.type;
+      if (vpnType == chromeos.networkConfig.mojom.VPNType.kThirdPartyVPN) {
+        const providerName = network.vpn.providerName;
+        if (providerName) {
+          assert(CrOncStrings);
+          return CrOncStrings.vpnNameTemplate.replace('$1', providerName)
+              .replace('$2', network.name);
+        }
+      }
+    }
+    return network.name;
+  }
+
+  /**
+   * Gets the SignalStrength value from |network| based on network.type.
+   * @param {!chromeos.networkConfig.mojom.NetworkStateProperties} network
+   * @return {number} The signal strength value if it exists or 0.
+   */
+  static getSignalStrength(network) {
+    const NetworkType = chromeos.networkConfig.mojom.NetworkType;
+    switch (network.type) {
+      case NetworkType.kCellular:
+        return network.cellular.signalStrength;
+      case NetworkType.kTether:
+        return network.tether.signalStrength;
+      case NetworkType.kWiFi:
+        return network.wifi.signalStrength;
+      case NetworkType.kWiMAX:
+        return network.wimax.signalStrength;
+    }
+    assertNotReached();
+    return 0;
+  }
+
+  /**
+   * Returns a NetworkStateProperties object with type set and default values.
+   * @param {!chromeos.networkConfig.mojom.NetworkType} type
+   * @return {chromeos.networkConfig.mojom.NetworkStateProperties}
+   */
+  static getDefaultNetworkState(type) {
+    const mojom = chromeos.networkConfig.mojom;
+    const result = {
+      connectable: false,
+      connectRequested: false,
+      connectionState: mojom.ConnectionStateType.kNotConnected,
+      guid: '',
+      name: '',
+      priority: 0,
+      proxyMode: mojom.ProxyMode.kDirect,
+      prohibitedByPolicy: false,
+      source: mojom.OncSource.kNone,
+      type: type,
+    };
+    switch (type) {
+      case mojom.NetworkType.kCellular:
+        result.cellular = {
+          activationState: mojom.ActivationStateType.kUnknown,
+          networkTechnology: '',
+          roaming: false,
+          signalStrength: 0,
+        };
+        break;
+      case mojom.NetworkType.kEthernet:
+        result.ethernet = {
+          authentication: mojom.AuthenticationType.kNone,
+        };
+        break;
+      case mojom.NetworkType.kTether:
+        result.tether = {
+          batteryPercentage: 0,
+          carrier: '',
+          hasConnectedToHost: false,
+          signalStrength: 0,
+        };
+        break;
+      case mojom.NetworkType.kVPN:
+        result.vpn = {
+          type: mojom.VPNType.kOpenVPN,
+          providerId: '',
+          providerName: '',
+        };
+        break;
+      case mojom.NetworkType.kWiFi:
+        result.wifi = {
+          bssid: '',
+          frequency: 0,
+          hexSsid: '',
+          security: mojom.SecurityType.kNone,
+          signalStrength: 0,
+          ssid: '',
+        };
+        break;
+      case mojom.NetworkType.kWiMAX:
+        result.wimax = {
+          signalStrength: 0,
+        };
+        break;
+    }
+    return result;
+  }
+
+  /**
+   * Converts an ONC dictionary to NetworkStateProperties. See onc_spec.md
+   * for the dictionary spec.
+   * @param {!CrOnc.NetworkProperties} properties
+   * @return {chromeos.networkConfig.mojom.NetworkStateProperties}
+   */
+  static oncPropertiesToNetworkState(properties) {
+    const mojom = chromeos.networkConfig.mojom;
+    const networkState = OncMojo.getDefaultNetworkState(
+        OncMojo.getNetworkTypeFromString(properties.Type));
+    networkState.connectable = !!properties.Connectable;
+    if (properties.ConnectionState) {
+      networkState.connectionState =
+          OncMojo.getConnectionStateTypeFromString(properties.ConnectionState);
+    }
+    networkState.guid = properties.GUID;
+    networkState.name = CrOnc.getStateOrActiveString(properties.Name);
+    if (properties.Priority) {
+      const priority = /** @type {number|undefined} */ (
+          CrOnc.getActiveValue(properties.Priority));
+      if (priority !== undefined) {
+        networkState.priority = priority;
+      }
+    }
+    if (properties.Source) {
+      networkState.source = OncMojo.getOncSourceFromString(properties.Source);
+    }
+
+    switch (networkState.type) {
+      case mojom.NetworkType.kCellular:
+        if (properties.Cellular) {
+          networkState.cellular.activationState =
+              OncMojo.getActivationStateTypeFromString(
+                  properties.Cellular.ActivationState || 'Unknown');
+          networkState.cellular.networkTechnology =
+              properties.Cellular.NetworkTechnology || '';
+          networkState.cellular.roaming =
+              properties.Cellular.RoamingState == CrOnc.RoamingState.ROAMING;
+          networkState.cellular.signalStrength =
+              properties.Cellular.SignalStrength || 0;
+        }
+        break;
+      case mojom.NetworkType.kEthernet:
+        if (properties.Ethernet) {
+          networkState.ethernet.authentication =
+              properties.Ethernet.Authentication ==
+                  CrOnc.Authentication.WEP_8021X ?
+              mojom.AuthenticationType.k8021x :
+              mojom.AuthenticationType.kNone;
+        }
+        break;
+      case mojom.NetworkType.kTether:
+        if (properties.Tether) {
+          networkState.tether.batteryPercentage =
+              properties.Tether.BatteryPercentage || 0;
+          networkState.tether.carrier =
+              properties.Tether.NetworkTechnology || '';
+          networkState.tether.hasConnectedToHost =
+              properties.Tether.HasConnectedToHost;
+          networkState.tether.signalStrength =
+              properties.Tether.SignalStrength || 0;
+        }
+        break;
+      case mojom.NetworkType.kVPN:
+        if (properties.VPN) {
+          networkState.vpn.providerName =
+              (properties.VPN.ThirdPartyVPN &&
+               properties.VPN.ThirdPartyVPN.ProviderName) ||
+              '';
+          networkState.vpn.vpnType = OncMojo.getVPNTypeFromString(
+              CrOnc.getStateOrActiveString(properties.VPN.Type) ||
+              CrOnc.VPNType.OPEN_VPN);
+        }
+        break;
+      case mojom.NetworkType.kWiFi:
+        if (properties.WiFi) {
+          networkState.wifi.bssid = properties.WiFi.BSSID || '';
+          networkState.wifi.frequency = properties.WiFi.Frequency || 0;
+          networkState.wifi.hexSsid =
+              CrOnc.getStateOrActiveString(properties.WiFi.HexSSID);
+          networkState.wifi.security = OncMojo.getSecurityTypeFromString(
+              CrOnc.getStateOrActiveString(properties.WiFi.Security) ||
+              CrOnc.Security.NONE);
+          networkState.wifi.signalStrength =
+              properties.WiFi.SignalStrength || 0;
+          networkState.wifi.ssid =
+              CrOnc.getStateOrActiveString(properties.WiFi.SSID);
+        }
+        break;
+      case mojom.NetworkType.kWiMAX:
+        if (properties.WiMAX) {
+          networkState.wimax.signalStrength =
+              properties.WiMAX.SignalStrength || 0;
+        }
+        break;
+    }
+    return networkState;
+  }
 }
 
 /** @typedef {chromeos.networkConfig.mojom.DeviceStateProperties} */
diff --git a/ui/webui/resources/js/icon.js b/ui/webui/resources/js/icon.js
index eac95d4..4954ac9f7 100644
--- a/ui/webui/resources/js/icon.js
+++ b/ui/webui/resources/js/icon.js
@@ -55,7 +55,7 @@
   function getImageSet(path) {
     const supportedScaleFactors = getSupportedScaleFactors();
 
-    const replaceStartIndex = path.indexOf('scalefactor');
+    const replaceStartIndex = path.indexOf('SCALEFACTOR');
     if (replaceStartIndex < 0) {
       return getUrlForCss(path);
     }
@@ -86,7 +86,7 @@
     const chromeThemePath = 'chrome://theme';
     const isChromeThemeUrl =
         (path.slice(0, chromeThemePath.length) == chromeThemePath);
-    return isChromeThemeUrl ? getImageSet(path + '@scalefactorx') :
+    return isChromeThemeUrl ? getImageSet(path + '@SCALEFACTORx') :
                               getUrlForCss(path);
   }
 
@@ -104,11 +104,12 @@
    * @return {string} -webkit-image-set for the favicon.
    */
   function getFavicon(url) {
+    // Note: Literal strings used below must match those in the description of
+    // chrome://favicon2 format in components/favicon_base/favicon_url_parser.h.
     return getImageSet(
-        'chrome://favicon/size/16@scalefactorx/' +
-        // Note: Literal 'iconurl' must match |kIconURLParameter| in
-        // components/favicon_base/favicon_url_parser.cc.
-        (FAVICON_URL_REGEX.test(url) ? 'iconurl/' : '') + url);
+        'chrome://favicon2/?size=16&scale_factor=SCALEFACTORx' +
+        '&url_type=' + (FAVICON_URL_REGEX.test(url) ? 'icon_url' : 'page_url') +
+        '&url=' + encodeURIComponent(url));
   }
 
   return {
diff --git a/ui/wm/core/window_properties.cc b/ui/wm/core/window_properties.cc
index aa0cc88d..506dd0c0 100644
--- a/ui/wm/core/window_properties.cc
+++ b/ui/wm/core/window_properties.cc
@@ -13,7 +13,6 @@
 
 namespace wm {
 
-DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSnapChildrenToPixelBoundary, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kUsesScreenCoordinatesKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(base::TimeDelta,
                              kWindowVisibilityAnimationDurationKey,
diff --git a/ui/wm/core/window_properties.h b/ui/wm/core/window_properties.h
index ec226806..8fd2fb5 100644
--- a/ui/wm/core/window_properties.h
+++ b/ui/wm/core/window_properties.h
@@ -21,11 +21,6 @@
 
 // Alphabetical sort.
 
-// Containers with this property (true) are aligned with physical pixel
-// boundary.
-WM_CORE_EXPORT extern const ui::ClassProperty<bool>* const
-    kSnapChildrenToPixelBoundary;
-
 // Property to tell if the container uses screen coordinates for the child
 // windows.
 WM_CORE_EXPORT extern const ui::ClassProperty<bool>* const
diff --git a/ui/wm/core/window_util.cc b/ui/wm/core/window_util.cc
index 6ce3536c5..e9edfc93 100644
--- a/ui/wm/core/window_util.cc
+++ b/ui/wm/core/window_util.cc
@@ -221,35 +221,4 @@
       HasTransientAncestor(transient_parent, ancestor) : false;
 }
 
-void SnapWindowToPixelBoundary(aura::Window* window) {
-  // TODO(malaykeshav): We want to snap each window layer to its parent window
-  // layer. See https://crbug.com/863268 for more info.
-
-  // Root window is already snapped by default.
-  if (window->IsRootWindow()) {
-    window->SetProperty(wm::kSnapChildrenToPixelBoundary, true);
-    return;
-  }
-
-  aura::Window* ancestor_window = window->parent();
-  while (ancestor_window) {
-    bool is_ancestor_window_snapped =
-        ancestor_window->GetProperty(wm::kSnapChildrenToPixelBoundary);
-
-    // Root windows are already snapped by default. Just mark them as snapped.
-    if (ancestor_window->IsRootWindow() && !is_ancestor_window_snapped) {
-      ancestor_window->SetProperty(wm::kSnapChildrenToPixelBoundary, true);
-      is_ancestor_window_snapped = true;
-    }
-
-    if (is_ancestor_window_snapped) {
-      window->SetProperty(wm::kSnapChildrenToPixelBoundary, true);
-      ui::SnapLayerToPhysicalPixelBoundary(ancestor_window->layer(),
-                                           window->layer());
-      return;
-    }
-    ancestor_window = ancestor_window->parent();
-  }
-}
-
 }  // namespace wm
diff --git a/ui/wm/core/window_util_unittest.cc b/ui/wm/core/window_util_unittest.cc
index c9e87e0..ea4268e 100644
--- a/ui/wm/core/window_util_unittest.cc
+++ b/ui/wm/core/window_util_unittest.cc
@@ -100,22 +100,4 @@
   EXPECT_EQ(window12->layer(), window1->layer()->children()[1]);
 }
 
-// Test if the root window is always snapped.
-TEST_F(WindowUtilTest, CheckRootWindowAlwaysSnapped) {
-  std::unique_ptr<aura::Window> window11(
-      aura::test::CreateTestWindowWithId(1, root_window()));
-  std::unique_ptr<aura::Window> window12(
-      aura::test::CreateTestWindowWithId(2, root_window()));
-
-  EXPECT_TRUE(root_window()->IsRootWindow());
-
-  wm::SnapWindowToPixelBoundary(window12.get());
-
-  // Root window is always marked as snapped.
-  EXPECT_TRUE(root_window()->GetProperty(wm::kSnapChildrenToPixelBoundary));
-
-  EXPECT_TRUE(window12->GetProperty(wm::kSnapChildrenToPixelBoundary));
-  EXPECT_FALSE(window11->GetProperty(wm::kSnapChildrenToPixelBoundary));
-}
-
 }  // namespace wm
diff --git a/url/url_constants.cc b/url/url_constants.cc
index 38f86bbb..e39b8c5 100644
--- a/url/url_constants.cc
+++ b/url/url_constants.cc
@@ -7,6 +7,7 @@
 namespace url {
 
 const char kAboutBlankURL[] = "about:blank";
+const char kAboutSrcdocURL[] = "about:srcdoc";
 
 const char kAboutBlankPath[] = "blank";
 const char kAboutSrcdocPath[] = "srcdoc";
diff --git a/url/url_constants.h b/url/url_constants.h
index 7f322f89..15f0190 100644
--- a/url/url_constants.h
+++ b/url/url_constants.h
@@ -12,6 +12,7 @@
 namespace url {
 
 COMPONENT_EXPORT(URL) extern const char kAboutBlankURL[];
+COMPONENT_EXPORT(URL) extern const char kAboutSrcdocURL[];
 
 COMPONENT_EXPORT(URL) extern const char kAboutBlankPath[];
 COMPONENT_EXPORT(URL) extern const char kAboutSrcdocPath[];